Thinking / 反思在 Agent 循环中的集成
Thinking / 反思在 Agent 循环中的集成
学习目标
- 理解 ChatDev 的 ThinkingPayload 容器与两阶段思考管线
- 掌握 Self-Reflection 的 After-Gen 反思实现
- 了解思考结果如何注入 Agent 对话上下文
- 能够评估何时启用思考与何时跳过以节省 Token
前置知识
本章涉及 Agent 核心循环的通用原理,建议先阅读:
下文聚焦 ChatDev 的具体思考管线实现。
项目实践
ThinkingPayload 容器
ChatDev 使用 ThinkingPayload 作为多模态上下文的传递容器:
ThinkingPayload: - text: 文本内容 - blocks: 多模态消息块列表 - metadata: 元数据字典 - raw: 任意原始数据这个容器贯穿思考管线的三个阶段,确保多模态上下文在思考过程中不丢失。
两阶段思考管线
ChatDev 的 ThinkingManagerBase 定义了抽象的两阶段思考接口:
Before-Gen Thinking(生成前思考)
- 在调用模型 API 之前执行
- 可对输入进行预处理、分析、规划
- 当前 ChatDev 内置实现未启用 Before-Gen
- 扩展点:
_before_gen_think()抽象方法
After-Gen Thinking(生成后思考)— Self-Reflection
ChatDev 内置的 Self-Reflection 是 After-Gen 的具体实现:
class SelfReflectionThinkingManager(ThinkingManagerBase): def _after_gen_think(self, agent_invoker, input_payload, agent_role, memory, gen_payload): conversations = [ f"SYSTEM: {agent_role}", f"USER: {input_payload.text}", f"ASSISTANT: {gen_payload.text}", ] if memory and memory.text: conversations = [memory.text] + conversations
prompt = self.base_prompt.format( conversations="\n\n".join(conversations), reflection_prompt=self.reflection_prompt )
reflection_message = agent_invoker( [Message(role=MessageRole.USER, content=prompt)] ) return reflection_message.text_content(), True # True = 替换原始输出反射过程:
原始对话: [System Role] + [User Input] + [Assistant Output] ↓反思 Prompt: "Here is a conversation... {reflection_prompt}" ↓二次模型调用 → 反思结果 ↓替换原始输出 (replace=True)YAML 中的 Thinking 配置
- id: Reviewer type: agent config: thinking: type: reflection config: reflection_prompt: "Review the generated code for bugs and security issues."当 thinking 为 null 时不启用思考:
thinking: # null → 不启用,节省 Token思考管线的执行时序
在 AgentNodeExecutor 的 execute() 方法中,思考管线的集成点:
1. 构建输入 (input_data, input_payload)2. ┌─ Before-Gen Thinking(可选)3. │ _apply_pre_generation_thinking()4. └─ 可能修改 conversation5. 记忆检索 _apply_memory_retrieval()6. 调用模型 _invoke_provider()7. ┌─ After-Gen Thinking(可选)8. │ _apply_post_generation_thinking()9. └─ 可能替换最终输出10. 工具调用循环(如有工具)11. 最终输出问题与规避
| 问题 | 表现 | 规避策略 |
|---|---|---|
| 反思结果质量差 | 模型反思偏离主题 | 设计精确的 reflection_prompt |
| Token 消耗倍增 | 每次 Agent 调用后额外一次 LLM 调用 | 仅在关键节点(Reviewer、Quality Inspector)启用 |
| 思考替换原始输出的风险 | replace=True 可能丢失有用信息 | 在反思结果中保留原始输出的关键部分 |
| Memory 注入导致上下文膨胀 | 历史记忆 + 对话 = 超长 prompt | 限制记忆检索的 top_k 和 similarity_threshold |
| 多级思考嵌套 | 思考结果再次触发思考 | ThinkingManager 不允许递归 |
设计取舍
内置 Reflection vs 外部反思 Agent
| 维度 | 内置 Reflection | 外部反思 Agent |
|---|---|---|
| 实现复杂度 | 低(复用同一 Provider) | 高(需定义独立节点) |
| 灵活性 | 低(固定管线) | 高(可自定义完整 Agent) |
| Token 效率 | 较高(共享上下文) | 较低(独立上下文) |
| ChatDev 的选择 | ✅ 内置为默认 | 可用独立节点实现 |
两阶段 vs 单阶段
ChatDev 设计了两个思考阶段但仅启用了 After-Gen:
- Before-Gen 的价值:在生成前进行分析规划(适合复杂任务分解)
- After-Gen 的价值:对生成结果进行质量检查(适合代码审查、事实核查)
- 仅启用 After-Gen 的原因:大部分场景的反思价值在于质量提升而非输入改造