Introduce ModelPurpose.ANALYSIS and a globally-unique is_analysis_default marker on chat model configs so campaign analysis can resolve its model. Service rejects disabled or non-chat configs; repo clears the previous holder on set. Documented the analysis role in CONTEXT.md.
9.5 KiB
9.5 KiB
v0.7 — 活动智能分析(分析 Agent + 结构化分析报告)
Problem Statement
评测工程师拿到活动报告时,看到的是一堆聚合数字(通过率、可用性、时延、趋势、分场景汇总),但「这个数字意味着什么、哪里出了问题、接下来该改什么」需要人自己钻进几十条子运行里翻失败对话,耗时且依赖经验。尤其是正式线(实时 ×1)活动跑完后,团队期望直接拿到一份可读的专业研判:总体结论、问题清单、改进建议——目前平台给不出。
Solution
为活动引入「分析(Analysis)岗位」(CONTEXT.md 已有术语):一个两阶段分析 Agent,对活动聚合结果做活动级、跨场景的叙述性研判,产出结构化分析报告(总体结论 + 问题诊断 + 分场景叙述 + 改善建议),嵌在活动报告抽屉顶部展示,证据可点击下钻到具体子运行。正式线活动完成后自动生成;调试线(加速)活动按需手动生成,结果缓存可重新生成。分析模型在模型配置中心设「分析默认」,活动创建时可覆盖。
User Stories
- 作为评测工程师,我希望正式线活动一完成就自动得到分析报告,以便不用记得去点按钮。
- 作为评测工程师,我希望调试线活动不自动消耗分析模型的 token,以便控制成本。
- 作为评测工程师,我希望在报告抽屉里对终态活动随时手动生成/重新生成分析,以便在调整数据视角后拿到新研判。
- 作为评测工程师,我希望进行中的活动不能生成分析,以便报告永远基于稳定数据。
- 作为评测工程师,我希望报告开头有一段总体结论,以便 30 秒内知道这个活动行不行。
- 作为评测工程师,我希望看到按严重度排列的问题诊断列表,每条注明涉及场景和证据,以便优先处理大问题。
- 作为评测工程师,我希望问题证据能直接跳到对应子运行报告,以便核实模型研判是否属实。
- 作为评测工程师,我希望每个场景有一段叙述性表现分析,以便理解数字背后的行为模式。
- 作为评测工程师,我希望改善建议按优先级排序且可执行,以便直接转成下一步工作。
- 作为评测工程师,我希望模型引用的证据都真实存在(不虚构 run_id),以便信任报告。
- 作为管理员,我希望在模型配置中心把某个 chat 模型设为「分析默认」,以便全平台统一分析口径。
- 作为管理员,我希望「分析默认」全局唯一、设置时自动互斥,以便不用手动清理旧标记。
- 作为评测工程师,我希望创建活动时能为该活动单独指定分析模型,以便重要活动用更强的模型。
- 作为评测工程师,我希望未配置分析模型时得到明确引导(去配置中心设置),以便知道为什么没有分析。
- 作为评测工程师,我希望分析失败时看到失败原因并能重试,以便区分是模型故障还是数据问题。
- 作为评测工程师,我希望导出的活动 Markdown 报告包含分析区块,以便离线分享完整报告。
- 作为评测工程师,我希望分析所用的模型配置被快照记录,以便事后追溯报告出自哪个模型。
- 作为评测工程师,我希望报告抽屉里能区分「生成中 / 已完成 / 失败」状态,以便知道后台任务进展。
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 模型配置中心约定)。