Elvis Wang
eb1333a678
feat: 支持通过 --stocks 一次性分析板块指数 ( #2300 )
...
* feat: add one-shot index analysis via --stocks with structured AnalysisTarget pipeline
* fix: render index data sources in aggregate reports
* fix: refresh index registry before --stocks parsing and filter indices on CN holidays
---------
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com >
2026-08-28 21:56:48 +08:00
青玉案
d0e66a1dc3
feat: 新闻检索为空时在报告中如实标注 ( #2229 )
...
* feat: 新闻检索为空时在报告中如实标注
消息面章节此前是「有内容才渲染」,检索一条没拿到时整段直接消失,
读报告的人无从判断是确实没新闻,还是检索静默失败了(搜索源限流、
未配置可用渠道等)。这把「抓取失败」呈现成了「确实没有新闻」。
- src/analyzer.py: AnalysisResult 新增 news_result_count,默认 None
- src/core/pipeline.py: 把 Step 4 已算好的计数交给结果对象
(此前只进了 diagnostic context snapshot,报告层拿不到)
- src/notification.py: news_lines 为空且计数为 0 时,渲染明确提示,
并说明结论未纳入新闻维度证据
- tests: 新增 5 条用例,含两条负例——计数为 None 时不得报警
(那是未配置搜索渠道,不是失败)、拿到新闻时行为与改动前一致
不触碰任何检索路径,纯展示层增量。
* fix: 把新闻缺失提示放进真实渲染路径,并独立于模型输出判定
按 review 三条意见修正:
P1-1 提示只存在于 generate_daily_report,而正常流程从不调用它——
_send_single_stock_notification 与聚合报告走的是 dashboard / brief /
single_stock。原实现对所有标准 REPORT_TYPE 都不生效。
改为抽出共享判定 _empty_news_disclosure,四个渲染器统一接入。
P2 检索零命中但模型按 schema 写出了 market_sentiment / hot_topics 时,
原 elif 分支被跳过,报告会展示模型生成的情绪判断却隐瞒无新闻证据。
改为独立判定 news_result_count == 0,与模型是否产出文字无关。
P1-2 补 docs/CHANGELOG.md [Unreleased] 条目,并在
docs/data-source-stability.md 的「用户可见提示建议」一节记录该行为,
含 None / 0 / >0 三态语义表。
测试从 5 条增至 10 条,新增覆盖 dashboard、brief、single_stock 三个真实
渲染器,以及「模型有输出但检索为空」这一最糟组合。39 passed
* fix: 把新闻零命中披露覆盖到模板链路与企业微信入口
按 review 指出的 blocker 修正。此前只接了字符串拼接分支,遗漏两类活路径:
1. REPORT_RENDERER_ENABLED=true 时,generate_dashboard_report /
generate_brief_report / generate_wechat_dashboard 会先 return render(...),
模板链路一路不渲染披露;
2. generate_wechat_dashboard 的非模板 fallback 从未接入,而 pipeline 在
企业微信非 brief 场景会直接调用它。
后果是同一份分析结果在部分渠道披露、在另一些渠道沉默。
改法不再逐点打补丁,而是抽出单一事实来源:
- 新增 src/services/empty_news.py 持有判定与中英文案
- src/notification.py 的 _empty_news_disclosure 改为委托该模块
- src/services/report_renderer.py 为每条结果预计算 empty_news_disclosure,
三个平台模板共用
- templates/report_markdown.j2 / report_brief.j2 / report_wechat.j2 各加渲染分支
- generate_wechat_dashboard 的 fallback 正文接入披露
新增 6 条回归测试:模板链路三个平台各一条、企业微信入口一条,
外加两条负例(未执行检索时模板与企业微信均不得提示)。
本文件测试 10 → 16 全过;全量 5824 passed,9 个既有失败与本 PR 无关
(干净 main 上同样失败,属测试顺序依赖)。
* fix: 修正计数源头的两处缺口(自查发现)
按 review 的 merge-base..HEAD 方法自查全链路,发现此前几轮都只盯着渲染出口,
从未核对计数源头,而源头本身在两条路径上是错的:
1. src/core/pipeline.py: news_result_count 只在 intel_results 非空时赋值,
搜索服务整体失败(正是所有搜索源限流全挂的场景)时停留在 None,
语义为「未执行检索」,于是本 PR 想解决的头号场景反而不提示。
改为检索一发起即置 0。
2. _analyze_with_agent: Agent 模式自行调用 search_stock_news 完成检索,
却从不回写计数,该路径下零命中永远静默。改为按检索结果回写 0 或实际条数。
渲染层再周全,源头数据不对则全部落空。
新增 2 条测试锁住这两处语义(18 passed,此前 16)。
全量 5826 passed,9 个既有失败与本 PR 无关。
* fix: disclose missing news search configuration
* chore: remove unrelated agent guidance
* test: run all empty news tests directly
* fix: preserve empty news disclosure across reports
* fix: 让 Agent 模式的新闻披露跟随实际消费的证据
原问题:agent_arch=multi 等受支持的 Agent 配置下,报告可能声称「未纳入新闻
面证据」而分析其实用了新闻,或反过来该提示而不提示。
根因:news_result_count 取自 executor.run() 结束后为持久化情报补打的一次
search_stock_news()。真实情报由 IntelAgent 通过 search_comprehensive_intel
取得,两者不等价,因此披露与真实证据链可能相反。
修复点:新增 src/agent/news_evidence.py,以运行期证据作用域收集 Agent 搜索
工具的真实返回条数;搜索渠道不可用为 None(未执行检索),可用则从 0 起步、
拿到多少算多少。pipeline 在 executor.run() 前后开启并读取该作用域,事后的
持久化补查不再回写计数。
回归风险:工具在 ThreadPoolExecutor 中执行,runner.py 以
contextvars.copy_context() 提交,故作用域中必须是可变累加器对象,换成不可变
值会让父线程读不到;已加回归测试锁住该机制。原 test_agent_path_records_count
断言的正是被修复的错误行为,已替换为反向断言。
Refs #2225
* fix: 让新闻披露以实际证据为准而非搜索命中数
原问题:本地已落库的资讯池或社交情绪进入 news_context 参与分析后,报告仍可能
声称「未配置搜索渠道,本次分析未纳入新闻面证据」或「零命中」。
根因:news_context 由三路来源拼成——实时检索、社交情绪(美股)、本地资讯池,
但只有实时检索会更新 news_result_count。披露断言的是「结论有没有用到新闻面
证据」,而计数只是「搜索命中了几条」,两者是不同命题,后两路参与时必然失真。
修复点:AnalysisResult 新增 news_evidence_present,由 news_context 是否非空
得出,pipeline 两条路径共用 src/services/empty_news.news_evidence_present()
这一个判定函数。披露改为先看有无证据;确无证据时才用计数解释原因
(None=未配置渠道,0=检索零命中)。历史重建同步恢复该字段。
回归风险:旧记录没有该字段,按计数回退推断,与该记录当时的报告表现一致,不会
追溯改变旧报告;已有用例锁住。review 只点名了本地资讯池,社交情绪属同一缺陷类,
本次一并修复并加测试。另加源码断言:任一 pipeline 路径改回只传计数即失败。
Refs #2225
* fix: 按来源登记新闻证据,不让零命中占位文本冒充证据
原问题:普通分析链路在「搜索已执行但一条证据都没拿到」时,报告不再显示零命中
披露——正是本 PR 要修的核心场景,反而比改动前更差。
根因:src/search_service.py 的 format_intel_report() 即使所有维度失败或为空,
也会输出「【XX 情报搜索结果】」标题和每个维度的「未找到相关信息」占位文本,
整段永远非空。上一版把拼好的 news_context 整段交给 news_evidence_present()
判定,于是 news_result_count == 0 时 evidence 被翻成 true,披露被吞掉,错误
状态还会经 to_dict() 持久化,继续影响历史、详情 API 与 Web。
修复点:判定改为按来源逐个登记——实时检索的真实命中数、社交情绪内容、本地
资讯池内容,任一为真才算有证据;两条 pipeline 路径都不再传拼好的整段。
news_evidence_present() 的契约随之改为接收各来源,并在文档串里写明为什么不能
传整段。
回归风险:新增反例用真实的 format_intel_report() 产出占位文本(不用 mock),
断言其不得被判成证据、且报告必须出现零命中披露。另有源码断言:谁把整段
news_context 交回判定函数即失败。上一版两条测试实际在保护该缺陷(一条名为
「任何非空 context 都算证据」,一条要求必须传入 news_context),已一并纠正。
Refs #2225
---------
Co-authored-by: Mach-Chan <zz-b240@zz-b240deMacBook-Air.local >
2026-08-22 21:27:04 +08:00
xushengasd
5d98d6d787
feat(phase1): converge multi-strategy evidence contract ( #2031 )
...
Introduce the StrategyEngine facade and centralize deterministic opinion partitioning, aggregation, synthesis, and signal normalization.
Keep invalid opinions in diagnostics, protect synthesis ownership across fallback paths, and add localized strategy rendering for notification, history, Markdown, and WeChat outputs.
Harden legacy and malformed strategy_synthesis payload handling through shared renderer helpers, with public-entry regression coverage and an updated Phase 1 contract.
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com >
2026-07-19 18:55:42 +08:00
zhulinsen
e8a9ca7742
fix: harden release packages and report market context ( #1994 )
...
* fix: bundle orjson in release packages
* fix: remove duplicate decision signal excerpts
* feat: add TickFlow sector ranking fallback
2026-07-12 18:56:21 +08:00
zhulinsen
093057a11b
fix: 统一报告动作展示口径 ( #1982 )
...
* fix: unify report action display
* fix(review-feedback-1982): add Korean action labels or fall back before returning display action
* fix(review-feedback-1982): Derive signal metadata from resolved actions
* fix(review-feedback-1982): Derive history signal metadata from resolved actions
* fix(review-feedback-1982): Derive renderer emojis from resolved actions
* fix(review-feedback-1982): src/notification.py:2838:build stock summary 先得到 display
* fix(review-feedback-1982): 将 PR 正文同步到最新 head,确保范围、验证结果与实际 diff 一致
* fix(review-feedback-1982): 将 GitHub PR 正文同步到当前 head,并修正 docs/CHANGELOG.md 中不准确的审查过程记录
2026-07-11 14:04:34 +08:00
hsms4710-pixel
743ca5b1b4
feat: 添加信号归因分析功能 (Issue #1742 ) ( #1796 )
...
* feat: 修复 reviewer blocker 问题
- 同步 agent 路径(executor.py, decision_agent.py)
- 添加 SignalAttribution 字段验证器(自动转换、归零、归一化)
- 更新 docs/CHANGELOG.md
- 添加回归测试(tests/test_signal_attribution.py)
- 修复 notification.py 和模板的 None 值显示问题
Closes #1742
* fix: 修复 signal_attribution 完整契约
Reviewer feedback 指出的完整契约收敛:
## 1. [Correctness] 归一化接入真实 parse 路径
- 问题:Pydantic validator 没有进入主分析路径(dashboard 是 raw dict)
- 修复:将归一化函数移到 src/utils/data_processing.py,
在 _parse_response() 和 agent runner.py 的 parse_dashboard_json() 中调用
- 确保 LLM 返回的字符串/负数/总和≠100 被正确处理
## 2. [Correctness] 同步 HistoryService 路径
- 问题:_generate_single_stock_markdown() 不读取 signal_attribution
- 修复:在 history_service.py 中添加信号归因展示代码
## 3. [Process] 修复 CHANGELOG.md 格式
- 问题:两行 [Unreleased] 条目拼在同一行
- 修复:分割成独立行
## 4. [验证] 添加真实路径回归测试
- tests/test_signal_attribution_real_paths.py:
- 归一化函数测试(9个)
- _parse_response 集成测试(1个)
- HistoryService 展示测试(2个)
## 5. [Process] 修复 executor.py prompt 模板格式
- 问题:signal_attribution JSON 例子没转义花括号,导致 .format() 报错
- 修复:将 { 转成 {{,} 转成 }}
Co-authored-by: qyj <jiangqiyuan@tencent.com >
* fix: remove trailing whitespace in notification.py and report_schema.py
* fix: converge signal_attribution contract across all paths
- Add signal_attribution to check_content_integrity() as recommended field
- Normalize signal_attribution in _parse_response() and parse_dashboard_json()
- Sync HistoryService._generate_single_stock_markdown() to render signal_attribution
- Update CHANGELOG.md to reflect actual implementation (explicit normalization, not schema-level)
- Add end-to-end tests covering all paths: _parse_response, notification, Jinja2, HistoryService
- Fix tests to accept signal_attribution as recommended field (missing does not fail integrity check)
* fix: address all reviewer blockers
- Fix generate_single_stock_report() to render signal_attribution
- Fix normalization: clamp values to [0, 100], keep all-zero as 0 (not 25)
- Update docs/full-guide.md and docs/full-guide_EN.md with signal_attribution description
- Add supplement tests covering generate_single_stock_report, normalization edge cases, and _parse_response integration
* fix: 修复 CI 静态检查失败和文档表述不一致
- 修复 tests/test_signal_attribution_supplement.py 的 flake8 错误(F821 undefined name 'AnalysisResult')
- 将 AnalysisResult import 移到文件顶部
- 更新 docs/CHANGELOG.md 表述,反映实际行为(all-zero 保留为 0,有效贡献度归一化到 100)
- 所有 40 个 signal_attribution 测试通过
* fix: converge signal attribution runtime contract
* fix: hide empty signal attribution blocks
* fix: reject non-finite signal attribution weights
---------
Co-authored-by: qiyuanjiang <qiyuanjiang@tencent.com >
Co-authored-by: qyj <jiangqiyuan@tencent.com >
Co-authored-by: hsms4710-pixel <228664208+hsms4710-pixel@users.noreply.github.com >
Co-authored-by: zhulinsen <zhuls97@163.com >
2026-06-27 22:41:44 +08:00
zhulinsen
0344901c6b
fix: keep decision signal details out of summaries ( #1781 )
2026-06-24 21:51:32 +08:00
Alfred
dda5269123
fix: address P6 signal linkage gaps ( #1724 )
2026-06-19 23:04:52 +08:00
Alfred
a7876fe11c
feat: link decision signals with alerts and portfolio risk ( #1715 )
...
Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com >
2026-06-19 09:43:40 +08:00
mumu
3153c764a4
fix: 收敛多股通知摘要的市场状态展示 ( #1612 )
...
* fix: compact aggregate report market status
* fix(review-feedback-1612): preserve input order when selecting market status
* chore: rerun review after clarifying PR scope
2026-06-05 22:47:39 +08:00
Alfred
9b76f8d240
feat: #1386 P6 联动告警、持仓和历史阶段摘要 ( #1589 )
...
* feat: link phase summaries across P6 surfaces
* fix: align portfolio analysis response status
* fix: stabilize backtest phase filtering
* fix: normalize portfolio analysis symbols
2026-06-04 09:17:39 +08:00
Alfred
3ea007ceb1
feat: 盘中决策护栏与 phase_decision 质量校验 P5 ( #1563 )
...
* feat: add phase decision guardrails
* fix: neutralize non-intraday immediate actions
* test: cover low-confidence non-intraday actions
2026-06-02 23:19:10 +08:00
mumu
b7d449783c
fix: 资金流数据缺失 ( #1367 ) ( #1368 )
...
* fix(issue-1367): [bug]-资金流数据缺失
* fix(review-feedback-1368): preserve agent-provided chip data when prefetch is unavailable and
* fix(review-feedback-1368): 补充具体受影响文件/字段、是否会清理或迁移用户已有配置、用户可见恢复方式,以及对应回归测试或确认不涉及运行时行为的证据
* fix(review-feedback-1368): 结构化检测显示本 PR 涉及模型名、provider、Base URL、废弃日期、LiteLLM 或 LLM
* docs: trim issue 1367 compatibility notes
* docs: keep issue 1367 note in changelog only
2026-05-20 23:32:14 +08:00
mumu
e407b5bf14
feat: Add report LLM model visibility toggle ( #1294 )
2026-05-16 15:12:39 +08:00
mumu
ef328e906b
feat: add configurable report language ( #764 )
...
* feat: add configurable report language
* Fix review follow-ups for history and fallback localization
* fix: prefer .env report language at startup
* fix: address report language review feedback
* fix: address latest report language review feedback
2026-03-19 22:55:17 +08:00
Alfred
53e3d2dead
[Feature] 报告引擎 P0:Schema、模板、完整性校验、Brief 模式、历史对比 ( #560 ) ( #561 )
...
* chore(config): add report engine feature flags and template settings
* feat(analyzer): add pydantic schema validation for llm report json
* feat(report): report engine P0 - schema, templates, integrity, brief mode, history comparison
- Pydantic AnalysisReportSchema for LLM JSON validation
- Jinja2 templates (markdown, wechat, brief) with configurable dir
- Content integrity check with retry and placeholder fallback
- Brief mode (REPORT_TYPE=brief) for 3-5 sentence summary
- History comparison service for signal changes across runs
- generate_aggregate_report unified entry, ReportType.from_str(detailed)
- Config: REPORT_TEMPLATES_DIR, REPORT_RENDERER_ENABLED, etc.
Fixes #560
2026-03-09 21:35:55 +08:00