16 KiB
智能深度研究系统 - 执行过程详细分析
基于: llm_calls_20251031_150543.json
测试问题: "Python asyncio最佳实践"
深度模式: quick
总LLM调用次数: 5次
总耗时: 49.49秒
整体架构回顾
┌─────────────────────────────────────────────────────────────┐
│ LangGraph 执行引擎 │
│ (持续调用Agent直到任务完成或无工具调用) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ResearchCoordinator (主Agent) │
│ - 协调整个研究流程 │
│ - 通过虚拟文件系统管理状态 │
│ - 使用task工具调用SubAgent │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 虚拟文件系统 (State) │
│ - /question.txt │
│ - /config.json │
│ - /search_queries.json │
│ - /iteration_N/*.json │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6个SubAgent (通过task调用) │
│ 1. intent-analyzer 4. content-analyzer │
│ 2. search-orchestrator 5. confidence-evaluator │
│ 3. source-validator 6. report-generator │
└─────────────────────────────────────────────────────────────┘
详细执行流程分析
🔵 LLM调用 #1 - 主Agent初始化 (步骤1)
时间: 15:04:53 → 15:05:01 (8秒) 执行者: ResearchCoordinator (主Agent) Token消耗: 输入5,920 + 输出103 = 6,023 tokens
输入
[SystemMessage] 完整的系统提示词
- 研究问题: "Python asyncio最佳实践"
- 7步执行流程说明
- 重要提示: write_file和task不能在同一个响应中调用
[HumanMessage] "请开始研究这个问题:Python asyncio最佳实践"
主Agent的思考过程
根据系统提示词,主Agent理解到:
- 需要先执行步骤1:初始化虚拟文件系统
- 写入
/question.txt和/config.json - 不能在同一响应中调用task工具
输出
{
"tool_calls": [
{
"name": "write_file",
"file_path": "/question.txt",
"content": "Python asyncio最佳实践"
},
{
"name": "write_file",
"file_path": "/config.json",
"content": "{\"depth_mode\": \"quick\", \"report_format\": \"technical\", ...}"
}
]
}
关键点
- ✅ 主Agent 只调用了write_file,没有调用task
- ✅ 两个文件被并行写入
- 🔄 LangGraph看到有工具调用,会继续执行下一轮
State更新
虚拟文件系统:
├── /question.txt ✅ (新增)
└── /config.json ✅ (新增)
🔵 LLM调用 #2 - 主Agent启动SubAgent (步骤2)
时间: 15:05:01 → 15:05:06 (4.5秒) 执行者: ResearchCoordinator (主Agent) Token消耗: 输入6,046 + 输出69 = 6,115 tokens
输入
[SystemMessage] 完整的系统提示词 (相同)
[HumanMessage] "请开始研究这个问题:Python asyncio最佳实践"
[AIMessage] 之前的响应 (write_file × 2)
[ToolMessage] "Updated file /question.txt" ✅
[ToolMessage] "Updated file /config.json" ✅
主Agent的思考过程
主Agent看到:
- 两个文件已成功写入(ToolMessage确认)
- 步骤1已完成,应该进入步骤2
- 现在可以调用task工具启动intent-analyzer
输出
{
"content": "已将研究问题和配置写入虚拟文件系统。现在,我将调用 `intent-analyzer` SubAgent 来分析研究问题并生成搜索查询。",
"tool_calls": [
{
"name": "task",
"args": {
"description": "分析研究问题并生成搜索查询",
"subagent_type": "intent-analyzer"
}
}
]
}
关键点
- ✅ 主Agent在独立的响应中调用task
- ✅ 此时虚拟文件系统已包含两个文件
- 🚀 task工具触发SubAgent创建和执行
🔵 LLM调用 #3 - SubAgent读取文件
时间: 15:05:06 → 15:05:08 (2.7秒) 执行者: intent-analyzer (SubAgent) Token消耗: 输入2,724 + 输出49 = 2,773 tokens
输入
[SystemMessage] intent-analyzer的专用系统提示词
- 任务: 分析问题并生成搜索查询
- 需要读取: /question.txt, /config.json
- 需要输出: /search_queries.json
[HumanMessage] "分析研究问题并生成搜索查询"
SubAgent的思考过程
SubAgent按照提示词:
- 需要先读取输入文件
- 使用ls查看文件系统结构
- 使用read_file读取两个文件
输出
{
"tool_calls": [
{"name": "ls"},
{"name": "read_file", "file_path": "/question.txt"},
{"name": "read_file", "file_path": "/config.json"}
]
}
关键机制解析
🔑 核心问题:SubAgent如何访问主Agent写入的文件?
根据DeepAgents源码 (deepagents/middleware/subagents.py:332):
def _validate_and_prepare_state(subagent_type, description, runtime):
# 创建SubAgent的state,从主Agent的state复制
subagent_state = {
k: v for k, v in runtime.state.items()
if k not in ("messages", "todos") # 排除这两个键
}
subagent_state["messages"] = [HumanMessage(content=description)]
return subagent, subagent_state
执行时序:
1. 主Agent调用 #1: write_file × 2
→ 返回 Command(update={"files": {...}})
2. LangGraph应用State更新
→ runtime.state["files"] = {
"/question.txt": {...},
"/config.json": {...}
}
3. 主Agent调用 #2: task(...)
→ task工具内部调用 _validate_and_prepare_state()
→ 复制 runtime.state (包括files) 给SubAgent
4. SubAgent启动时,state中已包含这两个文件 ✅
这就是为什么必须分两个响应:
- ❌ 如果在同一响应中调用write_file和task,两个工具会并行执行
- ❌ 当task开始复制state时,write_file的更新还没被应用
- ✅ 分两个响应后,第二次调用时state已包含更新
🔵 LLM调用 #4 - SubAgent分析并生成查询
时间: 15:05:08 → 15:05:28 (19.4秒) 执行者: intent-analyzer (SubAgent) Token消耗: 输入2,861 + 输出404 = 3,265 tokens
输入
[SystemMessage] intent-analyzer的系统提示词
[HumanMessage] "分析研究问题并生成搜索查询"
[AIMessage] 之前的工具调用 (ls, read_file × 2)
[ToolMessage] "[\"/question.txt\", \"/config.json\"]" ✅
[ToolMessage] " 1\tPython asyncio最佳实践" ✅
[ToolMessage] " 1\t{\"depth_mode\": \"quick\", ...}" ✅
SubAgent的思考过程
SubAgent成功读取到文件内容:
- 问题: "Python asyncio最佳实践"
- 配置: quick模式 → 生成3个查询
- 领域分析: 技术(编程)
- 查询策略: 基础概念 + 最佳实践 + 实现细节
输出
{
"content": "已经读取了原始研究问题和配置文件。研究问题是关于...",
"tool_calls": [
{
"name": "write_file",
"file_path": "/search_queries.json",
"content": "{\n \"original_question\": \"Python asyncio最佳实践\",\n \"queries\": [\n {\"query\": \"What is Python asyncio and how does it work?\", ...},\n {\"query\": \"Best practices for using Python asyncio\", ...},\n {\"query\": \"How to implement best practices with Python asyncio in real projects\", ...}\n ]\n}"
}
]
}
生成的3个搜索查询
- 基础概念: "What is Python asyncio and how does it work?"
- 最佳实践: "Best practices for using Python asyncio"
- 实现细节: "How to implement best practices with Python asyncio in real projects"
State更新
虚拟文件系统:
├── /question.txt
├── /config.json
└── /search_queries.json ✅ (新增,SubAgent写入)
🔵 LLM调用 #5 - SubAgent完成并返回
时间: 15:05:28 → 15:05:43 (14.6秒) 执行者: intent-analyzer (SubAgent) Token消耗: 输入3,278 + 输出241 = 3,519 tokens
输入
[SystemMessage] intent-analyzer的系统提示词
[HumanMessage] "分析研究问题并生成搜索查询"
[之前的所有消息和工具调用...]
[ToolMessage] "Updated file /search_queries.json" ✅
SubAgent的思考过程
SubAgent看到:
- 文件已成功写入
- 任务已完成(生成了3个查询)
- 可以总结并返回结果
输出
{
"content": "搜索查询已成功生成并写入`/search_queries.json`文件中。以下是生成的搜索查询:\n\n- **原始问题**: Python asyncio最佳实践\n- **领域**: 技术(编程)\n...",
"tool_calls": [] // ← 空数组,没有更多工具调用
}
关键点
- ✅ SubAgent没有工具调用(tool_calls为空)
- 🔄 LangGraph检测到SubAgent完成,返回结果给主Agent
- 📦 SubAgent的state更新(包括/search_queries.json)合并回主Agent
SubAgent返回机制
根据deepagents/middleware/subagents.py:316-323:
def _return_command_with_state_update(result, tool_call_id):
# 排除messages和todos,其他state都返回给主Agent
state_update = {
k: v for k, v in result.items()
if k not in ("messages", "todos")
}
return Command(update={
**state_update, # files会被合并
"messages": [ToolMessage(result["messages"][-1].text, tool_call_id)]
})
最终State
虚拟文件系统 (主Agent):
├── /question.txt (主Agent写入)
├── /config.json (主Agent写入)
└── /search_queries.json (SubAgent写入,已合并) ✅
执行流程图
sequenceDiagram
participant User
participant LangGraph
participant 主Agent
participant State as 虚拟文件系统
participant SubAgent as intent-analyzer
User->>LangGraph: "研究: Python asyncio最佳实践"
Note over LangGraph,主Agent: 🔵 LLM调用 #1 (8秒)
LangGraph->>主Agent: SystemMessage + HumanMessage
主Agent->>主Agent: 理解: 需执行步骤1 - 初始化
主Agent->>State: write_file(/question.txt)
主Agent->>State: write_file(/config.json)
State-->>主Agent: ToolMessage × 2
Note over LangGraph,State: State更新: files包含2个文件
Note over LangGraph,主Agent: 🔵 LLM调用 #2 (4.5秒)
LangGraph->>主Agent: 之前的消息 + ToolMessage
主Agent->>主Agent: 理解: 步骤1完成,进入步骤2
主Agent->>LangGraph: task(intent-analyzer)
Note over LangGraph,SubAgent: task工具复制state给SubAgent
LangGraph->>SubAgent: 创建SubAgent (state包含2个文件)
Note over LangGraph,SubAgent: 🔵 LLM调用 #3 (2.7秒)
LangGraph->>SubAgent: SystemMessage + HumanMessage
SubAgent->>SubAgent: 理解: 需读取输入文件
SubAgent->>State: ls()
SubAgent->>State: read_file(/question.txt)
SubAgent->>State: read_file(/config.json)
State-->>SubAgent: ToolMessage × 3 ✅ 文件存在!
Note over LangGraph,SubAgent: 🔵 LLM调用 #4 (19.4秒)
LangGraph->>SubAgent: 之前的消息 + ToolMessage
SubAgent->>SubAgent: 分析问题,生成3个查询
SubAgent->>State: write_file(/search_queries.json)
State-->>SubAgent: ToolMessage
Note over LangGraph,SubAgent: 🔵 LLM调用 #5 (14.6秒)
LangGraph->>SubAgent: 之前的消息 + ToolMessage
SubAgent->>SubAgent: 理解: 任务完成
SubAgent-->>LangGraph: 无工具调用 (完成)
Note over LangGraph,State: SubAgent state合并回主Agent
LangGraph->>主Agent: ToolMessage (SubAgent结果)
Note over 主Agent: 继续步骤3...
主Agent-->>User: (测试在此停止)
Token消耗分析
| 调用 | 执行者 | 输入Token | 输出Token | 总计 | 占比 |
|---|---|---|---|---|---|
| #1 | 主Agent | 5,920 | 103 | 6,023 | 31.2% |
| #2 | 主Agent | 6,046 | 69 | 6,115 | 31.7% |
| #3 | SubAgent | 2,724 | 49 | 2,773 | 14.4% |
| #4 | SubAgent | 2,861 | 404 | 3,265 | 16.9% |
| #5 | SubAgent | 3,278 | 241 | 3,519 | 18.2% |
| 总计 | 20,829 | 866 | 19,295 | 100% |
关键观察:
- 主Agent的Token消耗主要在系统提示词(非常详细)
- SubAgent的输入Token较少(专用提示词更简洁)
- 输出Token主要用于JSON生成(调用#4)
关键技术要点总结
✅ 成功解决的问题
-
虚拟文件系统共享
- SubAgent能成功读取主Agent写入的文件
- 通过state复制机制实现
-
工具调用顺序
- write_file在第一个响应
- task在第二个响应
- 确保state更新已应用
-
SubAgent生命周期
- 创建 → 接收任务描述
- 执行 → 读取文件、处理、写入结果
- 返回 → state合并回主Agent
🎯 设计亮点
-
声明式流程控制
- 通过系统提示词定义流程
- 不使用Python while循环
- LLM自主决策下一步
-
文件驱动的状态管理
- 所有状态通过虚拟文件系统
- 跨Agent通信通过文件
- 易于调试和追踪
-
降级运行策略
- 部分失败不影响整体
- 提示词中明确说明
后续步骤预测
如果测试继续运行,预期流程:
✅ 步骤1: 初始化 (已完成)
✅ 步骤2: 意图分析 (已完成)
⏭️ 步骤3.1: 并行搜索
- 主Agent调用search-orchestrator
- 使用Tavily API搜索3个查询
- 写入/iteration_1/search_results.json
⏭️ 步骤3.2: 来源验证
- 主Agent调用source-validator
- Tier 1-4分级
- 写入/iteration_1/sources.json
⏭️ 步骤3.3: 内容分析
- 主Agent调用content-analyzer
- 提取信息,交叉验证
- 写入/iteration_1/findings.json
⏭️ 步骤3.4: 置信度评估
- 主Agent调用confidence-evaluator
- 计算置信度 (50%+30%+20%)
- 写入/iteration_decision.json
- 决策: FINISH 或 CONTINUE
⏭️ 步骤7: 报告生成
- 主Agent调用report-generator
- 读取所有iteration数据
- 写入/final_report.md
性能优化建议
基于当前执行情况:
-
系统提示词优化
- 主Agent的提示词非常长(5,920 tokens)
- 可以精简部分重复说明
- 预期节省 ~20% Token
-
并行SubAgent调用
- 当前是串行:步骤3.1 → 3.2 → 3.3
- 某些步骤可以并行(如果依赖允许)
- 预期减少 30-40% 时间
-
缓存机制
- 相同问题的搜索结果可缓存
- 减少API调用次数
总结
✅ 测试成功证明:
- 虚拟文件系统在主Agent和SubAgent之间正确共享
- 工具调用顺序控制有效
- 基于提示词的流程控制可行
🎯 下一步工作:
- 完成剩余SubAgent的测试
- 实现完整的端到端流程
- 添加错误处理和降级策略
- 性能优化
📊 当前进度: 2/7步 (28.6%)
- ✅ 步骤1: 初始化
- ✅ 步骤2: 意图分析
- ⏳ 步骤3-7: 待实现
生成时间: 2025-10-31
测试数据: llm_calls_20251031_150543.json