489 lines
13 KiB
Markdown
489 lines
13 KiB
Markdown
# identify 阶段多智能体数据流转详解
|
||||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本文档详细分析 identify 阶段从用户输入到最终输出的 10 个 Agent 数据流转,包括每个 Agent 的输入、输出、数据类型,以及错误处理机制。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 用户输入 -> Router Agent
|
|||
|
|
|
|||
|
|
### 输入数据
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"query": "鉴定Ft.SPL基因家族",
|
|||
|
|
"user_input": {
|
|||
|
|
"prefix": "Ft.SPL",
|
|||
|
|
"stage": "identify",
|
|||
|
|
"species_name": "Fagopyrum tataricum"
|
|||
|
|
},
|
|||
|
|
"job_id": "job_xxx"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Router Agent 处理
|
|||
|
|
1. 调用 `RouterAgent.classify()` 判断模式: `analysis`
|
|||
|
|
2. 如果是单 stage 模式(`user_input.stage` 存在),直接设置 intent:
|
|||
|
|
- `stages = ["identify"]`
|
|||
|
|
- `prefix = "Ft.SPL"`
|
|||
|
|
3. 否则调用 `IntentAgentV2` 解析意图
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["mode"] = "analysis"
|
|||
|
|
state["intent_result"] = {
|
|||
|
|
"mode": "analysis",
|
|||
|
|
"stages": ["identify"],
|
|||
|
|
"prefix": "Ft.SPL",
|
|||
|
|
"single_stage_mode": True,
|
|||
|
|
"job_id": "job_xxx",
|
|||
|
|
"workspace": "/path/to/workspace/job_xxx"
|
|||
|
|
}
|
|||
|
|
state["next_node"] = "learning_recommend"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Router -> Learning Recommend Agent
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
intent = state.get("intent_result", {})
|
|||
|
|
target_stages = intent.get("stages", ["identify"]) # ["identify"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Learning Recommend Agent 处理
|
|||
|
|
- 查询历史执行记录,获取参数推荐
|
|||
|
|
- 如果有高置信度历史记录,返回推荐参数
|
|||
|
|
- 否则返回默认参数
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["learning_recommendations"] = {
|
|||
|
|
"confidence": "high", # 或 "medium" / "low"
|
|||
|
|
"params": {
|
|||
|
|
"evalue": "1e-5",
|
|||
|
|
"coverage": 0.5
|
|||
|
|
},
|
|||
|
|
"message": "基于历史执行推荐参数"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Learning Recommend -> Planning Agent
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
state["intent_result"] # 包含 stages, prefix 等
|
|||
|
|
state["learning_recommendations"] # 参数推荐
|
|||
|
|
state["completed_stages"] # 已完成的阶段(从 manifest 查询)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Planning Agent 处理
|
|||
|
|
1. 重新查询 `completed_stages`(从文件系统 manifest)
|
|||
|
|
2. 根据 intent 和 learning_recommendations 制定执行计划
|
|||
|
|
3. 设置 stages_to_run = ["identify"](排除已完成的)
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["plan_result"] = {
|
|||
|
|
"stages": [
|
|||
|
|
{
|
|||
|
|
"id": "identify",
|
|||
|
|
"name": "基因鉴定",
|
|||
|
|
"params": {
|
|||
|
|
"evalue": "1e-5",
|
|||
|
|
"coverage": 0.5
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"prefix": "Ft.SPL",
|
|||
|
|
"workspace": "/path/to/workspace/job_xxx",
|
|||
|
|
"job_id": "job_xxx"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Planning -> PreFlight Agent
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
plan = state.get("plan_result", {})
|
|||
|
|
job_id = state.get("job_id", "")
|
|||
|
|
prefix = intent.get("prefix", "Ft.SPL")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### PreFlight Agent 处理
|
|||
|
|
1. 检查输入文件是否存在:
|
|||
|
|
- `input/{prefix}.pep.fa`(参考序列)
|
|||
|
|
- `input/{species_prefix}.pep.fa`(物种序列)
|
|||
|
|
2. 检查契约文件是否存在
|
|||
|
|
3. 检查环境配置
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["preflight_result"] = {
|
|||
|
|
"valid": True, # 或 False
|
|||
|
|
"issues": [], # 如果 valid=False,列出问题
|
|||
|
|
"checked_files": [
|
|||
|
|
"input/Ft.SPL.pep.fa: EXISTS",
|
|||
|
|
"input/Ft.pep.fa: EXISTS"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 错误处理
|
|||
|
|
- 如果 `preflight_result.valid = False`:
|
|||
|
|
- 记录 `state["errors"]`
|
|||
|
|
- Planning Agent 可能重新规划(目前未实现)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. PreFlight -> Execution Agent 【关键】
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
plan = state.get("plan_result", {})
|
|||
|
|
# plan 包含:
|
|||
|
|
# - stages: [{"id": "identify", "params": {...}}]
|
|||
|
|
# - prefix: "Ft.SPL"
|
|||
|
|
# - workspace: "/path/to/workspace/job_xxx"
|
|||
|
|
# - job_id: "job_xxx"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Execution Agent 处理
|
|||
|
|
1. 解析 plan,获取 `stages_to_run = ["identify"]`
|
|||
|
|
2. 调用 `stage_registry.execute("identify", job_id, prefix, params)`
|
|||
|
|
3. 执行 `python apps/cli/run_identify.py --prefix Ft.SPL --workspace ...`
|
|||
|
|
4. 等待执行完成,读取 manifest
|
|||
|
|
|
|||
|
|
### 执行结果结构(关键)
|
|||
|
|
```python
|
|||
|
|
result = {
|
|||
|
|
"status": "success", # 或 "error"
|
|||
|
|
"manifest": {...}, # manifest.json 内容
|
|||
|
|
"manifest_path": "/path/to/manifest.json",
|
|||
|
|
"elapsed_sec": 45.2,
|
|||
|
|
"metrics": {
|
|||
|
|
"final_members": 15,
|
|||
|
|
"initial_candidates": 120
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)【问题点】
|
|||
|
|
```python
|
|||
|
|
execution_results = [
|
|||
|
|
{
|
|||
|
|
"stage": "identify",
|
|||
|
|
"result": {
|
|||
|
|
"status": "success",
|
|||
|
|
"manifest": {...},
|
|||
|
|
"manifest_path": "...",
|
|||
|
|
"message": "✅ Identify completed successfully"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
state["execution_results"] = execution_results # LIST 类型
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **LangGraph State 传递问题**
|
|||
|
|
- `_execution_node` 设置 `state["execution_results"] = execution_results`(list)
|
|||
|
|
- LangGraph 调用 `return state` 返回给下一个节点
|
|||
|
|
- **TypedDict 类型定义必须匹配**: `execution_results: list[dict[str, Any]]`
|
|||
|
|
- 如果类型不匹配,LangGraph 可能无法正确传递
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Execution -> ResultParser Agent
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
execution_results = state.get("execution_results", []) # LIST
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **Fallback 机制(关键)**
|
|||
|
|
如果 `execution_results` 为空(LangGraph state 传递失败):
|
|||
|
|
```python
|
|||
|
|
if not execution_results:
|
|||
|
|
# 从文件系统重新加载 manifest
|
|||
|
|
workspace = Path(f"/path/to/workspace/{job_id}/final")
|
|||
|
|
for manifest_file in workspace.glob(f"{prefix}.*.manifest.json"):
|
|||
|
|
manifest = json.load(manifest_file)
|
|||
|
|
execution_results.append({
|
|||
|
|
"stage": manifest.get("stage"),
|
|||
|
|
"result": {
|
|||
|
|
"status": manifest.get("status", "success"),
|
|||
|
|
"manifest": manifest
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ResultParser Agent 处理
|
|||
|
|
- 解析 execution_results,生成结构化报告
|
|||
|
|
- 提取关键指标:final_members, initial_candidates 等
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["parse_result"] = {
|
|||
|
|
"stage": "identify",
|
|||
|
|
"status": "success",
|
|||
|
|
"summary": "鉴定到 15 个 Ft.SPL 基因家族成员",
|
|||
|
|
"metrics": {
|
|||
|
|
"final_members": 15,
|
|||
|
|
"initial_candidates": 120
|
|||
|
|
},
|
|||
|
|
"files": [
|
|||
|
|
"final/Ft.SPL.identify.pep.fa",
|
|||
|
|
"final/Ft.SPL.identify.manifest.json"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. ResultParser -> Validator Agent 【关键决策点】
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
execution_results_list = state.get("execution_results", []) # LIST
|
|||
|
|
parse_result = state.get("parse_result", {})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **Fallback 机制(再次确认)**
|
|||
|
|
如果 `execution_results_list` 为空:
|
|||
|
|
```python
|
|||
|
|
# 再次从 manifest 加载(双重保险)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Validator Agent 处理
|
|||
|
|
1. 检查 `execution_result.status == "success"`
|
|||
|
|
2. 检查输出文件是否存在
|
|||
|
|
3. 检查数据合理性(final_members > 0)
|
|||
|
|
4. 检查 manifest 完整性
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["validation_result"] = {
|
|||
|
|
"overall_passed": True, # 或 False
|
|||
|
|
"stage": "identify",
|
|||
|
|
"score": 95.0,
|
|||
|
|
"errors": [], # 如果 overall_passed=False
|
|||
|
|
"warnings": [], # 可能有警告但不影响通过
|
|||
|
|
"summary": "✅ 验证通过,所有检查项正常"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. Validator -> Decision: _validate_decision
|
|||
|
|
|
|||
|
|
### 决策逻辑
|
|||
|
|
```python
|
|||
|
|
def _validate_decision(state: GraphState) -> str:
|
|||
|
|
validation = state.get("validation_result", {})
|
|||
|
|
|
|||
|
|
if validation.get("overall_passed", False):
|
|||
|
|
return "success" # -> Discovery Agent
|
|||
|
|
else:
|
|||
|
|
return "failure" # -> Diagnosis Agent
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **关键问题根因**
|
|||
|
|
如果 `validation_result` 为空 `{}`(LangGraph state 传递失败):
|
|||
|
|
- `validation.get("overall_passed", False)` 返回 `False`
|
|||
|
|
- 决策走 `"failure"` -> Diagnosis Agent
|
|||
|
|
- 触发错误诊断 -> retry
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Validator(success) -> Discovery Agent
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
execution_results = state.get("execution_results", [])
|
|||
|
|
parse_result = state.get("parse_result", {})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Discovery Agent 处理
|
|||
|
|
- 分析鉴定结果,生成科学发现报告
|
|||
|
|
- 例如:发现新的 SPL 基因亚家族
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["discovery_result"] = {
|
|||
|
|
"findings": ["发现 15 个 SPL 基因,分为 3 个亚家族"],
|
|||
|
|
"should_continue": False, # 是否继续迭代
|
|||
|
|
"updated_plan": None # 如果继续迭代,新的计划
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Discovery -> Decision: _discovery_decision
|
|||
|
|
|
|||
|
|
### 决策逻辑
|
|||
|
|
```python
|
|||
|
|
def _discovery_decision(state: GraphState) -> str:
|
|||
|
|
discovery = state.get("discovery_result", {})
|
|||
|
|
iteration_count = state.get("iteration_count", 0)
|
|||
|
|
max_iterations = state.get("max_iterations", 3)
|
|||
|
|
|
|||
|
|
if iteration_count >= max_iterations:
|
|||
|
|
return "learning"
|
|||
|
|
|
|||
|
|
if discovery.get("should_continue", False):
|
|||
|
|
return "replan" # -> Planning(循环迭代)
|
|||
|
|
else:
|
|||
|
|
return "learning" # -> Learning Agent
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Discovery -> Learning Agent
|
|||
|
|
|
|||
|
|
### 输入数据(从 state 获取)
|
|||
|
|
```python
|
|||
|
|
execution_results = state.get("execution_results", [])
|
|||
|
|
discovery_result = state.get("discovery_result", {})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Learning Agent 处理
|
|||
|
|
- 记录本次执行的参数、结果
|
|||
|
|
- 更新知识库
|
|||
|
|
- 生成学习总结
|
|||
|
|
|
|||
|
|
### 输出数据(设置到 state)
|
|||
|
|
```python
|
|||
|
|
state["final_output"] = {
|
|||
|
|
"response": "鉴定完成,发现 15 个 SPL 基因",
|
|||
|
|
"artifacts": [
|
|||
|
|
"final/Ft.SPL.identify.pep.fa",
|
|||
|
|
"final/Ft.SPL.identify.manifest.json"
|
|||
|
|
],
|
|||
|
|
"metrics": {...}
|
|||
|
|
}
|
|||
|
|
state["status"] = "completed"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 错误处理路径
|
|||
|
|
|
|||
|
|
### Validator 验证失败 -> Diagnosis Agent
|
|||
|
|
|
|||
|
|
### Diagnosis Agent 处理
|
|||
|
|
```python
|
|||
|
|
state["diagnosis_result"] = {
|
|||
|
|
"error_type": "missing_output_file",
|
|||
|
|
"suggested_fix": "检查输入文件路径",
|
|||
|
|
"retry_params": {...}
|
|||
|
|
}
|
|||
|
|
state["retry_count"] += 1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Diagnosis -> Decision: _diagnosis_decision
|
|||
|
|
```python
|
|||
|
|
if retry_count < max_retries:
|
|||
|
|
return "retry" # -> Execution Agent(重新执行)
|
|||
|
|
else:
|
|||
|
|
return "fail" # -> END(失败结束)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## LangGraph State 传递问题详解
|
|||
|
|
|
|||
|
|
### TypedDict 类型定义(state.py)
|
|||
|
|
```python
|
|||
|
|
class GraphState(TypedDict):
|
|||
|
|
execution_results: list[dict[str, Any]] # LIST 类型(已修复)
|
|||
|
|
validation_result: dict[str, Any] # DICT 类型
|
|||
|
|
...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### LangGraph 内部机制
|
|||
|
|
LangGraph 使用 TypedDict 进行类型检查:
|
|||
|
|
1. 节点函数返回 `state`(dict)
|
|||
|
|
2. LangGraph 检查返回值是否符合 TypedDict 定义
|
|||
|
|
3. 如果类型不匹配,可能导致:
|
|||
|
|
- 数据丢失(字段被忽略)
|
|||
|
|
- 类型转换错误
|
|||
|
|
- 默认值覆盖
|
|||
|
|
|
|||
|
|
### 已修复的问题
|
|||
|
|
之前 `execution_results` 定义为 `dict[str, Any]`,但实际使用 `list[dict]`,导致:
|
|||
|
|
- `_execution_node` 设置 list
|
|||
|
|
- `_validator_node` 获取空 `{}` 或 `[]`
|
|||
|
|
- fallback 机制从 manifest 加载(但可能加载失败)
|
|||
|
|
- `_validate_decision` 判断 failure
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 数据流转图(简化版)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户输入
|
|||
|
|
↓
|
|||
|
|
[Router] → state["intent_result"] = {stages, prefix, mode}
|
|||
|
|
↓
|
|||
|
|
[Learning Recommend] → state["learning_recommendations"] = {params}
|
|||
|
|
↓
|
|||
|
|
[Planning] → state["plan_result"] = {stages, prefix, workspace}
|
|||
|
|
↓
|
|||
|
|
[PreFlight] → state["preflight_result"] = {valid, issues}
|
|||
|
|
↓
|
|||
|
|
[Execution] → state["execution_results"] = [{stage, result}] ← 关键
|
|||
|
|
↓
|
|||
|
|
[ResultParser] → state["parse_result"] = {summary, metrics} ← 有 fallback
|
|||
|
|
↓
|
|||
|
|
[Validator] → state["validation_result"] = {overall_passed} ← 有 fallback
|
|||
|
|
↓
|
|||
|
|
[_validate_decision] → "success" / "failure"
|
|||
|
|
↓
|
|||
|
|
[Discovery] (success) → state["discovery_result"]
|
|||
|
|
↓
|
|||
|
|
[Learning] → state["final_output"]
|
|||
|
|
↓
|
|||
|
|
END
|
|||
|
|
|
|||
|
|
OR (failure):
|
|||
|
|
[Validator] → "failure"
|
|||
|
|
↓
|
|||
|
|
[Diagnosis] → state["diagnosis_result"] + retry_count++
|
|||
|
|
↓
|
|||
|
|
[_diagnosis_decision] → "retry" (if retry < max) / "fail" (if max reached)
|
|||
|
|
↓
|
|||
|
|
[Execution] (retry) → 重新执行
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 关键数据类型总结
|
|||
|
|
|
|||
|
|
| 字段名 | 类型 | 设置节点 | 使用节点 |
|
|||
|
|
|--------|------|----------|----------|
|
|||
|
|
| `intent_result` | `dict[str, Any]` | Router | Planning |
|
|||
|
|
| `learning_recommendations` | `dict[str, Any]` | Learning Recommend | Planning |
|
|||
|
|
| `plan_result` | `dict[str, Any]` | Planning | PreFlight, Execution |
|
|||
|
|
| `preflight_result` | `dict[str, Any]` | PreFlight | (记录 errors) |
|
|||
|
|
| `execution_results` | `list[dict[str, Any]]` | Execution | ResultParser, Validator, Diagnosis |
|
|||
|
|
| `parse_result` | `dict[str, Any]` | ResultParser | Validator |
|
|||
|
|
| `validation_result` | `dict[str, Any]` | Validator | _validate_decision |
|
|||
|
|
| `diagnosis_result` | `dict[str, Any]` | Diagnosis | _diagnosis_decision |
|
|||
|
|
| `discovery_result` | `dict[str, Any]` | Discovery | _discovery_decision |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 测试验证步骤
|
|||
|
|
|
|||
|
|
1. 启动 Celery 服务
|
|||
|
|
2. 前端触发 identify 任务
|
|||
|
|
3. 观察 Celery 日志:
|
|||
|
|
- `[Execution] DEBUG: state['execution_results'] set: 1 items`
|
|||
|
|
- `[Validator] ✅ 使用execution_results进行验证`
|
|||
|
|
- `[Validator] overall_passed: True`
|
|||
|
|
4. 检查 `workspace/job_xxx/logs/` 只有 1 个日志文件
|
|||
|
|
5. 前端显示 "执行成功"
|