16 KiB
P1-SMOKE — P1 只读兜底 · 真实端到端冒烟全记录(Agent-G)
冒烟员:Agent-G · 日期:2026-09-02 · 协调板:
GOAL-P1.md定位:P1 此前全部测试均为 fake runner 注入;本次是第一次全真实链路(真实 server / 真实对话入口 / 真实 Pi headless / 真实 Kimi K2.6)。 硬约束遵守:未 git commit/push;未修改任何产品代码文件;Python 一律.venv\Scripts\python.exe; server 单命令内启动→验证→杀树;全程临时 APS_HOME 隔离,server/data/零污染(git status 与 mtime 双重核实); LLM key 只进请求头/子进程 env,未打印未落盘。
0. TL;DR
严格判定:原样产品代码下真实链路 NO-GO——冒烟抓到一个 fake runner 永远摸不到的产品 bug:
fallback_lane.py 守卫模板注释行有一对未转义的 { },write_guard_extension 的 str.format 必抛
KeyError: ' block',真实 runner 路径 100% 失败(被正确归并为 harness_error 显式失败,用户侧回退话术正常)。
修复预判:一行修复后链路全通。 诊断轮(server 进程内做内存级单行修正,产品文件零改动)同一口令全绿: 真实 Pi 经新链路读真实快照、产出真实分析回到对话,run 目录/凭证/审计链全部齐全,34.9s 完成。
另发现一个环境级坑:本机 PATH 里 node 优先解析为宿主注入的 node.CMD 垫片,真实 runner 起不来;
用既有配置项 APS_FALLBACK_NODE 指向真实 node.exe 即可绕过(建议产品侧优先解析 .exe 或文档明示)。
1. 冒烟设置
| 项 | 值 |
|---|---|
| 启动方式 | .venv\Scripts\python.exe -m uvicorn server.main:app --host 127.0.0.1 --port <port>(诊断轮经 shim_server.py,见 §4) |
| 隔离 | APS_HOME/APS_WORLD_PATH/APS_DB_PATH/APS_FEATURES_PATH 全部指向 poc/pi-fallback/smoke/site{,2}/ |
| 开关 | 临时 features.json = {"version":1,"features":{"fallback":true}}(APS_FEATURES_PATH 指向) |
| 认证 | 开发态桌面模式:x-aps-client: desktop + APS_LICENSE_PROVIDER=mock + 授权码 APS-DAY-DEMO(/api/auth/license/activate → aps_desktop_session cookie)。Web/JMS 认证需要外部 JMS,不可用 |
| 模型 | APS_FALLBACK_MODEL=aps-fallback/kimi-k2.6(P0 已知 .env 的 kimi-k2-0711-preview 该 key 404;GET /models 实测清单 = kimi-k2.7-code-highspeed / kimi-k3 / kimi-k2.7-code / kimi-k2.6) |
| 口令 | 「琢磨个冷门的角度:把每张待交付订单的交货日期换算成一周里的第几天,交货压力集中在一周的开头还是末尾?再数数物料种类,凭数据给个备料松紧的判断,别改任何东西,只给结论。」——逐条避让 intent.py 全部快路正则(无 分析+数据 / 看看+订单 / 交期|看板 / 排产祈使 / 星期几 等),只读分析类、零写意图 |
| 脚本 | poc/pi-fallback/smoke/smoke_e2e.py(基线轮)、smoke_e2e_diag.py + shim_server.py(诊断轮) |
意图落点实测(两轮一致,SSE intent 事件):assistant.reply / confidence 0.9 / source LLM——
主线模型 kimi-k2-0711-preview 404 → 二级分类按设计降级为 assistant.reply,随后 workflow.py:2875 分支进 propose_reply。
2. 七条判据逐条结论
| # | 判据 | 结论 | 证据 |
|---|---|---|---|
| 1 | server 启动健康 | PASS | /api/health 200,interfaceVersion="1.0",storage.writable=true,home 指向临时 APS_HOME(两轮一致,见 results.json/results2.json health 段) |
| 2 | /api/features 开关语义 | PASS | fallback.enabled=true、defaultOff=["fallback"]、source=file(临时配置生效;默认关标注存在) |
| 3 | 未覆盖口令 → 真实 Pi 产出回复 | FAIL(原样代码)/ PASS(单行修复后) | 基线轮:回复=原话术+「智能兜底本次未完成:编排器内部错误」,[智能兜底·草稿] 标记未出现(bug A)。诊断轮:回复含 [智能兜底 · 草稿] run fb-20260902-075334-401b3c + 真实分析正文 775 字(摘录见 §3),其中「2026-08-08 星期六」经独立核对属实(Saturday),订单号 102285668、物料 22 种均与快照一致 |
| 4 | run 目录 + 计划/日志/报告 + calls.jsonl 凭证 | FAIL→PASS(同上分轮) | 诊断轮 run 目录:{events.jsonl, orchestrator.log, result.json, calls.jsonl, guard-<runId>.ts, inbox/{snapshot.md,orders.csv}, work/, outbox/report.md};calls.jsonl 4 行 = 2×issued+2×completed 配对,工具=fs_read;citation_check: valid=true, cited=[], missing=[], issued=2 |
| 5 | 审计链 agent.fallback.propose | PASS(两轮都落链) | 审计写在聊天身份所属的租户世界 data/tenants/<tenant>/projects/personal-<uid>/world.json 的 auditEvents:基线轮 result=FAILED(stopReason=harness_error),诊断轮 result=SUCCESS(stopReason=stop, steps=2, elapsedSec=34.89, reportPath 齐全),power=P1、actor=desktop-425e60c0、哈希链连续 |
| 6 | 开关关闭时行为不变 | PASS | 复跑 tests/golden/test_fallback_lane.py 13 例全绿(1.12s):test_flag_off_preserves_original_behavior 断言回复==原话术、零审计、零目录;test_fallback_default_off_when_config_missing/test_fallback_non_bool_stays_off 佐证默认关语义。代码路径:fallback_feature_enabled()=False → propose_reply 返回 None → 原路径逐字节不变。未起第二实例,以既有确定性测试佐证 |
| 7 | 收尾无残留 + 临时目录清单 | PASS | taskkill /T /F 后:8391/8392 端口释放(curl 超时)、无 uvicorn/smoke python 进程、全部 node.exe 命令行逐一核查无 pi-coding-agent/cli.js 残留;git status server/data 干净、world.json mtime 保持 Aug 4;临时目录清单见 §5 |
3. 真实回复摘录(诊断轮 run fb-20260902-075334-401b3c)
[智能兜底 · 草稿] run fb-20260902-075334-401b3c
当前待排订单仅 1 张(
102285668),交货日期为 2026-08-08,星期六。……交货压力出现在周末尾部。但样本量只有 1 张,远不足以判断统计趋势,只能如实记录这一个点落在周末。综合:整体备料环境在总量上很松,但在齐套精度上要求很高——22 根绳子吊 1 个篮子,每根都不能断。
非话术证据:数字来自真实快照(orders.csv 一行 = 102285668 / 28200003654300 / 1 / 2026-08-08 / RELEASED;snapshot.md = 物料 22 种);pi 真实发起 2 次 read 工具调用(events.jsonl:tool_execution_start×2,无 guard 拦截记录)。
4. 失败项与根因(本次冒烟的核心产出)
Bug A(产品 bug,必修,一行):守卫模板注释行花括号未转义
- 现象:真实 runner 路径必败——
result.json: stopReason=harness_error, errorMessage="KeyError: ' block'",0.001s 即败,pi 从未启动。 - 根因:
server/agent_core/fallback_lane.py_GUARD_TS_TEMPLATE第 2 行注释// pi.on("tool_call") 返回 { block: true, reason } 即可在工具执行前拦截(P0 已实测)。里的{ block: true, reason }是单花括号字面量,write_guard_extension里.format(RUN_ROOT_POSIX=...)把它当字段名解析 → KeyError。 - 为何测试没拦住:守卫文件只在真实 runner 内生成(fake runner 无 pi 进程可加载),13 例黄金测试全走注入 runner——正是本轮冒烟要补的缺口。
- 修法:注释行
{/}双写({{ block: true, reason }})。诊断轮用内存 shim 做等价修正后,生成的 guard TS 含正确return { block: true, reason };,链路全绿——可预判该行修复即解锁。 - 按硬约束未自行修改产品文件;shim 只存在于
poc/pi-fallback/smoke/shim_server.py(内存 patch + 自检断言,模板若变化 shim 会拒绝运行)。
坑 B(环境/健壮性,建议项):node 被解析为 .CMD 垫片时真实 runner 起不来
- 现象:修好 Bug A 后 pi 仍 0.41s 秒败(
error:no_stop_reason),stderr 为 GBK 的'""' 不是内部或外部命令。 - 根因:本机 PATH 中
node优先命中宿主环境的node.CMD垫片;shutil.which("node")返回它,Popen 以含换行/空格的参数起 .cmd 时 cmd 引号语义爆炸。 - 绕过:通过
APS_FALLBACK_NODE=<node.exe-absolute-path>显式注入真实 Node 可执行文件(配置项本就存在,诊断轮已验证)。 - 建议:
build_pi_runner在 Windows 下优先探测node.exe,或文档明示该配置项为现场必查项。P0 未踩到是因为当时 shell PATH 顺序不同。
冒烟环境处理(非产品问题,如实记录)
- 桌面 mock 身份的聊天读写租户世界(
data/tenants/<uuid>/projects/personal-<uid>/world.json),首访为空世界 → 首轮快照为空(订单 0 张)。诊断轮按 deviceId 的 sha256 确定性推出租户世界路径并预播种演示数据副本(全部在临时 APS_HOME 内)。 - 主线 LLM(kimi-k2-0711-preview)404 使意图二级分类稳定降级 assistant.reply——本次兜底触发依赖该合法降级路径;「主线模型正常时未登记口令经 LLM 分类是否落 assistant.reply」未覆盖,列为遗留(见 §6)。
5. 现场与产物清单(全部保留供查验)
| 路径 | 内容 |
|---|---|
poc/pi-fallback/smoke/smoke_e2e.py |
基线轮脚本(原样产品代码) |
poc/pi-fallback/smoke/smoke_e2e_diag.py + shim_server.py |
诊断轮脚本 + 内存单行修正 shim |
poc/pi-fallback/smoke/results.json / results2.json |
两轮结构化结果(health/features/intent/resultJson/审计摘录) |
poc/pi-fallback/smoke/chat1.sse / chat2.sse、reply1.txt / reply2.txt |
真实 SSE 原始流与回复全文 |
poc/pi-fallback/smoke/server.log / server2.log |
server 日志 |
poc/pi-fallback/smoke/site/ |
基线轮临时现场(APS_HOME:run fb-20260902-074954-990a0c、租户世界、features.json、master.db) |
poc/pi-fallback/smoke/site2/ |
诊断轮临时现场(run fb-20260902-075334-401b3c,成功run全产物) |
6. 遗留问题
- Bug A 未修(硬约束:不自行修产品代码)——修复后建议补一条「真实 runner 模板 format 冒烟」级别的轻量防线(哪怕只对
_GUARD_TS_TEMPLATE.format做单元断言),否则同类模板注释回归无网可拦。 - 坑 B 待产品决策:node 解析健壮性(优先 .exe / 文档化 APS_FALLBACK_NODE)。
- 主线 LLM 正常时的意图落点未验证:本次触发依赖 404 降级;待主线模型可用后补一轮「未登记口令 → LLM 分类 → assistant.reply」的触发确认。
- 超时预算:诊断轮 34.9s < 90s 熔断,余量健康;但更复杂的分析口令可能逼近预算,90s 同步上限的真实体验未压测。
- 凭证语义已知张力(P1-IMPL-NOTES 遗留 #4)本轮如实复现:真实 pi 报告未引用任何 callId → valid;若引用必 forged。P2 前需明确真实路径凭证语义。
7. 修复后复证(Agent-H · 2026-09-02 · 修复后原样代码真实冒烟)
结论:修复后复证 GO——七条判据全部 PASS,无任何进程内存补丁,产品文件原样跑通。
修复内容(改动最小化,未 commit)
| 项 | 改动 | 文件 |
|---|---|---|
| Bug A | _GUARD_TS_TEMPLATE 注释行 { block: true, reason } → {{ block: true, reason }}(双写转义,format 后生成 TS 仍含字面量单花括号)。已全文核查:该文件仅此一处 format 模板,其余代码花括号本就已双写,无同类残留 |
server/agent_core/fallback_lane.py(1 行) |
| 坑 B | 新增 _resolve_node():显式 APS_FALLBACK_NODE 最高优先级原样命中;默认 "node" 且 Windows 时 shutil.which("node.exe") 优先于 shutil.which("node"),避开 node.CMD 垫片;其余回退原逻辑。本机实测 where node.exe 可命中真实 Node 可执行文件 |
server/agent_core/fallback_lane.py(+14 行) |
| 防回归 | 新增 test_write_guard_extension_real_template_format:真实调用 write_guard_extension,断言不抛异常、守卫文件落盘、内容含字面量 { block: true、占位符替换干净——fake runner 盲区从此有网 |
tests/golden/test_fallback_lane.py(+1 例,14 例) |
影响面(GitNexus MCP 不可用,rg 盘点):write_guard_extension/build_pi_runner 调用方均仅 fallback_lane.py 自身(poc/ 下同名函数属 PoC 独立副本,未动),爆炸半径 LOW,无 HIGH/CRITICAL。
复证执行(全部真实,数字写实)
- 黄金测试:
.venv\Scripts\python.exe -m pytest tests/golden/test_fallback_lane.py tests/golden/test_feature_flags.py -q→ 20 passed(14 兜底 + 6 开关,1.41s)。 - ruff:
fallback_lane.py+test_fallback_lane.py→All checks passed!(零新增告警)。 - 真实冒烟复跑:
.venv\Scripts\python.exe poc/pi-fallback/smoke/smoke_e2e.py(无 shim、无内存补丁、未设APS_FALLBACK_NODE——orchestrator.log 实证node_bin='node',坑 B 修复生效自动命中 node.exe)。run = fb-20260902-075907-3af703,13.0s 完成(< 90s 熔断)。注:复跑按脚本设计重建了site/并刷新results.json/chat1.sse/reply1.txt/server.log(基线轮旧文件被覆盖,诊断轮 site2/results2 保留)。
七条判据复证结果
| # | 判据 | 结论 | 证据 |
|---|---|---|---|
| 1 | server 启动健康 | PASS | /api/health 200,interfaceVersion="1.0",storage.writable=true,dataDir 指向临时 APS_HOME(results.json health 段) |
| 2 | /api/features 开关语义 | PASS | fallback.enabled=true、defaultOff=["fallback"]、source=file |
| 3 | 未覆盖口令 → 真实 Pi 产出回复 | PASS | 回复 499 字,含 [智能兜底 · 草稿] run fb-20260902-075907-3af703 + 真实分析正文(reply1.txt)。本轮租户世界首访为空(同基线轮),Pi 如实报告「待排订单 0 张、物料 0 种」并列出未竟事项——这正是真实产出而非话术的力证(空快照不编造数字) |
| 4 | run 目录 + 计划/日志/报告 + calls.jsonl 凭证 | PASS | run 目录全产物:{events.jsonl, orchestrator.log, result.json, calls.jsonl, guard-<runId>.ts, inbox/{snapshot.md,orders.csv}, work/, outbox/report.md};守卫 TS 含正确 return { block: true, reason };(Bug A 修复直接证据);calls.jsonl 4 行 = 2×issued+2×completed(fs_read);citation_check: valid=true, cited=[], missing=[], issued=2;零 guard 拦截 |
| 5 | 审计链 agent.fallback.propose | PASS | 租户世界 data/tenants/demo0000000000000000000000000001/projects/personal-298897889579184597/world.json:1 条 agent.fallback.propose result=SUCCESS(power=P1、actor=desktop-425e60c0、stopReason=stop、steps=2、elapsedSec=13.0、reportPath 齐全)+ 3 条 tool.run,哈希链连续(逐对核验 prevHash==hash) |
| 6 | 开关关闭时行为不变 | PASS | 上述 20 passed 中 test_flag_off_preserves_original_behavior 断言回复==原话术逐字节、零审计、零目录;默认关三态测试同绿 |
| 7 | 收尾无残留 + 临时目录清单 | PASS | 脚本 finally taskkill /T /F 后:8391 端口释放(curl 超时);ps 无 uvicorn/pi 进程;全机 node.exe 命令行逐一核验均为用户既有 dev 环境(Kimi runtime/WebStorm/vite/electron),零 pi-coding-agent/cli.js 残留;git status server/data 干净、world.json mtime 保持 2026-08-04 00:36 |
P1 最终判定:GO。 Bug A 一行修复即解锁的预判成立;坑 B 产品化处置(Windows 优先 node.exe)真实生效;遗留项不变(主线 LLM 正常时的意图落点仍待验证,见 §6.3)。