# v0.7 — 活动智能分析(分析 Agent + 结构化分析报告) ## Problem Statement 评测工程师拿到活动报告时,看到的是一堆聚合数字(通过率、可用性、时延、趋势、分场景汇总),但「这个数字意味着什么、哪里出了问题、接下来该改什么」需要人自己钻进几十条子运行里翻失败对话,耗时且依赖经验。尤其是正式线(实时 ×1)活动跑完后,团队期望直接拿到一份可读的专业研判:总体结论、问题清单、改进建议——目前平台给不出。 ## Solution 为活动引入「分析(Analysis)岗位」(CONTEXT.md 已有术语):一个两阶段分析 Agent,对活动聚合结果做活动级、跨场景的叙述性研判,产出**结构化分析报告**(总体结论 + 问题诊断 + 分场景叙述 + 改善建议),嵌在活动报告抽屉顶部展示,证据可点击下钻到具体子运行。正式线活动完成后自动生成;调试线(加速)活动按需手动生成,结果缓存可重新生成。分析模型在模型配置中心设「分析默认」,活动创建时可覆盖。 ## User Stories 1. 作为评测工程师,我希望正式线活动一完成就自动得到分析报告,以便不用记得去点按钮。 2. 作为评测工程师,我希望调试线活动不自动消耗分析模型的 token,以便控制成本。 3. 作为评测工程师,我希望在报告抽屉里对终态活动随时手动生成/重新生成分析,以便在调整数据视角后拿到新研判。 4. 作为评测工程师,我希望进行中的活动不能生成分析,以便报告永远基于稳定数据。 5. 作为评测工程师,我希望报告开头有一段总体结论,以便 30 秒内知道这个活动行不行。 6. 作为评测工程师,我希望看到按严重度排列的问题诊断列表,每条注明涉及场景和证据,以便优先处理大问题。 7. 作为评测工程师,我希望问题证据能直接跳到对应子运行报告,以便核实模型研判是否属实。 8. 作为评测工程师,我希望每个场景有一段叙述性表现分析,以便理解数字背后的行为模式。 9. 作为评测工程师,我希望改善建议按优先级排序且可执行,以便直接转成下一步工作。 10. 作为评测工程师,我希望模型引用的证据都真实存在(不虚构 run_id),以便信任报告。 11. 作为管理员,我希望在模型配置中心把某个 chat 模型设为「分析默认」,以便全平台统一分析口径。 12. 作为管理员,我希望「分析默认」全局唯一、设置时自动互斥,以便不用手动清理旧标记。 13. 作为评测工程师,我希望创建活动时能为该活动单独指定分析模型,以便重要活动用更强的模型。 14. 作为评测工程师,我希望未配置分析模型时得到明确引导(去配置中心设置),以便知道为什么没有分析。 15. 作为评测工程师,我希望分析失败时看到失败原因并能重试,以便区分是模型故障还是数据问题。 16. 作为评测工程师,我希望导出的活动 Markdown 报告包含分析区块,以便离线分享完整报告。 17. 作为评测工程师,我希望分析所用的模型配置被快照记录,以便事后追溯报告出自哪个模型。 18. 作为评测工程师,我希望报告抽屉里能区分「生成中 / 已完成 / 失败」状态,以便知道后台任务进展。 ## Implementation Decisions - **分析岗位**:`ModelPurpose` 枚举新增 `ANALYSIS`。分析模型不按场景绑定(区别于出题/判卷),而是全局默认 + 活动覆盖(CONTEXT.md 术语定义)。 - **分析默认标记**:`model_configs` 表新增 `is_analysis_default` 布尔列(Alembic 迁移,batch mode,server_default 回填 false)。仓储层参照 `is_default` 的 `clear_default` 模式保证全局唯一;仅启用的 chat 能力配置可设为分析默认(校验与 `is_default and not enabled` 同款)。模型配置中心 UI 在 chat 配置上提供「分析默认」标记。 - **活动覆盖**:`campaigns` 表新增可空 `analysis_model_config_id` 列(迁移);创建活动 API 接受该字段(可空=跟随全局默认);创建表单左列「时间与速度」区加「分析模型」下拉(选项=启用的 chat 配置,默认项「全局默认(<默认配置名>)」)。 - **分析模型解析**:`活动.analysis_model_config_id ?? 全局分析默认`。两者都无 → 自动触发跳过;手动触发返回 409/400 并提示去配置中心。 - **分析存储**:新表 `campaign_analyses`,每活动一行(upsert 覆盖):`campaign_id` 唯一、`status`(generating/completed/failed)、`result`(结构化 JSON,可空)、`model_config_id`(快照)、`error`(可空)、`triggered_by`(auto/manual)、`created_at`/`updated_at`。后台任务与 Runs 同款 `asyncio.Task` + `try/finally` 关 Session。 - **API**:`GET /api/campaigns/{id}/analysis` → `{status, result?, error?, model_config_id?, updated_at?}`(从未生成时 status 为 none/空态);`POST /api/campaigns/{id}/analysis` → 触发生成(已存在则覆盖重跑);活动为进行中/计划中返回 400;无分析模型可解析返回 400 且 detail 引导配置。 - **自动触发**:`campaign_runner` 写入 COMPLETED 的同一处(campaign_runner.py:232 附近),若 `time_scale == 1` 且可解析到分析模型,则 enqueue 分析后台任务。失败/取消的活动不自动触发。 - **两阶段分析 Agent**(新模块 `evaluation/analysis.py`): - 输入数据:复用 `generate_campaign_report` 的聚合结果(不重算,遵循 ADR-0002/0004 口径)+ 每场景最多 3 条代表性失败对话(取自失败子运行的 turn:用户消息/回复/判定理由,各截断到合理长度)。 - 阶段一:每个场景一次 LLM 调用(并行 `asyncio.gather`)→ 该场景叙述 + 问题点草稿。 - 阶段二:汇总各场景产出 + 全局统计 → 总体结论 + 跨场景问题 + 优先级建议。 - 输出用 JSON schema 约束(prompt 内嵌 schema + 解析校验,解析失败按失败处理可重试)。 - 证据白名单:`run_id` 必须在提供的数据集内,模型虚构的引用在落库前剔除。 - **结构化报告 schema**: ``` { "overall": str, # 总体结论(一段话) "problems": [{"severity": "high"|"medium"|"low", "title": str, "description": str, "scenario_ids": [str], "evidence_run_ids": [str]}], "scenario_narratives": [{"scenario_id": str, "narrative": str}], "suggestions": [{"priority": int, "text": str}] } ``` - **前端报告抽屉**:顶部新增「智能分析」区块——状态行(生成中 spinner / 失败 error+重试 / 未生成时的生成按钮与状态说明 + 分析模型名);完成后渲染:总体结论 callout → 问题诊断列表(严重度 Tag、场景名、证据 chip 点击跳 `/reports?run=`)→ 分场景叙述 → 改善建议(按 priority 排序)。生成/重新生成按钮仅终态活动可用,进行中禁用并提示「活动完成后可生成」。 - **Markdown 导出**:`render_campaign_markdown` 追加分析区块(有 completed 分析时),按 总体结论/问题诊断/分场景叙述/改善建议 拼段。 - **语言**:分析输出一律中文。 ## Testing Decisions 好测试只测外部行为,不测实现细节。 - **唯一新 seam:分析服务**(`evaluation/analysis.py`),用假 LLM 客户端注入(先例:`tests/unit/mock_channel.py` 的 MockChannel、llm_score 的假网关)。覆盖:两阶段编排(阶段一并行、阶段二汇总)、JSON 解析失败→ failed 状态、run_id 白名单剔除虚构引用、无失败样例的场景也能产出叙述、无分析模型可解析→明确错误。 - **API 集成测试**(先例 `tests/integration/test_campaigns_api.py`):GET 空态;POST 触发→后台完成→GET 拿到结果;进行中活动 POST 400;无分析模型 POST 400;覆盖重跑 upsert 不新增行。 - **自动触发**:runner 完成正式线活动后分析任务被 enqueue(用假分析服务断言调用);加速活动不触发。 - **分析默认唯一性**:仓储/服务层测试(设第二个分析默认时第一个被清;停用配置不能设为分析默认)——先例 `is_default` 现有测试。 - **迁移**:Alembic upgrade 后两表新列存在且回填正确。 - **前端**:无测试框架,`npx tsc --noEmit` + `npm run build` + 浏览器实操验证(生成流程、状态展示、证据跳转、终态禁用)。 ## Out of Scope - **工具调用循环 Agent**(function calling 让模型自主拉数据)——本批固定两阶段编排。 - **HTML/PDF 专业报告页**——本批结构化 JSON + 抽屉原生渲染 + Markdown 导出。 - **分析报告的历史版本**——重新生成即覆盖(upsert),不保留旧版。 - **分析结果的通知推送**(完成提醒、Webhook)。 - **跨活动对比分析**。 - **进行中活动的增量分析**。 ## Further Notes - 分析输入完全来自既有聚合口径(ADR-0002:失败按 0.0 计入;ADR-0004:取消不计入分母),分析 Agent 不重算数字,只做叙述性研判。 - 阶段一按场景并行,场景数 = 该活动 capability_summary 中出现的场景数;阶段二单次调用。 - 分析模型的上下文窗口/最大输出元数据沿用模型配置中心的描述性元数据,不改变网关请求参数(AGENTS.md 模型配置中心约定)。