Skip to content

feat: 接入 #1390 P6 告警、通知与持仓信号联动 - #1715

Merged
ZhuLinsen merged 2 commits into
ZhuLinsen:mainfrom
massif-01:feat/1390-p6-signal-linkage
Jun 19, 2026
Merged

feat: 接入 #1390 P6 告警、通知与持仓信号联动#1715
ZhuLinsen merged 2 commits into
ZhuLinsen:mainfrom
massif-01:feat/1390-p6-signal-linkage

Conversation

@massif-01

@massif-01 massif-01 commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Refs #1390

背景

本 PR 落地 #1390 P6「告警、通知与持仓联动」的最小闭环:在不新增配置、不新增数据库表、不做迁移的前提下,让现有 DecisionSignal 能被告警触发、通知摘要和组合风险视图复用。

本 PR 不关闭 umbrella issue。#1390 仍保留 P7 文档/配置收尾和全局脱敏验收等后续项。

改动内容

  • 新增 src/services/decision_signal_summary.py,统一生成低敏 DecisionSignal 摘要和通知短摘要。
    • 只保留公开字段:id、股票标识、市场、动作、周期、状态、来源、reasonwatch_conditionsrisk_summary、创建/过期时间。
    • 不读取、不输出 metadataevidence、raw diagnostics、webhook/token 等敏感或诊断细节。
  • 告警 worker 在真实股票级 triggered 事件上联动信号。
    • 优先关联同标的 latest active 信号,并把 decision_signal_summary 写入 trigger diagnostics 和告警通知。
    • 无 active 信号时创建最小 source_type=alert/action=alert 信号。
    • 跳过 marketportfolio_account、overflow、无法解析为具体股票的目标,以及已知美股指数代码。
    • 新建告警信号不传 market_phase,避免同一规则跨 phase 重复创建。
  • 分析通知在保存分析历史并提取信号后,运行期挂载 decision_signal_summary,通知模板只读该运行期字段。
    • AnalysisResult.to_dict() 不新增该字段,避免扩大既有报告序列化契约。
  • 组合风险接口 GET /api/v1/portfolio/risk 追加 decision_signal_risk
    • 复用 DecisionSignalService.list_signals(holding_only=True, account_id=..., status="active")
    • 只聚合当前持仓中的 sell/reduce/alert,显式排除 avoid/buy/add/hold/watch
    • 信号服务失败时 fail-open,主风险报告照常返回,并保留异常栈日志便于排障。
  • Web 持仓风险区增加紧凑的「AI 风险信号」块,并补齐 TypeScript 类型和页面测试。
  • 同步更新 API schema、docs/full-guide.mddocs/full-guide_EN.mddocs/alerts.mddocs/notifications.mddocs/CHANGELOG.md
  • 审查后补充收敛:
    • 补齐静态 OpenAPI spec 缺失的 nested schemas,并新增 $ref 完整性测试,避免客户端生成遇到 dangling ref。
    • 新增 DecisionSignalService.normalize_stock_code_for_signal() 作为组合风险与信号 identity 对齐的公开入口,避免跨服务调用私有方法。
    • 补充 decision_signal_summary helper 单测,覆盖白名单、脱敏、格式化和非法输入。
    • 补充组合风险聚合负面断言,明确 buy/watch/expired/非持仓 sell 不进入 decision_signal_risk
    • 整理持仓页 decisionSignalRisk 预览项计算,避免重复 slice(0, 3)

特别说明

  • 本 PR 没有修改任何 Prompt、LLM 调用策略、EXTRACT_PROMPT、Agent prompt 或报告生成 prompt,因此没有附 prompt 全文。
  • 本 PR 没有新增环境变量、配置项、feature flag、数据库表或 migration,因此没有修改 .env.example、config registry 或 README。
  • trace_id=alert-rule-<hash> 只用于同源重试的 best-effort 去重;当前仓库层没有新增唯一约束,本 PR 不声称多 worker/多进程并发下的绝对幂等。
  • 本 PR 只在现有报告/通知中追加低敏摘要,不改变原有投资建议 taxonomy,不把告警类型推断成 sell/reduce/watch

验证

  • ./scripts/ci_gate.sh
    • 结果:3433 passed, 2 deselected, 52 warnings
  • cd apps/dsa-web && npm run lint
    • 结果:通过
  • cd apps/dsa-web && npm run build
    • 结果:通过
  • python -m pytest tests/test_decision_signal_summary.py tests/test_api_schema_pydantic.py::test_decision_signal_static_api_spec_matches_runtime_paths tests/test_portfolio_pr2.py::PortfolioPr2TestCase::test_risk_report_aggregates_active_defensive_decision_signals_for_holdings tests/test_portfolio_pr2.py::PortfolioPr2TestCase::test_risk_report_decision_signal_fail_open -q
    • 结果:8 passed
  • cd apps/dsa-web && npm run test -- src/pages/__tests__/PortfolioPage.test.tsx
    • 结果:25 passed
  • python -m py_compile src/services/decision_signal_service.py src/services/portfolio_risk_service.py src/services/decision_signal_summary.py tests/test_api_schema_pydantic.py tests/test_portfolio_pr2.py tests/test_decision_signal_summary.py
    • 结果:通过
  • git diff --check
    • 结果:通过

