Add generation harness runtime

This commit is contained in:
2026-06-21 22:31:38 +08:00
parent 7ebdfb2582
commit 459ca9edef
18 changed files with 2846 additions and 419 deletions

View File

@@ -0,0 +1,493 @@
# Harness Engineering 架构改造技术设计
**项目**: DreamWeaver 梦语织机
**版本**: 0.1
**日期**: 2026-06-21
**状态**: 阶段 0 已建立设计基线
**作者**: Codex
---
## 1. 背景
DreamWeaver 当前已经完成统一生成工作流的第一轮落地:`POST /api/generations` 会创建 `generation_jobs`,后台 worker 执行故事或绘本生成,`generation_job_events` 记录关键过程,前端通过 job 查询和故事状态展示进度、失败与重试入口。
现有架构已经具备 harness engineering 的雏形:
- `story_service` 负责上下文准备、主内容生成、故事落库、资产补全、状态同步和任务收敛。
- `generation_jobs` 负责 job/event、进度摘要、取消、重试和 Provider 聚合。
- `provider_router` 负责 Provider 选择、failover、熔断、成本和调用事件。
- `story_status` 负责把文本、图片、音频状态推导为统一故事状态。
当前主要问题不是缺少能力,而是生成运行时控制逻辑分散在多个 service 内。取消检查、事件记录、资产恢复、Provider 轨迹、失败分类和质量校验还没有形成显式的运行时边界。
本设计的目标,是把现有“隐性 harness”升级为可观测、可恢复、可验证、可演进的 Generation Harness Runtime同时保留当前 API、数据库和前端主行为。
## 2. 目标
### 2.1 产品目标
- 家长提交故事或绘本生成后,能获得稳定、可解释、可恢复的结果。
- 主内容生成成功后,即使图片或音频失败,也不影响阅读。
- 用户能清楚看到任务步骤、失败原因和可重试资产。
- 后续新增语音共创、更多内容模式或新资产类型时,复用同一套运行时模型。
### 2.2 工程目标
-`story_service` 中的运行时控制职责抽到 harness 层。
- 让 workflow step、artifact、trace、failure category 成为一等概念。
- 保持 `/api/generations`、旧兼容接口、现有状态字段和主要测试行为不破坏。
- 优先做渐进式重构,不引入复杂工作流引擎,不进行大爆炸重写。
- 每个大阶段都产出阶段报告,包含实现、审查、验证和风险。
### 2.3 非目标
- 本轮不引入 Temporal、Dagster、LangGraph 等外部工作流引擎。
- 本轮不彻底重做数据库结构。
- 本轮不废弃旧生成接口,只继续保持兼容层指向统一入口。
- 本轮不把 DreamWeaver 抽象成通用 agent 平台,所有抽象必须服务儿童故事生成业务。
- 本轮不要求一次性完成所有 eval、质量门和 replay 能力。
## 3. 架构原则
1. **主内容优先可读**
文本故事或绘本结构是 blocking artifact封面、分页插图、音频是 recoverable artifact。
2. **API 稳定优先**
先重构内部边界再考虑扩展响应字段。现有前端、smoke、测试不应被第一阶段打断。
3. **事件结构稳定**
继续复用 `generation_job_events`,但逐步标准化 metadata避免每个调用点随手定义不同结构。
4. **Provider 不等于产品能力**
Provider 只是 tool invocation 的实现。产品能力应由 capability、workflow step、artifact 和 recovery policy 共同定义。
5. **小步可验证**
每个最小任务都必须能通过单测、局部测试或文档审查验证。
## 4. 目标架构
```mermaid
flowchart TB
API["FastAPI API 层"] --> COMMAND["Generation Command"]
COMMAND --> HARNESS["Generation Harness Runtime"]
HARNESS --> CTX["Context Builder<br/>档案 / 宇宙 / 记忆 / 输入规范化"]
HARNESS --> PLAN["Workflow Plan<br/>story / storybook / asset_generation / asset_retry"]
HARNESS --> CONTROL["Execution Control<br/>取消 / 超时收敛 / 安全检查点"]
HARNESS --> TRACE["Trace Recorder<br/>job events / step metadata / provider trace"]
HARNESS --> ARTIFACT["Artifact Workflows<br/>story_text / storybook_pages / image / audio"]
HARNESS --> GUARD["Quality Gates<br/>schema / 儿童安全 / 内容完整性"]
ARTIFACT --> ROUTER["Provider Router<br/>策略 / failover / 熔断 / 成本"]
ROUTER --> ADAPTERS["Provider Adapters"]
HARNESS --> STATE["State Store<br/>stories / generation_jobs / generation_job_events"]
STATE --> FE["Vue 前端进度 / 详情 / 重试"]
```
## 5. 运行时核心概念
### 5.1 Generation Command
表示一次用户意图,来源包括:
- `POST /api/generations`
- `POST /api/generations/{story_id}/retry-assets`
- `POST /api/generations/jobs/{job_id}/retry`
- 旧兼容接口产生的同步生成调用
标准字段:
| 字段 | 说明 |
| --- | --- |
| `output_mode` | `story``storybook``asset_generation``asset_retry` |
| `input_type` | `keywords``full_story``image``audio` 或资产组合 |
| `user_id` | 当前用户 |
| `story_id` | 已有故事 ID可为空 |
| `request_payload` | 原始请求的 JSON 安全快照 |
### 5.2 Workflow Step
第一阶段先将现有 event type 映射为标准 step不立即改库
| Step | 当前事件 | 是否阻塞主内容 |
| --- | --- | --- |
| `request_acceptance` | `request_accepted``retry_queued` | 是 |
| `worker_start` | `worker_started` | 是 |
| `context_preparation` | `context_prepared` | 是 |
| `narrative_generation` | `narrative_generated` | 是 |
| `story_persistence` | `story_saved` | 是 |
| `image_generation` | `cover_image_*``storybook_*image*` | 否 |
| `audio_generation` | `audio_*` | 否 |
| `postprocessing` | `postprocessing_queued` | 否 |
| `completion` | `generation_completed``asset_*_completed` | 是 |
| `cancellation` | `cancel_requested``generation_canceled` | 是 |
| `stale_recovery` | `generation_stale_failed` | 是 |
### 5.3 Artifact
| Artifact | 来源 | 状态字段 | 恢复策略 |
| --- | --- | --- | --- |
| `story_text` | text provider | `text_status` | 失败则 job failed |
| `storybook_pages` | storybook provider | `text_status` | 失败则 job failed |
| `cover_image` | image provider | `image_status` | 失败后 `degraded_completed`,可重试 |
| `page_image` | image provider | `image_status` | 部分页失败后 `degraded_completed`,可重试 |
| `audio` | tts provider + file cache | `audio_status` | 失败后 `degraded_completed`,可重试 |
| `achievement_memory` | Celery 后处理 | 事件记录 | 失败不影响主内容 |
### 5.4 Failure Category
第一阶段先在代码中定义枚举和 metadata 规范;后续逐步写入事件:
| Category | 说明 |
| --- | --- |
| `provider_error` | 外部 Provider 返回失败或不可用 |
| `schema_error` | Provider 输出无法解析或字段不完整 |
| `safety_error` | 儿童安全或内容安全校验失败 |
| `timeout` | 调用超时或 job 超时 |
| `canceled` | 用户取消 |
| `stale_job` | 后台任务卡住后被系统收敛 |
| `storage_error` | 数据库、文件缓存或持久化失败 |
| `validation_error` | 用户输入、档案或宇宙校验失败 |
| `unknown_error` | 未归类错误 |
### 5.5 Trace Metadata
标准 event metadata 应逐步包含:
```json
{
"step": "narrative_generation",
"artifact": "story_text",
"capability": "text",
"adapter": "demo",
"provider_id": null,
"strategy": "priority",
"latency_ms": 42,
"estimated_cost_usd": 0.01,
"failure_category": null,
"error": null,
"retryable": false,
"blocks_main_result": true
}
```
短期兼容要求:
- 不删除现有 metadata 字段。
- 新增字段必须向后兼容。
- 前端仍可使用当前 `event_type``status``message``event_metadata`
## 6. 模块设计
### 6.1 `app/services/harness/types.py`
新增纯类型模块,不依赖数据库。
职责:
- 定义 `WorkflowStep`
- 定义 `ArtifactKind`
- 定义 `FailureCategory`
- 定义 `StepStatus`
- 定义 `TraceMetadata`
- 定义从旧 event type 到标准 step 的映射函数
验收:
- 单测覆盖 event type 到 step 的映射。
- 未知 event type 映射到 `unknown` 或保守默认,不抛出非预期异常。
### 6.2 `app/services/harness/trace.py`
封装 job event 写入。
职责:
- 提供 `TraceRecorder.record_step(...)`
- 提供 `TraceRecorder.record_provider_call(...)`
- 统一补齐 `step``artifact``failure_category``retryable``blocks_main_result`
- 内部继续调用现有 `record_generation_event`
验收:
- 现有 `generation_job_events` 行为保持。
- 新事件 metadata 含标准字段。
- job 为空时安全跳过,兼容旧同步接口。
### 6.3 `app/services/harness/control.py`
封装执行控制。
职责:
- 提供 `ExecutionControl.stop_if_cancel_requested(...)`
- 保留现有取消语义和 `GenerationJobCanceledError`
- 后续可扩展 step timeout、checkpoint、stale recovery
验收:
- 取消中的 job 仍被标记为 `canceled`
- 现有取消测试继续通过。
### 6.4 `app/services/harness/artifacts.py`
第二阶段再拆。第一阶段先保留 `story_service` 中的资产函数,只把共享结果类型移动或桥接。
职责:
- 表达 `AssetCompletionResult`
- 后续承载封面、绘本插图、音频工作流
验收:
- 资产重试行为不变。
- `retryable_assets` 行为不变。
### 6.5 `story_service` 改造方式
第一阶段仅做低风险替换:
- `_record_job_event_if_present` 代理到 `TraceRecorder`
- `_stop_if_job_cancel_requested` 代理到 `ExecutionControl`
- `AssetCompletionResult` 可先留在原文件,第二阶段再移动
- 不重写 `generate_generation_service``run_generation_job_service` 主流程
第二阶段再拆分:
- `story_generation_workflow.py`
- `storybook_generation_workflow.py`
- `asset_generation_workflow.py`
- `asset_retry_workflow.py`
## 7. 阶段计划
### 阶段 0: 设计与基线
目标:
- 产出本技术设计。
- 产出阶段 0 报告。
- 明确阶段拆解、验收标准和验证方式。
最小任务:
| ID | 任务 | 验收 |
| --- | --- | --- |
| H0-1 | 梳理现有统一生成 PRD、架构文档和测试 | 文档引用现有边界,不重复设计入口 |
| H0-2 | 定义目标 runtime 架构 | 技术设计包含模块、数据、阶段计划 |
| H0-3 | 拆分最小可执行任务 | 每个阶段有任务 ID 和验收标准 |
| H0-4 | 建立阶段报告机制 | `docs/planning/` 下有阶段报告 |
### 阶段 1: Harness 基础类型与事件封装
目标:
- 新增 harness 包。
- 标准化 workflow step、artifact、failure category。
-`TraceRecorder` 封装事件写入。
-`ExecutionControl` 封装取消检查。
最小任务:
| ID | 任务 | 验收 |
| --- | --- | --- |
| H1-1 | 新增 `app/services/harness/__init__.py` | 后端 import 正常 |
| H1-2 | 新增 `types.py` 枚举和映射 | 单测覆盖核心 event type |
| H1-3 | 新增 `trace.py` | record 后 metadata 含 `step` |
| H1-4 | 新增 `control.py` | 取消行为与旧逻辑一致 |
| H1-5 | 替换 `story_service` 中事件和取消 helper 的内部实现 | 现有 API 行为不变 |
| H1-6 | 补 `tests/test_harness_runtime.py` | 后端测试通过 |
验证命令:
```bash
cd backend
pytest tests/test_harness_runtime.py tests/test_generation_jobs.py
ruff check app tests
```
阶段报告:
- `docs/planning/harness-stage-1-report.md`
### 阶段 2: 资产工作流边界抽取
目标:
- 将封面、绘本插图、音频补全从 `story_service` 中拆成 artifact workflow。
- 保持当前 `StoryAssetStatus``sync_story_status` 语义。
最小任务:
| ID | 任务 | 验收 |
| --- | --- | --- |
| H2-1 | 移动或桥接 `AssetCompletionResult` | import 稳定 |
| H2-2 | 抽普通故事封面工作流 | 封面生成、失败、重试测试通过 |
| H2-3 | 抽绘本图片工作流 | 绘本逐页事件顺序不破坏 |
| H2-4 | 抽音频工作流 | 音频缓存和失败状态测试通过 |
| H2-5 | 补 artifact workflow 单测 | 资产相关测试通过 |
验证命令:
```bash
cd backend
pytest tests/test_stories.py tests/test_generation_jobs.py tests/test_audio_cache.py
ruff check app tests
```
阶段报告:
- `docs/planning/harness-stage-2-report.md`
### 阶段 3: Workflow Plan 与执行器
目标:
- 用显式 `WorkflowPlan` 表达 story、storybook、asset_generation、asset_retry。
-`_generate_generation_service_with_job` 的分支压缩到计划构建和执行器。
最小任务:
| ID | 任务 | 验收 |
| --- | --- | --- |
| H3-1 | 定义 `WorkflowPlan``WorkflowTask` | 不依赖 FastAPI schema |
| H3-2 | 为普通故事构建 plan | 不生成图片时步骤正确 |
| H3-3 | 为完整故事构建 plan | 图片为 recoverable step |
| H3-4 | 为绘本构建 plan | 绘本结构和图片步骤可区分 |
| H3-5 | 为资产任务构建 plan | 重试和后台生成共用计划 |
| H3-6 | 增加 plan 单测 | 核心模式计划快照稳定 |
验证命令:
```bash
cd backend
pytest tests/test_harness_runtime.py tests/test_generation_jobs.py tests/test_stories.py
ruff check app tests
```
阶段报告:
- `docs/planning/harness-stage-3-report.md`
### 阶段 4: Quality Gates 与输出验证
目标:
- 在 Provider 输出进入持久化之前,加入低成本、确定性的质量门。
- 后续可扩展模型评审,但第一轮避免额外 AI 成本。
最小任务:
| ID | 任务 | 验收 |
| --- | --- | --- |
| H4-1 | 定义 quality gate 接口 | 不绑定具体 Provider |
| H4-2 | 文本故事完整性检查 | 标题、正文、封面 prompt 缺失可识别 |
| H4-3 | 绘本结构检查 | 页数、页码、正文、图片 prompt 可识别 |
| H4-4 | 儿童内容基础安全检查 | 明显不适龄词或空内容阻断 |
| H4-5 | gate 失败写入 `failure_category` | job event 可解释 |
验证命令:
```bash
cd backend
pytest tests/test_harness_runtime.py tests/test_generation_jobs.py tests/test_stories.py
ruff check app tests
```
阶段报告:
- `docs/planning/harness-stage-4-report.md`
### 阶段 5: Trace Analytics 与前端增量展示
目标:
- 前端继续消费现有 job/event但可展示标准 step、artifact、failure category。
- 管理端可按 failure category 聚合失败原因。
最小任务:
| ID | 任务 | 验收 |
| --- | --- | --- |
| H5-1 | 后端聚合支持标准 step/failure category | 旧字段兼容 |
| H5-2 | 用户端生成轨迹展示 step 名称 | 移动端不溢出 |
| H5-3 | 管理端 Provider dashboard 增加 failure category | 构建通过 |
| H5-4 | 更新 smoke 脚本校验标准 metadata | demo smoke 通过 |
验证命令:
```bash
cd backend
pytest
ruff check app tests
cd ../frontend
npm run build
cd ../admin-frontend
npm run build
```
阶段报告:
- `docs/planning/harness-stage-5-report.md`
## 8. 需求与验收
### 功能需求
| ID | 优先级 | 需求 | 验收标准 |
| --- | --- | --- | --- |
| FR-001 | MUST | 系统必须保留统一生成 API 行为 | `/api/generations` 测试继续通过 |
| FR-002 | MUST | 系统必须有标准 workflow step 类型 | 核心 event type 可映射到 step |
| FR-003 | MUST | 系统必须有标准 artifact 类型 | story、storybook、image、audio 可区分 |
| FR-004 | MUST | 系统必须标准化 job event metadata | 新事件含 `step`,旧字段不删除 |
| FR-005 | MUST | 用户取消语义必须保持 | 取消测试继续通过 |
| FR-006 | SHOULD | Provider 调用应记录标准 trace 字段 | provider event 含 capability、adapter、latency、cost、step |
| FR-007 | SHOULD | 资产工作流应从主 service 拆出 | `story_service` 行数和职责减少 |
| FR-008 | SHOULD | 输出验证应在持久化前执行 | schema 缺失可被测试捕获 |
| FR-009 | COULD | 前端展示标准 step/failure category | 构建通过且无布局溢出 |
### 非功能需求
| ID | 优先级 | 需求 | 验收标准 |
| --- | --- | --- | --- |
| NFR-001 | MUST | 向后兼容 | 现有测试和 smoke 主链路不破坏 |
| NFR-002 | MUST | 可测试 | 每个新增 harness 模块有单测 |
| NFR-003 | MUST | 可观测 | job/event 可以解释 step、artifact、失败原因 |
| NFR-004 | SHOULD | 低耦合 | harness 类型模块不依赖 FastAPI 和 SQLAlchemy |
| NFR-005 | SHOULD | 性能稳定 | 不新增阻塞式外部调用 |
| NFR-006 | SHOULD | 中文一致性 | 文档、用户可见文案和新增注释使用简体中文 |
## 9. 风险与缓解
| 风险 | 影响 | 缓解 |
| --- | --- | --- |
| 过度抽象导致改造变慢 | 中 | 第一阶段只抽类型、trace、control不拆主流程 |
| 事件 metadata 变化影响前端 | 中 | 只新增字段,不删除旧字段 |
| 取消/重试语义被破坏 | 高 | 优先跑 `tests/test_generation_jobs.py` |
| Provider trace 与 job event 重复 | 低 | 保持 Provider 事件专注调用层workflow 事件专注产品步骤 |
| 文档与实现偏离 | 中 | 每个阶段报告必须记录实现偏差 |
| 质量门误伤内容 | 中 | 第四阶段先做确定性低风险检查,模型评审延后 |
## 10. 审查清单
每个阶段完成前必须检查:
- [ ] 是否保持 `/api/generations` 行为兼容
- [ ] 是否有对应测试或验证命令
- [ ] 是否没有引入不必要的外部框架
- [ ] 是否没有重写无关功能
- [ ] 是否保留用户已有工作区改动
- [ ] 是否更新阶段报告
- [ ] 是否更新本设计中的阶段状态或偏差记录
## 11. 当前状态
| 阶段 | 状态 | 备注 |
| --- | --- | --- |
| 阶段 0 | 已完成设计基线 | 已建立本设计与阶段 0 报告 |
| 阶段 1 | 已完成基础实现 | 已新增 harness 类型、trace recorder、execution control并通过定向测试 |
| 阶段 2 | 已完成主要资产补全抽取 | 封面、音频、持久化绘本缺失图片补全已迁入 harness asset workflows |
| 阶段 3 | 已完成计划建模基线 | 已定义 WorkflowPlan/WorkflowTask 和核心模式计划快照;执行器接管留待后续 |
| 阶段 4 | 已完成确定性质量门 | 已接入文本故事和绘本结构完整性/儿童安全基础检查 |
| 阶段 5 | 待执行 | Trace Analytics 与前端展示 |