137 lines
16 KiB
Markdown
137 lines
16 KiB
Markdown
# 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. 遗留问题
|
||
|
||
1. **Bug A 未修**(硬约束:不自行修产品代码)——修复后建议补一条「真实 runner 模板 format 冒烟」级别的轻量防线(哪怕只对 `_GUARD_TS_TEMPLATE.format` 做单元断言),否则同类模板注释回归无网可拦。
|
||
2. **坑 B 待产品决策**:node 解析健壮性(优先 .exe / 文档化 APS_FALLBACK_NODE)。
|
||
3. **主线 LLM 正常时的意图落点未验证**:本次触发依赖 404 降级;待主线模型可用后补一轮「未登记口令 → LLM 分类 → assistant.reply」的触发确认。
|
||
4. **超时预算**:诊断轮 34.9s < 90s 熔断,余量健康;但更复杂的分析口令可能逼近预算,90s 同步上限的真实体验未压测。
|
||
5. 凭证语义已知张力(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。
|
||
|
||
### 复证执行(全部真实,数字写实)
|
||
|
||
1. **黄金测试**:`.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)。
|
||
2. **ruff**:`fallback_lane.py` + `test_fallback_lane.py` → `All checks passed!`(零新增告警)。
|
||
3. **真实冒烟复跑**:`.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)。
|