docs/decision-signals.md
本页收口 #1390 P7,说明 DSA 如何把个股分析、Agent、告警和组合风险中的 AI 建议沉淀为可查询、可反馈、可后验评估的 DecisionSignal 资产。它是报告之上的结构化索引,不替代 Markdown 报告、operation_advice、三态 decision_type、告警规则或真实交易系统。
DecisionSignal 只记录建议、证据摘要、风险、观察条件、生命周期和来源,不执行下单或调仓。.env.example 内容。DECISION_SIGNAL_* 开关;信号功能的关闭或回滚通过 revert 对应代码完成。核心字段由 api/v1/schemas/decision_signals.py 定义,主要包括:
stock_code、stock_name、market、source_type、source_agent、source_report_id、trace_id、trigger_source。action、action_label、confidence、score、horizon、market_phase、plan_quality、status。entry_low、entry_high、stop_loss、target_price、invalidation、watch_conditions、reason、risk_summary、catalyst_summary。evidence、data_quality_summary、metadata。expires_at、created_at、updated_at。枚举取值:
| 字段 | 取值 |
|---|---|
market | cn、hk、us、jp、kr、tw |
source_type | analysis、agent、alert、market_review、manual |
market_phase | premarket、intraday、lunch_break、closing_auction、postmarket、non_trading、unknown |
action | buy、add、hold、reduce、sell、watch、avoid、alert |
horizon | intraday、1d、3d、5d、10d、swing、long |
plan_quality | complete、partial、minimal、unknown |
status | active、expired、invalidated、closed、archived |
Web 展示必须把这些 wire value 映射为当前 UI 语言的用户可读标签;API 响应继续保留原始枚举值。
src/services/decision_signal_service.py 是信号生命周期的主入口:
horizon 和 expires_at 显式传入时优先。horizon 时,alert 或盘前/盘中/午间休市/集合竞价阶段默认 intraday,盘后、非交易时段、未知阶段或缺少阶段时默认 3d。intraday 过期时间优先读取低敏 metadata.market_phase_summary.minutes_to_close/minutes_to_open;缺失时按市场 fallback TTL。expired、invalidated、closed、archived 不能通过 PATCH /status 直接恢复为 active。(source_report_id, source_type, market, stock_code, action, horizon, market_phase);没有 report 但有 trace_id 时使用 trace 维度。invalidated,并把失效来源写入 metadata。当前公开接口由 api/v1/endpoints/decision_signals.py 和 docs/architecture/api_spec.json 描述:
POST /api/v1/decision-signals:创建或按同源键去重,返回 { item, created }。GET /api/v1/decision-signals:分页查询,支持市场、股票、动作、阶段、来源、状态、时间范围和持仓过滤。GET /api/v1/decision-signals/{signal_id}:查询单条。PATCH /api/v1/decision-signals/{signal_id}/status:更新状态和可选 metadata。GET /api/v1/decision-signals/latest/{stock_code}:查询股票最新 active 信号。POST /api/v1/decision-signals/outcomes/run:显式触发后验评估。GET /api/v1/decision-signals/outcomes、GET /api/v1/decision-signals/outcomes/stats、GET /api/v1/decision-signals/{signal_id}/outcomes:查询后验结果与统计。GET/PUT /api/v1/decision-signals/{signal_id}/feedback:查询或写入 useful / not useful 反馈。这些接口继承现有 /api/v1/* 管理员鉴权;ADMIN_AUTH_ENABLED=true 时需要有效管理员会话 Cookie。
Web 入口位于 /decision-signals:
status=active。GET /api/v1/decision-signals list API,不新增 timeline endpoint。时间线必须输入非空 stockCode 后才会查询;空 stockCode 只显示引导态,不拉取 market-only 或 global timeline。30d、90d、180d 三个时间范围,默认 90d;每次最多请求 100 条。若返回 total > items.length,Web 会显示“仅展示最近 100 条信号,请缩小时间范围”,避免静默展示不完整轨迹。all 与 active:all 不传 status,active 传 status=active。P1 不提供 terminal status filter,也不做前端 terminal 过滤。decision_profile 仍只存在于 metadata 中,不能可靠 server-side 过滤。历史缺失或非法 profile 的信号在 Web 中显示为 unknown,不会误标为 balanced。cn/hk/us/jp/kr/tw;jp/kr/tw 的前端本地化标签均已补齐,tw 信号可经 API 正常写入、按 market=tw 查询,并可在 Web DecisionSignal 页面通过市场筛选项选择台股(tw);告警(大盘红绿灯)市场支持 cn/hk/us/jp/kr。closed、invalidated 或 archived,不提供 terminal 状态恢复为 active。source_type=analysis 信号,也不会因打开报告详情触发 source_report_id 信号查询;需要查看报告来源信号时统一进入 /decision-signals 页面按来源报告 ID 精确筛选,或打开 /decision-signals?sourceReportId=<recordId> deep link。该筛选和 deep link 都会使用 source_type=analysis + source_report_id 的精确查询,以保留旧报告的 best-effort 懒回填入口。所有用户可见枚举必须使用 i18n 标签;技术 ID、股票代码、API 字段名、env key、URL 示例可以保留英文。
P1 自动生成的 source_type=analysis 信号会在 metadata 中写入默认决策风格元数据:
decision_profile=balancedprofile_source=auto_default:普通新分析生成路径。profile_source=backfill_defaulted:历史报告 lazy backfill 路径。profile_policy_version=decision-profile-v1signal_generation_version=legacy-report-extractor-v1decision_signal_metadata_version=decision-signal-metadata-v1profile_policy_version 只表示默认 profile metadata contract version,不代表已经实现独立 profile policy engine、scoring engine 或多 profile 生成。P1/P2 不写入 scoring_version 或 scoring_breakdown;这些字段如需引入,应由后续 reassess / scoring issue 定义。
decision_signal_summary 写入 alert_triggers.diagnostics。source_type=alert/action=alert 信号。trace_id=alert-rule-<hash> 只用于同源重试的 best-effort 去重,不覆盖 active 信号本体。action、horizon、reason、watch_conditions、risk_summary、source_report_id。metadata、evidence、raw diagnostics、webhook URL、token 或 cookie。GET /api/v1/portfolio/risk 的 decision_signal_risk 只统计当前持仓中的 active sell/reduce/alert 信号,查询失败时 fail-open。更多告警和通知细节见 docs/alerts.md 与 docs/notifications.md。
P5 通过 sidecar 表保存用户反馈和后验结果,不扩展 decision_signals 主表:
decision_signal_feedback 保存每个信号最新的 useful|not_useful 反馈、可选原因/备注和来源。decision_signal_outcomes 按 (signal_id, horizon, engine_version) 幂等保存后验评估结果。engine_version=decision-signal-v1。1d/3d/5d/10d;intraday/swing/long、非方向动作、缺价和 forward bars 不足会写入 eval_status=unable 与明确 unable_reason。信号写入和状态更新使用 src/utils/sanitize.py 中的 sanitize_decision_signal_text() 与 sanitize_decision_signal_payload():
trace_id 是同源去重身份字段;如果包含会被脱敏的 credential,API 会拒绝请求,而不是保存被 redaction 破坏后的身份值。P7 的全局验收是确认信号池、通知摘要和 Web 展示不泄露 token、cookie、webhook URL、API key、邮箱密码等敏感信息。
本功能在 P1-P6 已完成所需表和 sidecar 结构;P7 不新增 migration。
迁移说明:
.env、.env.example 或 Web 设置项。source_type=analysis + source_report_id 且无命中时,才会 best-effort 懒回填。decision_signals、feedback 和 outcome 数据保持兼容。回滚说明:
DECISION_SIGNAL_* 开关;关闭信号提取/写入的回滚方式是 revert 相关代码。decision_signals、decision_signal_feedback 或 decision_signal_outcomes 数据;如需清理,应由维护者单独制定数据清理策略。