docs/run-diagnostics-p3.md
本文档记录 #1391 Phase 3 的交付范围:在不新增配置的前提下,补齐运行诊断可见性并将历史排障信息回填到后端上下文快照,便于自部署环境快速定位异常。
GET /api/v1/history/{record_id}/diagnostics
diagnostic_summary,前端可直接展示,不额外请求历史接口。copy_text,用于 issue 或部署排障。context_snapshot.diagnostics,历史诊断接口统一聚合为用户可读摘要。运行流增量不新增独立 SSE endpoint,继续复用:
GET /api/v1/analysis/tasks/stream
兼容契约:
task_progress。flow_event 字段;旧客户端忽略该字段即可。flow_event 使用与 RunFlowSnapshot.events[] 相同的脱敏事件结构:id、timestamp、severity、type、node_id、title、message、metadata。context_snapshot.diagnostics 和历史 RunFlowSnapshot 为准。示例:
{
"task_id": "3f87...",
"trace_id": "3f87...",
"stock_code": "600519",
"status": "processing",
"progress": 64,
"message": "LLM 正在生成分析结果",
"flow_event": {
"id": "flow_0002",
"timestamp": "2026-06-08T22:30:24",
"severity": "success",
"type": "llm_run",
"node_id": "llm_analysis_1",
"title": "LLM 成功",
"message": "LLM deepseek-chat 成功"
}
}
运行诊断记录函数会在 provider、LLM、历史保存、通知记录成功写入内存诊断后 fail-open 触发 event sink。sink 失败只记录 warning,不改变分析、保存或通知的成功/失败判定。
新闻情报搜索也纳入同一 provider 诊断语义:SearchService.search_stock_news() 会以 data_type=news_search 记录 Tavily、SearXNG、Bocha、Brave 等搜索 provider 的尝试、过滤后结果数、缓存命中和失败原因。多个搜索 provider 连续尝试时,Web 运行流主图默认聚合为一个“新闻舆情”节点,卡片展示 provider 链路与状态,节点详情展示成功/失败次数、fallback / retry 次数;需要排障时可展开该聚合节点查看单次 provider 尝试。
运行流拓扑的数据来源泳道优先按节点开始时间排序;provider / LLM 节点若只有完成时间和耗时,会以 ended_at - duration_ms 推导 started_at,并在卡片上展示开始时间。无可用时间的节点保留原展示顺序作为兜底。主图表达“入口 -> 数据来源 -> ContextPack -> LLM -> 保存/通知”的流程结构;完整排障细节保留在事件流、节点详情和聚合节点展开态中。
Web 运行流主图使用前端内部展示模型,不改变后端 RunFlowSnapshot 契约:
metadata.data_type 聚合为数据来源节点,例如实时行情、日线数据、新闻舆情。context_block_* 节点默认折叠进 ContextPack 详情,避免与 provider attempts 在数据来源泳道混排。GET /api/v1/analysis/tasks/{task_id}/flow
GET /api/v1/history/{record_id}/flow
RunFlowSnapshot 契约。flow_event,snapshot 会返回这些真实事件,并可根据事件中的节点元数据补出临时节点。context_snapshot.diagnostics 与 analysis_context_pack_overview 构建完整拓扑。code=MARKET、report_type=market_review,同样走 /history/{record_id}/flow 与 Web 运行流面板,不提供单独 UI 分叉。cancel_requested 与 cancelled 是合法运行流状态;用户取消不应映射为 failed。运行流视图是在运行诊断摘要之上的可视化排障入口,用于串联一次分析从触发、数据获取、ContextPack 组装、LLM 生成到保存/通知的大致链路。它不替代诊断摘要的 copy_text,而是把同一批脱敏诊断证据组织为节点、连线、事件和摘要指标,方便从 Web 首页快速定位异常或降级环节。
后端提供两个只读快照接口:
GET /api/v1/analysis/tasks/{task_id}/flow
GET /api/v1/history/{record_id}/flow
tasks/{task_id}/flow 面向活跃任务。任务仍在内存队列中时优先返回当前任务快照;任务已完成时可按同一 task_id/query_id 尝试读取历史诊断。缺少诊断时返回 skeleton flow,不伪造 provider、LLM 或通知事件。history/{record_id}/flow 面向历史报告,支持历史记录主键 ID 或可解析的 query_id。普通个股分析与 MARKET/market_review 大盘复盘复用同一 RunFlowSnapshot 契约。summary、lanes、nodes、edges、events 和 generated_at。节点状态使用 pending/running/success/failed/degraded/fallback/timeout/cancel_requested/cancelled/skipped/unknown,其中用户取消类状态不会被映射成 failed。context_snapshot.diagnostics 或证据不足时,后端返回 unknown 或 skeleton 节点;Web 端按空/未知状态展示,不影响报告详情读取。Web 入口:
task_id 拉取任务快照。脱敏与兼容边界:
context_snapshot.diagnostics 中的低敏诊断字段,不新增配置项、不改数据库结构、不迁移旧历史。model、provider、fallback_model 仅用于展示实际诊断到的调用信息;不参与模型选择、请求路由、Base URL 解析或配置保存。metadata、错误信息和本地路径会经过后端裁剪与脱敏,避免暴露 API key、token、cookie、webhook、prompt/raw response、代理头和本地绝对路径。总体状态:
normal:正常degraded:部分降级failed:失败unknown:未知组件状态:
ok:正常degraded:最近失败后已降级failed:失败unknown:未知not_configured:未配置skipped:已跳过unknown,不影响报告阅读。.env 配置项,不修改数据库结构,不引入数据迁移。src/core/pipeline.py、src/services/run_diagnostics.py、src/storage.py 与 src/services/history_service.py 的诊断持久化与刷新逻辑,并通过 api/v1/endpoints/history.py 提供可读端点。context_snapshot.diagnostics 诊断快照和摘要,不改变分析主流程、通知发送成败语义或历史报告主体字段。本轮 review 的结构化检测命中了外部模型/API 兼容和运行时配置迁移风险;复核后结论如下:
LITELLM_MODEL、AGENT_LITELLM_MODEL、VISION_MODEL、LITELLM_FALLBACK_MODELS、OPENAI_*、GEMINI_*、ANTHROPIC_*、DEEPSEEK_* 的解析优先级。requirements.txt、package.json 依赖约束或 LiteLLM/OpenAI-compatible 调用默认参数;外部来源仍以 docs/llm-providers.md 和 docs/LLM_CONFIG_GUIDE*.md 中已记录的官方文档与当前锁定依赖说明为准。.env、Web 设置页 channel、桌面端用户数据目录、Docker 运行时配置文件或历史旧配置的迁移、清理、删除、回写策略变更。context_snapshot.diagnostics,并通过历史只读接口和 Web 默认折叠面板展示;诊断记录失败按 fail-open 处理,不改变分析或通知的成功/失败判定。tests/test_pipeline_market_phase_context.pytests/test_realtime_types.pytests/test_scheduler_background.pytests/test_analysis_api_contract.py(子集:诊断上下文入出参/状态查询契约)tests/test_analysis_history.py(子集:历史 API 与持久化链路)tests/test_analysis_api_contract.py 与 tests/test_analysis_history.py 覆盖;任务编排、历史保存和 context_snapshot.diagnostics 由 tests/test_pipeline_market_phase_context.py 覆盖;通知路径通过 ./scripts/ci_gate.sh 中的既有通知回归与导入检查兜底。./scripts/ci_gate.sh
python -m pytest tests/test_realtime_types.py tests/test_scheduler_background.py tests/test_pipeline_market_phase_context.py tests/test_analysis_api_contract.py tests/test_analysis_history.py
cd apps/dsa-web && npm run lint && npm run build
cd apps/dsa-web
npm run lint
npm run build
可补充执行(非阻断):
cd apps/dsa-web
npm test -- --run src/components/report/__tests__/ReportDiagnostics.test.tsx src/components/tasks/__tests__/TaskPanel.test.tsx src/hooks/__tests__/useTaskStream.test.tsx
可补充确定性脚本校验:
python -m py_compile api/v1/endpoints/analysis.py api/v1/endpoints/history.py api/v1/schemas/analysis.py api/v1/schemas/history.py src/core/pipeline.py src/services/run_diagnostics.py src/storage.py
最小回滚方式:revert Phase 3 PR。由于本轮为可选字段与可读接口增强,回滚后后端历史快照与已落库数据保留,Web 不再展示诊断面板与 trace 诊断入口。