52 lines
7.4 KiB
Markdown
52 lines
7.4 KiB
Markdown
|
|
# 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 单现场部署语义下可接受。
|