可视证据

  • 已补持仓页截图验证,覆盖新增「AI 风险信号」块的有数据状态。
    • 生成方式:本地 Vite dev server + headless Chrome + Playwright route mock;保留真实前端代码、CSS 和组件,仅替换 API 响应,避免为了截图向本地数据库写入临时验收数据。
    • 脚本断言通过:AI 风险信号风险信号: 2卖出: 1 · 减仓: 1 · 预警: 0600519 · 卖出300750 · 减仓 均出现在页面中。
  • 截图已作为 PR 图片附件展示;没有作为仓库文件提交,遵守 AGENTS.md 中“一次性验收截图不得合入仓库”的约束。
  • 自动化替代证据:PortfolioPage.test.tsx 额外覆盖了有风险信号、无防御型信号、available=false 三种 UI 状态。
dsa-pr-1715-portfolio-ai-risk-signals

兼容性与风险

  • API 只追加字段:
    • AlertTriggerItem.decision_signal_summary
    • PortfolioRiskResponse.decision_signal_risk
      现有客户端可继续忽略新增字段。
  • 通知失败继续沿用现有 fail-safe,不影响 trigger 写入和信号创建。
  • 组合风险信号查询失败时返回 available=false,total=0,items=[],不阻断原有风险报告。
  • 残余风险:不新增迁移的前提下,告警信号同源去重只能覆盖顺序重试;并发创建的强唯一性留给后续需要迁移约束的阶段处理。

回滚方案

可直接 revert 本 PR。由于没有新增配置、迁移或数据库表,回滚不会要求 schema downgrade。若线上已经产生 source_type=alert 的信号记录,可按维护者策略保留历史记录,或后续通过既有信号状态更新接口归档/关闭。

@github-actions github-actions Bot added documentation Improvements or additions to documentation notification size/XL testing labels Jun 18, 2026
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

🤖 自动审查报告

项目 结果
📊 变更文件 28 个
➕ 新增行数 2047 行
➖ 删除行数 5 行
🔍 静态检查 ✅ 通过
🧠 AI 审查 ✅ 已完成

📁 修改的文件

  • 📝 api/v1/schemas/__init__.py (+4/-0)
  • 📝 api/v1/schemas/alerts.py (+1/-0)
  • 📝 api/v1/schemas/portfolio.py (+15/-0)
  • 📝 apps/dsa-web/src/locales/featureText.ts (+2/-2)
  • 📝 apps/dsa-web/src/pages/PortfolioPage.tsx (+29/-1)
  • 📝 apps/dsa-web/src/pages/__tests__/PortfolioPage.test.tsx (+61/-1)
  • 📝 apps/dsa-web/src/types/alerts.ts (+2/-0)
  • 📝 apps/dsa-web/src/types/portfolio.ts (+22/-0)
  • 📝 docs/CHANGELOG.md (+1/-0)
  • 📝 docs/alerts.md (+1/-0)
  • 📝 docs/architecture/api_spec.json (+1044/-0)
  • 📝 docs/full-guide.md (+2/-0)
  • 📝 docs/full-guide_EN.md (+2/-0)
  • 📝 docs/notifications.md (+2/-0)
  • 📝 src/core/pipeline.py (+6/-1)
  • 📝 src/notification.py (+14/-0)
  • 📝 src/services/alert_service.py (+4/-0)
  • 📝 src/services/alert_worker.py (+162/-0)
  • 📝 src/services/decision_signal_service.py (+6/-0)
  • 🆕 src/services/decision_signal_summary.py (+111/-0)

... 还有 8 个文件


🧠 AI 代码审查意见

结论

Ready to Merge

结构化审查结果

  • 必要性:通过。本 PR 明确落地了 Issue [Feature] DSA 后续开发规划:[AI 建议池 / 决策信号 1.0] 结构化沉淀操作建议、价格计划与后验验证 #1390 中 P6 的核心功能“告警、通知与持仓联动”,旨在复用现有 DecisionSignal,在不增加基础设施负担的前提下,提升告警智能化和风险洞察能力,具有清晰的业务价值。

  • 关联性:通过。PR 描述明确指出 Refs #1390,与关联 Issue 建立了清晰的追踪。

  • 类型feat (功能)。PR 标题 feat: 和描述中的“落地 [Feature] DSA 后续开发规划:[AI 建议池 / 决策信号 1.0] 结构化沉淀操作建议、价格计划与后验验证 #1390 P6”表明这是一个新功能实现。

  • 描述完整性:完整。PR 描述包含了背景、改动内容、特别说明、详细的验证命令与结果、可视化证据(带说明)、兼容性风险、回滚方案,以及修改文件列表。特别是对验证的充分性(包括 ./scripts/ci_gate.sh 的执行)和 CI 检查状态的引用都非常到位。

  • 风险级别:低。

    • API 变更均为追加字段,保持了向后兼容性,不会影响现有客户端。
    • 核心后端服务(告警联动、组合风险信号查询、通知)均设计了明确的 fail-safe 或 fail-open 机制,确保局部失败不阻断整体流程。
    • 不引入新的配置、数据库表或迁移,使得回滚方案非常直接且风险低。
    • 提到的并发幂等性问题被明确为当前 PR 不声称解决的残余风险,符合预期。

