aps-agent/poc/pi-fallback/P1-IMPL-NOTES.md

52 lines
7.4 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.

# P1-IMPL-NOTES — P1 只读兜底 · 实施记录(Agent-E)
> 实施员:Agent-E · 日期:2026-09-02 · 施工图:`P1-DESIGN.md` · 协调板:`GOAL-P1.md`
> 未 git commit / push;全部验证用 `.venv\Scripts\python.exe`。
## 1. 实际改动清单
| # | 文件 | 新建/修改 | 规模 | 内容 |
|---|------|----------|------|------|
| 1 | `server/agent_core/fallback_lane.py` | 新建 | 809 行 | 兜底编排器(moduleId: core-fallback-lane):FallbackConfig/from_env、fallback_feature_enabled、run 目录三区、build_child_env 环境清洗、kill_process_tree、resolve_model 模型协商(300s 缓存)、_write_models_json(apiKey 只写 env 名引用)、write_guard_extension、build_pi_runner(读线程+queue+心跳+finally 杀树)、_run_events 三重熔断主循环、propose_reply 接线入口(绝不抛出) |
| 2 | `server/integrations/pi_bridge.py` | 新建 | 259 行 | 工具桥(moduleId: integ-pi-bridge):TOOL_REGISTRY 仅 fs_read/aps_query/report_emit 三个只读项、PiBridge(issue/complete/list/validate_report_citations/handle_fs_read 限 run 目录/export_snapshot 快照制)、_TASK_TEMPLATE + render_task_brief(USER_REQUEST 隔离标记) |
| 3 | `server/agent_core/harness.py` | 修改 | +2 行 | `_POWER_MAP` / `_POLICY_DESC` 各纯追加一行 `agent.fallback.propose = P1`;未动任何函数 |
| 4 | `server/agent_core/feature_flags.py` | 修改 | ±33 行 | FEATURE_CATALOG 追加 fallback;新增 `_DEFAULT_OFF_KEYS=frozenset({"fallback"})`;`_all_enabled`→`_default_enabled`(设计已授权改名,模块内私有、rg 核实无外部调用方);三处 error 文案改「回退默认值(fallback 默认关,其余默认开)」;返回体追加 `defaultOff`;头注释语义同步 |
| 5 | `server/aps_domain/workflow.py` | 修改 | +8/-1 行 | 2875 `assistant.reply/unknown` 分支按设计 §3.2 原样接线(propose_reply → None 时原路径逐字节不变) |
| 6 | `tests/golden/test_feature_flags.py` | 修改 | ±20 行 | 三条 all-enabled 断言修复(见偏差 1) |
| 7 | `tests/golden/test_fallback_lane.py` | 新建 | 323 行 | 设计 §6 测试矩阵 13 例全实现,全部确定性(fake runner 注入 + tmp_path 隔离 + 清 LLM env) |
**未动**:gateway/app.py、contracts.py、tool_runtime.py、intent.py、assistant.py、sidecar.cjs、poc/(只读参照,零 import)。
## 2. 与设计的偏差及理由
1. **test_feature_flags.py 修了三条而非两条**(设计 §6.0 只列了两条):`test_corrupt_json_fails_open_with_error` 同样断言 `all(enabled)`,默认关加入后必然打破。设计漏列;改法与其余两条一致(排除 defaultOff 键 + 显式断言 fallback=False)。
2. **`result.json` 落盘位置从 `_run_events` 挪到 `propose_reply`**:设计的 FallbackOutcome 含 `citation_check`,而凭证校验在编排循环之后做;在 propose_reply 收尾一次性落盘可避免写两次。`_run_events` 仍负责 events.jsonl / orchestrator.log。
3. **unavailable(运行时不可用)路径不建 run 目录**:可用性检查在 create_run_dirs 之前,审计 rationale 的 runDir 记 `""`。理由:最小副作用(没跑起来就不留目录),且设计测试 13 不要求目录。成败判定、审计、失败话术与设计一致。
4. **空报告防御**:`stopReason=stop` 但报告体为空时判 `error:empty_report` 显式失败(设计未覆盖此边角;不防御会产出空草稿回复,违背诚实原则)。
5. **pi 工具名 → 桥工具名映射 `_PI_TOOL_MAP`**(read/grep/find/ls → fs_read):设计 §3.5 要求每个 tool_execution_start 签 callId,但 pi 事件流的 toolName 是 `read` 等内置名,不在桥注册表;不加映射则每次工具调用都抛 ToolBridgeViolation。映射外工具出现 → ToolBridgeViolation 经主循环归并为 `harness_error` 显式失败(设计 stop_reason 集合内)。
6. **`write_guard_extension(run_dir)` 参数名实为 run 目录**(设计签名写的 `run_root` 语义即此);runId 取自目录名。守卫扩展仅在真实 runner 内生成(fake runner 无 pi 进程可加载它);P1 守卫为 bash/edit/write 全禁(比 P0 模板更紧,无白名单参数)。
7. **新增两个模块级同步辅助 `_append_run_log` / `_write_result_json`**:把阻塞文件 IO 移出 async 函数体(ruff ASYNC230)。行为不变。
8. **pi_bridge 新增公开 helper `render_task_brief()`**(设计只写了 `_TASK_TEMPLATE` 常量):避免跨模块填充私有模板。
## 3. 自验结果(命令 + 输出摘要)
| 验证 | 命令 | 结果 |
|------|------|------|
| 新增/修改测试 | `pytest tests/golden/test_fallback_lane.py tests/golden/test_feature_flags.py -q` | **19 passed**(13 兜底 + 6 开关) |
| 设计 §7 受影响回归 | `pytest test_contract_sync test_assistant test_guidance test_folder_pack test_tool_runtime test_gov_api -q`(与上合并跑) | **61 passed** |
| 聊天/网关/门禁切片 | `pytest test_chat_ensure_session test_closed_loop_gateway test_automation_gateway test_automation_gears test_dialog_clarify test_harness_p3 test_tool_runtime_gateway -q` | **41 passed** |
| ruff 新增文件 | `ruff check fallback_lane.py pi_bridge.py test_fallback_lane.py test_feature_flags.py` | **All checks passed!**(有意豁免处以 `# noqa` + 中文理由逐条标注:BLE001/S110/PLW1510 等) |
| ruff 修改文件 | 三个修改文件改动前后告警输出逐行数对比 | **202 == 202,零新增**(存量为 round-41 记录的 informational 基线,CI 不阻断) |
| 全量 tests/golden | 尝试过一次 | **300s 超时未跑完**(未验证,见遗留) |
爆炸半径复核(施工前已按设计 §7 再核对):`_default_enabled` 无外部调用方(rg 核实);`feature_flags` 产品调用方仅 app.py `/api/features`;`handle_intent` 调用方开关关时逐字节不变(测试 1 已断言回复 == 原话术、零审计、零目录)。
## 4. 遗留问题 / 风险
1. **test_preference_features.py::test_extract_features_from_world 失败 = 既有日期炸弹,与本轮无关**:该用例 dueDate 写死 2026-08-03/04,今天 today0=2026-09-02,半周期窗口已过 → urgencyRatio 0.0≠0.5。已核实 `server/knowledge/` 零 diff(`git diff --stat HEAD` 为空)。建议另开轮次修(dueDate 改相对 today 生成)。
2. **全量 tests/golden 未跑完**(300s 超时);上述受影响切片全绿,但全量回归建议 Agent-F 用更长窗口补跑。
3. **真实 pi 链路未实测**:本轮全部确定性测试走 fake runner;node/pi/真实 LLM 的端到端冒烟(开关开 + 真实 runner)未做——需要带 key 的环境,建议 Agent-F 或人工按 P0 方法补一次真实冒烟。
4. **真实 pi 无法获知桥侧 callId**(凭证是编排器事后签发):简报已按设计写入 [callId: ...] 引用格式,但真实 pi 报告若引用 callId 几乎必然 forged_citation 判败;不引用则 valid。这是设计本身的已知张力,P1 保留为边界声明,建议后续阶段明确「真实路径的凭证语义」。
5. **文档未动**(GOAL 交付 7 归 Agent-F):harness.md 权力矩阵需追加 `agent.fallback.propose` 行(harness.py 内有文档同步铁律注释)、新增 docs/architecture/fallback.md、CHANGELOG 条目。
6. **模型协商缓存是进程级单值**(`_MODEL_CACHE`):多租户/多 base_url 切换需等 300s TTL 或重启;P1 单现场部署语义下可接受。