feat: 接入 #1390 P6 告警、通知与持仓信号联动 - #1715
Conversation
🤖 自动审查报告
📁 修改的文件
... 还有 8 个文件 🧠 AI 代码审查意见结论Ready to Merge 结构化审查结果
必改项无。基于提供的 PR 信息、描述和截断的 diff,没有发现代码存在正确性或安全性问题,CI 检查均已通过,且 PR 描述与实际改动内容不存在实质性矛盾,回滚方案清晰。 建议项
|
6d1770a to
c6d9993
Compare
ZhuLinsen
left a comment
There was a problem hiding this comment.
评审结论
- 必要性:通过。功能范围明确,落地 #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 自动生成,仅供参考。如有疑问请 @维护者。
There was a problem hiding this comment.
💡 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".
| response = self.decision_signal_service.list_signals( | ||
| holding_only=True, | ||
| account_id=account_id, | ||
| status="active", |
There was a problem hiding this comment.
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 👍 / 👎.
| signal_excerpt = self._decision_signal_excerpt(r, report_language) | ||
| if signal_excerpt: | ||
| lines.append(signal_excerpt) |
There was a problem hiding this comment.
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 👍 / 👎.
…#1715) Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
Refs #1390
背景
本 PR 落地 #1390 P6「告警、通知与持仓联动」的最小闭环:在不新增配置、不新增数据库表、不做迁移的前提下,让现有
DecisionSignal能被告警触发、通知摘要和组合风险视图复用。本 PR 不关闭 umbrella issue。#1390 仍保留 P7 文档/配置收尾和全局脱敏验收等后续项。
改动内容
src/services/decision_signal_summary.py,统一生成低敏DecisionSignal摘要和通知短摘要。id、股票标识、市场、动作、周期、状态、来源、reason、watch_conditions、risk_summary、创建/过期时间。metadata、evidence、raw diagnostics、webhook/token 等敏感或诊断细节。triggered事件上联动信号。decision_signal_summary写入 trigger diagnostics 和告警通知。source_type=alert/action=alert信号。market、portfolio_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。docs/full-guide.md、docs/full-guide_EN.md、docs/alerts.md、docs/notifications.md和docs/CHANGELOG.md。$ref完整性测试,避免客户端生成遇到 dangling ref。DecisionSignalService.normalize_stock_code_for_signal()作为组合风险与信号 identity 对齐的公开入口,避免跨服务调用私有方法。decision_signal_summaryhelper 单测,覆盖白名单、脱敏、格式化和非法输入。buy/watch/expired/非持仓 sell不进入decision_signal_risk。decisionSignalRisk预览项计算,避免重复slice(0, 3)。特别说明
EXTRACT_PROMPT、Agent prompt 或报告生成 prompt,因此没有附 prompt 全文。.env.example、config registry 或 README。trace_id=alert-rule-<hash>只用于同源重试的 best-effort 去重;当前仓库层没有新增唯一约束,本 PR 不声称多 worker/多进程并发下的绝对幂等。sell/reduce/watch。验证
./scripts/ci_gate.sh3433 passed, 2 deselected, 52 warningscd apps/dsa-web && npm run lintcd apps/dsa-web && npm run buildpython -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 -q8 passedcd apps/dsa-web && npm run test -- src/pages/__tests__/PortfolioPage.test.tsx25 passedpython -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.pygit diff --check可视证据
AI 风险信号、风险信号: 2、卖出: 1 · 减仓: 1 · 预警: 0、600519 · 卖出、300750 · 减仓均出现在页面中。AGENTS.md中“一次性验收截图不得合入仓库”的约束。PortfolioPage.test.tsx额外覆盖了有风险信号、无防御型信号、available=false三种 UI 状态。兼容性与风险
AlertTriggerItem.decision_signal_summaryPortfolioRiskResponse.decision_signal_risk现有客户端可继续忽略新增字段。
available=false,total=0,items=[],不阻断原有风险报告。回滚方案
可直接 revert 本 PR。由于没有新增配置、迁移或数据库表,回滚不会要求 schema downgrade。若线上已经产生
source_type=alert的信号记录,可按维护者策略保留历史记录,或后续通过既有信号状态更新接口归档/关闭。