新增样例文章自动验收工作流设计

This commit is contained in:
czj
2026-07-01 14:40:55 +08:00
parent 016b653b76
commit 28bf0b4135
@@ -0,0 +1,329 @@
# GEO 样例文章 E2E skill 工作流设计
## 目标
设计一个未来可以包装成 Codex skill 的自动验收工作流。用户启动该 skill 后,系统使用 `samples/articles/*.json` 中的测试文章,打开当前网页,按真实用户路径完成一键流式优化,并生成可复查的测试报告。
默认模式必须使用当前真实 LLM 配置,不使用 mock,不静默降级。本工作流的定位是“近似人工验收”的自动化,而不是快速单元测试或只测 API 的冒烟测试。
## 当前实现依据
当前首页主路径是一键流式优化:
- 用户填写 `访问密钥`
- 用户在文章输入区填写 `文章内容``图片描述或图片链接``目标平台``用户要求`
- 用户点击 `开始优化`
- 前端调用 `POST /api/jobs/optimize-stream`,读取 NDJSON 流式事件。
- 页面依次展示事实卡、草稿或终稿、质量报告、导出入口。
- 终稿到达后显示 `optimized.md``optimized.docx``qa_report.json` 三个导出链接。
现有 `tests/e2e/mvp.spec.ts` 仍保留旧的“分析文章 -> 确认事实卡 -> 开始优化”路径,后续实现时应新增或替换为当前一键流式路径。
样例文件形状为:
```json
{
"name": "Sample name",
"input": {
"title": "",
"body": "",
"image_lines": "",
"platform": "official_site",
"user_instructions": ""
},
"expectedHardFailures": [],
"expectedWarnings": []
}
```
## 工作流范围
本设计覆盖:
- 本地启动或复用 Next.js 开发服务。
- 读取 `samples/articles` 下的 JSON 样例。
- 使用 Playwright 打开网页并完成当前 UI 主流程。
- 使用真实 LLM 跑事实提取、文章优化、QA 和必要的定向修复。
- 验证页面结果、质量报告和导出文件。
- 为每篇样例保存截图、日志、耗时和结构化结论。
- 输出汇总报告,供用户判断当前实现是否可交付或需要排查。
本设计不覆盖:
- 自动修复失败。
- token 级模型流式输出验证。
- 线上 Cloudflare 环境压测。
- 多用户协作、权限矩阵或发布平台 API。
- 把测试报告提交进 Git。
## 总体架构
建议把能力拆成三层。
第一层是仓库内 runner,作为真实执行入口。它负责读取配置、启动服务、调用 Playwright、收集报告。未来可以通过 `npm run test:e2e:samples` 或等价命令运行。
第二层是 Playwright 测试和辅助库。它负责页面操作、等待流式结果、下载导出文件、截图和 trace 采集。
第三层是 Codex skill 包装器。skill 本身保持轻薄,只做前置说明、参数解析、运行仓库命令、总结结果。仓库脚本仍是行为来源,避免把项目选择器和业务断言硬编码进用户级 skill。
## 默认执行模式
默认模式为 `live`
-`.env.local` 和当前 shell 环境读取配置。
- 要求 `API_ACCESS_KEY` 可用,除非 `API_AUTH_DISABLED=true`
- 要求当前 `LLM_PROVIDER` 对应的真实 API key 已配置。
- 不安装 mock handler。
- 不把 LLM/provider/schema 错误转换成通过。
- 每篇样例顺序执行,默认并发为 1,避免真实 LLM 请求互相干扰或触发限流。
可选模式可以后续增加:
- `--sample <name-or-glob>`:只跑指定样例。
- `--limit <n>`:只跑前 n 篇样例。
- `--headed`:打开可见浏览器,方便人工观察。
- `--reuse-server`:复用已有服务。
- `--mock`:开发调试用 mock 模式,但不是默认入口。
## 服务启动策略
默认策略是启动专用本地服务:
1. 读取 `.env.local`,合并当前 shell 环境。
2. 为本次运行创建报告目录。
3. 设置 `APP_DATA_DIR=<reportDir>/app-data`,隔离 SQLite 数据和导出文件。
4. 找一个可用端口,优先 `3000`,被占用时使用下一个可用端口。
5. 运行 `npm run dev -- --port <port>`
6.`curl` 或 Playwright request 确认首页返回 `200 OK`
7. 捕获服务 stdout/stderr,保存到报告目录。
如果用户显式传入 `--reuse-server`,runner 可以复用已有服务,但报告必须标记:
- `server_mode: "reused"`
- 无法保证数据隔离。
- 无法完整证明 LLM 日志来自本次运行,除非用户同时提供服务日志。
## LLM 参与证明
由于默认是 live 模式,runner 必须做两类证明。
前置证明:
- `LLM_PROVIDER` 有效。
- DeepSeek 模式下 `DEEPSEEK_API_KEY` 存在。
- OpenAI 模式下 `OPENAI_API_KEY` 存在。
- 报告只记录 provider 和 model,不记录密钥。
运行证明:
- 专用服务模式下,从服务日志中提取 `[llm:start]``[llm:response]``[llm:validated]``[llm:error]`
- 每篇成功样例至少应看到 `fact_extractor``article_optimizer``quality_inspector` 任务。
- `targeted_rewriter` 只在 QA 失败触发修复时出现,不作为所有样例的必需任务。
- 如果 LLM 报错或 schema 校验失败,该样例失败,但报告保留真实错误和阶段。
## 样例读取
runner 按文件名排序读取 `samples/articles/*.json`,保证运行顺序稳定。
每个样例先做静态校验:
- `input.body` 必须是非空字符串。
- `input.platform` 必须是当前系统支持的发布平台。
- `input.image_lines``input.user_instructions` 缺失时按空字符串处理。
- `expectedHardFailures``expectedWarnings` 缺失时按空数组处理。
无效样例不进入浏览器流程,直接记为 `sample_invalid`
## 浏览器操作流程
每篇样例使用新的页面,减少状态串扰。
页面操作按当前实现执行:
1. 打开首页。
2. 如果存在 `访问密钥` 输入框,填写 API key。
3. 填写文章输入框。当前标签是 `文章内容`,后续实现可兼容 `粘贴文章`
4. 填写 `图片描述或图片链接`
5. 选择 `目标平台`
6. 填写 `用户要求`
7. 点击 `开始优化`
8. 等待事实卡区从待生成状态进入可见事实卡状态。
9. 等待结果区出现草稿、终稿或完成提示。
10. 等待 `质量报告` 出现。
11. 等待 `optimized.md``optimized.docx``qa_report.json` 三个导出入口出现。
等待终稿的默认超时建议为每篇 10 分钟。长文和真实 LLM 可能较慢,超时值必须可通过参数覆盖。
## 导出验证
当前导出链接是页面上的普通 `<a>`,但导出 API 仍需要 `x-api-key`。普通浏览器点击无法附加自定义 header。因此测试拆成两层:
- UI 层:确认页面显示三个导出入口。
- API 层:用 Playwright request 或 Node fetch 携带 `x-api-key` 请求导出 URL,确认文件可读。
导出断言:
- `optimized.md` 返回 200,正文非空,并包含优化标题或正文片段。
- `optimized.docx` 返回 200content-type 或文件头符合 Word 文档预期。
- `qa_report.json` 返回 200,可解析为 JSON,并包含 `overall_status``checks`
如果未来产品要求“用户直接点击即可下载”,应另加一个点击下载断言。按当前实现,该断言预期会暴露访问密钥无法通过 `<a>` header 传递的问题。
## QA 期望对照
`expectedHardFailures``expectedWarnings` 用于校准样例意图,但第一版不把它们作为硬性失败条件。
原因是 live LLM 输出会波动,而且优化后文章可能已经修复了原文缺陷。测试报告应记录:
- 样例预期的 hard failures。
- 样例预期的 warnings。
- 实际 QA 中 fail/warn/pass 的 rule_id。
- 预期缺陷是否被命中。
- 预期缺陷是否被修复或未复现。
这些差异在 `summary.md` 中作为“质量对照”呈现,不影响浏览器全流程是否通过。
## 失败分类
每篇样例只能有一个主失败分类,方便汇总:
- `preflight_failed`:环境、服务、密钥或 LLM 配置不满足。
- `sample_invalid`:样例 JSON 不符合最低要求。
- `page_flow_failed`:页面选择器、按钮、导航或 UI 状态异常。
- `stream_timeout`:超过单篇超时仍未出现终稿或失败提示。
- `stream_failed`:页面或捕获的流式事件返回失败。
- `llm_failed`LLM provider、网络、额度、schema 校验等真实 LLM 错误。
- `export_failed`:导出入口缺失或带密钥请求导出文件失败。
- `console_error`:页面出现未允许的 console error 或 pageerror。
- `unknown_failed`:无法归类的异常。
失败时必须保留:
- 最后一张页面截图。
- 当前 URL。
- 控制台日志。
- 网络请求摘要。
- 服务日志片段。
- 如果能提取到,保留 LLM task 和错误文本。
## 报告输出
报告目录建议为:
```text
test-results/geo-sample-flow/<timestamp>/
```
目录结构:
```text
summary.json
summary.md
server.log
samples/
<sample-slug>/
result.json
console.jsonl
network.jsonl
final.png
failure.png
trace.zip
exports/
optimized.md
optimized.docx
qa_report.json
```
`summary.json` 保存机器可读信息:
```json
{
"started_at": "2026-07-01T00:00:00.000Z",
"mode": "live",
"provider": "deepseek",
"model": "deepseek-v4-pro",
"base_url": "http://127.0.0.1:3000",
"totals": {
"passed": 0,
"failed": 0,
"skipped": 0
},
"samples": []
}
```
每篇 `result.json` 保存:
```json
{
"file": "samples/articles/title-quality.json",
"name": "title quality sample",
"status": "passed",
"duration_ms": 180000,
"job_id": "job_xxx",
"qa_status": "warn",
"qa_fail_rules": [],
"qa_warn_rules": ["title_quality"],
"expected_hard_failures": [],
"expected_warnings": ["title_quality"],
"exports": {
"optimized.md": "passed",
"optimized.docx": "passed",
"qa_report.json": "passed"
},
"llm_tasks": ["fact_extractor", "article_optimizer", "quality_inspector"]
}
```
`summary.md` 面向人阅读,包含:
- 本次运行环境。
- 总通过率。
- 每篇样例的状态、耗时、QA 状态、失败原因。
- LLM 任务参与情况。
- 导出文件校验结果。
- 需要人工复查的问题列表。
## 安全和 Git 边界
报告目录可能包含模型输出、客户文章、服务日志和导出文件,不能提交进 Git。
后续实现时应确认:
- `test-results/``.gitignore` 忽略。
- 报告不写入真实 API key。
- 日志中的 `x-api-key`、Authorization header 和 provider key 必须脱敏。
- 不清理或覆盖用户已有 `data/`,除非 runner 启动了自己的隔离 `APP_DATA_DIR`
## 后续实现建议
第一阶段:
- 新增 repo 内 sample runner 和 Playwright 辅助函数。
- 新增 `npm run test:e2e:samples`
- 让 runner 默认 live 模式、顺序跑全部样例。
- 更新或替换旧的 `tests/e2e/mvp.spec.ts`,避免继续测试已下线的分步主路径。
第二阶段:
- 增加 `--sample``--limit``--headed``--reuse-server` 参数。
- 报告中加入 LLM 日志解析和 QA 期望对照表。
- 对失败样例生成更易读的 Markdown 复盘。
第三阶段:
- 创建 Codex skill。
- skill 读取本设计约定,调用 repo 命令。
- skill 在最终回复里摘要展示通过率、失败样例、报告路径和下一步排查入口。
## 验收标准
本设计实现后,用户启动 skill 应得到以下结果:
- 本地网页被真实打开。
- `samples/articles` 中的样例被逐篇填入页面。
- 每篇样例真实触发当前 LLM provider。
- 成功样例展示事实卡、优化结果、质量报告和三个导出入口。
- 导出文件通过带密钥请求验证可读。
- 失败样例保留可复查证据,而不是只返回一行超时或错误。
- 最终报告能回答三个问题:哪些样例跑通了,失败在哪里,当前实现是否值得人工继续验收。