Files
Video-Tree-TRM5/research-wiki/designs/2026-07-11-agent-runtime-fixes-design.md
T

71 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Spec-1Agent 执行环境修复(解析容错 + 步级重试 + 摘要附实体)
- **日期**: 2026-07-11
- **状态**: 已批准(用户确认,步级重试退避改为 20s/40s)
- **依据**: `research-wiki/findings/2026-07-11-benchmark-failure-taxonomy.md` §四(T85 题)与 §二 M1view_node 摘要吞 entities
- **系列**: Spec-1/2/3 三件套之一,见 [2026-07-11-batch-tree-build-design.md]、[2026-07-11-question-gen-v2-design.md]
## 1. 问题
| # | 缺陷 | 证据 | 影响 |
|---|------|------|------|
| A1 | `_parse_response``core/agent/loop.py:265-300`)只接受 `action.args` 嵌套结构;deepseek 稳定输出变体(args 平铺 + ```json 围栏)解析三连拒 → 0 步阵亡 | 637-3、615-3 | 整题报废,且 json_repair 修不了结构错位 |
| A2 | LLM 调用异常(`loop.py:118-129`)直接整题终止,无步级重试 | 796-3SSL BAD_RECORD_MAC 废掉 13 步上下文) | 一次网络抖动损失全部已积累推理 |
| B | `summarize_node``app/search/summarizer.py`)两轮按题摘要后 entities/visible_text 不保证幸存 | 786-2、872-3、750-1Agent 站在证据节点上漏读实体) | M1 负证据幻觉的恶化因素 |
## 2. 设计
### A1 解析容错(结构归一化层)
在现有 `repair_json → json.loads → 校验` 之后、返回 None 之前,增加确定性归一化:
1. **围栏剥除**repair_json 前先剥 ```json / ``` 围栏(正则,幂等)。
2. **args 收拢**:若 `action` 为 dict 且含 `tool` 但缺 `args`,把 action 下除 `tool` 外的所有平铺键收拢为 `args` 嵌套。
3. 归一化成功 → 照常执行;失败 → 走现有 retry 追问路径(行为不变)。
纯函数实现,用 637-3/615-3 的真实坏输出作单测样本。
### A2 步级重试
`_call_llm` 异常处理改为步级重试循环:
| 参数 | 值 | 说明 |
|------|-----|------|
| 重试次数 | 2 | 第 3 次失败才整题终止(stop_reason=error 不变) |
| 退避 | 20s / 40s | 用户指定 |
| 可重试异常 | 显式类型元组:`ssl.SSLError`、`TimeoutError`、`ConnectionError`、`OSError`、openai SDK 传输类异常(`APIConnectionError`/`APITimeoutError` | 遵循 CLAUDE.md P5(不做全 Exception 兜底);`asyncio.CancelledError` 绝不吞;其余未知异常 fail-fast 整题终止(现状行为) |
| 上下文 | 原样保留 | messages 不回滚,重试即重发 |
| 遥测 | 失败尝试的 error 记录由 `GovernedLLMClient` 内部负责(已有);AgentLoop 侧只以 loguru 记录步级重试事件(不注入 TelemetryRecorder | AgentLoop 无遥测端口,不越层补写 |
### B 摘要附带实体原文
在 **dispatcher 侧**`SearchToolDispatcher._handle_view_node`)由确定性代码(非 LLM)在摘要结果末尾追加节点 card 的字段原文:
```
[实体] <entities + visible_entities 原文>
[画面文字] <visible_text 原文>
```
- **调用链改动**(Codex 审查修正):`summarize_node` 只收 `raw_text: str`、无结构化 card 访问,且 `_node_full_text` 递归收值不保留字段名——因此在 `TreeEnvironment` 新增结构化字段提取方法(如 `node_entity_fields(node_id) -> dict[str, str]`),dispatcher 调用它并把区块拼接到 summarize_node 返回值之后;summarize_node 本体不改
- 摘要后追加 → LLM 无法吞掉;字段为空则不加对应区块
- 对 anchor / 非 anchor 两种模式一致生效
## 3. 不做什么(YAGNI
- 不改 GovernedLLMClient 的内部重试栈(已有四层治理)
- 不改判分协议、不动 prompt 版本化内容
- 不做异常分型重试策略(统一兜底已覆盖已知案例)
## 4. 验证
1. 单测:坏输出样本(围栏/平铺/两者叠加)归一化正确;空 content、缺 tool 仍拒
2. 单测:步级重试计数与退避(mock LLM 抛错)
3. 单测:summarize_node 追加区块(有/无实体字段两种节点)
4. 集成:抽 10 道 T2/T8 错题重跑(637-3、615-3、786-2、872-3、750-1 必含),对比修复前后
5. `make test` 全绿 + 覆盖率不降
## 5. 被否方案
- **prompt 层要求 LLM 修正输出格式**:治标,deepseek 变体是稳定行为,代码归一化是确定性修复
- **重试时区分异常类型**(仅网络类重试):已知案例全是穿透型异常,分型收益低且易漏