Files
liqiang b119135836
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

370 lines
20 KiB
Markdown
Raw Permalink 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.
# Experiment 5-8: Production Log Diagnosis / 实验 5-8:生产日志的智能诊断系统
> Companion lab for *AI Agents in Depth*, Chapter 5 — diagnose trajectories + architecture + PRD → structured report → regression tests → real replay → GitHub Issues through the official MCP server.
> 《深入理解 AI Agent》第 5 章:诊断 Agent 读轨迹/架构/PRD,定位根因、生成回归测试、真实重放验证,并通过官方 GitHub MCP 创建 Issue。
← [Chapter 5 index / 返回第 5 章目录](../README.md)
---
## Canonical manuscript experiment / 正文正式实验
The manuscript contract is satisfied by
[`validation/runs/exp5-8-live-http-mcp-20260730-053403/manifest.json`](validation/runs/exp5-8-live-http-mcp-20260730-053403/manifest.json),
not by the smaller deterministic demo described later in this README. The
canonical campaign:
- recorded two trajectories from a real local HTTP subprocess, including raw
HTTP results and measured latency;
- made two live `doubao-seed-1-6-250615` calls to diagnose the failures and
generate executable tests tied to trajectory IDs and turn numbers;
- executed all three generated tests against both HTTP implementations: every
test failed on the buggy service and passed after the fix; and
- called `issue_write(method=create)` on the official
`github/github-mcp-server`, creating
[Issue #502](https://github.com/bojieli/ai-agent-book/issues/502).
All nine acceptance gates are true and the manifest SHA-256 is
`68e09e7c8b4fc100e0612a6f81978079c393a822025bfa794b6dda85134a5813`.
Raw provider calls, live replays, the generated tests, and the credential-free
MCP receipt are retained beside the manifest.
正文合同由
[`validation/runs/exp5-8-live-http-mcp-20260730-053403/manifest.json`](validation/runs/exp5-8-live-http-mcp-20260730-053403/manifest.json)
中的正式活动满足,而不是下文较小的确定性演示。该活动从真实本地 HTTP
子进程采集带原始响应和实测延迟的轨迹;用真实
`doubao-seed-1-6-250615` 调用完成诊断和可执行测试生成;让三个测试在有
缺陷实现上全部失败、修复实现上全部通过;最后通过官方
`github/github-mcp-server``issue_write(method=create)` 创建了
[Issue #502](https://github.com/bojieli/ai-agent-book/issues/502)。九项验收门禁全部为
`true`,原始模型回执、重放、测试与去凭据 MCP 回执均随 manifest 保留。
The sections below document the compact teaching/CI implementation. Its
default GitHub sink is intentionally a mock; `--create-issue` selects its live
MCP branch. Running that legacy path alone is not evidence for the canonical
experiment.
下文记录的是适合教学和 CI 的小型实现;其 GitHub 默认输出有意采用 mock
`--create-issue` 才进入真实 MCP 分支。只运行该旧路径不能作为正式实验完成证据。
---
## English
### Purpose
Production Agents emit large **trajectory logs**. Finding issues, root causes, and building regression tests is expensive. This lab automates:
**Read trajectory set + architecture + PRD → locate issues, structured report → generate regression cases → replay framework executes for real → (mock) GitHub Issues via MCP.**
### Diagnosis pipeline
```
data/trajectories.jsonl (production trajectories with known issues)
data/architecture.md (system architecture) ┐
data/PRD.md (product requirements) ├─► [LLM] diagnose() structured issue report
┘ │
[LLM] gen_test_cases() regression cases (trajectory IDs + turns)
replay.py replay framework
(A) unfixed SUT → FAIL (reproduce bug)
(B) fixed SUT → PASS (verify fix)
github_mcp.py (mock) print/write GitHub Issues
```
- `diagnoser.py`: diagnosis Agent; two real OpenAI calls (default gpt-5.6-luna, JSON mode).
- `sut.py`: **deterministic** system-under-test simulator. `fixed=False` reproduces bugs; `fixed=True` fixed behavior.
- `replay.py`: regression replay. Trajectory input → replay `sut` → evaluate asserts on new trajectory (4 built-in assert DSL kinds).
- `github_mcp.py`: GitHub Issue create; default mock (print + `output/github_issues.json`).
### Seeded known issues (Agent should find)
| Trajectory | Issue | PRD violated | Module |
|------|------|----------|----------|
| T-1001 / T-1002 | Skipped mandatory `verify_refund_eligibility` before refund | R1 (P0) | order_service |
| T-1002 | `process_refund` **repeated failures**, no backoff, false success | R2 (P0) | payment_service |
| T-1003 | `check_stock` latency 8300ms **timeout without degrade** | R3 (P1) | inventory_service |
| T-1004 | Healthy trajectory (control) | — | — |
### Run
```bash
# From the repository root: use the shared Chapter 5 environment
uv sync --locked --python 3.12 --extra ch5
# Activate it before changing directories:
# macOS/Linux:
source .venv/bin/activate
# Windows PowerShell: .\.venv\Scripts\Activate.ps1
# Windows cmd: .venv\Scripts\activate.bat
# pip fallback when uv is not installed:
# python -m pip install -e ".[ch5]"
cd chapter5/log-diagnosis
# Single-project compatibility path, still supported during migration:
# python -m pip install -r requirements.txt
cp env.example .env # OPENAI_API_KEY (default gpt-5.6-luna); or OPENROUTER_API_KEY
python demo.py # full pipeline (two real LLM calls)
```
`demo.py` once: read trajectories → diagnosis report → regression cases → replay pass/fail → (mock) GitHub Issue.
Common flags (`python demo.py -h`):
- `--smoke`: **no-API smoke**—skip LLM; built-in diagnosis + replay + GitHub mock only (exit 0 if green). CI / no key.
- `--model gpt-5.6`: override model (same as `OPENAI_MODEL`).
- `--data-dir DIR`: input dir with `trajectories.jsonl` + `architecture.md` + `PRD.md` (default `data/`).
- `--output FILE`: mock Issue write path (default `output/github_issues.json`).
- `--create-issue`: **real** Issues via MCP (`GITHUB_TOKEN` + `GITHUB_REPO`; falls back to mock if missing).
- `--no-github`: skip step 4.
### Sample real output (excerpt)
Diagnosis found all 3 seeded issue classes:
```
[问题 1] 未进行退款资格校验
优先级 : P0 模块: order_service PRD: R1
轨迹 : ['T-1001', 'T-1002'] 关键轮次: [3]
[问题 2] 支付重试机制未正确实现
优先级 : P0 模块: payment_service PRD: R2
[问题 3] 库存查询延迟未降级处理
优先级 : P1 模块: inventory_service PRD: R3
```
Replay **actually runs** cases (reproduce then verify fix):
```
(A) 对『线上未修复』系统重放 —— 期望复现 bug(FAIL)
[FAIL] RT-001 (T-1001) 工具 verify_refund_eligibility 缺失
[FAIL] RT-002 (T-1002) process_refund 调用 3 次, 失败 3 次, 末次失败
[FAIL] RT-003 (T-1003) check_stock 最大延迟 8300ms, 阈值 5000ms
(B) 对『修复后』系统重放 —— 期望修复被验证(PASS)
[PASS] RT-001 (T-1001) 工具 verify_refund_eligibility 出现
[PASS] RT-002 (T-1002) process_refund 调用 2 次, 失败 1 次, 末次成功
[PASS] RT-003 (T-1003) check_stock 最大延迟 400ms, 阈值 5000ms
小结:复现 bug 3/3 条;修复后通过 3/3 条。
```
Mock Issue example written to `output/github_issues.json`:
```
title : [P0][order_service] 未进行退款资格校验
labels : ['module:order_service', 'priority:critical', 'auto-diagnosis']
body : ## 问题描述 ... ## 关联回归测试用例 - RT-001 (轨迹 T-1001 第 3 轮) ...
```
### Regression assert DSL (built into replay)
Generated cases must use one of:
- `step_present` `{tool}`: tool must appear (e.g. mandatory pre-check).
- `tool_succeeds` `{tool}`: tool eventually succeeds; no “many fails then fake success”.
- `latency_under` `{tool, threshold_ms}`: single-call latency under threshold.
- `final_status_is` `{value}`: final task status equals value.
### Adapt / extend
- **Model**: `OPENAI_MODEL` or `python demo.py --model <name>`. Default `gpt-5.6-luna`, JSON mode.
- **Provider**: official `openai` SDK + `OPENAI_BASE_URL` + provider key/model, e.g.:
```bash
export OPENAI_BASE_URL=https://api.moonshot.cn/v1
export OPENAI_API_KEY=your-openai-api-key
export OPENAI_MODEL=kimi-k3
python demo.py
```
- **Logs**: replace `data/trajectories.jsonl` (`trajectory_id / task / task_input / turns[] / final_status`; turns with `module/tool/input/output/status/latency_ms`); update `architecture.md` / `PRD.md`. Adjust `sut.py` / `replay.py` if fields differ.
- **Real GitHub MCP**: next section; `GITHUB_TOKEN` + `GITHUB_REPO` + `--create-issue`.
### Real GitHub MCP (token required; default mock)
Default is mock; `--create-issue` goes live. Implementation in `github_mcp._create_issues_via_mcp()`: MCP client (`mcp` SDK, stdio) to official GitHub MCP Server, `create_issue` with `build_issue()` fields.
1. GitHub PAT with `repo` → `.env` `GITHUB_TOKEN`; set `GITHUB_REPO=owner/repo`.
2. Machine can start official GitHub MCP Server (default Docker `ghcr.io/github/github-mcp-server`); override with `GITHUB_MCP_COMMAND` (token as `GITHUB_PERSONAL_ACCESS_TOKEN`).
3. The root `ch5` extra includes the MCP SDK; the compatibility path above also keeps the old project-local install available.
4. `python demo.py --create-issue`. Missing token/repo → tip + mock fallback.
### Limitations
- `sut.py` is a **deterministic sim** so replay can truly pass/fail; real systems need real stubs/replay.
- Diagnosis quality depends on LLM. gpt-5.6-luna stably finds R1/R2/R3 on this data; it often splits R1 across T-1001 and T-1002 (4 issues vs 3 merged). For payment retry asserts it may choose `final_status_is:failed` instead of `tool_succeeds` (fixed SUT ends success → FAIL after fix, e.g. 3/4). Tool names sometimes get module prefixes incompatible with bare-name matching (further fails). `--smoke` built-in cases are deterministic 3/3.
- GitHub create is mock unless `--create-issue` with token + repo + MCP server.
- Trajectory schema is simplified vs production (tokens, sub-agent trees, etc.).
---
## 中文
### 目的
生产环境的 Agent 会产生大量**轨迹日志**(trajectory)。从中识别问题、定位根因、构建回归测试成本很高。
本实验让一个诊断 Agent 自动完成这条流水线:
**读轨迹集合 + 架构文档 + PRD → 定位问题、生成结构化报告 → 生成回归测试用例 → 重放框架真正执行验证 → (mock) 通过 MCP 对接 GitHub 创建 Issue。**
### 诊断流水线
```
data/trajectories.jsonl (含已知问题的生产轨迹)
data/architecture.md (系统架构) ┐
data/PRD.md (产品需求) ├─► [LLM] diagnose() 结构化问题报告(优先级/模块/描述/建议)
┘ │
[LLM] gen_test_cases() 回归测试用例(引用轨迹ID+交互轮次)
replay.py 重放框架 ── 对同一输入重放被测系统并断言
(A) 未修复系统 → FAIL(复现bug)
(B) 修复后系统 → PASS(验证修复)
github_mcp.py (mock) 渲染并打印/落盘 GitHub Issue
```
- `diagnoser.py`:诊断 Agent,两次真实调用 OpenAI(默认 gpt-5.6-lunaJSON 模式)。
- `sut.py`:被测系统的**确定性仿真器**。`fixed=False` 复现线上 bug`fixed=True` 模拟修复后行为。
- `replay.py`:回归测试重放框架。取轨迹输入 → 重放 `sut` → 在新轨迹上求值断言(内置 4 种断言 DSL)。
- `github_mcp.py`GitHub Issue 创建,默认 mock(打印 + 写 `output/github_issues.json`)。
### 预置的已知问题(Agent 应能定位)
| 轨迹 | 问题 | 违反 PRD | 定位模块 |
|------|------|----------|----------|
| T-1001 / T-1002 | 退款前**跳过**了强制的 `verify_refund_eligibility` 校验 | R1 (P0) | order_service |
| T-1002 | `process_refund` **反复失败**、无退避、且最终误报成功 | R2 (P0) | payment_service |
| T-1003 | `check_stock` 延迟 8300ms **超时未降级** | R3 (P1) | inventory_service |
| T-1004 | 正常轨迹(对照组,无问题) | — | — |
### 运行
```bash
# 在仓库根目录使用统一的第 5 章环境
uv sync --locked --python 3.12 --extra ch5
# 切换目录前先激活环境:
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell.\.venv\Scripts\Activate.ps1
# Windows cmd.venv\Scripts\activate.bat
# 未安装 uv 时可用 pip 兜底:
# python -m pip install -e ".[ch5]"
cd chapter5/log-diagnosis
# 迁移期间仍支持单项目兼容路径:
# python -m pip install -r requirements.txt
cp env.example .env # 填入 OPENAI_API_KEY(模型默认 gpt-5.6-luna);未配置时设 OPENROUTER_API_KEY 自动改走 OpenRouter
python demo.py # 完整流程(两次真实 LLM 调用)
```
`demo.py` 一次跑完:读轨迹 → 诊断报告 → 回归测试用例 → 重放执行(通过/失败) → (mock) GitHub Issue。
常用参数(`python demo.py -h` 查看全部):
- `--smoke`:**免 API 快速自检**,跳过 LLM,用内置诊断结果仅跑重放框架 + GitHub mock,验证管道是否端到端连通(全绿退出码 0)。适合无 Key 环境或 CI。
- `--model gpt-5.6`:临时覆盖模型(等价于设置 `OPENAI_MODEL`)。
- `--data-dir DIR`:换用自己的输入目录(需含 `trajectories.jsonl` + `architecture.md` + `PRD.md`,默认 `data/`)。
- `--output FILE`mock GitHub Issue 的落盘路径(默认 `output/github_issues.json`)。
- `--create-issue`:**经 MCP 在真实仓库创建 Issue**(需 `GITHUB_TOKEN` + `GITHUB_REPO`,见下节;缺失时自动回退 mock)。
- `--no-github`:跳过步骤 4,不生成 GitHub Issue。
### 真实运行输出(节选)
诊断阶段,Agent 定位到全部 3 个预置问题:
```
[问题 1] 未进行退款资格校验
优先级 : P0 模块: order_service PRD: R1
轨迹 : ['T-1001', 'T-1002'] 关键轮次: [3]
[问题 2] 支付重试机制未正确实现
优先级 : P0 模块: payment_service PRD: R2
[问题 3] 库存查询延迟未降级处理
优先级 : P1 模块: inventory_service PRD: R3
```
回归测试用例被重放框架**真正执行**(先复现 bug、再验证修复):
```
(A) 对『线上未修复』系统重放 —— 期望复现 bug(FAIL)
[FAIL] RT-001 (T-1001) 工具 verify_refund_eligibility 缺失
[FAIL] RT-002 (T-1002) process_refund 调用 3 次, 失败 3 次, 末次失败
[FAIL] RT-003 (T-1003) check_stock 最大延迟 8300ms, 阈值 5000ms
(B) 对『修复后』系统重放 —— 期望修复被验证(PASS)
[PASS] RT-001 (T-1001) 工具 verify_refund_eligibility 出现
[PASS] RT-002 (T-1002) process_refund 调用 2 次, 失败 1 次, 末次成功
[PASS] RT-003 (T-1003) check_stock 最大延迟 400ms, 阈值 5000ms
小结:复现 bug 3/3 条;修复后通过 3/3 条。
```
mock GitHub Issue 打印并写入 `output/github_issues.json`,示例:
```
title : [P0][order_service] 未进行退款资格校验
labels : ['module:order_service', 'priority:critical', 'auto-diagnosis']
body : ## 问题描述 ... ## 关联回归测试用例 - RT-001 (轨迹 T-1001 第 3 轮) ...
```
### 回归测试断言 DSLreplay 框架内置)
Agent 生成的测试用例须使用以下断言之一,框架可自动求值:
- `step_present` `{tool}`:某工具必须出现(如强制前置校验)。
- `tool_succeeds` `{tool}`:某工具最终成功、且不存在"多次失败后误报成功"。
- `latency_under` `{tool, threshold_ms}`:某工具单次延迟低于阈值。
- `final_status_is` `{value}`:任务最终状态等于给定值。
### 如何适配/扩展
- **换模型**:设置 `OPENAI_MODEL`(或 `python demo.py --model <名称>`)。`diagnoser.py` 默认 `gpt-5.6-luna`,均走 JSON 模式;更强模型对复杂/隐性问题更稳。
- **换供应商**:本项目用官方 `openai` SDK,只需再设 `OPENAI_BASE_URL` 指向兼容 OpenAI 接口的服务(如 Moonshot / 火山方舟 / 本地 vLLM),配合该供应商的 `OPENAI_API_KEY` 与 `OPENAI_MODEL` 即可,无需改代码。例如:
```bash
export OPENAI_BASE_URL=https://api.moonshot.cn/v1
export OPENAI_API_KEY=your-openai-api-key # 该供应商的 Key
export OPENAI_MODEL=kimi-k3
python demo.py
```
- **换日志**:把你自己的生产轨迹按 `data/trajectories.jsonl` 的结构(`trajectory_id / task / task_input / turns[] / final_status``turns` 内含 `module/tool/input/output/status/latency_ms`)落盘替换即可;同时更新 `data/architecture.md` 与 `data/PRD.md` 作为诊断依据。若轨迹字段不同,`sut.py`(重放桩)与 `replay.py`(断言求值)按新字段小幅调整。
- **接入真实 GitHub MCP**:见下一节,通过 `GITHUB_TOKEN` + `GITHUB_REPO` + `--create-issue` 把 `mock=False` 接通。
### 接入真实 GitHub MCP(需 token,默认 mock
本实验默认 mock`--create-issue` 才会真正联网。真实创建的实现已内置在
`github_mcp._create_issues_via_mcp()`:通过 MCP 客户端(`mcp` SDKstdio)连接官方
GitHub MCP Server,逐个调用其 `create_issue` 工具,传入 `build_issue()` 生成的
`title / body / labels / assignees`。启用步骤:
1. 准备一个 GitHub Personal Access Token`repo` 权限),写入 `.env` 的 `GITHUB_TOKEN`
并设置目标仓库 `GITHUB_REPO=owner/repo`。
2. 确保本机可启动官方 GitHub MCP Server。默认启动命令用官方 Docker 镜像
`ghcr.io/github/github-mcp-server`;可用 `GITHUB_MCP_COMMAND` 覆盖为任意暴露
`create_issue` 工具的 MCP Servertoken 经 `GITHUB_PERSONAL_ACCESS_TOKEN` 注入其环境)。
3. 根目录 `ch5` extra 已包含 MCP SDK;上方兼容路径仍保留旧版单项目安装。
4. 运行 `python demo.py --create-issue`。缺少 `GITHUB_TOKEN` / `GITHUB_REPO` 时会打印提示并
自动回退 mock,避免误联网。
### 局限
- 被测系统 `sut.py` 是**确定性仿真**,用于让回归测试可真正重放、给出稳定的通过/失败;真实场景下重放需对接实际系统或录制/回放的依赖桩。
- 诊断质量取决于 LLMgpt-5.6-luna 在本数据集能稳定定位全部 3 类预置问题(R1 校验缺失 / R2 重试误报 / R3 延迟未降级),步骤 1 诊断稳定;但它倾向把 R1「校验缺失」按 T-1001、T-1002 各报一条,故常输出 4 条问题(而非上文示例合并成的 3 条)。步骤 2 生成断言时,它稳定地为「支付重试」问题选用 `final_status_is:failed`(两次实跑均如此)而非上文示例的 `tool_succeeds`——因修复后的被测系统会重试成功(final_status=success),该断言在修复后重放中判 FAIL,使修复后通过数降为 3/4 而非满绿。此外它偶尔给 `step_present`/`latency_under` 的工具名加上模块前缀(如 `order_service.verify_refund_eligibility`),与重放框架按裸工具名匹配不符,会进一步压低修复后通过数(两次实跑分别得到 3/4 与 0/4)。`--smoke` 内置用例则确定性给出 3/3。
- GitHub 创建默认 mock,不联网;`--create-issue` 才经真实 MCP Server 联网创建(需 token + repo + 可用的 GitHub MCP Server)。
- 轨迹格式为简化示意,生产环境轨迹字段更丰富(token 用量、子 Agent 调用树等)。
---
## Notes / 说明
- Prefer `--smoke` without a key. / 无 Key 优先 `--smoke`。
- Commands/code/paths/env vars are identical in both language sections. / 命令、代码、路径与环境变量在中英文两侧保持一致。