# LLM 后台架构观测标签页设计 ## 1. 背景 当前首页提供 `GEO 文章优化` 和 `普通文案优化` 两个功能标签。GEO 主流程通过 `POST /api/jobs/optimize-stream` 返回 NDJSON 事件,前端可以看到事实卡、优化草稿、质量检查、定向修复和终稿等阶段性结果。 所有真实模型调用集中在 `src/lib/llm/client.ts`。该客户端目前会向服务端控制台打印 `[llm:start]`、`[llm:response]`、`[llm:validated]` 和 `[llm:error]`,并通过审计回调保存 Provider、模型、任务名、耗时、Schema 状态和输入输出哈希。现有持久化审计不保存 SDK 实际发送的完整请求对象,也不保存 SDK 收到的完整响应对象,因此前端不能准确还原一次 LLM 调用。 本功能增加第三个首页标签 `后台架构`,用于只读展示文章优化后台的真实执行过程,重点呈现每次 LLM 调用的完整请求、完整响应和校验结果。架构图必须由后端真实任务事件驱动,而不是前端按预估时间播放动画。 ## 2. 目标 本功能需要同时服务两类受众: - 客户或非技术人员通过默认摘要视图理解文章经历了哪些处理阶段。 - 已授权的研发或运营人员查看完整的 LLM 请求、响应、耗时、Token 用量、错误和 Schema 校验结果。 完成后,用户应当能够: 1. 在首页切换到 `后台架构` 标签。 2. 观察当前任务的架构节点随真实后台状态变化。 3. 在没有运行中任务时查看最近一次终态任务。 4. 按真实发生顺序选择任意一次 LLM 调用。 5. 在授权后查看 SDK 实际发送的完整请求对象。 6. 在授权后查看 SDK 收到的完整响应 JSON。 7. 区分 Provider 失败、响应解析失败、Schema 校验失败和业务质检未通过。 ## 3. 非目标 本次不包含: - 浏览、筛选或搜索全部历史任务的完整 LLM 日志。 - 在架构页发起、重试、取消或管理任务。 - 接入 OpenTelemetry、Sentry、Grafana 或其他外部可观测平台。 - 将现有优化流程改造成持久化队列、Durable Workflow 或后台任务系统。 - 为已有历史案例补造过去没有保存的请求或响应。 - 展示 API Key、Authorization、HTTP 请求头或底层网络数据包。 - 改变现有文章优化、质量检查、定向修复和导出业务规则。 ## 4. 已选方案 采用一等公民的 LLM 调用事件方案。 `src/lib/llm/client.ts` 在模型请求前后产生包含真实对象的内部追踪事件。任务追踪收集器先持久化完整正文,再通过现有 NDJSON 响应流向首页发送不含正文的状态事件。架构标签页与 GEO 标签共享当前页面的任务状态,并在用户选择某次调用后通过授权接口按需读取完整正文。页面刷新后,通过只读追踪接口恢复运行中任务已经保存的事件,或读取最近一次终态任务。 没有采用以下方案: - 前端根据阶段事件和控制台文本日志重建调用过程。该方案无法保证完整请求和响应与真实 SDK 数据一致。 - 外部可观测平台。该方案适合更大规模的运维场景,但超出当前产品范围。 ## 5. 总体架构 ```mermaid flowchart LR subgraph UI["浏览器界面"] GEO["GEO 文章优化标签"] ARCH["后台架构标签
只读观察器"] end subgraph API["Next.js / Cloudflare Worker"] STREAM["POST /api/jobs/optimize-stream
NDJSON 实时事件"] TRACEAPI["只读 LLM Trace API
刷新后恢复"] COLLECTOR["任务追踪收集器"] end subgraph WORKFLOW["真实文章优化工作流"] FACT["fact_extractor
事实提取"] DRAFT["article_optimizer
生成优化稿"] QA["quality_inspector
质量检查"] REWRITE["targeted_rewriter
定向修复"] FACT --> DRAFT --> QA QA -->|"未通过,最多两轮"| REWRITE REWRITE -->|"重新检查"| QA end CLIENT["src/lib/llm/client.ts
唯一 LLM 调用边界"] LLM["DeepSeek / OpenAI-compatible API"] D1[("D1
任务、调用索引、状态")] R2[("R2
完整请求与响应 JSON")] AUDIT[("案例结果版本
摘要与内容哈希")] GEO -->|"携带访问密钥发起任务"| STREAM STREAM --> FACT FACT & DRAFT & QA & REWRITE --> CLIENT CLIENT -->|"SDK 实际请求对象"| LLM LLM -->|"SDK 完整响应对象"| CLIENT CLIENT -->|"started / responded / validated / failed"| COLLECTOR COLLECTOR -->|"实时事件"| STREAM STREAM -->|"同一任务流"| GEO GEO -.->|"切换标签,共享当前任务状态"| ARCH COLLECTOR --> D1 COLLECTOR --> R2 COLLECTOR -->|"任务结束后写入"| AUDIT ARCH -->|"携带访问密钥读取"| TRACEAPI TRACEAPI --> D1 TRACEAPI --> R2 ``` ### 5.1 真实请求的定义 “完整请求”指应用完成默认值合并后、立即传给 OpenAI-compatible SDK 的请求对象,包括: - `model` - `temperature` - `response_format` - 按实际顺序排列的完整 `messages` - 后续真实加入 SDK 请求的其他非敏感参数 追踪对象不包含 SDK 客户端构造参数、API Key、Authorization 或 HTTP 请求头。 ### 5.2 真实响应的定义 “完整响应”指 SDK 返回、但尚未提取 `choices[0].message.content`、执行 JSON 解析或 Zod 校验之前的完整可序列化响应对象。它应保留 Provider 实际返回的字段,包括存在时的: - `id` - `object` - `created` - `model` - `choices` - `finish_reason` - `usage` - Provider 返回的其他可序列化非敏感字段 若 Provider 调用没有返回正常响应,则保存请求和安全化错误对象,不伪造响应。 ## 6. 模块边界 ### 6.1 LLM 客户端 `src/lib/llm/client.ts` 继续作为唯一模型调用边界,负责: - 构造最终 SDK 请求对象。 - 在调用前把最终 SDK 请求对象交给追踪收集器。 - 保存 SDK 返回的完整响应对象。 - 在 JSON 解析和 Schema 校验后把对应结果交给追踪收集器。 - 将现有审计摘要从同一份追踪事实派生出来。 工作流节点只传递任务名、修复轮次和追踪回调,不自行拼装日志。 ### 6.2 任务追踪收集器 新增独立的任务追踪收集器,负责: - 为每次调用分配稳定 `call_id` 和递增 `sequence`。 - 将 LLM 事件映射到任务、案例、工作流阶段和修复轮次。 - 先保存请求或响应正文,再将不含完整正文的实时状态事件发送给现有优化流。 - 增量写入 D1 索引和 R2 正文。 - 在任务结束后执行完整日志保留清理。 - 生成现有结果版本需要的审计摘要和哈希。 追踪收集器不参与 Prompt 构造、文章优化或业务质检判断。 ### 6.3 追踪存储 D1 使用 `llm_trace_runs` 和 `llm_trace_calls` 两张表保存轻量、可查询的结构化数据: - 追踪任务标识、任务状态和当前阶段。 - 调用顺序、任务名、Provider、模型和耗时。 - 请求及响应的 R2 对象键。 - Schema 名称、校验状态和问题摘要。 - 错误分类、错误摘要和追踪完整性状态。 - 创建、响应、校验和完成时间。 R2 保存完整正文: - `llm-traces///request.json` - `llm-traces///response.json` R2 对象保持私有,只能通过应用 API 读取。应用不返回公开对象 URL。 ### 6.4 架构观察组件 前端组件只负责: - 消费当前页面已经收到的追踪事件。 - 在刷新后读取追踪清单。 - 按调用选择并按需读取请求或响应正文。 - 根据后端事件映射固定架构图的节点状态。 - 以纯文本方式渲染 JSON。 组件不推断不存在的阶段,不解析服务器控制台日志,也不发起业务操作。 ## 7. 追踪数据模型 ### 7.1 Trace Run 每个文章优化任务对应一个追踪运行记录,至少包含: - `job_id` - `case_id` - `status`: `running | completed | failed | interrupted` - `current_stage` - `trace_completeness`: `complete | incomplete` - `started_at` - `finished_at` - `error_stage` - `error_summary` ### 7.2 Trace Call 每次 LLM 调用对应一个记录,至少包含: - `call_id` - `job_id` - `sequence` - `task` - `workflow_stage` - `rewrite_round` - `provider` - `model` - `status`: `started | responded | validated | failed` - `request_object_key` - `response_object_key` - `schema_name` - `schema_valid` - `validation_issues` - `duration_ms` - `started_at` - `responded_at` - `validated_at` - `error_type` - `error_summary` 完整请求和响应不重复写入结果版本的 D1 JSON。结果版本继续保存紧凑审计摘要和哈希。 ## 8. 事件契约 现有 `OptimizationStreamEvent` 联合类型增加以下事件: ### 8.1 `llm_call_started` SDK 调用前产生。内部追踪事件包含最终请求对象;写入 R2 后,NDJSON 事件只包含调用标识、任务信息、Provider、模型、最终参数摘要和 `request_available: true`。架构页收到后立即将对应节点标记为运行中,用户打开“请求”视图时再调用授权接口读取完整对象。 ### 8.2 `llm_call_responded` SDK 正常返回后、解析前产生。内部追踪事件包含完整响应对象;写入 R2 后,NDJSON 事件只包含调用标识、耗时、Token 用量和 `response_available: true`。架构页允许立即切换到响应视图并按需读取完整对象。 ### 8.3 `llm_call_validated` JSON 解析和 Zod 校验后产生。包含 Schema 名称、校验结果和完整问题列表。业务质检结果 `pass` 或 `fail` 与 Schema 是否有效分开表达。 ### 8.4 `llm_call_failed` Provider、JSON 解析或 Schema 校验造成调用失败时产生。包含错误分类和安全化错误详情。已经保存的请求或响应继续可见。 ### 8.5 `trace_warning` 请求或响应正文持久化、追踪索引更新或清理失败时产生。包含安全化错误摘要和 `trace_completeness: incomplete`,但不改变文章优化业务状态。 事件必须按单次调用顺序发送。不同调用通过 `call_id` 区分,通过 `sequence` 排列。顺序固定为: - 成功:`started -> responded -> validated(success=true)`。 - Provider 失败:`started -> failed(provider)`。 - JSON 解析失败:`started -> responded -> failed(json_parse)`。 - Schema 失败:`started -> responded -> validated(success=false) -> failed(schema_validation)`。 ## 9. API 设计 ### 9.1 实时事件 `POST /api/jobs/optimize-stream` 保持当前请求方式和 NDJSON 格式,并增加 LLM 追踪事件。现有文章结果事件保持兼容。 ### 9.2 最近追踪 `GET /api/llm-traces/latest` - 有运行中任务时返回运行中任务的追踪清单。 - 没有运行中任务时返回最近一次终态任务。 - 没有任何追踪记录时返回明确的空状态,不返回错误页面。 ### 9.3 任务追踪清单 `GET /api/jobs/:jobId/llm-trace` 返回任务状态、当前阶段、架构节点状态和按顺序排列的调用元数据,不内嵌大体积请求或响应正文。 ### 9.4 完整请求 `GET /api/jobs/:jobId/llm-trace/:callId/request` 返回该调用保存的完整请求 JSON。 ### 9.5 完整响应 `GET /api/jobs/:jobId/llm-trace/:callId/response` 返回该调用保存的完整响应 JSON。调用仍在进行时返回明确的等待状态;调用失败且无响应时返回明确的无响应状态。 所有追踪读取接口复用现有访问密钥校验,并设置 `Cache-Control: no-store`。 ## 10. 保留策略 完整日志只保留: - 当前仍在运行的任务。 - 最近一次进入终态的任务。 当新任务进入终态后: 1. 它成为最近一次终态任务。 2. 删除更旧终态任务的请求和响应 R2 对象。 3. 删除更旧终态任务的 `llm_trace_runs` 和 `llm_trace_calls` 记录。 4. 保留案例结果版本中已有的审计摘要、错误摘要和输入输出哈希。 若存在多个并发运行任务,不删除任何运行中任务的完整记录。最近一次终态任务按完成时间确定。 ## 11. 页面与交互设计 ### 11.1 应用标签 首页导航增加第三项: - `GEO 文章优化` - `普通文案优化` - `后台架构` `后台架构` 是只读观察器。切换标签不取消正在读取的优化流,也不清空首页中的当前任务状态。 ### 11.2 页面结构 页面按以下顺序组成: 1. 任务状态条:显示当前任务或最近一次任务、任务 ID、Provider、模型、状态和技术详情授权状态。 2. 固定执行图:显示输入归一化、事实提取、生成草稿、质量检查、定向修复、保存与导出。 3. LLM 调用轨迹:按 `sequence` 显示每次实际调用;复检和多轮修复均为独立记录。 4. 调用详情:提供 `请求`、`响应`、`校验` 三个子视图。 ### 11.3 架构图状态 固定拓扑不随调用次数改变,节点状态由真实事件更新: - `waiting`: 尚未开始。 - `running`: 当前正在处理。 - `completed`: 已成功完成。 - `failed`: Provider、解析或 Schema 失败。 业务质检未通过不是技术失败。质量检查节点应显示“检查完成,需要修复”,然后高亮定向修复节点,并在修复后回到质量检查节点。 ### 11.4 分级展示 追踪页面整体复用现有访问密钥校验。访问密钥缺失或无效时,不返回任务清单、摘要或正文。授权成功后,页面默认进入客户友好视图,只展示: - 阶段名称和中文说明。 - Provider、模型、耗时和 Token 用量。 - 请求与响应的脱敏摘要。 - Schema 和业务结果。 用户切换到技术详情后,前端通过同一访问密钥按需加载: - 完整 SDK 请求对象。 - 完整 SDK 响应对象。 - 完整 Schema 校验问题。 - 安全化错误对象。 前端不把完整请求或响应内嵌在任务清单和 NDJSON 状态事件中,也不依靠 CSS 隐藏已经下载的敏感内容。 ### 11.5 空状态和恢复 - 无当前或历史追踪:解释需要先在 GEO 标签运行一次优化。 - 当前任务:实时跟随 NDJSON 事件。 - 最近一次任务:明确标记为历史快照,避免误认为仍在运行。 - 追踪不完整:显示已保存数据,并明确指出缺失阶段或正文。 ## 12. 安全与隐私 - API Key、Authorization 和请求头永不进入追踪事件、D1、R2 或前端状态。 - Provider 错误采用字段白名单序列化,只保留安全的状态码、错误类型、错误码和消息。 - R2 追踪对象保持私有。 - 所有追踪读取接口都要求访问密钥,并禁用缓存。 - 前端通过文本节点或 JSON 文本组件展示内容,不使用 `dangerouslySetInnerHTML`。 - 模型响应中的 HTML、Markdown 或脚本不会作为可执行页面内容渲染。 - 完整请求和响应不进入 Git、导出文章文件或客户端持久化存储。 ## 13. 错误处理 ### 13.1 Provider 失败 保存完整请求和安全化错误,产生 `llm_call_failed`,将对应架构节点标记为失败,并保持现有优化错误上抛行为。 ### 13.2 JSON 解析失败 保存完整原始响应和解析错误。响应视图仍可读取完整响应,校验视图标记为未进入 Schema 校验。 ### 13.3 Schema 校验失败 保存完整响应、Schema 名称和所有 Zod 问题。架构页区分 Schema 失败与 QA 业务检查未通过。 ### 13.4 追踪持久化失败 追踪系统不得使原本可以完成的文章优化失败。持久化异常产生 `trace_warning`,将 `trace_completeness` 设为 `incomplete`,主流程继续执行,架构页显示“追踪记录不完整”。 ### 13.5 连接中断 切换首页标签不会中断同一页面中的流式请求。页面刷新或网络中断后,架构页可以恢复已经增量保存的事件,但本次功能不承诺现有非持久化工作流在浏览器请求断开后继续执行。 ## 14. 测试策略 ### 14.1 LLM 客户端单元测试 - 保存的请求对象与传给 SDK mock 的对象深度相等。 - 保存的响应对象与 SDK mock 返回对象深度相等。 - 请求事件先于 Provider 调用产生。 - 响应事件先于正文解析和 Schema 校验产生。 - API Key、Authorization 和请求头不出现在任何追踪对象中。 ### 14.2 追踪存储测试 - D1 元数据和 R2 请求、响应对象键正确关联。 - 调用顺序、任务名、阶段和修复轮次正确。 - 每个事件增量持久化。 - 并发运行任务不会被清理。 - 新终态任务产生后,旧完整正文被删除,审计摘要和哈希仍保留。 ### 14.3 API 测试 - NDJSON 事件顺序为 `started -> responded -> validated`。 - Provider、解析和 Schema 失败产生正确的失败事件。 - 未授权读取完整追踪返回 401。 - 请求或响应尚不存在时返回明确状态。 - 所有读取响应包含 `Cache-Control: no-store`。 ### 14.4 UI 组件测试 - 覆盖无任务、当前任务、最近任务、成功、失败和追踪不完整。 - 选择不同调用会同步更新架构节点和详情。 - 请求、响应、校验子视图显示对应数据。 - 业务质检未通过不会显示为 Provider 或 Schema 技术失败。 - 未授权状态不下载完整正文。 ### 14.5 浏览器端到端测试 - 从 GEO 标签发起任务后切换到后台架构标签,优化流继续。 - 每次调用按真实顺序出现。 - 修复循环在固定架构图中正确高亮。 - 刷新后可读取已经保存的当前状态或最近终态任务。 - 移动端和窄屏下调用清单与详情改为纵向排列,无横向溢出。 ### 14.6 真实 Provider 冒烟测试 使用安全样例调用真实 DeepSeek,核对: - 模型和最终参数。 - 完整 `messages` 顺序与正文。 - 完整响应 `choices`、`finish_reason` 和 `usage`。 - Schema 校验结果。 - 架构节点最终状态。 真实 Provider 冒烟测试不进入普通 CI,避免消耗额度并避免依赖生产密钥。 实现完成后运行: ```bash npm run lint npm test npm run build ``` ## 15. 验收标准 功能完成必须满足: 1. 首页出现 `后台架构` 标签,且为只读观察器。 2. 架构节点由真实后端事件驱动,不使用模拟计时。 3. 技术详情中的请求与传给 SDK 的对象一致。 4. 技术详情中的响应与 SDK 返回对象一致。 5. 完整请求、完整响应和 Schema 校验可以按调用查看。 6. 当前任务和最近一次终态任务可被恢复。 7. 更旧任务不保留完整正文,但现有审计摘要和哈希继续存在。 8. API Key、Authorization 和请求头不会被记录。 9. 追踪失败不会使文章优化失败,但会明确显示记录不完整。 10. Provider 失败、解析失败、Schema 失败和业务质检未通过在界面中可以区分。 11. 单元、集成、组件、端到端测试以及项目 lint、test、build 全部通过。