# 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 `:只跑指定样例。 - `--limit `:只跑前 n 篇样例。 - `--headed`:打开可见浏览器,方便人工观察。 - `--reuse-server`:复用已有服务。 - `--mock`:开发调试用 mock 模式,但不是默认入口。 ## 服务启动策略 默认策略是启动专用本地服务: 1. 读取 `.env.local`,合并当前 shell 环境。 2. 为本次运行创建报告目录。 3. 设置 `APP_DATA_DIR=/app-data`,隔离 SQLite 数据和导出文件。 4. 找一个可用端口,优先 `3000`,被占用时使用下一个可用端口。 5. 运行 `npm run dev -- --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 可能较慢,超时值必须可通过参数覆盖。 ## 导出验证 当前导出链接是页面上的普通 ``,但导出 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`。 如果未来产品要求“用户直接点击即可下载”,应另加一个点击下载断言。按当前实现,该断言预期会暴露访问密钥无法通过 `` 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// ``` 目录结构: ```text summary.json summary.md server.log samples/ / 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。 - 成功样例展示事实卡、优化结果、质量报告和三个导出入口。 - 导出文件通过带密钥请求验证可读。 - 失败样例保留可复查证据,而不是只返回一行超时或错误。 - 最终报告能回答三个问题:哪些样例跑通了,失败在哪里,当前实现是否值得人工继续验收。