# 03 — 两阶段分析 Agent + 存储 + 分析 API **What to build:** 对终态活动可触发生成结构化分析报告。新表 `campaign_analyses`(每活动一行 upsert:status=generating/completed/failed、result JSON、model_config_id 快照、error、triggered_by、时间戳)。新模块 `evaluation/analysis.py`:输入复用 `generate_campaign_report` 聚合结果(不重算,ADR-0002/0004 口径)+ 每场景最多 3 条截断的代表性失败对话;阶段一每场景并行一次 LLM 调用产场景叙述+问题草稿,阶段二汇总产总体结论+跨场景问题+优先级建议;输出 JSON schema 约束,解析失败记 failed;证据 run_id 白名单校验,虚构引用落库前剔除。模型解析 = 活动覆盖 ?? 全局分析默认,两者皆无 → 明确错误。API:`GET /api/campaigns/{id}/analysis`(未生成返回空态)、`POST /api/campaigns/{id}/analysis`(触发/覆盖重跑;进行中/计划中 400;无模型 400 且 detail 引导配置)。后台 asyncio 任务 + try/finally 关 Session,失败落 error。 **Blocked by:** 02(模型解析依赖活动覆盖列) **Status:** ready-for-agent - [ ] Alembic 迁移:`campaign_analyses` 表(campaign_id 唯一) - [ ] 仓储层 upsert / get_by_campaign - [ ] `evaluation/analysis.py` 两阶段编排(阶段一 `asyncio.gather` 并行;输出 schema 见 spec) - [ ] 失败样例选取:每场景 ≤3 条失败 turn(用户消息/回复/判定理由,截断) - [ ] run_id 白名单:模型引用的虚构 id 落库前剔除 - [ ] 假 LLM 客户端单测:编排、解析失败→failed、白名单剔除、无失败样例场景仍产叙述、无模型→明确错误 - [ ] API 集成测试:GET 空态;POST→完成→GET 拿结果;进行中 400;无模型 400;重跑 upsert 不新增行 - [ ] `pytest` + `ruff` 全绿