必改项

无。基于提供的 PR 信息、描述和截断的 diff,没有发现代码存在正确性或安全性问题,CI 检查均已通过,且 PR 描述与实际改动内容不存在实质性矛盾,回滚方案清晰。

建议项

  1. 文档细节确认:PR 描述指出更新了 docs/full-guide.mddocs/full-guide_EN.mddocs/alerts.mddocs/notifications.mddocs/CHANGELOG.md。请确保这些文档,尤其是用户指南和告警/通知文档中,对新增的“AI 风险信号”模块以及告警与信号的联动逻辑有清晰、准确的说明和示例,帮助用户理解和使用新功能。由于 diff 截断,无法逐一检查文档内容。
  2. 前端布局可扩展性:在 apps/dsa-web/src/pages/PortfolioPage.tsx 中,风险区布局从 md:grid-cols-3 调整为 md:grid-cols-2 xl:grid-cols-4。目前新布局可以很好地容纳新增的“AI 风险信号”卡片。考虑到未来可能的更多风险指标,可以持续关注这种多列布局的可扩展性,确保在不同屏幕尺寸下都能保持良好的用户体验。

💡 提示: 请确保代码已通过本地测试,并遵循项目代码规范。

@massif-01
massif-01 force-pushed the feat/1390-p6-signal-linkage branch from 6d1770a to c6d9993 Compare June 18, 2026 16:16
@massif-01
massif-01 marked this pull request as ready for review June 18, 2026 16:35
@massif-01
massif-01 requested a review from ZhuLinsen as a code owner June 18, 2026 16:35

@ZhuLinsen ZhuLinsen left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

评审结论

  • 必要性:通过。功能范围明确,落地 #1390 P6 最小闭环,约束清晰(不新增配置、表、迁移)。
  • 是否有对应 issue:有,#1390
  • PR 类型:feat。新增告警与 DecisionSignal 联动、通知低敏摘要、持仓风险聚合三个核心能力。
  • description 完整性:完整。涵盖改动详情、特别说明(无 Prompt/配置/迁移改动)、分层验证(CI/Web/测试)、可视证据、兼容性与风险、回滚方案。
  • 是否可直接合入:可。CI 全部通过(ai-governance、backend-gate、docker-build、web-gate),改动边界清晰无越界,代码质量通过 ci_gate.sh(3433 passed),兼容性保证充分(API 仅追加字段、fail-open 策略),风险已充分说明且可回滚。

主要问题

  • 未发现阻断性问题。

🤖 此回复由 OpenReview Bot 自动生成,仅供参考。如有疑问请 @维护者。

@ZhuLinsen
ZhuLinsen merged commit a7876fe into ZhuLinsen:main Jun 19, 2026
4 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b7c17fdfb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +115 to +118
response = self.decision_signal_service.list_signals(
holding_only=True,
account_id=account_id,
status="active",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Query signals from the requested snapshot holdings

When the risk endpoint has to backfill drawdown history, _ensure_drawdown_snapshot_window() calls get_portfolio_snapshot() for missing earlier dates after the initial as_of snapshot, and those calls rewrite the cached portfolio_positions table. Because this new block then uses holding_only=True, DecisionSignalService reads identities from that mutable cache instead of from the snapshot already passed into _build_decision_signal_risk, so a first risk request can omit signals for positions opened on the requested as_of date if the cache was left at the last backfilled prior date. Use the held_positions identities directly when querying/filtering signals, or restore the cache to as_of before the holding-only query.

Useful? React with 👍 / 👎.

Comment thread src/notification.py
Comment on lines +1641 to +1643
signal_excerpt = self._decision_signal_excerpt(r, report_language)
if signal_excerpt:
lines.append(signal_excerpt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include signal excerpts in default aggregate reports

This appends the DecisionSignal excerpt only in generate_brief_report() (and the separate single-stock path), but the normal scheduled/batch aggregate path still falls through to generate_dashboard_report() for the default non-brief report types, so users on the default report configuration will not see the new P6 signal summary in analysis notifications. Please add the same excerpt to the dashboard/report-renderer templates or route the default aggregate path through a format that includes it.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation notification size/XL testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants