12 KiB
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 仍保留旧的“分析文章 -> 确认事实卡 -> 开始优化”路径,后续实现时应新增或替换为当前一键流式路径。
样例文件形状为:
{
"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 模式,但不是默认入口。
服务启动策略
默认策略是启动专用本地服务:
- 读取
.env.local,合并当前 shell 环境。 - 为本次运行创建报告目录。
- 设置
APP_DATA_DIR=<reportDir>/app-data,隔离 SQLite 数据和导出文件。 - 找一个可用端口,优先
3000,被占用时使用下一个可用端口。 - 运行
npm run dev -- --port <port>。 - 用
curl或 Playwright request 确认首页返回200 OK。 - 捕获服务 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。
浏览器操作流程
每篇样例使用新的页面,减少状态串扰。
页面操作按当前实现执行:
- 打开首页。
- 如果存在
访问密钥输入框,填写 API key。 - 填写文章输入框。当前标签是
文章内容,后续实现可兼容粘贴文章。 - 填写
图片描述或图片链接。 - 选择
目标平台。 - 填写
用户要求。 - 点击
开始优化。 - 等待事实卡区从待生成状态进入可见事实卡状态。
- 等待结果区出现草稿、终稿或完成提示。
- 等待
质量报告出现。 - 等待
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返回 200,content-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 和错误文本。
报告输出
报告目录建议为:
test-results/geo-sample-flow/<timestamp>/
目录结构:
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 保存机器可读信息:
{
"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 保存:
{
"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。
- 成功样例展示事实卡、优化结果、质量报告和三个导出入口。
- 导出文件通过带密钥请求验证可读。
- 失败样例保留可复查证据,而不是只返回一行超时或错误。
- 最终报告能回答三个问题:哪些样例跑通了,失败在哪里,当前实现是否值得人工继续验收。