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

7.4 KiB
Raw Blame History

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 单现场部署语义下可接受。