Technical Documentation: ZhuLinsen/daily_stock_analysis
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
💖 赞助商 (Sponsors)
🖥️ 产品预览
✨ 功能特性
| 能力 | 覆盖内容 |
|---|---|
| AI 决策报告 | 核心结论、评分、趋势、买卖点位、风险警报、催化因素、操作检查清单 |
| 多市场数据聚合 | 覆盖 A股、港股、美股、日股、韩股、台股和 ETF,支持行情、K 线、技术指标、新闻、公告、基本面与报告辅助数据;不同市场的数据源和能力边界见 市场支持边界 |
| Web / 桌面工作台 | 手动分析、任务进度、历史报告、完整 Markdown、回测、持仓、配置管理、浅色 / 深色主题 |
| Agent 策略问股 | 多轮追问,支持均线、缠论、波浪、趋势、热点、事件、成长、预期等 15 种内置策略,覆盖 Web/Bot/API |
| 智能导入与补全 | 图片、CSV/Excel、剪贴板导入;股票代码/名称/拼音/别名补全 |
| 自动化与推送 | GitHub Actions、Docker、本地定时任务、FastAPI 服务和企业微信/飞书/Telegram/Discord/Slack/邮件推送 |
功能细节、字段契约、基本面 P0 超时语义、交易纪律、数据源优先级、Web/API 行为请看 完整配置与部署指南。
技术栈与数据来源
| 类型 | 支持 |
|---|---|
| AI 模型 | Modelflare、AIHubMix、Gemini、OpenAI 兼容、DeepSeek、通义千问、Claude、Ollama 本地模型等 |
| 行情数据 | AkShare、Tushare、Pytdx、Baostock、YFinance、Longbridge |
| 新闻搜索 | Anspire、SerpAPI、Tavily、Bocha、Brave、MiniMax、SearXNG |
| 社交舆情 | Stock Sentiment API(Reddit / X / Polymarket,仅美股,可选) |
项目默认内置 AkShare、Baostock、YFinance 等免费行情源,可零配置运行;免费源受上游限流、接口变动和网络波动影响,稳定性不保证。长期定时、批量分析或更稳定行情建议配置 Tushare、Longbridge 等 token 型数据源,适用市场、Actions 映射和 fallback 规则见 数据源配置。
🚀 快速开始
方式一:[GitHub Actions(推荐)](https://www.bilibili.com/video/BV11FEb66EXG/)
5 分钟完成部署,零成本,无需服务器。
1. Fork 本仓库
点击右上角 Fork 按钮(顺便点个 Star⭐ 支持一下)
2. 配置 Secrets
Settings → Secrets and variables → Actions → New repository secret
AI 模型配置(至少配置一个)
默认先选一个模型服务商并填写 API Key;需要多模型、图片识别、本地模型或高级路由时,再参考 LLM 配置指南。
| Secret 名称 | 说明 | 必填 |
|---|---|---|
MODELFLARE_KEY |
Modelflare API Key,低成本接入全球热门大模型 | 推荐 |
AIHUBMIX_KEY |
AIHubMix API Key,一Key切换使用全系模型,无需科学上网,本项目可享 10% 优惠 | 推荐 |
GEMINI_API_KEY |
Google Gemini API Key | 可选 |
ANTHROPIC_API_KEY |
Anthropic Claude API Key | 可选 |
OPENAI_API_KEY |
OpenAI 兼容 API Key(支持 DeepSeek、通义千问等) | 可选 |
OPENAI_BASE_URL / OPENAI_MODEL |
使用 OpenAI 兼容服务时填写 | 可选 |
Ollama 更适合本地 / Docker 部署,GitHub Actions 推荐使用云端 API。
通知渠道配置(至少配置一个)
| Secret 名称 | 说明 |
|---|---|
WECHAT_WEBHOOK_URL |
企业微信机器人 |
FEISHU_WEBHOOK_URL |
飞书机器人 |
TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID |
Telegram |
DISCORD_WEBHOOK_URL |
Discord Webhook |
SLACK_BOT_TOKEN + SLACK_CHANNEL_ID |
Slack Bot |
EMAIL_SENDER + EMAIL_PASSWORD |
邮件推送 |
更多渠道、签名校验、分组邮件、Markdown 转图片等配置见 通知渠道详细配置。
自选股配置(必填)
| Secret 名称 | 说明 | 必填 |
|---|---|---|
STOCK_LIST |
自选股代码,如 600519,hk00700,AAPL,7203.T,005930.KS,2330.TW;已登记指数显式代码(如 sh000016、930606.CSI)经一次性 --stocks 或 GitHub Actions 入口支持,规则见 指数自选股配置 |
✅ |
新闻源配置(推荐)
新闻源会显著影响舆情、公告、事件和催化因素质量,建议至少配置一个搜索服务。
| Secret 名称 | 说明 | 必填 |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire AI Search:汇聚全球舆情信息,适配A股、美股、港股等新闻和舆情检索;同一Key可复用大模型服务,本项目新用户提供免费30元等额的免费点数 | 推荐 |
SERPAPI_API_KEYS |
SerpAPI:搜索引擎结果补强,适合实时金融新闻 | 推荐 |
TAVILY_API_KEYS |
Tavily:通用新闻搜索 API | 可选 |
BOCHA_API_KEYS |
博查搜索:中文搜索优化,支持 AI 摘要 | 可选 |
BRAVE_API_KEYS |
Brave Search:隐私优先,美股资讯补强 | 可选 |
MINIMAX_API_KEYS |
MiniMax:结构化搜索结果 | 可选 |
SEARXNG_BASE_URLS |
SearXNG 自建实例:无配额兜底,适合私有部署 | 可选 |
更多搜索源、社交舆情和降级规则见 搜索服务配置。
行情数据源配置(可选)
默认使用 AkShare、Baostock、YFinance 等免费数据源,日志中"未配置"的提示不影响运行。
如需更稳定的行情,可按市场配置以下 Secret:
| Secret 名称 | 适用市场 | 说明 |
|---|---|---|
TUSHARE_TOKEN |
A 股 | 提升历史行情稳定性 |
LONGBRIDGE_OAUTH_CLIENT_ID + LONGBRIDGE_OAUTH_TOKEN_CACHE_B64 |
港股/美股 | 补齐量比、换手率、PE 等字段 |
详见 数据源配置。
3. 启用 Actions
Actions 标签 → I understand my workflows, go ahead and enable them
4. 手动测试
Actions → 每日股票分析 → Run workflow → Run workflow
完成
默认每个**工作日 18:00(北京时间)**自动执行,也可手动触发。默认非交易日(含 A/H/US 节假日)不执行;强制运行、交易日检查、断点续传等规则见 完整指南。
方式二:[客户端配置教程](https://www.bilibili.com/video/BV11FEb66Eyr/) / 本地运行 / Docker 部署
# 克隆项目
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git && cd daily_stock_analysis
# 安装依赖
pip install -r requirements.txt
# 配置环境变量
cp .env.example .env && vim .env
# 运行分析
python main.py常用命令:
python main.py --debug
python main.py --dry-run
python main.py --stocks 600519,hk00700,AAPL,2330.TW
python main.py --market-review
python main.py --schedule
python main.py --serve-only📱 推送效果
决策仪表盘
🎯 2026-02-08 决策仪表盘
共分析3只股票 | 🟢买入:0 🟡观望:2 🔴卖出:1
📊 分析结果摘要
⚪ 中钨高新(000657): 观望 | 评分 65 | 看多
⚪ 永鼎股份(600105): 观望 | 评分 48 | 震荡
🟡 新莱应材(300260): 卖出 | 评分 35 | 看空
⚪ 中钨高新 (000657)
📰 重要信息速览
💭 舆情情绪: 市场关注其AI属性与业绩高增长,情绪偏积极,但需消化短期获利盘和主力流出压力。
📊 业绩预期: 基于舆情信息,公司2025年前三季度业绩同比大幅增长,基本面强劲,为股价提供支撑。
🚨 风险警报:
风险点1:2月5日主力资金大幅净卖出3.63亿元,需警惕短期抛压。
风险点2:筹码集中度高达35.15%,表明筹码分散,拉升阻力可能较大。
风险点3:舆情中提及公司历史违规记录及重组相关风险提示,需保持关注。
✨ 利好催化:
利好1:公司被市场定位为AI服务器HDI核心供应商,受益于AI产业发展。
利好2:2025年前三季度扣非净利润同比暴涨407.52%,业绩表现强劲。
📢 最新动态: 【最新消息】舆情显示公司是AI PCB微钻领域龙头,深度绑定全球头部PCB/载板厂。2月5日主力资金净卖出3.63亿元,需关注后续资金流向。
---
生成时间: 18:00大盘复盘
🎯 2026-01-10 大盘复盘
📊 主要指数
- 上证指数: 3250.12 (🟢+0.85%)
- 深证成指: 10521.36 (🟢+1.02%)
- 创业板指: 2156.78 (🟢+1.35%)
📈 市场概况
上涨: 3920 | 下跌: 1349 | 涨停: 155 | 跌停: 3
🔥 板块表现
领涨: 互联网服务、文化传媒、小金属
领跌: 保险、航空机场、光伏设备⚙️ 配置说明
完整环境变量、模型渠道、通知渠道、数据源优先级、交易纪律、基本面 P0 语义和部署说明请参考 完整配置指南。
🖥️ Web 界面
Web 工作台提供配置管理、任务监控、手动分析、历史报告、完整 Markdown 报告、Agent 问股、回测、持仓管理、智能导入和浅色 / 深色主题。启动方式:
python main.py --webui
python main.py --webui-only访问 http://127.0.0.1:8000 即可使用。认证、智能导入、搜索补全、历史报告复制、云服务器访问等细节见 本地 WebUI 管理界面。
🤖 Agent 策略问股
配置任意可用 AI API Key 后,Web /chat 页面即可使用策略问股;如需显式关闭可设置 AGENT_MODE=false。
- 支持均线金叉、缠论、波浪理论、多头趋势、热点题材、事件驱动、成长质量、预期重估等内置策略
- 支持实时行情、K 线、技术指标、新闻和风险信息调用
- 支持多轮追问、会话导出、发送到通知渠道和后台执行
- 支持自定义策略文件与多 Agent 编排(实验性)
🧩 相关项目 (Related Projects)
DSA 聚焦日常分析报告;选股实现参考 AlphaSift,AlphaEvo 用于策略验证与进化。
| 项目 | 定位 |
|---|---|
| AlphaSift | DSA 选股实现的参考项目 |
| AlphaEvo | 策略回测与自我进化,用于验证策略规则,并通过迭代探索策略参数与组合 |
📬 联系与合作
| 合作邮箱 | [email protected]
项目咨询、部署支持与功能扩展 |
扫码关注小红书 |
| --- | --- | --- |
| 小红书 | 欢迎关注小红书 | |
| 问题反馈 | 提交 Issue | |
📄 License
MIT License © 2026 ZhuLinsen
欢迎在二次开发或引用时注明本仓库来源,感谢支持项目持续维护。
⚠️ 免责声明
本项目仅供学习和研究使用,不构成任何投资建议。股市有风险,投资需谨慎。作者不对使用本项目产生的任何损失负责。
INDEX (`docs/INDEX.md`)
文档中心
这里是项目文档入口。README 负责项目概览和快速开始;更完整的配置、部署、功能说明和排障内容从这里进入。
按场景选择
| 我想要 | 先看 | 继续看 |
|---|---|---|
| 快速了解项目能做什么 | README | 完整配置与部署指南 |
| 第一次把项目跑起来 | 小白客户端安装与配置 | 完整配置与部署指南 |
| 配置大模型渠道 | LLM 配置指南 | LLM 服务商配置指南(含 xAI Grok 直连) |
| 配置推送通知 | 通知能力基线 | 完整配置与部署指南 |
| 部署到服务器或云平台 | 部署指南 | 云端 WebUI 部署、Zeabur 部署 |
| 使用 Bot / IM 接入 | Bot 命令与接入 | Bot 平台配置、Grok Bot 集成 |
| 排查运行问题 | FAQ | 更新日志 |
| 处理数据源失败或降级 | 数据源稳定性与故障处理图示 | FAQ |
| 参与开发或提交 PR | 贡献指南 | API 规格 |
快速开始
| 文档 | 内容 |
|---|---|
| README | 项目定位、核心能力、快速开始、推送效果 |
| 小白客户端安装与配置 | 面向不会代码用户的客户端下载、Anspire Open / AIHubMix 模型配置、新闻源配置和常见问题 |
| 完整配置与部署指南 | 环境准备、运行方式、配置说明、部署路径和常见问题 |
| FAQ | 常见配置、模型、通知、部署和运行问题 |
| 数据源稳定性与故障处理图示 | Tushare、TickFlow、AkShare、Efinance、YFinance、Longbridge 等已接入源的使用场景、fallback 链路和推荐配置 |
| 数据中心 | 只读 Provider 能力、数据集质量、精确市场支持与运行时优先级 |
| 更新日志 | 版本变化、能力调整和迁移说明 |
配置
| 文档 | 内容 |
|---|---|
| LLM 配置指南 | 大模型渠道、三层配置、Web 设置页和常见模型配置 |
| LLM 服务商配置指南 | Provider 预设、Actions 映射、错误分类和诊断建议 |
| LiteLLM YAML 示例 | LiteLLM 多渠道配置示例 |
| Grok Bot Skill 示例 | 给 Grok Bot 粘贴的最小 Skill,调用现有 REST |
| 通知能力基线 | 企业微信、飞书、Telegram、Discord、Slack、邮件等通知渠道配置 |
| Tushare 股票列表指南 | Tushare 股票列表相关配置和使用说明 |
使用专题
| 文档 | 内容 |
|---|---|
| 自选股变化状态与下一步 | 首页自选股列表的变化、状态、下一步三段信号、映射规则和后续扩展边界 |
| Bot 命令与接入 | Bot 命令、Webhook、平台接入和回调说明 |
| Bot 平台配置 | 飞书、钉钉、Discord 等 Bot 配置截图和补充说明 |
| 实时告警中心 | EventMonitor 基线、Web 规则管理、通知结果、冷却状态和 Phase 边界 |
| 个股研究聚合 API | 单一 stock-profile 契约、分块质量状态、代码归一与后续 Web 边界 |
| DecisionSignal 决策信号专题 | AI 建议池字段语义、API、Web 展示、告警/通知/组合风险联动、后验评估、脱敏、迁移与回滚 |
| ETF 轮动 | --etf-rotation 规则化双动量轮动:规则、配置、报告章节、参数平原解读与数据告警边界 |
| ResearchArtifact 结构化研究产物 | structured_report 字段、Thesis / Evidence / Invalidation / Next Action / Data Quality 契约和旧报告兼容边界 |
| 组合风险与暴露看板 | 持仓页风险旗标、市场暴露、币种暴露和价格质量聚合展示 |
| 选股解释契约 | Why Selected / Why Now 的后端确定性解释、来源、质量状态与缺失值边界 |
| 资讯 / 情报源 | RSS/Atom 合规资讯源配置、测试、拉取、去重、存储、查询与安全边界 |
| 分析上下文包契约、运行态消费与可见性 | AnalysisContextPack 首版范围、字段质量状态、P1/P2 内部契约、P3 Prompt 摘要消费、P4 历史/API/Web 低敏可见性、P5 数据质量评分、P6 迁移回滚与源码锚点;完整指南补充 #1386 阶段感知分析、迁移与回滚入口 |
| 图片识别 Prompt | 图片识别股票信息的 Prompt 与使用边界 |
| OpenClaw Skill 集成 | OpenClaw / Skill 外部集成说明 |
| Grok Bot 集成 | 2026-08-11 Grok Bot(AI teammate)与 DSA REST / DecisionSignal / Skill 的对接边界 |
部署与打包
| 文档 | 内容 |
|---|---|
| 部署指南 | 服务器部署、Docker、systemd、Supervisor 等部署方式 |
| 云端 WebUI 部署 | 云服务器访问 WebUI 的部署说明 |
| Zeabur 部署 | Zeabur 平台部署说明 |
| 桌面端打包说明 | Electron 桌面端和 Web 构建产物打包说明 |
参考与开发
| 文档 | 内容 |
|---|---|
| API 规格 | FastAPI OpenAPI 规格产物 |
| 贡献指南 | Issue、PR、测试、文档同步和协作要求 |
多语言
| 文档 | 内容 |
|---|---|
| 英文文档索引 | English documentation index |
| 英文 README | English project overview and quick start |
| 繁中 README | 繁體中文項目概覽與快速開始 |
Examples Grok Bot README (`docs/examples/grok_bot/README.md`)
Grok Bot Skill 示例
给 2026-08-11 Grok Bot 用的最小 Skill 包。只调用 DSA 已有 REST,不引入新 API、环境变量或 MCP server。
把 SKILL.md 拷进 Bot 的 Skills 目录,设置 DSA_BASE_URL(例如 http://127.0.0.1:8000),并先让 DSA 以 python main.py --serve-only 或 Docker 长期运行。
完整边界、DecisionSignal Routine、认证限制见 Grok Bot 集成。openclaw 用户看 openclaw Skill,契约相同。
CHANGELOG (`docs/CHANGELOG.md`)
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
For user-friendly release highlights, see the GitHub Releases page.
[Unreleased]
- [修复] Telegram 推送按 UTF-16 长度判断与分段,修复含大量 emoji 的报告超出 4096 限制导致发送失败的问题。
[3.33.0] - 2026-10-15
发布亮点
- feat: 问股讨论对象由服务端保存,刷新、重开及 Bot/Web 交替使用可恢复;修复完整股票名称被内部短别名误判的问题。
- feat: 新增规则化 ETF 双动量轮动,提供最新调仓信号、扣成本回测、年度对比及数据质量告警,无需 LLM。
- feat: Web 新增只读数据中心和持仓风险与暴露看板,自选股列表提供“变化 / 状态 / 下一步”提示。
- feat: 选股提供带来源与质量状态的入选理由和时点证据,补齐新闻事件,并修复日线新鲜度、交易日期及缓存降级判断。
- feat: 简化 Modelflare 单 Key 配置,补充已合入的 Requesty 渠道、妙想筹码与资金流数据及 FXMacroData 宏观工具。
- fix: 修复基本面与资金流解析、科创板成交量单位、重复定时派发和桌面 MiniRacer 资源打包;调度超时可推送已完成的部分结果。
新功能
- 问股由服务端保存已确认的主讨论对象,刷新、重开和会话切换可恢复;Bot/Web 交替问股共享已确认对象,保留股票/指数、跨市场身份及严格工具范围(#2397、#2406)。
- 新增
python main.py --etf-rotation规则化 ETF 双动量轮动,输出最新调仓信号、扣成本回测、分年度对比、参数平原检验和数据质量告警;不调用 LLM,配置与数据边界见 ETF 轮动(#2376)。 - Web 新增只读数据中心,展示 Provider 能力、数据集质量和运行时优先级,不展示 API Key 或原始配置值;短窗口中的导航列表可滚动,并保留主题、语言与退出控件(#2304)。
- Web 持仓页新增风险与暴露看板,聚合个股/行业集中度、回撤、止损、AI 信号、价格质量、市场及币种暴露(#2297)。
- 选股候选新增后端生成的 Why Selected / Why Now 解释、来源与质量状态;保留模型 reason/thesis 和逐条 provenance,区分真实零值与缺失占位值,旧历史不重新评分或回写(#2305)。
- 新增
/api/v1/screening/screen/check,对调用方提供的单条快照解释策略硬过滤条件、阈值、当前值及通过/失败/缺失状态,不抓取行情、不调用模型、不改变既有选股结果(#2382)。 - 新增单票决策信号复盘只读接口
GET /api/v1/decision-signals/stocks/{stock_code}/review,返回样本量、命中率、常见 miss 原因和置信度调整方向;启用AGENT_MEMORY_ENABLED时作为独立记忆段注入,样本不足或数据质量弱时仅供观察(#2331)。 - 新增妙想补充数据源
MX_APIKEY/MX_PRIORITY,用于筹码分布和个股资金流降级,接入 Web 配置与每日 Actions;不提供日线能力,调用受剩余阶段预算和跨实例非阻塞准入约束(#2387、#2388)。 - Agent 新增 FXMacroData 官方宏观数据工具,USD 基础数据无需密钥;每日 Actions 映射可选
FXMACRODATA_API_KEY,Windows/macOS 冻结产物收集操作目录并校验工具注册(#2336)。 - 新增 Requesty 聚合网关预设,补齐 Web、环境示例、Actions 的
LLM_REQUESTY_*映射与配置说明;模型发现返回的 vendor/model ID 保留 OpenAI-compatible 网关路由(#2367)。
改进
- Modelflare 支持只配置
MODELFLARE_KEY,后端、Web、选股及 Actions 自动识别渠道并补齐默认地址和模型;单渠道未声明模型时可复用主模型与 OpenAI 备用模型,否则使用默认模型,显式 YAML、模型列表和多渠道规则保持优先(#2403)。 - 模型设置预览、连接测试及保存失败时显示具体字段与修正提示;Modelflare 预设提供条件注册链接和说明,同步简化三语 README 赞助区并使用对应语言横幅(#2403、#2407)。
- Web 自选股新增“变化 / 状态 / 下一步”信号,结合今日分析、历史报告、运行任务和详情状态提示操作;“仅未分析”排除运行中任务,兼容股票/指数身份(#2293)。
- Web 始终保留选股导航,未开启时提供开启提示;状态加载失败可重试,不误报功能未开启,也不自动启用或提交任务(#2392)。
- 选股 LLM 重排前按候选上限补充新闻与事件,保留来源、发布时间和检索时间,隔离不可信资料与排序指令;长度预算包含 system 消息和重试提示,同次运行复用查询,缺失/失败不视为无风险(#2390)。
- 研究与搜索证据保留来源观测和实际检索完成时间,在归一化、时效过滤、排序及缓存中持续传递;缓存命中不刷新检索时间,没有明确时效证据时保持 unknown(#2381、#2390)。
- 新增意图解析器基础模块,支持二维任务组、多意图解析、跨市场候选消歧、LLM 兜底及确认状态簿记;本版本尚未将其接入 Web SSE 执行入口(#2385)。
- Multi-Agent 轨迹评估增加阶段快照、阶段本地工具指标、局部/累计步数及完成/失败/跳过状态;顶层步数由阶段快照累计,单 Agent 既有指标保持兼容(#2351)。
修复
- 问股将完整注册名称视为一个标的,避免“长电科技”中的“长电”或“京东方A”中的代码片段触发错误澄清;独立别名、跨市场同名和名称/代码真正冲突仍沿用确认规则(#2406)。
- 问股实时请求与旧历史恢复共用对象决策,正确处理明确切股、重复输入、普通追问、比较、否定、条件句及引用;工具和缓存保留股票/指数及跨市场身份,防止同码误读与模型名称改变新闻目标(#2397)。
- 问股接受事务统一保存消息、显式 Skill 和对象状态,校验会话实例、版本及用户锚点;迟到列表、摘要或回复不覆盖新状态,比较或执行失败不改写已确认对象,显式空 Skill 保留默认执行语义(#2397)。
- 严格回测按已验证身份和关联分析来源查询,兼容正常 CN 写入器的数字键及同身份存储别名,保持跨市场隔离、总数与分页一致性(#2397)。
- Codex 问股向最终请求传递当前交易 Skill 指令,保留会话继承、显式清空及默认技能语义;能力边界限定于三个已保存数据工具,缺失数据或能力时明确披露(#2371)。
- ETF 轮动按真实 A 股交易日历保留全池缺报价日及尾部缺口,防止调仓顺延或信号提前;缺价不虚构成交,报价恢复后计入完整持仓损益,防守资产不可买入时保留现金;日历不可用时停止计算,主回测与参数扫描共用前复权行情(#2376)。
- 选股日线核验交易日期、有效收盘价及收盘时段,拒绝休市/未来日期、非法日期与盘前占位行情;缓存使用持久化获取时间,无时区旧时间回退文件 mtime,避免跨时区或桌面恢复误判新鲜度(#2389)。
- 选股全源陈旧时优先保留最新合法日线,同日保留来源优先级;比较 DSA、备用来源及文件缓存,不以陈旧/非法结果覆盖 last-good,完整传递各来源错误和健康状态以参与质量评分(#2389)。
- 选股来源仅在行情通过日期验证后记录成功,非法响应参与既有熔断和恢复;yfinance 按美东日期推进排他的 end 边界,避免收盘后漏取当日 K 线(#2389)。
- 选股响应和历史保留当次实际因子权重(含零值),无候选时也不使用策略目录原始或后续变更权重补填;入选解释仍只消费正权重(#2389)。
- 选股时点证据与预补充共用时效和来源校验,按新闻/事件分别刷新无效缓存,有效摘要可用于 Why Now;失败保持 unknown 和告警,无理由或仅 thesis 的 LLM 结果仍保留 inferred 标记、真实重排与风险说明(#2390、#2305)。
- 选股后分析器完成非零调分却未给出有效摘要时,仍保留来源、质量、完成状态及分差;模型理由、本地因子和各后分析来源可同时保存在响应与历史中(#2305)。
- Web 设置保护普通配置、模型渠道及调度未保存草稿,重置/成功导入清理旧草稿,保存及导入期间防止编辑与离开竞态;登录后保留安全站内目标及 query/hash,首次引导可滚动聚焦
STOCK_LIST(#2386、#2399)。 - 持仓看板缺价、过期汇率、异常估值或行业分类覆盖不足时保持不可用,不展示误导的集中度、权重或饼图;兼容舍入后的零权重尾行,并正确聚合混合币种和零市值账户(#2297)。
- 妙想按明确金额指标选表选列并规范列表型 nameMap,避免价格/占比被当成资金流金额;缺失值保留在交易日窗口中,超时任务结束前抑制重复请求,避免排队消耗额度(#2387、#2388)。
- AkShare 基本面、资金流及龙虎榜超时后按操作跨管理器抑制重复后台请求;大盘统计降级调用使用可终止子进程超时,诊断耗时按实际等待记录,避免线程拒绝或超时虚报完整预算(#2345、#2372、#2380)。
- 基本面按明确报告期与目标股票读取业绩预告、快报、机构持股和十大股东,修正市场代码;财务宽表按最新报告期与真实指标解析,补齐新浪收入/利润增长、ROE、经营现金流别名,不跨报告期补值或把空结果标成功(#2362、#2370、#2373)。
- yfinance 财报同比按日期匹配去年同季度,缺同期时回退 info 同比,避免固定行偏移把更早季度误当作去年同期(#2365)。
- 腾讯科创板(688/689)日线成交量按“股”保留,不再统一乘以 100;存量数据修正要求见下方升级注意事项(#2360,Fixes #2350)。
REPORT_LANGUAGE=en使用完整英文 system/user 模板,ko复用英文骨架并保留韩文指令;JSON 键名、枚举和解析契约保持不变,中文模板不变(#2368)。- CLI 与 Web/API 每日调度共享 SQLite 计划时刻认领,避免同一数据库上的并发或错峰重复派发;不同时间/分析范围及手动补跑独立执行,认领后失败不会自动重派(#2379)。
- Web/API 调度硬超时后记录已完成/待完成摘要,并可推送已落库的部分结果;默认通知行为及关闭方式见下方(#2338)。
- 告警列表披露有效环境规则和去重后数量,明确页面删除/禁用不停止环境规则;来源统计只读且请求内复用,不查实时行情或改写持仓快照;规则变更后的刷新保留当前筛选和分页(#2374、#2378)。
- Windows/macOS 桌面后端完整收集 MiniRacer 原生运行文件并实际执行 JavaScript 检查;macOS 补齐选股策略 YAML 资源及 Bash 参数构造校验(#2361、#2384)。
- 智能导入兼容带 UTF-8 BOM 的 CSV 与剪贴板文本,避免表头误判或有效代码丢失;默认 Web smoke 排除独立 fixture,避免错误收集需特定环境的用例(#2333、#2390)。
升级注意事项
- 默认行为变化:Web/API 调度硬超时后,
DSA_TIMEOUT_PARTIAL_NOTIFY默认开启,可能新增部分完成通知;设为false可关闭。该能力不保证通知送达,也不会自动重试已认领的整轮任务。 - 历史行情:科创板成交量修复不自动重写已有数据;修正旧记录时须限定
data_source = 'TencentFetcher'及受影响的 688/689 股票,不能统一调整其他来源或市场。 - 会话升级与回滚:升级前备份数据库及配置;回滚时先停止新后端,保留新增可空列与数据库版本默认值,不删列或重建数据库,避免旧新写入后端并行。旧版写入后再次升级需重新确认讨论对象,已有消息及 Skill 保留,详见 问股事件与回滚边界。
- 功能范围:新意图解析器仍为基础模块,尚未接入 Web SSE 执行入口;CLI 启动分析行为保持现状。ETF 轮动及可选数据/模型渠道按各自配置启用。
测试与文档
- 补充真实索引下完整名称、HTTP/SSE、会话恢复与重复确认回归;意图解析器覆盖任务切分、确认消费、LLM 结果校验及状态簿记。
- 补充选股新鲜度、备用来源、缓存、检索时间、真实重排及响应/历史一致性验证,保留风险与缺失数据反例。
- 告警来源和页面交互采用无后台、无密钥的确定性浏览器验收,CI 保存桌面与窄屏证据;修复名称解析冷启动并发测试竞态。
- 同步模型、数据源、ETF 轮动、数据中心、问股升级/回滚及桌面指南,更新三语赞助说明和 SerpAPI 链接,修复文档锚点。
[3.32.0] - 2026-09-06
发布亮点
- feat: 已登记 A 股指数贯通 Web/API、Bot、CLI 与 GitHub Actions 分析入口,注册表扩展至 33 项,并统一指数身份、任务去重、历史与 Chat 上下文。
- feat: 新增个股研究聚合 API、ResearchArtifact 结构化研究产物,以及数据源能力与数据集质量只读接口。
- feat: 完善 Futu OpenD 港股数据源接入,新增 Web Chat 分词基础模块与 Agent 轨迹评估入口。
- feat: 自建 SearXNG 搜索超时可配置,Tavily 改用 basic 搜索以降低 credit 消耗。
- fix: 定时分析采用独立进程与硬超时保护,选股结果支持历史恢复,桌面端增加全局更新入口,并修复 Linux/Docker 分享图中韩文字体缺失。
- fix: 收敛 LiteLLM 兼容版本窗口,更新 Web/桌面依赖,并修复美股数据源优先级与股票名称归一问题。
变更明细
[修复] 入口分类的模式边界修复(PR3 review):不消费个股列表的模式(
--backtest/--market-review/--serve-only/--webui-only/--portfolio/--schedule/config.schedule_enabled)在模式分发前整体跳过--stocks与 GitHub ActionsSTOCK_LIST的分类与索引刷新,未登记.CSI等坏 token 不再拦截这些模式(此前GITHUB_ACTIONS=true下--backtest、--portfolio futu、--schedule配坏 watchlist 会在进入模式主体前被整批拒绝);--schedule --stocks的"警告后忽略启动快照"语义保留;--stocks与 GitHub Actions 入口的指数分类与整批拒绝契约不回归,文档同步把指数自选股配置收窄为仅这两类入口(本地.env/Docker 无参数默认运行保持股票语义,分析指数请配--stocks)。[新功能] 指数注册表新增国证粮食(
sz399365)与中证钢铁(csi930606):已登记 seed(scripts/stock_index_seeds/index_registry.csv)与 bundled 指数清单(apps/dsa-web/public/stocks.index.json)由 31 项扩展到 33 项,已登记指数的sh/sz/csi前缀与.SH/.SZ/.CSI显式形态均可作为自选股指数目标;ETF 与美股指数不进 CN 注册表,路由语义不变。[文档] 中英 full-guide 新增「指数自选股配置」小节:说明已登记指数前缀/显式后缀规则、未登记
.CSI整批拒绝、裸码不自动提升为指数、NDX 等美股指数裸码即正确路由、ETF 走股票路径,并同步补充 README / DEPLOY 的STOCK_LIST提示;修正 full-guide 中已过期的「已登记 5 个沪深指数」表述。[新功能] GitHub Actions 每日工作流支持已登记指数入口:
GITHUB_ACTIONS=true下无参数python main.py把STOCK_LIST按与一次性--stocks相同的判型规则分类为结构化 target(显式指数 token 进入指数路径、个股 token 保持既有 legacy 路径),指数与个股同批复用同一 Pipeline,不新增第二条能力矩阵;本地与--schedule热刷新默认路径不在入口构造 target,语义不变。[修复]
main.py --stocks对未登记.CSI目标在入口明确报错并整批拒绝本轮运行(此前被静默按股票 token 交给 provider),拒绝发生在任何 provider 请求之前,与 API 层"provider 调用前明确拒绝"的边界对齐;CLI/Actions 整批拒绝与 API 异步批量仅拒绝该目标的差异已在文档中说明。[测试] 新增 Bot 指数入口 transport-independent 在线 E2E smoke(
scripts/smoke_bot_index_entry.py):worker 子进程经真实CommandDispatcher.dispatch_async提交并在同进程轮询TaskService贯穿到StockAnalysisPipeline,父进程只负责 deadline、进程树清理(Windowstaskkill /T /F、POSIX 杀进程组)与退出码(0=成功/1=失败/124=超时),输出单行E2E_EVENT {json}事件(phase=submitted|completed|failed|timeout);smoke 覆盖SH.000016/上证50/930955.CSI矩阵,期望 code/name 取脚本内置权威映射(不信任响应/结果自报身份,提交 code mismatch 输出含期望值与实际值的显式错误并携带结构化实际stock_code,dispatcher 路由错误也失败),矩阵外 target 与非正--timeout在提交与 spawn 之前即被拒绝(退出码 2),completed 须 exact canonical code、exact 注册名称且analysis_summary/operation_advice/trend_prediction非空(仅空白视为空),失败或结果不完整即非零退出;超时清理进程树(Windowstaskkill /T /F、POSIX 杀进程组),清理成功输出timeout事件并退出 124、清理失败输出含清理错误的failed事件并退出 1,不回滚 DB/报告/通知副作用;用户 Ctrl-C 中止时父进程同样先清理进程树,清理成功透传中断、清理失败输出含清理错误的failed事件并退出 1,绝不静默吞掉清理失败;worker 意外异常输出failed事件并退出 1(stderr 保留异常证据,KeyboardInterrupt不按普通失败处理),父进程将 worker 任意其他退出码归一化为 1(运行时契约只暴露 0/1/124);父进程以内部--workerflag 显式拉起子进程(不依赖环境变量,防外部预置绕过硬超时);不 mock 在线依赖、不 dry-run、不修 transport,不加入离线 gate。[新功能] Bot
/analyze支持已登记指数入口:显式代码(sh000016)、CSI alias(930955.CSI收敛为csi930955)与注册中文名(上证50)均可提交,指数以结构化AnalysisTarget经TaskService贯穿到 Pipelineprocess_single_stock(sh000016不再被改写为SH000016);注册名称查询独立于 parser identity alias(中文名不进入find_by_explicit_key/parse_analysis_target),同名歧义返回明确错误并要求显式代码,未登记 CSI 与未知名称返回明确错误且不提交任务;个股代码(A/HK/US)保持既有 legacy code 路径不变,股票名称输入(如贵州茅台)由本次 Bot 入口新暴露——复用既有名称解析器(resolve_name_to_code)解析后提交 legacy code,不携带结构化 target;提交成功响应在BotResponse.extra暴露内部任务 identity(task_id/stock_code)供在线验收等内部流程使用,文本与错误路径不变、平台适配器可忽略extra。[新功能] 新增最小 Agent 轨迹评估入口
evals/agent_trajectory/(Refs #1956):纯函数指标层只消费真实tool_calls_log + AgentResult,冻结最小指标契约(工具命中、冗余/缓存、失败/重试、总步数/max_steps),run_eval.py经build_agent_executor真实执行并输出文本摘要 + 结构化 JSON 报告;评估为 reporter 非 gate,零src/改动[修复] 将 litellm 依赖窗口上界收敛到
<1.99.0:1.99.0 起把prompt_cache_key透传给 OpenAI provider,破坏 provider 缓存测试对不透传行为的既有断言(CI backend-tests 3/3 与 backend-gate 失败);保留历史最低版本与!=1.82.7/!=1.82.8事故排除,同时同步更新各 LLM 兼容文档中写死的依赖约束表述,避免文档与 requirements.txt 漂移[新功能] 新增
SEARXNG_TIMEOUT_SECONDS配置自建 SearXNG 单次搜索超时(默认 10 秒),已接线全部 SearchService 构造入口(含题材搜索子进程重建)与默认 GitHub Actions 工作流[修复] 美股日线路由现按各数据源当前优先级排序,单项
*_PRIORITY配置(如YFINANCE_PRIORITY=0)对美股即时生效;指数固定首选与 Longbridge preferred 语义保持不变[新功能] 新增个股研究聚合 API,以统一 canonical code 返回行情、历史、研究产物、资讯、缓存持仓关系和监控规则,并对每个块独立标记 fresh/partial/unavailable。
[修复] 个股研究聚合拒绝交易所冲突的股票身份,在市场限定后无历史候选时保持空结果,兼容市场限定裸码与混合大小写旧数据,并从独立基本面快照补齐 ResearchArtifact 的财报与分红证据。
[新功能] 支持通过
main.py --stocks一次性分析已登记板块指数,自动使用指数适用的数据与分析能力,并保持报告、历史和决策信号兼容。[修复]
main.py --stocks在解析股票列表前先 best-effort 刷新股票索引注册表,保证首次运行能吃到刷新后的指数 alias/身份;刷新失败、超时或禁用不阻断分析。[修复] 交易日过滤对市场未知的指数 code(如
sh000016/csi930955/930955.CSI)按market=cn参与 A 股休市过滤,避免休市日指数被 fail-open 保留;市场仍未知的非指数 code 继续保留。[修复] 指数分析将实际命中的日线数据源归因保存到历史记录,并由 Dashboard/Brief aggregate 报告展示;来源无效时保持原有输出。
[文档] 在中英繁 README 顶部关联 DSA arXiv 论文,并新增
CITATION.cff统一项目引用信息。[新功能] 新增数据源能力与数据集质量只读契约,提供
/api/v1/data/overview和/api/v1/data/capabilities,为大盘看板、数据中心、个股详情和自选 2.0 统一暴露 provider capability、dataset quality 和 source priority。[修复] 统一美股指数实时行情与数据能力概览为 YFinance-only 路由,避免 YFinance 失败后误用 Longbridge fallback。
[改进] PR CI 增加文档路径检测:仅修改普通文档、非治理 Markdown 或 LICENSE 时跳过后端测试分片、Docker、Web 与桌面打包,保留轻量治理和门禁汇总;契约文档、静态 API 规格与测试 fixture 仍执行后端回归。
[新功能] 新增 ResearchArtifact 结构化研究产物契约,在
AnalysisReport.structured_report中承载 Thesis、Evidence、Invalidation Conditions、Next Actions 和 Data Quality,并提供从现有报告生成结构化产物的后端 helper 与 Web 类型。[修复] 同步 ResearchArtifact 与
AnalysisReport.structured_report到静态 OpenAPI,避免公开 API 规格与运行时契约漂移。[修复] Linux/Docker 分享图补齐 Noto CJK 字体与中韩文字体栈,避免 PNG 只显示数字和英文、中文或韩文内容消失。
[修复] 股票名称归一:AkShare 部分名称带无意义内嵌空格(“五 粮 液”、“万 科A”)与全角宽度拼写(“京东方A” 的全角 A),统一经
_normalize_stock_name()做 NFKC 宽度归一 + 去空白(原_compact_stock_name仅去空白):本地映射初值、_build_name_map_from_df()构建与extend_AkShare()合并、磁盘缓存加载三个入库口(含归一后为空的条目守卫)与resolver_name_to_code_list()/is_known_stock_name()/resolve_name_to_code()三个查询入口同源归一;消除空格/全角拼写与本地无空格半角拼写比较不相等造成的假性改名别名,源形态输入(带空格/全角)与常规拼写同样可解析,覆盖/analyzeAPI、按名称导入与 Bot 文本解析等全部调用路径;全角拼写此前与分词管道 NFKC 归一后的半角输入(“京东方A”)永不相等——全名精确匹配落空且会被更短库内名误切出错误实体(“京东”),归一后半角/全角输入产出一致、展示名统一为归一拼写;磁盘缓存中的历史未归一数据(带空格/全角)经合并与加载入口自动归一,无需迁移。[新功能] Web Chat 意图识别层新增分词模块:
web_intent_tokenizer六步管道(多股票全名实体扫描 → 标点/空白切分 → 代码形提取 → 市场关键词 → 无歧义关键词 → 残存 gap 多策略 DFS 匹配)把用户消息切分为携带语义标签的 Token 序列;配套web_intent_types数据字典(Token 结构、Market 枚举、21 个语义 tag、clean/extend 双词池与正则机器)。核心原则"宁可不做,不可做错":Step 1~5 只做精确匹配,Step 6 要求整段 TAG 全覆盖(交叉验证)才产出,未覆盖片段保持空 tag 交下游 LLM 兜底;代码形 token 辨认为stock_code(附 code/name/market 三元组)/wrong_{market}_code/unknown_{market}_code三态,token 层代码拼写统一 canonical 归一(a=6 位裸数字、hk=HK+5 位、us=大写 ticker)。意图枚举与意图识别结果随后续web_intent_resolverPR 引入。新增 183 个分词单元测试。[新功能] 完善 Futu OpenD 港股数据源接入:系统设置支持 OpenD 地址、端口和港股实时数据源优先级,保留 Longbridge、AkShare、YFinance fallback。
[测试] 增加 Futu 配置 schema、港股实时路由和 fallback 契约覆盖。
[新功能] 建立唯一、可生成、可校验、可降级的指数身份注册表:由
scripts/stock_index_seeds/index_registry.csv的 31 项 manifest 确定性合并进apps/dsa-web/public/stocks.index.json,运行时唯一真源为 JSON 中通过校验的active=true/assetType=index行,移除stock_list_parser的 5 项硬编码白名单;支持--index-only生成与字节稳定输出。[新功能] 补齐显式 SH/SZ/CSI 指数 alias 收敛与 CSI 身份:
sh000300/000300.SH/sz399300/399300.SZ/000300.CSI均解析到sh000300,csi930955/930955.CSI解析到csi930955;未登记.CSI输入返回unsupported;裸数字恒为 stock 并仅通过matched_index暴露歧义。[新功能] 数据管理器按 SH/SZ/CSI 支持矩阵映射 provider symbol:CSI 仅 AkShare 支持(
csi{code}),Tencent/TickFlow/Yahoo 返回空 symbol 并记录unsupportedprovider-run,不触发指数健康熔断。[改进] 存储层
_derive_canonical_id统一为 parser 推导(裸码=stock、显式指数=index),并新增幂等分批修复历史裸码错误 canonical 串桶(000001/000016/000688/930955等),显式指数行与正确 stock 行不受影响,registry 为空时修复 no-op。[改进] Web loader 完整解压含 index 的共享 payload,但在返回给 autocomplete/popular/group 消费面前过滤
assetType=index,股票与 ETF 行为保持不变。[测试] 为生成器、loader、parser、provider 路由、存储修复与 Web 门槛补充 TDD 回归锚点。
[修复] PR #2267 review 收敛 CSI 显式身份:将
csiprefix(canonical)与.CSIsuffix(显式 alias)在 parser/build/runtime 规范化器中分离,未登记显式csiNNNNNN/NNNNNN.CSI一律返回unsupported(不再落入美股或猜测 SH/SZ),并防止未登记csi000300被等价成已登记000300.CSIalias;存储_derive_canonical_id对 unsupported 输入返回 NULL,避免进入持久化 canonical 桶。[修复] 在 seed、build entry 与 runtime candidate 三层严格校验 alias 唯一性与整数 popularity:NFKC/casefold 等价 alias 跨条目冲突被拒绝(无静默覆盖),非负整数之外(小数/布尔/负值/字符串)popularity 一律拒绝,整数
100保持有效。[修复] 收敛已登记 CSI 显式身份在 resolver、任务去重键与历史候选中的分裂:
csi930955/930955.CSI/CSI930955统一解析为 parser canonicalcsi930955,未登记csi930956/930956.CSI保持既有降级语义;is_code_like()、REST/watchlist 输入边界与完整 Pipeline 透传不变。[修复] 阻止任意更新的非 bundled 指数候选(含 legacy
static子集)在 remote 缺失/损坏时以 active-index 子集覆盖 bundled baseline:所有非 bundled 候选必须为 bundled active-index canonical 集合的合法超集,否则回退 bundled 并记录 WARNING。[新功能] 桌面端全局右上角增加更新入口,与设置页共用更新状态;普通浏览器 WebUI 不展示,且不会在挂载时重复触发后台检查。
[修复] 桌面端右上角更新入口与设置页共用检查中状态,避免一侧检查时另一侧仍可重复触发 GitHub Releases 检查;主进程手动检查路径同步增加 in-flight 防重。
[新功能] Web 自动补全与搜索放行已登记指数(注册中文名/
sh/sz前缀/csi/.CSI显式形式均可检索与提交),热门候选仍仅股票;/analyzeAPI 对显式指数输入构造结构化AnalysisTarget(INDEX 用canonical_id去重,与同码个股互不折叠,CSI alias 收敛),未登记 CSI 单请求返回明确 4xx、异步批量仅该目标进入响应rejected列表;指数 target 经任务队列贯穿到 Pipelineprocess_single_stock,DecisionSignal以market_override="cn"落库并有真实分支测试。[修复]
/analyze在解析前按 strip 后非空原始 token 数限制 50 上限,rejected/duplicate token 也计入,防止用拒绝或重复 token 绕过批量上限;以唯一is_single = len(stock_codes) == 1 and not rejected_entries统一驱动 metadata、409 与单任务 202,duplicate+rejected 混合批次不再误返 legacy 409(单 duplicate 仍 409、部分 rejected 仍 202、全 rejected 仍 400)。[新功能] 报告 meta 补充可选
asset_type(stock/index),由后端 canonical code 经parse_analysis_target生成权威类型;指数报告在 Web 报告页与 Chat 自选入口隐藏 stock-only 自选操作,裸同码股票(如000016)保持股票行为,market review 与旧客户端可缺省。[修复] Web 批量分析把 accepted/duplicates/rejected 三类计入确认数,含 rejected 的完整 chunk 继续提交下一 chunk 而非误报 incomplete,最终以 warning 展示拒绝数量与首个拒绝原因(中英文文案)。
[修复] 历史筛选/删除/计数与 stock-bar 对已登记指数按 parser canonical 身份隔离:
sh000016/SH000016/000016.SH等显式形态互相可达(lowercase canonical + uppercase legacy canonical + 显式 alias),但永远不命中同码裸股票,裸码查询也不会命中指数记录;同一指数多条显式形态旧记录在/history/stocks合并为一行并计数全部形态,无记录删除仍返回deleted=0。SH/SZ/CSI 共用同一 parser 分支,股票 alias、港股与海外市场行为不变。历史列表、历史详情与 stock-bar 对已登记指数(含旧 uppercase/显式 alias 持久化记录,如SZ399300、000300.CSI)的 APIstock_code一律输出 parser canonical(sh000300/csi930955)。[改进] 任务列表/SSE 事件、历史列表项与 stock-bar 项追加可选
asset_type(stock/index):任务侧从已提交的analysis_target透传(不重新猜测),历史与 stock-bar 侧由持久化record.code经parse_analysis_target生成;旧客户端与 market review 可缺省,字段可选追加不破坏既有契约。[修复] Web 首页与自选工作区改用资产感知身份键:任务/报告/历史的
assetType优先,且被后端保证为 parser canonical 的代码只做大小写折叠(SH000016→sh000016),禁止再用前缀/后缀正则猜 canonical(否则000300.CSI会被误猜成csi000300、sz399300被误当成独立 canonical,违反注册表唯一判型真源);仅 watchlist 原始字符串缺少类型时,才用已加载stocks.index.json的assetType=index行 canonical/display/显式 alias 精确命中(不先 normalize、不用前缀正则猜测;加载期间禁用批量分析,加载失败或请求超过 10 秒时按既有股票语义 fail-open);行选中、active task 与完成自动选中按资产类型分桶,sh000016指数行与000016股票行状态独立,完成后自动选中正确 canonical 指数报告。[修复] Chat 消息恢复、发送与报告追问对显式 SH/SZ/CSI 已登记指数统一按注册表 canonical 判型并覆盖默认 LiteLLM 与 Codex:所有 Chat 后端首帧加载 registry,加载期间延迟 URL/历史恢复并禁用发送,settle 后
sh000016/sz399001/000016.SH/930955.CSI/csi930955只产出一个 lowercase canonical;后端resolve_stock_scope、工具守卫与 cache key 复用 parserINDEX身份,保持指数与裸同码股票隔离;未登记、空 registry 或加载失败时维持既有股票 fail-open,绝不按前缀猜指数。[修复] 选股结果持久化恢复:选股页新增历史记录区块,任务完成后保留 run_id,刷新页面可从历史 API 恢复上次选股结果(此前刷新后结果丢失)。
[修复] Web/API runtime scheduler 使用跨平台独立进程执行分析,并在默认 45 分钟硬超时或服务停止后清理进程树;停止返回后不再派发新的自动任务,避免一次卡死阻断后续调度。
[改进] Tavily 搜索深度由
advanced调整为basic,降低搜索 credit 消耗(#2314)。[修复] Web 的 axios 依赖升级至
^1.18.0,桌面端通过 override 固定 js-yaml 为4.3.1,纳入依赖安全更新(#2256、#2254)。
[3.31.0] - 2026-08-23
发布亮点
- feat: Agent 工具调用新增按类别和单工具配置的超时契约,并补齐防重试、协作取消、并发预算与热重载一致性。
- feat: 股票名称解析、
canonical_id双写和 A 股指数多数据源路由共同完善股票身份与行情降级链路。 - improve: 新闻检索为空时在报告中如实披露证据边界,Anspire 默认覆盖全球区域,公共 SearXNG 实例改为显式启用。
- fix: 定时任务恢复、无报告失败反馈、通知发送和大盘复盘历史/诊断信息进一步收敛,减少静默失败与误导展示。
- security: 桌面端升级
builder-util-runtime,修复 CVE-2026-54673 涉及的重定向凭据头信息泄露风险。 - docs: 增加 xAI Grok 的 LiteLLM 配置示例、Grok Bot 集成说明与可复用 Skill。
变更明细
[修复] 大盘复盘历史列表与详情统一展示持久化短摘要;旧记录缺少摘要时从完整 Markdown 生成无内部标记的纯文本节选。
[修复] 大盘复盘按实际执行的生成后端和模型记录诊断,避免 Codex CLI 或 fallback 被误显示为配置模型。
[修复] 已配置钉钉 Webhook 时不再误报“未配置通知渠道”;钉钉 Stream 仍仅用于交互,不作为定时静态推送渠道。
[修复] WebUI/API/Desktop 以
--serve-only重启后会恢复已启用的定时任务,同时保持启动时不立即执行分析;通知路由示例补充钉钉 Webhook 渠道。[改进] AIHubMix 注册与引流链接统一使用 inferera.com,改善中国大陆网络直连体验。
[修复] 单股推送模式在未配置通知渠道时仍会落盘本地个股报告;CLI 启动分析若因空股票列表、个股结果全失败或本地报告保存失败而未生成报告,会显式返回失败并记录原因。
[修复] 合并推送模式下即使个股汇总报告落盘失败,仍会先发送已有的合并通知;仅启用大盘复盘但最终未生成任何复盘内容时,分析任务会显式返回失败。
[修复] SearXNG 公共实例发现的默认值由启用改为关闭:公共实例普遍存在限流、下线或不返回 JSON 的情况,默认开启会让未配置搜索 key 的用户每次分析多耗 30~60 秒且新闻面最终为空。运行时默认值、配置模板、中英文档与工作流诊断同步调整;显式设为 true 的用户行为不变。
[改进] 新闻检索未执行或零命中时,报告中如实标注结论未纳入新闻面证据:零命中与「未配置搜索渠道」使用各自独立的文案,覆盖日报 / dashboard / brief / 个股 / 企业微信与模板渲染的详细与摘要分支、历史报告与分享导出、报告详情 API 与 Web 报告详情页,并按
zh/en/ko分别本地化。此前该情况下消息面章节直接消失,读者无从区分「确实没有新闻」与「检索静默失败」。披露以本次分析实际收到的消息面证据为准,涵盖实时检索、社交情绪与本地已落库的资讯池三路来源;搜索命中数仅用于在确无证据时说明原因(未配置渠道 / 检索零命中),避免把已用到本地或社交证据的分析误报成「未纳入新闻面证据」。Agent 模式的命中数取自 Agent 实际消费的搜索工具结果,而非分析结束后为持久化情报而补打的查询。[新功能] Agent 工具调用支持按类别(data/search/analysis/action/market)配置默认超时,并允许单工具声明
timeout_seconds;有效超时按 first-wins 优先级解析(显式 per-runtool_call_timeout_seconds> 单工具显式timeout_seconds> 类别默认 > 无限制),剩余 wall-clock 预算仅作不可突破的外层 cap,超时后返回结构化{"timeout": true}错误(标记retriable: false并写入non_retriable_tool_results防重试重复执行)供 Agent 继续执行而非中断循环(fixes #1890)。[修复] Agent 工具注册表(
src/agent/factory.get_tool_registry)由模块级缓存改为按「类别超时映射的值」比对失效,规避 CPython 回收对象后地址复用(id(config)相同)导致配置 reload 后的Config被误判为未变、沿用过期超时的真 bug;新增_coerce_config_timeout类型白名单,使调用方传入MagicMock/ 缺属性 stub / 脏字符串(如float(MagicMock())静默得到 1.0)时降级为「无类别限制」而非崩溃或强加 1 秒超时;build_agent_executor(config)/build_agent_chat_executor(config)现已把调用方config透传给get_tool_registry(config)(不再无参调用冻结首构 registry);main._reload_runtime_config与SystemConfigService._reload_runtime_singletons(及update()→reload_now路径)在配置热重载时调用reset_tool_registry()强制重建;回归测试补充「传入新 config 后 registry 重建」「reload 后新超时应生效」及「builder 透传 config」三类场景(#1890 的 review follow-up,闭环 OR-COM-dd1e8fa7 / OR-COM-bff42110)[修复] Agent 工具超时 review 闭环(fixes #1890 的 4 个 blocker):超时解析由 min 契约改为 first-wins(显式 per-run
tool_call_timeout_seconds> 单工具ToolDefinition.timeout_seconds> 类别默认 > 无限制,剩余 wall-clock 预算只作不可突破的外层 cap;research 路径不再传tool_call_timeout_seconds以免覆盖类别限制);超时结果标记retriable: false并写入non_retriable_tool_results阻断 LLM 同调用重试重入,且超时触发时为仍在后台运行的 handler 武装协作取消信号(is_tool_cancellation_requested()与既有check_tool_execution()检查点均响应,handler 从不轮询则行为不变),作为 review 要求的「handler 内协作取消」缓解,规避 Python 线程无法 force-stop 导致的重复执行与副作用;_coerce_config_timeout对inf/nan/负数降级为「无限制」,根绝future.result(timeout=inf)触发OverflowError;get_tool_registry/reset_tool_registry加threading.Lock双检锁,且重建后返回本次构建的局部 registry(而非全局缓存),消除并发重建竞态与跨调用超时串扰;@tool装饰器将ToolPolicy.timeout_seconds折叠进ToolDefinition单一来源;统一单/并行工具超时包装(单一 executor + deadline 驱动的 wait loop,消除并行路径嵌套 executor 与线程翻倍,duration 精确到各工具自身超时值),并新增快慢工具混合并行回归;同步docs/full-guide_EN.md的超时环境变量文档;测试覆盖 first-wins、non-retriable、协作取消接线、finite 校验、缓存线程安全与快慢混合并行。[修复] 按最新 review 复核收敛 3 处正确性问题(OR-COM-7f3d3f5b / 3d6b61f8 / a1e8b0c2):
BaseAgent._filtered_registry()携带源 registry 的类别超时映射(工具子集仍生效类别上限,不再绕过 #1890 类别超时);并行批次 >5 时排队调用的 per-tool 超时自 worker 实际开始起算(不再提交即烧预算导致对未启动调用的假超时);get_tool_registry()缓存命中快路径在锁内读取一致对(消除与reset_tool_registry()竞态返回None或错配 registry)。新增对应回归测试。[新功能] 股票名称解析引擎重构增强:新增
resolver_name_to_code_list()公开 API,返回按市场排序(A 股→港股→美股)的Stock候选列表(最多 5 个),新增US_stock_code_match()匹配美股 ticker(1~5 位字母且仅限本地库已存在代码,避免 hello/open 等英文词误判为股票);AkShare 全量 A 股数据经幂等extend_AkShare()合并进全局stockDB(30 分钟缓存 + 失败 5 分钟退避 + Future 单飞:TTL 过期 stale-while-revalidate 零等待、冷启动等待上界由拉取超时推导(拉取经子进程封顶 25s)、worker 先清账唤醒等待者再做日志/落盘(finally 兜底 BaseException)、成功拉取落盘data/cache跨重启复用,非中文输入跳过网络扩展),匹配策略升级为「精确→子串(≥2 汉字)→拼音子串(≥5 字母)→difflib 模糊(0.8,单字误写 0.7 兜底)」;resolve_name_to_code()保持既有本地优先语义(本地精确命中零网络,调用方离线低延迟契约不变),跨市场候选能力由resolver_name_to_code_list()独立提供;解析全链路线程安全(stockDB读写加锁、名称/拼音索引随库变更自动失效),新增 40 个单元测试覆盖精确/跨市场排序/子串/拼音/模糊/幂等扩展/失败退避/多候选场景。[改进]
StockDaily表新增可空canonical_id列并支持双写(Expand-Contract PR2,issue #2207):自愈式迁移幂等加列 + 普通索引ix_stock_daily_canonical_id,存量行与save_daily_data未显式传参时均经 index-aware 推导(裸指数码命中注册表时统一到指数canonical_id,避免同一指数按输入形态分裂到不同桶——例如裸000300与显式sh000300现在都收敛到sh000300,而非裸码被推导为sz000300),推导失败写 NULL 降级;读路径仍用code列,(code, date)唯一约束保留不变。显式登记契约漂移:PRD Glossary/FR-1/DD-3 与架构 AD-1/AD-7 中 canonical_id 的点分格式描述(000016.SH)已被 Phase 1 已合入代码的前缀格式(sh000016)取代,本变更遵循代码,PRD/架构文档的同步修正留待后续 PR 统一收敛。[新功能] 当前注册表已识别的 5 个沪深 A 股指数以显式市场输入按 canonical 身份路由:名称优先使用注册表并以 Tencent、AkShare、TickFlow 兜底,日线固定使用 Tencent、AkShare、TickFlow、yfinance 多源降级链,不读取普通 A 股日 K 的
*_PRIORITY配置;裸代码仍按股票处理,并隔离同码股票名称缓存。[修复] Anspire 搜索默认切换为全球区域模式(
region_mode=2),使海外股票新闻检索能够覆盖境外信息。[修复] 桌面端将
builder-util-runtime升级至 9.7.0,修复 CVE-2026-54673 涉及的 HTTP 重定向凭据头信息泄露风险。[文档] 增加 xAI Grok 的 LiteLLM 配置示例、Grok Bot 集成指南和异步分析 Skill,明确分析模型与 AI teammate 两类接入边界。
[测试] 固定 yfinance 股息 TTM 与单股报告文件名的时间夹具,消除跨日期和合并后时间基准冲突造成的 CI 波动。
[3.30.0] - 2026-08-09
发布亮点
- feat: LLM 渠道新增显式 Chat Completions / Responses API Surface,统一连接测试、分析、选股、图片识别与状态诊断的协议路由。
- feat: Agent Chat 按会话持久化 Skill 选择,刷新和切换会话后可恢复,并完整保留省略、显式空列表与非空选择三态。
- feat: Electron 桌面端恢复历史报告、市场复盘和完整报告分享图;未配置自定义品牌时,统一使用随包二维码与默认“小红书@霸天土小豆”账号文案。
- feat: 新增单条分析目标解析契约,收紧交易所后缀、指数别名及美股代码的规范化边界。
- fix: 修复移动端侧栏滚动、自选股详情状态、长通知标题分片及 Lark 国际域名等用户可见稳定性问题。
- improve: 后端 CI 按测试文件分成三个 runner 并行执行,并收紧 Web 共享资产与跨层契约的门禁范围。
新功能
- LLM 渠道支持显式选择 Chat Completions 或 Responses API Surface,兼容 Responses-only 模型,并让连接测试、主分析、选股、图片识别和状态诊断复用统一路由契约。
- Agent Chat 将 Skill 选择按会话持久化,刷新或切换会话后恢复;历史会话继续保留运行时默认,非法 Skill 请求不会误清空已有选择。
- Electron 桌面端复用安装包自带 Chromium,为历史个股报告、市场复盘和完整报告生成 PNG 分享图;Web 与桌面端统一使用随包分发的小红书二维码。
STOCK_LIST新增parse_analysis_target()单条目解析契约,并对外暴露可注入的指数注册表及解析结果类型。
改进
- 优化首页侧栏任务面板和自选股工作区,支持折叠任务摘要、自选股直接打开最新详情,并压缩头部操作以释放列表空间。
- 后端 CI 按完整测试文件拆分为三个 runner 并行执行,由统一
backend-gate汇总结果;继续保留稳定的离线测试语义并降低全局状态竞态风险。
修复
- 固定分享图小红书二维码下方的账号文案格式为“小红书@昵称”,未配置自定义昵称时默认显示“小红书@霸天土小豆”;不再渲染数字 ID,历史 ID 配置不会改变分享图模板。
- 修复首页移动端历史、自选和今日列表无法稳定纵向触摸滚动的问题,同时保持桌面端卡片裁剪行为。
- 长通知包含一级 Markdown 标题时按对应标题边界正确分片,避免错误递归耗尽长度预算并中断发送。
- 收敛自选股详情与今日状态语义:刷新和查询期间不再开放过期报告,失败时不会将旧历史误标为今日分析,并限制逐股票 fallback 并发及取消失效批次。
- 收敛 Responses 渠道的协议、provider、route alias 与 wire-model 契约,拒绝协议不匹配、同名 alias 混用 Surface 及非法历史配置。
- 飞书交互机器人在
FEISHU_DOMAIN=lark时让 Stream 长连接和消息回复统一使用 Lark 国际版 API 域名,避免连接国内域名后返回Incorrect domain name。 - 显式交易所后缀、畸形混合 alias、dotted-prefix 及外盘半显式后缀的非法输入不再静默改写或降级为错误市场。
us前缀保持大小写不敏感,但 ticker base 必须满足规范的大写美股代码形态;非法小写、标点或数字输入直接返回unsupported。- 带
.US后缀的规范美股代码不再因前两字符碰撞sh、hk、bj或us前缀而被错误拆分。
测试
- 非 Web 改动默认执行完整后端门禁;纯 Web、共享 public 资产、渠道模板和设置帮助的过滤语义补充回归测试,Docker 构建继续按实际输入过滤。
文档
- FAQ 补充 macOS 桌面应用被 Gatekeeper quarantine 阻止启动时,对受信任安装包进行临时放行的步骤。
[3.29.0] - 2026-08-02
发布亮点
- feat: 将参考 AlphaSift 实现的选股核心与策略正式纳入 DSA,新增选股运行历史、数据源历史和候选深度分析链路。
- feat: 新增 1080px 个股决策卡与高密度市场复盘分享图,支持 Web 原生分享和下载回退。
- feat: 新增 Skill Opinion Outcome 计算、表现统计与基于真实样本的有界运行时权重。
- improve: 优化选股快照复用、热点按需加载、多源并发和候选轮换,缩短长流程等待并提升结果多样性。
- fix: 加固关闭认证、短凭证诊断脱敏、CI 超时取证和桌面冻结包启动链路。
- fix: 修复 Longbridge 量比、股票代码窗口解析、选股后置重排及分享图交互等稳定性问题。
新功能
- SkillAggregator 基于独立满足 30 条 evaluated 门槛的真实 Skill Outcome bucket,使用 Beta 先验收缩、unable 惩罚和多周期证据加权生成有界运行时权重;缺失、低样本或异常统计保持中性。
- 选股结果按
run_id持久化到 DSA 数据库,新增运行历史和数据源历史 API,接入公告事件上下文及其搜索缓存,并支持将候选连同筛选策略映射的 skill 交给单股深度分析。 - 新增按 skill、horizon 与 outcome engine version 独立聚合的只读 Skill Opinion 表现统计;少于 30 条 evaluated 样本时仅返回观察性计数,不输出表现指标或调整运行时权重。
- 新增按 individual SkillAgent 自身 signal、版本化 engine 与本地已存同源日线窗口计算并持久化
skill_opinion_outcomes的核心服务。
改进
- 选中热点后先展示榜单已有摘要和核心股,后台再补充完整详情,并将单次热点源等待上限收紧为 8 秒。
- 热点成分股并行获取东方财富与同花顺数据,并按固定数据源优先级合并;真实供应商调用增加限流、可终止 timeout、并发槽和 worker 回收,题材详情可按需复用 DSA 原生搜索服务补充安全且带链接的近期消息。
- 精简选股页面的重复说明,将任务标识、快照统计和排序诊断折叠到运行详情。
- 将参考 AlphaSift 实现的选股核心与策略正式纳入 DSA,统一使用
ScreeningService、SCREENING_ENABLED和/api/v1/screening,并保留 Apache-2.0 归因与来源版本记录。 - Web 选股使用浏览器匿名种子与运行 ID 在最终评分后的有界近分池中生成每次运行的候选组合;本地评分覆盖完整短名单,远程分析继续遵守数量上限,硬过滤、风险否决和得分保持不变。
- 热点榜单刷新与选股长流程解除双向串行等待,热点详情改为选中后按需加载;选股默认复用 5 分钟内且数据源优先级一致的成功全市场快照,并展示快照、候选上下文、LLM 重排、最终评分和新闻事件增强阶段。
- 图片报告改用独立的 1080px 个股决策卡和高密度市场复盘卡,优先从结构化 payload 精确填充数据并保留 Markdown 回退;小红书账号与二维码支持关闭或替换,Web 支持原生分享与下载回退。
修复
- Web 分享图在按需生成完成后通过第二次用户点击同步打开系统分享,避免首次异步生成使原生分享退化为下载。
- Web 分享图改为用户点击“分享”后才按需生成,不再在报告加载时自动请求。
- 移除基础设置选股卡片中仍跳转到“数据源”的过期“查看配置项”按钮,并将
SCREENING_ENABLED及 Web 选股功能开关归入“基础设置”。 scripts/ci_gate.sh的离线测试增加单测 timeout 与 faulthandler 取证,并同步 Docker 发布流程的 CI 依赖,避免无 traceback 卡住或发布门禁缺少pytest-timeout。- 选股策略栏稳定展示完整中文策略列表,并保留自定义策略 ID 入口。
- 选股热点详情统一使用中文业务文案,不再显示内部类名、字段名和原始数据源错误。
- 刷新热点榜单并保留当前题材时同步绕过详情缓存重拉该题材,避免新榜单继续搭配旧路线与成分股。
- 选股尾部轮换以分析器输入顺序为权威并保留并列分候选顺序;热点消息增强、共享缓存 owner、全局并发容量和新闻搜索 deadline 统一收敛。
- Outcome 候选按上次尝试时间公平调度,避免持续新增的缺失 key 使旧
pendingoutcome 永久得不到重试。 - 选股主模型返回空内容、非 JSON 或低覆盖结构时继续尝试备用模型;全部失败时明确展示确定性因子排序状态,且不把
reasoning_content当作最终结果。 - 选股日线增强改用请求级 DSA-first fetcher 注入,多个后置分析器按最新分数逐级重排,远程分析状态跟随实际提交候选,避免重叠请求泄漏 wrapper 或改写未提交候选。
- 统一等价股票代码的本地日线候选与同源窗口解析;冲突沪深交易所代码不再降级匹配裸码,回测仅接受快照或交易日历确认的起点。
- 关闭认证时强制再次校验当前管理员密码,命中 rate limit 时返回 429,前端在当前密码缺失时阻止提交并显示内联提示(#1970)。
- 本地 CLI 的
stdout_preview/stderr_preview按环境变量、JSON、YAML/日志标量与 URL 的独立契约脱敏短凭证,避免 API key、secret 或 token 进入诊断(refs #1784)。 - PyInstaller 冻结包在 NLTK 3.10 导入保护下误判内置
_internal标准库时不再启动失败;Windows/macOS 打包脚本统一接入兼容 runtime hook。 - 分享图按字段合并历史结构化数据与 Markdown,多市场逐区域复用持久化 payload,隐藏不可用市场灯号维度并保留配色方案;中英韩模板跟随报告语言,原生分享失败时自动回退下载。
- 飞书文件报告在写入或上传前清理隐藏的市场 metadata;桌面运行时默认隐藏未随包提供 renderer 的 Web 分享按钮。
redact_diagnostic_text()的命令替换扫描不再吞掉尾随非敏感诊断字段,并统一export FOO=$(...)与FOO=$(...)的脱敏行为。- Longbridge 量比改用 adaptive keyword args 调用
history_candlesticks_by_offset,兼容 0.2.74 与 4.x SDK 参数顺序(fixes #2100)。
[3.28.0] - 2026-07-26
发布亮点
- feat: Multi-Agent 多策略综合支持分层 deliberation、mediator/self-review、revision projection 与 multi-round,并统一最终动作和解释契约。
- feat: AI 建议页新增按决策风格分组的历史表现,specialist opinion 样本可持久化并用于后验评估。
- feat: 新增
--portfolio futu,可只读导入 Futu OpenD 真实账户的沪深 A 股、港股和美股 LONG 正股持仓。 - feat: Web 首页与 API 支持按单个或多个市场临时触发大盘复盘,不修改全局配置。
- feat: Tushare 支持通过
TUSHARE_HTTP_URL接入自建网关或兼容镜像。 - fix: 改进港股行情路由与缓存、外股英文新闻匹配、数据源兜底顺序及桌面端发包稳定性。
新功能
- Multi-Agent 多策略综合新增受控 deliberation v0、可注入 mediator/self-review v1-v2、只读 revision projection v3 与 multi-round v4;增强层相对上一层 baseline 只能保持或继续 softened,不覆盖权威最终信号。
specialist模式最多选择 4 个策略专家,并通过AGENT_SKILL_CONCURRENCY控制 1–4 个 worker 并发;worker 继承主管线冻结的 target date 等上下文,单个 skill 失败不阻断其它策略或最终决策。- Multi-Agent 报告按八态用户 action 追踪 Pipeline 最终调整,排除非法 Agent 意见;仅在 canonical action 可唯一解析时生成 explanation 与 DecisionSignal,并以同一个
final_action统一最终动作契约。 - specialist 在分析历史保存成功后持久化版本化、低敏且幂等的有效 opinion 样本,为后续后验评估提供真实数据;本阶段不计算 outcome、不统计表现、不调整权重。
- AI 建议页新增决策风格历史表现,按每个分组独立的 30 个已完成样本门槛展示命中、区间涨跌、无法评估和最大不利波动,并保持旧统计接口兼容。
- 新增
--portfolio futu,只读导入 Futu OpenD 真实账户的沪深 A 股、港股和美股 LONG 正股持仓作为分析列表。 - Web 首页与
POST /api/v1/analysis/market-review支持用严格校验的region临时选择单个或多个复盘市场;一次性覆盖不读写全局配置,并贯穿任务提交、状态、SSE、结果与历史记录。 - Tushare 数据源支持通过
TUSHARE_HTTP_URL自定义接入地址;留空时继续使用官方默认地址(fixes #1985)。
改进
- 暂停 PR Review 的自动触发,仅保留
workflow_dispatch手动入口,避免辅助评审重复运行及评论权限失败产生误导性红灯;正式 CI 检查保持不变。 .env.example与每日分析 workflow 同步映射TUSHARE_HTTP_URL,保持本地和云端配置入口一致。
修复
- 修复外股代码映射到中文显示名时英文新闻相关性漏判,统一外股代码、英文名和别名解析,并对展开后的检索词去重(fixes #2026)。
- 特权
pull_request_target流程不再检出 fork PR head;敏感步骤仅执行主分支可信脚本,PR 元数据与 diff 通过 GitHub API 读取(fixes #2051)。 - PR Review 事件载荷缺失、不可读或 JSON 非法时输出可定位且不泄露载荷的警告,并保留原有降级行为(fixes #2070)。
- 修复 Windows 上
mimetypes冷启动读取注册表导致进程卡死的问题。 - 统一
DataFetcherManager、AkShare 与 Longbridge 对 4–5 位裸港股码的识别,避免 4 位代码被错误路由或静默失败(fixes #2091)。 - AkShare 港股实时行情增加 20 分钟全市场缓存与并发冷启动 single-flight,热缓存命中不再等待网络限速,主接口异常时仍保留新浪备用接口降级(refs #1852)。
- 将
TencentFetcher默认优先级调整为 A 股日 K 数据源的最终兜底,并新增TENCENT_PRIORITY显式覆盖项(refs #2032)。 - Web 设置页和通知测试入口补齐普通钉钉群机器人配置,支持安全遮罩保存 webhook 与 secret、查看帮助并发送测试通知(refs #1957)。
- Agent Chat 普通与流式接口在请求未指定
report_language时继承全局REPORT_LANGUAGE,显式请求值仍优先。 - WebUI 分开展示发布版本、代码版本与构建时间,并用构建输入摘要避免复用时间戳未变化的旧静态资源(fixes #2093)。
- macOS unsigned 打包显式禁用 Electron 签名与 Hardened Runtime,在冻结后端和 electron-builder 阶段清理残缺签名,并审计原始应用与 DMG 产物;该缓解不替代 Apple Developer 签名与公证(refs #2075)。
文档
- 修复文档中的失效相对链接。
- [修复] #2026 外股代码映射到中文显示名时英文新闻相关性判定漏判:新增同源 STOCK_ENGLISH_NAME_MAP 单一真源、canonicalize_foreign_stock_code 规范化入口与 _foreign_english_query_terms 别名解析,使 AAPL/00700/BABA 等 ticker 即使 stock_name 为中文也能在查询构建、相关性打分与多维度情报路径上复用 canonical 英文名,并补齐 .US/.HK suffix / HK 前缀全形式的归类与回归用例;同时在 _score_news_relevance 对 alias 展开 term 做去重,避免 legal alias 展开短名与显式 short alias 重复计分。
- [新功能] Tushare 数据源支持通过
TUSHARE_HTTP_URL环境变量自定义接入地址,便于网络无法直达api.tushare.pro时切换自建网关或第三方兼容镜像;留空保持官方默认地址不变(fixes #1985) - [文档]
.env.example与.github/workflows/00-daily-analysis.yml同步映射TUSHARE_HTTP_URL,避免出现"配置项有但 workflow 漏映射"的半修状态 - [修复] #2051 PR Review 的特权
pull_request_target流程不再检出 fork PR head:敏感文件、标签、报告与 AI 审查统一通过 GitHub API 将 PR 元数据和 diff 作为数据读取,只执行主分支可信脚本;Python 语法、Flake8、确定性检查和离线测试继续由无 secrets 的pull_requestCI /backend-gate执行,兼容actions/checkout新增的 fork checkout 安全保护。 - [修复] 修复 Windows 上 mimetypes 冷启动时读取注册表导致的进程卡死
[3.27.0] - 2026-07-19
发布亮点
- feat: 新增 Codex App Server single-agent 问股实验原型,并保持 LiteLLM、Multi Agent、普通报告和定时任务等默认链路不变。
- feat: Web AI 建议页支持保存基于历史报告快照重算的决策风格信号,补齐去重、续期、失效和可审计 guardrail 语义。
- feat: 引入多策略观点结构化输出第一阶段契约,覆盖观点标准化、基础冲突检测、聚合元数据和报告兼容边界。
- improve: 报告页明确展示输入数据状态、来源、异常影响、处理建议和诊断码,并区分页面资讯与本次分析输入。
- fix: 修复 MiniMax 推理内容污染最终 JSON、字符串 `` 包装兼容及多 Agent 风险覆盖后结论未按最终信号收敛的问题。
- fix: 补齐美股实时行情 PE/PB 估值字段、多市场工具描述和 macOS Gatekeeper 安装排障说明。
新功能
- 新增 #1743 Phase 6 Codex App Server single-agent 问股实验原型,仅开放三个既有只读 Tool Surface 工具;默认 LiteLLM、Multi Agent、Deep Research、普通报告、定时任务与 Phase 1/2
codex_cli路径保持不变。 - Web AI 建议页支持确认保存基于历史报告快照重算的决策风格信号,以 created/existing/refreshed 区分新建、原样复用和既有记录续期或维度补齐,并复用 profile-aware 去重与失效语义。
- 多策略观点结构化输出第一版新增策略观点标准化、基础冲突检测与聚合 metadata,作为 #1964 的阶段性基础契约;本版本不声明完成并发执行、完整策略调度 MVP 或前端完整多语言展示。
改进
- Codex 设置页仅检查配置、命令和所需协议是否允许尝试,用户保存后可直接提问;Chat 以服务端
accepted事件提交问题并按实际 backend 停止。 - Web 报告页输入数据块沿用状态、来源、告警和说明字段,在说明中补充异常影响、处理建议与诊断码,并区分报告页资讯和本次分析输入。
- 更新 Anspire 数据源的项目展示信息,并将
get_stock_info工具说明从 A 股限定修正为覆盖 A 股、港股和美股。
修复
- 修复 MiniMax 分析与渠道 JSON 测试把推理内容和最终文本拼接后导致结果无法解析、无法持久化的问题;字符串响应仅剥离开头完整的 `` 包装,并保留 JSON 内容中的同名字面标签。
- 修正多 Agent 内部 runtime facts 的 timeout 归因,并让 risk application 覆盖后的 dashboard 决策字段及一句话核心结论基于 post-risk signal 完成 finalization。
- 收敛多策略综合器语义:正确处理 Signal 枚举、缺失 signal、有效 opinion_count 和 deterministic synthesis,并兼容历史与外部 dashboard 的宽松字段形状。
- Codex 问股只接受 App Server 明确完成的终态回答,并统一整体时限、累计输出、事件、工具预算和进程回收边界。
codex_cli普通分析显式固定无人值守批准策略与只读沙箱,避免新版 Codex 在非交互任务中因请求人工批准而中断。- yfinance 美股实时行情补齐
pe_ratio和pb_ratio,供估值分析和下游报告使用。
文档
- 补充 macOS 未签名、未公证 DMG 被 Gatekeeper 拦截时的架构选择、安全排查与官方安装包临时放行步骤。
[3.26.1] - 2026-07-12
发布亮点
- feat: Web 首页新增历史、自选与今日工作区,支持批量分析、今日覆盖判断和评分排行。
- feat: 新增 A 股市场结构与题材主线上下文,并贯通报告、Agent、DecisionSignal 与 Web 展示。
- feat: 飞书支持文件形式推送报告,多 Agent 支持子 Agent 独立超时钳位。
- feat: 补齐内部 DSA Tool Surface、DecisionAgent 分歧摘要和 DecisionSignal profile 契约。
- fix: 统一报告动作口径,修复按股票代码批量删除历史记录和通知理由静默截断问题。
- fix: 改进 Web、桌面端、数据源缓存及发行包资源的稳定性。
新功能
- 新增 A 股市场结构与题材主线上下文,并在报告、Agent、DecisionSignal 和 Web 市场位置卡中复用。
- 飞书推送新增文件上传能力:
FeishuSender.send_feishu_file(file_path)通过 App Bot SDK (im.v1.file.create) 上传文件并发送文件消息;Webhook 模式回退为发送文件内容文本;新增FEISHU_SEND_AS_FILE=true配置开关,开启后飞书以文件形式发送报告而非文字消息。 - 多 Agent 编排 Pipeline 新增子 Agent 独立超时钳位:支持 6 个环境变量为 TechnicalAgent、IntelAgent、RiskAgent、DecisionAgent、PortfolioAgent、SkillAgent 各自配置独立硬上限,互不挤占配额;默认 0 表示关闭钳位。
改进
- 为 multi-agent DecisionAgent 增加内部低敏分歧摘要输入管线,作为 #1904 P1 解释输出的前置 plumbing;不改变 public API、dashboard schema 或最终解释字段。
- GitHub Actions 每日分析工作流补齐 TickFlow 数据源环境变量映射,并收敛 README 数据源稳定性说明到完整指南。
- Web 首页个股栏新增历史 / 自选 / 今日切换,保留历史分析默认视图,并支持在自选页一键分析全部或仅分析今日未覆盖股票、在今日页按评分查看当天分析排行;分块提交部分失败时保留已确认计数、停止后续提交并刷新任务列表。
- GitHub Actions 每日分析工作流新增钉钉通知环境变量映射,支持在云端定时任务中直接使用钉钉机器人。
STOCK_LIST自选股解析支持中文逗号、顿号、分号、空格和换行等常见粘贴分隔符,运行时、定时热刷新、CLI--stocks、Web 设置保存和自选 API 统一识别,并在写回时规范为英文逗号。- 新增
NEWS_INTEL_AUTO_FETCH_ENABLED单开关,开启后个股分析、Agent 分析和大盘复盘会 fail-open 自动初始化并刷新 RSS/Atom/NewsNow 本地资讯池。 - Web AI 建议页新增主股票上下文,复用最近分析和股票索引候选,并改进表现统计零样本说明。
- DecisionSignal 将
decision_profile升级为正式 nullable 字段,统一 same-profile 查询、去重、续期和失效语义,并保持 create metadatanull兼容与 SQLite 幂等回填诊断。 - 设置页移动端分类导航改为横向滚动列表并保证设置内容首屏可见,桌面端保留分类说明并收紧字段布局层级与间距。
- 新增 #1743 Phase 6a 内部 DSA Tool Surface 契约,统一工具 schema、stock scope fail-closed guard、结构化错误、审计摘要和脱敏诊断边界,并明确外部 AgentBackend 工具能力仍需 wire-level probe 证明。
src/services/analysis_service.py在report详情层新增details.raw_result回填,补齐与 API/历史详情的报告载荷一致性;不改变 provider、model、Base URL 或配置迁移语义。
修复
- 按股票代码删除历史记录时分批清理全部匹配项,并拒绝空白代码,避免超过 10000 条后残留记录或无筛选删除。
- 市场结构概念排行为空或超时时复用本轮负结果,避免批量个股分析重复请求同一概念排行数据源。
- Windows/macOS 桌面后端打包显式收集并校验 AkShare
file_fold/calendar.json,避免发行包因缺少交易日历 package data 导致热点题材和选股日线增强降级。 - 邮件、Telegram 与报告共享的 DecisionSignal 摘要完整展示已脱敏的理由,避免固定 120 字符在句中无提示截断;Telegram 按最终 Markdown payload 长度安全分片。
- 推送报告、Jinja 报告与历史 Markdown 导出复用 Web/API 的评分-action 口径:高分但旧
operation_advice仍为持有且无降级原因时,建议文案与三类统计展示为买入;有明确 guardrail reason 时继续保留持有/观望。 - WebUI 启动时显式
--host/--port不再被.env中的WEBUI_HOST/WEBUI_PORT覆盖,未传 CLI 参数时统一使用解析后的运行时配置。 - Web 首页今日状态与排行使用带时区偏移的历史时间戳和完整分页数据,在查询失败、跨服务器时区边界或任务完成刷新时保持安全且准确。
- Web 首页 stock bar 刷新序列化:并发或乱序返回时仅最新请求可清除
stockBarRefreshFailed,避免旧响应覆盖任务完成后的刷新结果。 - Web 持仓页首屏快照改用
include_realtime=false快速估值,跳过逐票实时行情预取后先展示持仓列表,避免外部实时行情源变慢时长时间空白等待。 - 修复任务状态接口重建报告动作字段时把合法情绪分
0当成空值的问题,确保低分报告能按评分口径纠正为卖出建议。 - 修复 Agent 流式回复在未收到完成事件就断开时被显示为“(无内容)”的问题,改为提示流式响应中断并保留用户消息。
- 修复桌面端
WEBUI_HOST=*/WEBUI_HOST=[::]会被原样传给端口探测和后端启动导致无法监听的问题,启动前分别规范化为0.0.0.0/::。
文档
- 在 README 快速开始中补充行情数据源配置说明(
TUSHARE_TOKEN/ Longbridge),明确未配置时仍可使用 AkShare、Baostock、YFinance 等免费兜底源,并同步中英文完整指南。
[3.25.0] - 2026-07-03
发布亮点
- feat: 新增
claude_code_cli、opencode_cligeneration-only 本地 CLI backend,并补齐生成后端状态诊断、预览、冒烟测试 API 和 Web 状态面板。 - feat: 台股报告完整接入三大法人资料,覆盖报告渲染、LLM prompt、TWD 币别标示、收盘集合竞价识别和 fetcher 韧性加固。
- feat: 新增钉钉群机器人通知、韩语报告输出和 AI 建议决策风格重评估预览。
- feat: Agent
/chat/stream标准化 progress event,新增阶段开始/完成、pipeline timeout 和预算跳过语义。 - fix: 修复桌面端 WebUI host/port 绑定、macOS Homebrew CLI PATH 诊断、Discord 长报告分片、AlphaSift 超时、yfinance 分红解析、A 股回测代码归一化等稳定性问题。
新功能
- 钉钉群机器人通知支持
DINGTALK_WEBHOOK_URL和DINGTALK_SECRET,并对长文本自动切片以适配 20KB 限制。 - 报告输出语言新增韩语(
REPORT_LANGUAGE=ko),覆盖个股报告、大盘复盘、Prompt 输出语言、决策护栏、通知模板标签与 Web 报告详情页文案。 - 新增
claude_code_cli与opencode_cligeneration-only 本地 CLI backend,保留 LiteLLM 默认路径、Agent 工具调用边界、per-preset extractor、最小 env allowlist 与结构化错误。 - 新增生成后端状态、预览和冒烟测试 API,以及 Web 生成后端状态面板,区分轻量检查与 JSON 冒烟测试,并保持本地 CLI “仅生成、不支持问股工具调用”的边界。
- Agent
/chat/streamprogress event 新增stage_start、stage_done、pipeline_timeout、pipeline_budget_skipped,补齐阶段进度、超时和预算跳过语义。 - 台股个股报告的 institution 区块展示 TWSE T86 / TPEx 三大法人原始买卖超净额,并将三大法人净买卖超表格注入 LLM 分析 prompt 作为台股筹码过滤器。
- 新增 AI 建议决策风格重评估预览接口与页面预览。
改进
- 台股三大法人 fetcher 增加并发缓存防击穿、TWSE/TPEx 分市场熔断、TPEx 日期保护和剩余 stage 预算复用,降低限流、端点故障和冷抓取超时带来的降级概率。
- AlphaSift 默认依赖 pin 更新到
9f522747caafd3c0b1ddb7e14d5cf44c8580b6cf,接入 wrapper 数据源 caller-side timeout、东财直连限速/抖动、策略目录元数据和防守策略。 - 选股任务状态轮询遇到可恢复超时时提示后台任务仍会自动重试,
.env.example补充相关超时调优项。 - 收敛个股分析评分与 DecisionSignal action 口径,统一 80/60/40/20 分段,并在风控降级时记录 raw/adjusted score、final action 与原因。
- Web 设置页左侧分类切换时仅在相关分类展示首次启动检查和 AlphaSift 辅助卡片,减少跨分类残留。
修复
- 修复 Windows 桌面端启动后端时固定传入
--host 127.0.0.1导致.env中WEBUI_HOST=0.0.0.0不生效、局域网无法访问 WebUI 的问题;桌面端仍默认使用127.0.0.1,仅在显式配置WEBUI_HOST后按配置绑定。 - 修复桌面端启动时
.env中WEBUI_PORT与 Electron 自动选择端口不一致,导致窗口继续等待旧端口并连接超时的问题。 - 修复 macOS 桌面端从 Finder/Dock 启动时后端 PATH 看不到 Homebrew Codex CLI 的问题,并明确 Codex CLI 主分析与 Agent LiteLLM 工具调用分流诊断。
- 修复 Discord 长报告推送按 2000 字符上限分片逐段发送,遇到 429 限流会按
retry_after/Retry-After有限重试,避免中途失败后只收到前半段报告。 - 修复日股、韩股和台股
market_phase收盘集合竞价识别,避免临近收盘阶段仍被标记为普通intraday。 - 修复 A 股个股分析遇到空
belong_boards占位时不会继续补查所属板块、关联板块模块展示不稳定的问题。 - 修复大盘复盘在 LLM 标题漂移或正文缺少板块段时,Web 与推送报告偶发缺少板块主线的问题。
- 修复 Web 大盘复盘结构化数据成交额、指数点位、涨跌幅和高/低值格式化,避免浮点长尾或缺失值
0.00直接展示。 - 修复 Web 首页个股栏在 stock-bar 摘要字段缺失或动作建议无法归类时隐藏情绪分与建议标识的问题。
- 修复 Web 设置页定时任务“立即执行一次”后台线程未传
stock_codes导致任务崩溃的问题。 - 修复
opencode_cli静态指令,避免全局 JSON-only 约束影响generate_text()与大盘复盘自由文本输出。 - 修复 yfinance 1.2.x 将
Ticker.dividends返回为单列 DataFrame 时分红解析被丢弃的问题,恢复 TTM 每股分红与分红次数计算。 - 修复台股财务金额币别标示,将 TWD 金额标注为“新台币”,避免在 A 股语境下误读为人民币。
- 修复回测日线补全将
605066.SH、SS605066、SS.605066等 A 股等价代码误向数据源请求SS605066,导致回测数据不足的问题。
文档
- 新增 Agent
/chat/streamprogress event 契约文档,说明新增事件字段语义、Web 兼容边界、验证方式和回滚方式。 - 同步本地 CLI backend 隐私/部署边界,明确 local CLI 不是离线模型,Docker/CI/远端需自行安装登录,DSA 不读取 Claude/OpenCode credential 文件。
- 更新 README 三语入口和市场支持边界,说明台股
.TW/.TWO、三大法人报告区块、TWD 标注与收盘竞价识别能力边界。
测试
- 台股三大法人 fetcher 新增 live-smoke 脚本与
@pytest.mark.network漂移检测测试,用于非阻断 network-smoke 定时任务核对 TWSE T86 / TPEx 核心字段与解析结果。
[3.24.1] - 2026-06-28
修复
- 修正 Longbridge SDK 版本约束为按平台选择可安装版本,避免桌面与 Docker 发布在
pip install -r requirements.txt时因不存在的0.2.75版本失败。
[3.24.0] - 2026-06-28
发布亮点
- feat: 扩展台股、日股、韩股市场支持,覆盖台股 suffix-only 分析、台股三大法人资料层、JP/KR 大盘复盘和跨服务市场枚举。
- feat: 新增 GenerationBackend 抽象、
codex_cli本地 CLI backend、reserved Hermes 本地 HTTP 渠道和 prompt cache capability registry。 - feat: Web/API/Desktop 支持多时间定时推送与 runtime scheduler 热重建,Web 设置页补齐首次启动检查与定时任务面板。
- feat: 报告链路补齐信号归因、单股信号时间线、概念板块排行和通知/报告关联板块展示。
- fix: 修复 Docker/启动探针、静态资源 MIME、回测空结果、组合估值、通知 Markdown、AlphaSift 数据源和测试环境隔离等稳定性问题。
新功能
- 新增台股 suffix-only 个股分析 MVP:
.TW/.TWO代码可走 YFinance 日线与近实时行情,并补齐市场识别、交易日历和 Prompt 能力边界。 - 台股
tw纳入 DecisionSignal、Portfolio、Intelligence 服务层、API 枚举和 Web 筛选,避免台股分析信号被市场归一化静默丢弃。 - 新增台股三大法人资料层 fetcher
TwInstitutionalFetcher,支持 TWSE/TPEx 来源、日期转换、单日缓存和 fail-open 退化。 - 大盘复盘新增
jp/kr市场,支持日经225/TOPIX、KOSPI/KOSDAQ 指数复盘,并扩展MARKET_REVIEW_REGION、交易日过滤和 Web 设置枚举。 - 新增 GenerationBackend Phase 1 抽象和显式 opt-in 的
codex_cli本地 CLI generation backend,提供结构化错误、fallback、stream 降级和 usage unavailable contract。 - 新增 reserved Hermes 本地 HTTP generation 渠道,提供 JSON generation、no-proxy 本地调用和 saved secret endpoint 绑定。
- 新增 Provider Cache Capability Registry,按 provider、API surface、gateway 与 verification status 建模 prompt cache 能力。
- 支持
SCHEDULE_TIMES多时间定时推送,长运行 Web/API/Desktop 进程保存调度配置后可热启停或重建 runtime scheduler。 - 新增信号归因分析和 Web AI 建议页单股信号时间线,并为自动生成与历史回填的 DecisionSignal 写入默认
decision_profilemetadata。 - 大盘复盘、Web 报告页和通知关联板块补齐概念板块排行与概念信号展示。
改进
- TickFlow 扩展为可选 A 股日 K、实时行情、股票列表/名称数据源,并增加 count、完整性校验和批量预取缓存保护。
- 硬化 JP/KR/TW suffix 识别、日韩股票种子索引、YFinance 报价/基本面上下文,以及 JP/KR Portfolio 与 Market Light 边界。
- Web 设置页新增首次启动配置检查卡与定时任务面板,隐藏内部
SCHEDULE_TIMES键,并改善重复任务提示的关闭与自动消失体验。 - Web 历史报告详情不再内嵌 AI 建议卡片,结构化决策信号集中到 AI 建议页,并保留来源报告 ID/URL 参数精确定位。
GENERATION_BACKEND=codex_cli下普通分析与大盘复盘不再因缺少 LiteLLM API Key 被误判不可用,并改用--output-last-message文件读取最终响应。- 本地 CLI backend 对 stdout/stderr 诊断预览和最终响应实行执行期总量上限,并补齐新增 generation backend 数字配置最大值校验。
- AlphaSift 默认依赖 pin 更新到
0a7b9cd59e81718f851890535241bc105d4ddc64,并默认走 DSA EastMoney 兜底 provider、暴露 source health 诊断。 - Docker Compose 默认内存建议提升到 1G;每日分析 workflow 兼容误将
STOCK_LIST配到同名 Environment variables 的场景。 - Agent 路径同步 signal attribution prompt,通知报告摘要不再展开 AI 决策信号明细,完整信号保留在个股详情与单股报告。
修复
- API 异步批量分析共享概念板块排行缓存,避免同批多股重复拉取全市场概念排行。
- 修复通知 Markdown 表格转换在空单元格后将后续内容错配到错误表头的问题。
- 修复 Market Light 区域归一化拒绝
jp/kr、日韩历史列表市场阶段摘要误传analysis_phase和默认通知报告缺少dashboard.phase_decision的问题。 - 固定 Docker 可安装的 Longbridge SDK 版本为 0.2.75,并修复 Docker 镜像中 efinance 缓存目录属主导致 A 股数据源降级的问题。
- 持仓快照今日估值改为受限并发预取实时价,减少持仓较多时 Web 组合页面刷新超时。
- Web 首页重新分析完成后自动切换到同一股票最新报告,并修复 Windows 环境下 Web/Desktop 静态 JS 资源可能以
text/plain返回导致黑屏的问题。 - 修复
--serve --schedule与 Web/API runtime scheduler 状态脱节、立即执行忙碌状态误提示、重建定时任务重复监听和启动参数语义丢失。 - 修复
main.py --serve-only在低配主机上因惰性 import 应用超出 uvicorn 启动自检窗口而反复重启的问题。 - 修复 Web 回测未传分析日期范围、股票代码未归一化导致成功响应但结果为空的问题,并为空候选、行情不足和非法后缀提供诊断信息。
- 修复 unsupported
GENERATION_BACKEND被当成空响应/模板 fallback、codex_clistdout 重复计入输出上限和主分析 JSON schema fallback 语义回退的问题。 - Docker 部署中 Web 设置页保存自定义 Webhook 模板时会转义
$content_json等占位符,并在运行时还原,避免 Compose 重新部署展开为空。
文档
- 补齐概念板块排行字段契约、通知报告行业/概念类型列展示和数据源稳定性与故障处理图示。
- 补充 JP/KR/TW suffix-only MVP、
MARKET_REVIEW_REGION保存/校验/回退矩阵、Market Light 边界和 PR 提交流程约束。 - 补充本地 CLI backend 隐私边界、非离线模型说明、Docker/CI 登录态限制和
codex_cliexperimental/limited 状态。 - 补充回测请求链路说明,并同步更新
docs/full-guide.md与docs/full-guide_EN.md示例。
测试
- 新增/更新台股、JP/KR 大盘复盘、GenerationBackend、
codex_cli、Hermes、本地 CLI、runtime scheduler、回测和概念板块排行相关回归测试。 - 加强
tests/test_analysis_api_contract.py、tests/test_analysis_history.py与tests/test_backtest_service.py的临时.env隔离,避免本地真实.env污染系统配置测试。
[3.23.0] - 2026-06-20
发布亮点
- feat: DecisionSignal 贯通报告提取、Web 展示、反馈/后验、告警通知和组合风险,AI 建议信号进入可追踪闭环。
- feat: 新增合规 RSS/Atom 与 NewsNow 资讯源情报池,分析、Agent 和大盘复盘可 fail-open 复用本地资讯 evidence。
- feat: 新增日本/韩国 suffix-only 个股分析 MVP,支持
.T、.KS、.KQ标的通过 YFinance 获取行情与技术上下文。 - feat: 新增 Token 用量监控看板、legacy LLM usage telemetry 和 message stability audit,增强 LLM 调用可观测性。
- fix: 修复运行流 live 状态、AlphaSift 缓存/字段兼容、发布说明诊断和日韩股票输入/历史展示等稳定性问题。
新功能
- 个股分析历史成功保存后会从最终报告 best-effort 提取
DecisionSignal决策信号,复用现有信号去重、计划质量计算和脱敏契约。 - 新增 Web AI 建议页、持仓页 latest active 信号摘要、历史报告信号展示和更完整的信号详情卡片,展示评分、置信度、价格计划、催化、风险与失效条件。
- 新增 DecisionSignal 用户反馈、信号级日线后验评估、统计 API 与 Web 展示,使用 outcome/feedback sidecar 表并保留主信号表契约。
- 将 DecisionSignal 复用到告警、通知和组合风险:告警触发关联 latest active 信号或创建最小 alert 信号,通知追加低敏信号摘要,持仓风险聚合 active sell/reduce/alert 信号并保持 fail-open。
- 新增合规 RSS/Atom 资讯源配置、拉取、去重、入库、查询、retention 与基础安全校验 API,作为个股/市场资讯情报池基线。
- 资讯源新增
newsnow类型、NEWSNOW_BASE_URL配置和/api/v1/intelligence/sources/defaults默认源初始化接口,内置财联社热门、雪球热门股票、华尔街见闻快讯、金十数据和格隆汇事件等财经热点源。 - 个股分析、Agent 分析和大盘复盘会 fail-open 读取本地资讯/情报池,并把来源链接作为新闻上下文和 evidence 输入。
- 新增日本/韩国 suffix-only 个股分析 MVP:手输
.T/.KS/.KQ代码可走 YFinance 日线与近实时行情,补充市场识别、交易日历、Prompt 语义、Web/API 类型和能力边界文档。 - 新增 Token 用量监控看板与
/api/v1/usage/dashboard接口,展示 LLM 调用总量、Prompt/Completion 拆分、模型用量、调用类型分布和最近调用明细。
改进
- 为
DecisionSignal补齐默认生命周期、同源窄 relaxed 去重、相反 active 信号自动 invalidated、terminal 状态不可 PATCH 复活和低敏 market phase hints 提取。 - 补充 Web decision-signals typed API wrapper 与契约隔离测试,并将历史报告 AI 建议查询收口到精确报告懒提取。
- DSA 数据源链路新增 Tencent 日 K 直连 fetcher、daily source health 短期熔断,并升级 AlphaSift 默认 pin/runtime bridge。
- 默认启用
DAILY_SOURCE=auto、Sina snapshot 优先级、候选级 quote context 与 LLM ranking timeout/max tokens 边界。 - 新增 legacy LLM usage provider/cache telemetry、message HMAC 诊断字段和普通个股分析 legacy message stability audit,不改变公开 Usage API、prompt 或 provider 参数。
- 问股页移动端策略选择改为默认收起的按钮入口,展开后仍可多选策略并在发送后自动收起,减少对对话内容的遮挡。
修复
- 修复运行流 live SSE 脱敏、后期 LLM/通知卡片重复、数据源聚合卡片过早成功、Web 首页窄侧栏挤压股票信息,以及个股分析自动生成大盘上下文时运行诊断互相串扰的问题。
- 修复 AlphaSift 热点题材 EastMoney 瞬断且无缓存时的空态、桌面更新热点缓存保留,以及
leader_stocks/stocks双字段兼容问题。 - 修复 Web AI 建议页筛选/状态更新分页、价格计划单边入场价展示、持仓 latest 信号刷新、详情 JSON 安全渲染和卡片交互语义问题。
- 仅允许历史报告存在明确
action或可解析动作时才触发决策信号懒回填,避免decision_type=hold等统计口径在建议不明确场景误回填。 - 修复 #1390 P6 DecisionSignal 在组合风险快照语义和默认聚合通知展示中的遗漏。
- 默认禁用
/api/v1/intelligence/sources/defaults新建源,避免公开示例 NewsNow 实例被默认启用,同时统一 500 响应细节仅入日志、响应返回通用错误信息。 - Web 股票自动补全、输入校验、历史/任务展示和筛选补齐日韩 Yahoo 后缀代码、常用日韩股票索引与股票池裸码解析,避免
000660、005930、7203.T、005930.KS、035720.KQ等场景崩溃、误入 A 股语义或历史分裂展示。 - 日韩个股分析在本地历史上下文缺失时会用 YFinance 日线兜底构造 K 线与技术指标上下文,避免报告误称日股/韩股核心行情和技术数据不可用。
- 发布说明生成查询 PR 作者失败时保留降级并输出包含 PR 编号和异常类型的 warning,便于排查 token、权限、网络或 GitHub API 异常。
文档
- README、完整指南和市场支持文档补充日股/韩股示例(
7203.T、005930.KS),并明确.T/.KS/.KQ当前为 YFinance-only MVP。 - 新增 DecisionSignal 决策信号专题文档,补齐字段/API/Web/告警通知/组合风险/后验评估、脱敏、迁移与回滚说明,并收口 Web i18n 显示边界。
- 补充 AlphaSift 迁移与回退边界:明确
ALPHASIFT_INSTALL_SPEC显式覆盖语义、requirements.txt + DEFAULT_ALPHASIFT_INSTALL_SPEC与运行时兼容边界。 - 补充资讯源基线文档,说明
NEWS_INTEL_*配置、NewsNow 自建建议、模型/provider/base URL 不变更边界,以及禁用或移除情报源变量的回退路径。
测试
- 新增/更新 DecisionSignal 服务、提取、反馈/后验、摘要、文档、通知、告警、持仓风险、Web 展示和 label 的回归覆盖。
- 新增/更新 RSS/Atom / NewsNow 情报源服务、API、安全校验、分析接入和配置兼容测试。
- 新增/更新日韩市场识别、股票索引、YFinance 行情兜底、Web 自动补全和输入校验测试。
- 新增/更新 LLM usage、运行流、AlphaSift、发布说明生成和移动端交互相关回归。
[3.22.0] - 2026-06-13
发布亮点
- feat: 新增 DecisionSignal 独立存储与 API、运行流快照 API 和 Web 运行流视图,补齐建议动作结构化字段与历史/回测展示链路。
- feat: AlphaSift 热点题材链路升级为新版合约,支持热点榜单、题材详情、发酵路线、概念股详情、缓存与兜底数据源。
- feat: 个股分析默认注入当日大盘环境摘要,并在高风险/退潮环境下软化激进买入建议。
- fix: 修复问股历史追问标的上下文、自选股等价代码匹配、低质量新闻过滤、运行流脱敏与 AlphaSift 热点详情展示等稳定性问题。
新功能
- 新增独立
DecisionSignal存储、Repository、Service 与/api/v1/decision-signalsAPI,支持来源/市场/股票/动作/期限/阶段去重、查询、续期、状态更新、懒过期、持仓过滤和敏感信息脱敏。 - 新增分析任务与历史报告运行流快照 API,提供 lanes、nodes、edges、events、summary 等统一契约,并从任务队列、运行诊断和 AnalysisContextPack overview 构建脱敏数据流/信息流。
- Web 端为活跃任务、历史报告和大盘复盘报告补充运行流视图入口,支持查看运行摘要、拓扑节点、事件流和基础排障详情。
- 新增 AlphaSift 热点题材链路:后端提供
/api/v1/alphasift/hotspots与/api/v1/alphasift/hotspots/{topic}API,Web 选股页新增热点题材区域并支持发酵路线与概念股查看。
改进
- 个股分析新增按当日/市场复用的大盘环境摘要,普通 Pipeline 与 Agent 分析 Prompt 可读取低敏大盘背景;新增默认开启的
DAILY_MARKET_CONTEXT_ENABLED配置,用户仍可显式关闭。 - 个股分析与历史/回测展示新增可选八态
action/action_label建议动作字段,保留operation_advice自由文本和decision_type=buy|hold|sell统计口径。 - 补充 Web decision-signals typed API wrapper 与契约隔离测试,暂不接入 UI。
- 完善运行时日志上下文,补充 logger name、触发来源、市场统计与实时行情预取链路状态,便于排查调度、API、Bot 和数据源降级路径。
- 持仓管理页新增持仓账户删除入口,复用现有账户软删除接口,误建账户会从默认列表、快照、风险、录入入口和事件列表隐藏且不物理清理历史流水。
- AlphaSift 依赖锁定更新到
d038c52c468543726fc1fd830b53c27d3f09d6da,并为新版 last-good snapshot、日线历史、行业/概念 provider cache、hotspot 榜单、题材发酵路线、概念股详情、上次成功热点缓存与 post-analysis 元信息补齐 DSA 运行期和 Web 适配。 - AlphaSift 热点题材读取默认优先使用上次成功缓存,手动刷新才实时拉取并覆盖缓存,实时拉取失败时尽量回退旧缓存。
- AlphaSift 热点题材区域改为默认折叠,展开并选中具体题材后再读取详情;发酵路线改为带时间标记的时间线展示,概念股可点击进入首页并直接启动分析。
- AlphaSift 热点题材数据链路复用同一次东方财富板块异动快照,并从真实涨跌幅、异动次数和高频个股推导趋势分、持续分、阶段与龙头样本。
- AlphaSift 热点题材刷新在合约层返回少量或缺少关键字段时改用 DSA 东方财富板块异动直连榜单,忽略少于 3 条的本地热点缓存,并补齐板块兜底字段。
- AlphaSift 热点题材卡片改为更紧凑的多列布局,概念股列表改为独立“分析”按钮触发个股分析;详情优先合并东方财富成分股、同花顺解析和板块异动龙头兜底并按日聚合发酵时间线。
- AlphaSift 热点题材详情新增 DSA 侧 30 分钟磁盘缓存,重复点开同一题材时复用发酵时间线与概念股详情;题材事件仅展示 AlphaSift 合约时间线、同花顺摘要、已配置新闻搜索或东财板块异动等真实来源。
- AlphaSift 热点题材消息催化改为摘要展示:配置 LLM 时优先压缩为一句题材催化摘要,未配置或调用失败时回退本地短摘要。
- AlphaSift 热点题材列表新增可选
include_details详情预取,Web 默认随热点列表批量带回 Top 题材发酵路线与概念股并复用前端内存缓存;新闻催化在 LLM 不可用时改为本地事件归纳。 - 改造
main.py --webui-only启动行为:若 FastAPI 监听端口已被占用,启动即 fail-fast 抛出明确错误并退出。
修复
- 问股从历史报告进入后的追问会持续携带当前标的,切回或重载已有会话时可从历史消息恢复基础当前标的,并由后端阻断未明确切换时的错误股票工具调用、交易所片段和指标缩写误路由。
- 自选股加入和删除按等价股票代码匹配港股及大小写美股变体,避免
00700、HK00700、00700.HK或aapl、AAPL被误判为不同标的。 - 收紧建议动作 legacy fallback:否定/回避表达、中文金融上下文、
buy or sell、多 guard 歧义文本以及英文复合词不再误渲染成 action badge;有结构化action时回测/历史趋势等入口按界面语言显示 action 标签。 - 股票新闻与多维情报搜索在相关度排序后新增域名无关的准入过滤,剔除下载/安装包/应用评分页及成人/招嫖服务垃圾页,并在同批已有有效标的/行业候选时移除
score=0背景填充项。 - 修复历史报告运行流快照在混合时区事件时间戳下返回 500 的问题。
- 修复运行流 live SSE 事件未复用快照层递归脱敏规则的问题,避免本地路径、prompt/raw response、代理头等敏感诊断字段在 refetch 前短暂暴露。
- AlphaSift 热点题材默认加载在无缓存且旧适配层缺少
alphasift.hotspot模块时返回空态,不再一打开选股页就显示 AlphaSift 未就绪;手动刷新仍会提示依赖需更新。 - 为 THS 发酵路线补充列名兜底:当
stock_board_concept_summary_ths返回缺列时仅跳过该来源富化,不影响热点题材详情 API 返回。 - 桌面发布打包改用冻结可执行文件运行时探针校验
alphasift.dsa_adapter,避免 macOS PyInstaller 将模块内嵌进可执行文件时被文件系统/zip 扫描误判为缺失。 - AlphaSift 热点题材详情展示改为优先使用后端融合后的
route,避免旧timeline覆盖新闻/LLM 摘要;手动刷新热点榜单时会同步绕过同题材详情缓存。
文档
- README 与繁中 README 快速开始入口补充视频教程链接,并将桌面客户端入口文案调整为客户端配置教程。
- 补充
docs/alphasift-integration.md:明确 AlphaSift 锁定 commit 来源、Hotspot 契约边界、LLM/LiteLLM 兼容语义与关闭开关下回退路径。 - 补充 #1381 运行时范围、兼容边界、官方语义依据与常规发布回滚说明。
测试
- 覆盖 #1381 后端 runtime 与兼容核验:
tests/test_main_schedule_mode.py、tests/test_pipeline_daily_market_context.py、tests/test_daily_market_context.py、tests/test_daily_market_context_guardrail.py、tests/test_agent_executor.py、tests/test_config_env_compat.py、tests/test_config_registry.py与apps/dsa-web/tests/system_config_i18n.test.ts。 - 新增/更新 AlphaSift 后端回归:
python -m pytest tests/test_alphasift_api.py -q、python -m pytest tests/test_docker_entrypoint.py -q、python -m pytest tests/test_main_schedule_mode.py -q -k "start_api_server_fails_before_thread_when_port_is_busy"。
[3.21.0] - 2026-06-07
发布亮点
- feat: 新增 Web UI 中英文界面语言切换和飞书 App Bot 通知模式,提升多人部署和企业通知场景体验。
- feat: 大盘复盘报告、历史入口和个股栏继续收口到结构化数据与统一 Markdown/GFM 渲染,Web/API 人工触发入口不再被交易日 gate 短路。
- feat: AlphaSift 选股链路改为可恢复后台任务,并完善 DSA LLM runtime bridge、默认适配层预置和兼容回归。
- fix: 修复英文界面残留中文、诊断展示、运行时环境变量展示、健康检查、桌面更新路径、工作流变量读取和多处 Web 窄布局问题。
新功能
- WebUI 新增独立界面语言状态与中英文切换入口,覆盖主导航、首页、登录、设置页和通用控件文案;UI 语言与
report_language解耦,不改写报告语言链路。 - 飞书通知新增应用机器人(App Bot)模式,支持通过
FEISHU_APP_ID/FEISHU_APP_SECRET/FEISHU_CHAT_ID配置,无需额外创建自定义机器人。 - Web 大盘复盘报告新增专用展示视图,历史入口和首页即时结果统一使用 Markdown/GFM 渲染并隐藏个股专属模块。
- 大盘复盘新增结构化
market_review_payload,Web、历史详情和推送统一基于结构化数据渲染,并保留 Markdown 兼容展示。 - 新增默认关闭的 AlphaSift 选股页签,通过
ALPHASIFT_ENABLED明确控制,并保留/install作为显式修复路径。
改进
- Web/API 大盘复盘人工触发入口不再因交易日检查或相关市场休市而短路跳过;定时任务、GitHub Actions 手动运行和 CLI 默认入口仍保持原交易日 gate。
- AlphaSift Web 选股改为后台任务提交与状态轮询,新增可恢复任务状态展示,避免外部快照、行情或 LLM 变慢时浏览器长请求超时。
- AlphaSift 选股 API 与服务层收敛到
AlphaSiftService,endpoint 仅做路由参数接收与错误映射。 - AlphaSift 与 DSA 的运行时 LLM 兼容桥接改为调用期注入,保留
provider/model/base_url/custom headers/fallback语义链路,不做持久化迁移。 - Web 首页侧栏不再单独展示大盘复盘历史集合,最新大盘复盘作为
MARKET并入个股栏,按最近分析时间参与排序,并复用个股栏的选择、删除、完整报告与历史趋势查看能力。 - 多股通知报告将市场阶段收敛为总览下方单行
市场状态,不再在每只股票摘要下重复展示数据质量和限制详情。 - API 错误响应构造收敛到共享 helper,保持既有错误 envelope 形状并降低 endpoint 重复代码。
- WebUI 绑定公网地址或 CORS 全开放且未启用管理员认证时新增运行时 warning;仅增加可观测性,不阻断启动、不改写配置。
- 数据库初始化新增
schema_migrationsbaseline 标记表与幂等记录,用于后续 schema 演进追踪;不迁移、不清理、不改写既有业务表数据。 - #1386 P6 复用市场阶段与 AnalysisContextPack 公开摘要联动告警、持仓手动分析、历史、回测和通知展示,不新增数据库迁移。
修复
- Web 英文界面补齐回测、组合风险与告警规则相关文案本地化,避免英文模式下残留中文筛选器、按钮和枚举标签。
- 综合情报搜索中的机构分析与业绩预期维度改用 180 天 provider 请求窗口,避免默认短新闻窗口漏掉财报、研报等周期性财经材料。
- Web 个股栏和历史卡片在窄布局下不再让市场阶段标签遮挡股票名称。
- 问股自由文本追问不再将 TTM、PE、YOY 等金融缩写误识别为新股票代码。
- [修复] GitHub Actions 每日分析工作流读取 SearXNG 自建实例地址时支持 Variables 优先、Secrets 回退,修复仅配置 Variables 时 URL 不生效的问题。
- Web/桌面端左侧导航选中态改用 border 实现,避免蓝色竖条指示器溢出侧栏边界;侧栏展开宽度 116px -> 136px,新增 rail 紧凑模式。
- Windows 桌面端自动更新安装目录不再预先加引号,避免带空格路径在自动安装时触发“缺少快捷方式 / 找不到 Daily Stock Analysis.exe”的系统弹窗。
- Agent 分析路径生成 AnalysisContextPack overview 前复用已落库日线分析上下文,避免日线已抓取成功仍显示
daily_bars_missing。 - 修正大盘复盘结构化
breadth的可用性判断:当市场不支持或抓取失败时不下发breadth,前端展示“暂无数据”,避免误导性 0 值。 - 大盘复盘语言行为遵循全局
report_language,并在美股中文场景下本地化市场标签与策略蓝图,避免混入英文策略段落。 - Docker Web 设置页读取配置时在活跃
.env文件缺项时回退展示启动注入的同名环境变量,并补清相关挂载边界文档。 - 报告页运行诊断会区分数据源抓取成功与进入 LLM 分析输入,相关新闻区标注为报告页补充/后续检索资讯,避免与输入数据块状态互相误读。
/health根路径健康检查现在始终返回 JSON,避免静态 Web fallback 吞掉健康探针;/api/health与/api/v1/health继续保持兼容。ALPHASIFT_ENABLED关闭时不触发alphasift运行时注入;开启后优先复用已配置的 DSA/provider 配置并注入LITELLM_*与LLM_*运行时变量。- 补齐 openai-compatible 场景下 base URL、
extra_headers与LITELLM_FALLBACK_MODELS的兼容路径与回退链验证。 - 桌面/镜像打包链路保持与运行时一致的 AlphaSift 适配层预置,避免
pip install作为线上修复依赖。
文档
- 明确 Issue #777 UI 语言切换采用仓内
UiLanguageContext+uiText实现,持久化 key 为dsa.uiLanguage,并补充对应可视化验收指引。 - 明确大盘复盘展示链路、结构化 payload、语言行为、交易日 gate 差异和回滚边界。
- 补充 LLM / LiteLLM 兼容键在 Settings 展示与校验上下文中的回退边界,说明不改写、不迁移、不清理用户现有 provider/model/base URL 持久化配置。
- 补齐 #1602 运行诊断口径修复覆盖范围,说明仅统一输入与展示口径,回滚方式为常规发布回滚。
- 明确 AnalysisContextPack P6 文档、迁移与回滚边界,并同步既有
SAVE_CONTEXT_SNAPSHOT到.env.example、配置注册表、Web 设置帮助和完整指南。 - 补齐 #1386 P7 盘前/盘中/盘后分析的入口、迁移、回滚和用户可见说明。
- 为 AlphaSift runtime bridge 增加官方兼容依据落点,明确 provider/model/base_url/extra_headers/fallback 与回退边界。
测试
- Web 方向执行
npm run lint、npm run build、相关 Vitest 和 smoke 命令;未设置DSA_WEB_SMOKE_PASSWORD时 smoke 用例按设计 skip。 - Web 测试运行时声明 Node
>=20.19.0 <27与 npm>=10,并补 localStorage 测试兜底以稳定 Vitest。 - 增补 AlphaSift runtime bridge 与打包脚本静态验证,覆盖
LLM_CHANNELS、LITELLM_FALLBACK_MODELS、alphasift.dsa_adapter、--collect-all alphasift。
chore
- 移除随 issue / PR 验收流程误入库的截图资产,并明确一次性截图证据应保留在 PR 描述、评论、附件或 artifact 中,不作为仓库文件合入。
[3.20.0] - 2026-06-03
发布亮点
- feat: 新增 AlphaSift 选股入口、自动安装与稳定适配层,支持 Web 策略执行、LLM 重排展示和默认关闭的可控启用。
- feat: 完善个股历史、自选队列、市场阶段与 AnalysisContextPack 可见性,增强 Web 报告和 API 的结构化上下文能力。
- feat: MiniMax 默认模型升级到
MiniMax-M3,并补齐相关价格、预设和测试覆盖。 - fix: 修复健康检查、Windows 桌面更新与首次运行编码、ETF 日线 secid、LLM base_url 校验和 Agent 日线上下文误判等稳定性问题。
新功能
- 新增默认关闭的 AlphaSift 选股页签,通过
ALPHASIFT_ENABLED开启后经由稳定适配层读取策略并执行选股。 - Web 首页左侧栏改为个股栏,按股票去重展示,大盘复盘置顶,点击个股加载最新报告,支持按代码变体(.SZ/.SH/.SS)归一化去重合并。保留全选、批量删除和删除确认入口;新增按股票代码批量删除 API
DELETE /api/v1/history/by-code/{stock_code}。 - 报告详情右侧栏新增自选操作入口,支持查看当前股票是否在自选队列、一键加入或移除;大盘复盘报告不显示该操作。
- 问股页面输入区上方新增自选操作按钮,用户发送包含股票代码的消息后自动显示加入自选/从自选删除入口。
- Web 报告页新增同股历史趋势抽屉入口,历史列表摘要补充趋势、摘要、模型和分析时行情字段,支持按当前股票查看历史分析并加载更多。
- AnalysisContextPack P4 低敏 overview 接入历史详情、同步分析响应、completed 任务状态和 Web 报告页,展示数据块状态、来源、缺失原因与降级摘要。
- #1386 P5 为个股分析报告新增
dashboard.phase_decision盘中决策护栏,并在保存历史前按市场阶段与数据质量限制高置信盘中买卖结论。 - #1386 P4a 新增
analysis_phase=auto|premarket|intraday|postmarketAPI 参数,并在异步任务 accepted、内存 status、list、SSE 与分析 pipeline 中透传请求阶段。 - #1386 P4b Web 报告页新增最终市场阶段标签,任务面板展示请求阶段,并复用 AnalysisContextPack 低敏数据质量摘要。
- MiniMax 渠道模型列表升级:新增
MiniMax-M3并作为默认,按官方 OpenAI-compatible 文档支持 1M 输入上下文(项目保守注册为<=512K价格档:context_window 512K、max_tokens128K,对应 $0.6/M 输入、$2.4/M 输出,>512K 输入价格档未建模),保留MiniMax-M2.7与MiniMax-M2.7-highspeed,并保留MiniMax-M2.5legacy 价格条目以兼容现有用户配置的成本估算。Web 设置页 MiniMax 预设模型与价格按 M3 刷新。 - 新增 AnalysisContextPack P1 内部契约与脱敏序列化测试。
- 市场阶段低敏摘要接入历史详情、同步分析响应和 completed 任务状态的 report metadata。
改进
- 首次运行配置校验补充缺失 AI Key、空 STOCK_LIST、Telegram/邮件成对字段和 Webhook URL 前缀诊断。
- AlphaSift 选股入口在 Web 侧边栏中移动到“问股”下方,贴近 Agent/研究辅助工作流。
- Docker 镜像构建阶段预置默认 AlphaSift 适配层,与桌面发布包一样避免运行期额外安装。
- AlphaSift 选股改为依赖
alphasift.dsa_adapter的稳定接口,Web 策略列表由 AlphaSift 动态提供,不再在前端硬编码。 - AlphaSift 选股页补充 Run ID、快照数、过滤后数量、因子和风险详情,展开候选时展示真实明细,并暂时仅开放当前支持的 A 股市场。
- Web 设置页新增 AlphaSift 选股开关卡片,可直接开启或关闭选股页签。
- 开启 AlphaSift 选股时先切换
ALPHASIFT_ENABLED并检查适配层可用性,缺失时自动调用受控安装接口,不再要求用户额外点击安装。 - AlphaSift 已开启但适配层缺失时,策略列表和选股接口会串行化自动安装锁定来源,并强制重装以覆盖旧版
alphasift包。 - AlphaSift 选股页合并重复的快照源 fallback 提示,并保留 AlphaSift 自身的 Tushare 优先快照源逻辑。
- AlphaSift 选股页在 LLM 重排降级时展示 warning/source error/parse error,并避免把本地因子评分误显示为 LLM 判断。
- Web 设置页不再把
ALPHASIFT_ENABLED作为普通数据源配置项重复展示,该值仅作为“开启选股”按钮背后的持久化状态。 - AlphaSift 关闭时隐藏 Web 左侧“选股”导航入口,避免误导未开启用户。
- 补充 AlphaSift 选股自定义策略显示逻辑,避免未匹配预设项时误显示“均衡多因子”。
- 新增 GET /api/v1/history/stocks 端点按 code 分组返回不重复个股列表;新增 GET /api/v1/stocks/watchlist、POST /api/v1/stocks/watchlist/add、POST /api/v1/stocks/watchlist/remove 端点支持自选队列增删查。STOCK_LIST 读写保持原样,不做自动归一化;add/remove 时归一化比较判断等价代码变体。
- 新增 useWatchlist hook 统一管理自选队列前端状态,复用 SystemConfigService 的 STOCK_LIST 配置项实现持久化。
- AnalysisContextPack P5 增加数据质量评分、
fetch_failed状态、Prompt 数据限制区块和 Web 低敏质量展示。 - #1386 P2-full 在 AnalysisContextPack Prompt 数据限制中追加市场阶段与降级数据的交叉约束,并修正中文分析 Prompt 的阶段化行情标签。
- 通知报告默认发送路径恢复既有渠道兼容转换与分片逻辑,新增 renderer 能力仅保留为未来扩展基础。
- 关联板块缺少类型数据时改为单行展示板块名称,避免生成整列
N/A的板块表格。 - 优化 Web 报告详情页信息层级,将输入数据块和运行诊断下移为主体内容后的折叠辅助信息。
- 盘中分析补齐实时行情获取时间、provider 时间、stale、fallback 与 partial/estimated 标记,供 AnalysisContextPack 映射输入数据限制。
修复
- Agent 分析路径生成 AnalysisContextPack overview 前复用已落库日线分析上下文,避免日线已抓取成功仍显示
daily_bars_missing。 - 注册 /api/v1/health 路由并加入认证豁免,修复该路径返回 404 以及开启 ADMIN_AUTH_ENABLED 后健康探针收到 401 的问题。
- Windows 本地首次运行环境检查兼容非 UTF-8 控制台输出,并将
requirements.txt注释改为 ASCII 以降低默认代码页下的依赖安装失败概率。 - AlphaSift DSA 适配层默认开启 LLM 重排,后端显式请求
use_llm=True,选股页展示 LLM 分数、判断、覆盖率和关注项。 - AlphaSift 嵌入 DSA 时复用 DSA 已解析的 LLM 模型、渠道和密钥配置,避免 Web 已配置 LLM 但选股 LLM 重排仍因缺少 provider key 降级。
- AlphaSift 选股复用 DSA LLM 路由时过滤未声明的托管 provider 备选模型,并把已声明渠道模型补入回退链,避免残留 Gemini fallback 覆盖可用的 DSA 渠道。
- AlphaSift 默认安装来源改为锁定 commit 的受信任 GitHub 地址;桌面模式自动安装不要求管理员会话,非桌面部署要求管理员认证会话,并继续限制安装来源。
- 修复 Web 开启 AlphaSift 时先安装后写配置导致默认关闭状态无法开启的问题。
- AlphaSift 状态与安装接口不再返回
install_spec明文,仅返回install_spec_is_default等非敏感状态字段。 - AlphaSift 状态探测区分可选依赖缺失与非预期异常,异常场景记录 warning 并返回非敏感诊断信息。
- 调整 AlphaSift 筛选调用兼容:
screen以max_results为主并支持历史max_output关键词,同时允许策略透传以对齐前端手动策略参数。 - AlphaSift Web 选股请求使用独立长超时,避免开启 LLM 重排后被通用 30 秒 API 超时提前中断。
- 桌面端打包阶段预置 AlphaSift 并收集适配层,避免发布包运行时再要求管理员自动安装。
- AlphaSift 自动安装仅在
status诊断为missing_module时触发(仅模块缺失场景);适配层可导入但运行时异常不再自动pip install,而是返回424并保留诊断,避免把真实运行时故障掩盖为重装。 - 收口 Web 中文界面残留英文文案与设置页 help 缺口,回测页改为中文展示,并让 Web 设置页仅展示已注册且带说明的配置项。
- Windows 桌面端自动更新静默安装时显式复用当前安装目录,避免自定义安装目录场景下卸载旧版本文件失败。
- Windows 安装器重试旧卸载器时对
_?=安装目录参数加引号,修复旧版本安装在带空格路径时返回 2 导致自动更新失败。 - Windows 桌面端自动更新传给 NSIS 的
/D=目录参数在包含空格时自动加引号,避免安装位置注册表被截断。 - 加固 LLM channel base_url 校验,避免解析差异导致 SSRF 绕过。
- 修正 efinance ETF 日线 Eastmoney secid 路由,避免沪市 ETF 被按深市 quote id 查询导致日线为空。
文档
- 明确 AlphaSift 与 LiteLLM 兼容边界:仅桥接 DSA 已声明 provider/model/base URL 为调用期注入,不对
.env做 provider/model 路由迁移;回退方式为关闭 AlphaSift 并恢复原有LITELLM_*/LLM_*配置。 - 明确 AlphaSift 仅复用 DSA 现有 LLM/LiteLLM 配置语义,不新增
LITELLM_MODEL、OPENAI_MODEL、OPENAI_BASE_URL、LLM_TIMEOUT_SEC等模型语义迁移;失败提示与回退路径统一沿用既有系统配置链路,仅影响 AlphaSift 选股能力本身。 - 明确 AlphaSift 自动安装来源锁定、
missing_module与运行时异常行为边界,以及 LLM/provider/base URL 与自定义通道回退路径,便于问题溯源与回滚到原有 LLM 配置。 - 明确同股历史趋势新增模型字段为历史快照展示元数据,不影响运行时 LLM Provider/Model/Base URL 路由与配置迁移清理;回退方式为按常规发布回滚本变更。
- 明确 #1311 的兼容性边界:渲染层仅消费分析结果
model_used展示字段,未改动wechat/slack/feishu/telegramsender 发送链路,不触发 provider/model/base_url 兼容迁移。 - 明确 AlphaSift 锁定 commit 的
alphasift.dsa_adapter契约依据,以及当前 DSA API/Web 调用结构的兼容边界。 - 明确 Settings 页面对 LLM 配置仅做展示分组与字段归并,不改写或触发 LLM 迁移/回退路径;兼容现有
LLM配置保存与回退语义。 - 新增 AnalysisContextPack P0 上下文盘点。
- 补齐告警中心 P8 文档与配置收口说明,明确 legacy JSON、高级规则、Web/API、Docker、GitHub Actions 与 Desktop 边界。
测试
- 同步更新
llmProviderTemplates、LiteLLM fallback pricing 与 MiniMax 预设相关单测,断言新默认模型。 - 补充 ETF 日线数据源路由、输入变体、fallback 与 MA 字段回归覆盖。
chore
- 新增通知报告渠道能力画像、PreparedMessage 与结构感知 Markdown 分片基础设施,为 #1311 全渠道渲染适配打底。
- 预置企业微信、飞书、Telegram、钉钉、Slack 平台 renderer 元数据,暂不改变默认推送报告入口和可见版式。
[3.19.0] - 2026-05-29
新功能
- 落地 #1391 Phase 1 运行诊断最小链路:任务/SSE 追加 trace_id,并记录日线与实时行情 ProviderRun 快照。
- 告警中心新增 P7 大盘红绿灯结构化规则,支持
market_light_status与market_light_score_drop并复用现有 worker、触发历史、通知和冷却链路。 - 落地 #1391 Phase 2 运行诊断摘要:生成用户可读 RunDiagnosticSummary,提供历史报告诊断 API 与脱敏复制文本。
- 落地 #1391 Phase 3 运行诊断可见性:报告详情和任务面板默认折叠展示运行状态、trace 与可复制排障信息;后端通过
api/v1/history/{record_id}/diagnostics与context_snapshot.diagnostics提供历史链路回填。 - 新增 AnalysisContextPack P1 内部契约与脱敏序列化测试。
- 新增 AnalysisContextPack P2 builder,从普通分析 pipeline 已有 artifacts 组装内部上下文包。
- 问股新增默认关闭的可见对话上下文压缩,支持 Web 开关、Agent 高级 preset、滚动摘要和最近轮次原文保护,降低长会话 token 消耗。
- 股票自动补全索引默认支持从 GitHub main 远程刷新并缓存到本地,Web/CLI 分析入口失败时自动降级到内置索引,降低摘帽和更名后旧简称污染分析的概率。
- 普通分析与 Agent 运行时 Prompt 接入 AnalysisContextPack 低敏摘要,保持 history/API/Web 输出兼容。
改进
scripts/fetch_tushare_stock_list.py可对 A 股中带XD/XR/DR/N/C前缀的名称进行回填修正,供自动补全刷新流程默认使用。- Web 路由页面改为按需加载,降低首包体积并增加路由加载失败恢复提示。
- Web 完整报告 Markdown 抽屉改为按需加载。
- 新增市场阶段推断基线并明确盘前、盘中、午休、临近收盘、盘后和非交易日语义。
- 新增运行态市场阶段上下文构造与降级测试。
- 设置页配置帮助阶段性补齐 Web 设置页实际展示/可配置字段的中英双语文案,覆盖 Agent、回测、报告、通知路由、系统运行时、AI legacy、数据源和通知高级配置。
- P2-min:LLM Prompt 注入市场阶段上下文。
修复
- 股票自动补全索引生成缺少
pypinyin时改为直接失败,避免写出缺失拼音字段的降级索引。 - 归一腾讯实时行情成交量为股口径,避免量能变化倍数被放大并误导分析报告。
- Docker 默认部署移除
.env单文件挂载,避免 WebUI 保存配置时因os.replace更新挂载点触发Device or resource busy。 - 收敛 #1391 Phase 0 A 股代码归属边界:补齐
SH/SZ前缀场景的归属一致性,明确data_provider/baostock_fetcher.py、data_provider/pytdx_fetcher.py、data_provider/tushare_fetcher.py的本轮修复范围。 - 修复
STOCK_LIST使用裸 A 股代码时 Baostock 等数据源 fallback 的内部格式转换,保持用户配置继续使用 6 位股票编号。 - Windows 桌面端自动更新在用户确认重启安装后改为静默执行安装器,并在停止内置后端后清理进程引用,降低安装器提示“每日股票分析无法关闭”的概率。
- macOS 桌面端将运行时配置迁移到用户数据目录,并在旧
.app包内文件仍可访问时迁移.env、数据库和日志,避免后续替换升级后重新配置。 - 恢复 Agent/历史兼容快照中的关联板块与板块联动字段提取,修复新版首页报告缺少“板块联动”的回归问题。
- 修正 Web 设置帮助中 legacy 告警 JSON 字段名与静默时段投递语义说明。
- 修复 Web 中文设置页在数据源、通知、系统与 Agent 区域的配置标题、说明和关键下拉选项漏翻问题。
- 修复问股会话切换和首页任务重连后可能残留 Agent/分析任务进行中状态的问题。
- 问股 single-agent 新增 provider-aware trace 分轨,跨轮保留 DeepSeek V4 thinking + tool-call 的
reasoning_content与工具协议材料。 - 为 Akshare 新浪/腾讯 A 股历史兜底接口增加调用级超时,并补齐 Tushare
605xxx沪市代码路由回归测试,避免定时分析因数据源无响应而挂起。 - 将
exchange-calendars依赖下限提升到4.13.0,避免 pandas 3 环境导入交易日历时因 Timedelta 单位T失效导致分析失败。 - 交互式命令(钉钉会话、飞书会话、Telegram)触发的分析结果只回到来源会话,不再同时广播到静态通知渠道。
- 适配 Longbridge OAuth 2.0 认证与 token 缓存恢复,避免新后台无 Legacy Access Token 时长桥数据源被误判为未配置。
- Longbridge OAuth 路径在当前 SDK 不支持
OAuthBuilder/Config.from_oauth时明确日志降级,避免 Linux/Docker 仅可安装旧 SDK 时构建失败。 - 兼容 YFinance 日线返回未命名日期索引的场景,避免标准化后缺少
date列导致美股日线 fallback 中断。
文档
- 新增 #1391 Phase 0 运行诊断契约文档,明确 trace_id、诊断摘要、关键链路范围与脱敏/fail-open/retention 边界。
- 补齐告警中心 P8 文档与配置收口说明,明确 legacy JSON、高级规则、Web/API、Docker、GitHub Actions 与 Desktop 边界。
- 说明本次桌面修复仅覆盖 Windows NSIS 更新安装链路与后端进程生命周期清理;未改动设置项保存/模型运行时清理语义。移除此前误入的
docker/Dockerfilenpm registry变更,恢复部署构建与更新修复的职责隔离。 - 新增 AnalysisContextPack P0 上下文盘点,明确字段质量状态、现有状态映射和首版 pack 边界。
- 明确 #1391 Phase 2 的结构化检测告警为非配置迁移信号:
agent_max_steps/agent_orchestrator_timeout_s非法值会 fallback 至默认并产生日志告警,新增诊断链路仅新增context_snapshot/RunDiagnosticSummary读写字段,不改写litellm_model、agent_litellm_model、openai_base_url、LLM channel 路由或配置迁移语义。 - 补充 #1391 Phase 3 兼容性说明:记录后端诊断持久化、历史查询与通知回写链路变更边界与回滚策略,并补齐后端门禁级验证要求。
测试
- 收敛 #1391 Phase 3 后端/API 与 Web 回归检查:
./scripts/ci_gate.sh、test_pipeline_market_phase_context.py、test_analysis_api_contract.py、test_analysis_history.py、npm run lint、npm run build。 - 执行
python -c "import exchange_calendars as xcals; xcals.get_calendar('XSHG'); print('ok')"通过验证,以覆盖导入与交易日历初始化兼容性。
[3.18.0] - 2026-05-21
发布亮点
- feat: 告警中心扩展到 P2-P6,补齐后台评估、真实通知结果、业务冷却、技术指标规则,以及自选股 / 持仓 / 账户联动规则。
- feat: 个股分析支持策略选择,新增热点题材、事件驱动、成长质量和预期重估策略,并为 HK/US 报告补充基本面、财务摘要、股东回报和关联板块。
- feat: 新增 Finnhub / AlphaVantage 美股数据源适配器,扩展美股日线 failover 链,提升美股行情获取韧性。
- fix: 修复桌面端发布打包、分析状态接口、AlphaVantage 涨跌幅、持仓实时估值、告警历史去重、数据库冷启动和 fallback pricing 注册等稳定性问题。
What's Changed
- feat: Add alert-center P2-P6, Web strategy selection, HK/US fundamental context, static-report financial sections, and Finnhub / AlphaVantage US-market fallback.
- improve: Refine LiteLLM parameter recovery, yfinance currency/dividend handling, RSI calculation, market-review presentation, stock-news relevance ranking, and report table rendering.
- fix: Harden desktop packaging/update assets, completed analysis-status responses, AlphaVantage pct_chg routing, portfolio realtime snapshots, alert trigger dedupe, DatabaseManager cold start, and fallback pricing registration.
- docs/tests: Add beginner setup and settings-help docs, document compatibility/rollback boundaries, and extend regression coverage for API, alert, packaging, and release paths.
[3.17.1] - 2026-05-16
发布亮点
- fix: 桌面端 Windows / macOS 打包脚本显式关闭 electron-builder 自动发布,避免 tag 构建时因缺少
GH_TOKEN在本地打包完成后失败;Release workflow 继续负责上传和发布产物。
What's Changed
- fix: Add
--publish neverto the Windows and macOS Electron packaging scripts so tag builds only create local artifacts and GitHub Actions handles release upload/publish.
[3.17.0] - 2026-05-16
发布亮点
- feat: 新增 Alert API MVP,支持告警规则 CRUD、启停、一次性测试以及触发/通知结果查询,首版覆盖
price_cross/price_change_percent/volume_spike并保持 legacy 配置兼容。 - feat: 通知网关新增 ntfy 与 Gotify 一等渠道,并补齐通知降噪、静态渠道隔离、诊断、Web 测试和 GitHub Actions env 对照校验。
- feat: Windows 桌面安装版接入自动更新安装链路,支持后台下载、确认重启安装、运行时文件备份/恢复和发布产物元数据校验。
- improve: 大盘复盘新增概念排行、人气股、涨停池等底层数据源,支持指数涨跌颜色语义配置,并将复盘结果写入历史记录。
- improve: Web 设置页支持
.env配置备份导入/导出和通知/Agent 区域局部错误兜底;报告新增REPORT_SHOW_LLM_MODEL开关控制模型信息展示。 - improve: Docker 启动入口自动修复挂载目录权限并在日志目录不可写时降级到控制台,减少普通部署的手动修复步骤。
- fix: 数据源缺凭据或连接失败时更温和降级,Longbridge / Pytdx 加入冷却,资金流缺失时避免输出高置信买入结论。
- fix: 分析与报告链路兼容 OpenAI-compatible
content_blocks响应,归一策略价格字段,并修复大盘复盘滚动和历史记录丢失问题。 - docs: 补齐通知、告警中心、桌面打包、README / 指南和 PR title 治理说明,明确多处配置兼容边界与回滚路径。
- test: 增加 Alert API、通知降噪/路由、Docker entrypoint、数据源预取、桌面更新链路和分析历史等回归覆盖。
What's Changed
- feat: Add an Alert API MVP with rule CRUD, enable/disable, one-shot testing, trigger history, notification results, and legacy config compatibility.
- feat: Promote ntfy and Gotify to first-class notification channels with Web tests, routing, Actions integration, diagnostics, and noise control.
- feat: Add the Windows desktop auto-update install flow with runtime state backup/restore and release artifact metadata verification.
- improve: Extend market review data sources, add configurable index color semantics, and persist market review results into analysis history.
- improve: Add Web
.envbackup import/export, local settings panel error boundaries, and a report model visibility toggle. - improve: Harden Docker startup by repairing mounted directory permissions and falling back to console logging when mounted logs are not writable.
- fix: Cool down unavailable optional fetchers, reduce noisy Longbridge/Pytdx retries, and downgrade buy advice when capital flow data is missing.
- fix: Handle OpenAI-compatible
content_blocks, normalize strategy price fields, and recover market review scrolling/history behavior. - docs/tests: Update notification, alert, desktop packaging, README/guide, and governance docs; add focused regression coverage for the new release paths.
[3.16.0] - 2026-05-10
发布亮点
- feat: Web 首页新增“大盘复盘”触发入口、任务轮询与完成后报告直出;首次启动配置状态可提示缺口并引导到系统设置。
- feat: 新增通知路由策略,支持按 report、alert、system_error 将通知收窄到指定渠道;Web 设置页支持通知渠道一键测试。
- feat: 系统设置页新增配置项帮助入口与多语言帮助文案基础设施,首批覆盖自选股、LLM 主模型、LLM 渠道、飞书 Webhook 与 WebUI 监听地址。
- improve: 大盘复盘 API、CLI、Bot 共用
build_market_review_runtime装配路径,补齐litellm_model/llm_model_list与 legacy key 回退说明。 - improve: 个股报告操作建议结合支撑/压力、量能、筹码与主力资金流校准,减少买入/卖出剧烈切换,并补强 Agent 决策兜底。
- improve: Docker 镜像支持非 root 用户运行,LiteLLM 依赖约束放宽到后续安全 1.x 修复版本。
- fix: 修正 LLM 渠道测试中
Model disabled、provider blocked 等错误分类,避免被误报为网络异常。 - fix: 港股日线跳过不支持港股的内置历史数据源;北交所
BJ前缀与.BJ后缀代码校验保持一致。 - fix: Web 大盘复盘按钮可观测性、Windows fallback 锁进程探测和催化线索展示更稳健。
- docs: 新增文档中心与配置帮助维护说明,清理 README、完整指南与配置指南中的临时 PR/文档同步说明。
What's Changed
- feat: Add a Web home market-review trigger with task polling and inline report display; setup status now points users to missing configuration.
- feat: Add notification routing by report, alert, and system_error; add one-click notification channel testing in Web settings.
- feat: Add settings field help infrastructure with multilingual help text for the first batch of core configuration fields.
- improve: Share
build_market_review_runtimeacross API, CLI, and Bot market review paths; documentlitellm_model/llm_model_listand legacy key fallback behavior. - improve: Calibrate stock advice with support/resistance, volume, chips, and main-force capital flow; strengthen Agent decision fallback behavior.
- improve: Run Docker images as a non-root user and relax LiteLLM constraints to allow safe future 1.x fixes.
- fix: Classify
Model disabled, provider blocked, and related LLM channel test errors more accurately instead of reporting them as generic network failures. - fix: Avoid unsupported built-in historical providers for Hong Kong daily data; align Beijing Stock Exchange
BJprefix and.BJsuffix validation. - fix: Improve Web market-review observability, Windows fallback lock probing, and market catalyst snippet rendering.
- docs: Add the documentation index and settings-help maintenance guide; remove temporary PR/doc-sync notes from README and user-facing guides.
[3.15.0] - 2026-05-05
发布亮点
- LLM 渠道配置体验继续升级:新增 Anspire OpenAI-compatible 网关接入,并补齐常用服务商预设、官方来源、能力标签、配置注意事项和 GitHub Actions 显式映射。
- Web LLM 配置检测更可诊断:细分错误 reason,并支持用户显式触发 JSON、tools、vision、stream 运行时 smoke。
- LLM 运行时配置清理更稳健:只清理托管 provider 的失效运行时选择,并保留
cohere/*、google/*、xai/*等直连 provider 兼容语义。 - 通知与 Bot 状态可观测性增强:自定义 Webhook 支持 JSON body 模板,Bot
/status展示更完整的 LLM、Agent 与通知渠道状态。 - 大盘复盘、实时告警、Agent weak 兜底和持仓估值继续补强,降低默认值覆盖、缺价污染和配置排障成本。
新功能
- 支持
ANSPIRE_API_KEYS默认接入 Anspire OpenAI-compatible 大模型网关,并在 LLM 渠道编辑器补充 Anspire Open 预设。 - 自定义 Webhook 支持
CUSTOM_WEBHOOK_BODY_TEMPLATEJSON body 模板,便于适配 AstrBot、NapCat 和自建推送服务。 - 大盘复盘结构化区块新增大盘红绿灯结论,基于盘面温度输出 green/yellow/red、核心原因和操作建议。
- EventMonitor 支持
price_change_percent涨跌幅阈值规则,可按上涨或下跌方向触发实时告警。 - Web LLM 渠道编辑器新增常用服务商配置模板与预设,覆盖 MiniMax、火山方舟、OpenAI、Claude、Gemini、Kimi、Qwen、GLM、豆包等入口。
改进
- Web LLM 配置检测补充细分错误分类,并新增显式触发的 JSON/tools/vision/stream 运行时 smoke;默认测试和保存流程不变,检测结果仅作为当前配置的一次 best-effort 诊断。
- Bot
/status展示统一 LLM 主模型、Agent 模型、渠道模式、YAML 配置和更多通知渠道状态。 - Web LLM 渠道编辑器展示 provider 能力标签、官方来源链接和配置注意事项提示;这些标签仅用于配置参考,不代表运行时能力已验证通过。
- 抽出 Web LLM provider preset 单一模板数据源,保持现有配置保存语义不变。
- 补齐 LLM provider channel 在 GitHub Actions 中的显式映射,并同步
.env示例与配置文档。
修复
- Agent weak 完整性兜底在模型缺少评分、趋势、操作建议或 dashboard 关键块时优先保留本地趋势分析结果,并只补齐真正缺失的仪表盘字段,避免首页评分被默认 50 覆盖。
- 统一持仓快照输出现价、市值、浮盈亏、收益率与价格元信息,避免缺价或 stale 价格污染持仓估值。
- LLM 渠道测试补充结构化诊断与设置页排障提示,便于定位 provider、模型、Base URL 和鉴权配置问题。
- 明确 runtime 清理兼容边界:仅对托管 provider(
gemini、vertex_ai、anthropic、openai、deepseek)触发保存前失效值清理,cohere/*、google/*、xai/*直连值按 legacy 兼容路径保留,不做无提示迁移或覆写。 - 将 MiniMax 预设调整为官方 OpenAI-compatible Base URL 和当前模型示例,并补充 MiniMax、火山方舟、LiteLLM 兼容来源与回退说明。
- 移除截图识别对 Gemini 3 Vision 模型的过时降级逻辑,默认推断改用当前 Gemini 模型配置。
文档
- 完善 LLM provider 配置文档,补充配置方式选择、Actions 变量对照、运行时检测边界、错误 reason 排障和回滚路径(#1180)。
- 补充 LLM 渠道编辑器的官方来源、依赖兼容窗口、保存时的运行时模型清理规则,以及旧配置回退路径说明。
- 为
cohere/*、google/*、xai/*直连语义补充官方 provider/model 说明、litellm>=1.80.10,<1.82.7兼容依据引用,并明确示例模型名仅为配置保留行为说明而非可用性背书。 - 明确
price_change_percent事件告警仅为配置与运行时规则扩展,未变更模型/provider/base URL/LiteLLM 兼容语义;回退路径为关闭/移除 Event Monitor 配置。 - 同步 README、DEPLOY、full-guide、Anspire、AIHubMix 与 SerpAPI 相关说明,统一外链、配置口径和评审一致性说明。
测试
- 补齐 AI 配置页与
task_queue的 LLM 运行时清理/同步回归证据:恢复渠道模型时保留 fallback、编辑模型列表期间不静默清空运行时选择,渠道无可用模型时清理失效 runtime 引用,并覆盖 legacy key 与cohere/*、google/*、xai/*直连 provider 保留语义。 - 覆盖 Web LLM 配置检测的细分错误分类,以及 JSON、tools、vision、stream 运行时 smoke 的显式触发路径。
[3.14.2] - 2026-04-30
发布亮点
- 大盘复盘扩展到港股,并让 Bot
/market与 CLI/调度入口使用一致的交易日过滤语义。 - 问股与 Agent 链路增强配置缺失、决策 fallback 和多策略选择体验。
- LLM 与分析报告链路提升稳定性:非法 JSON 响应会继续尝试备用模型,LiteLLM DEBUG 日志默认降噪。
- 新增只读首次启动配置状态接口,为后续配置向导和 smoke run 奠定基础。
新功能
- 大盘复盘支持港股市场:
MARKET_REVIEW_REGION新增hk选项;both扩展为 A股+港股+美股,并新增港股指数(HSI/HSTECH/HSCEI)复盘链路。 - 新增只读首次启动配置状态接口
GET /api/v1/system/config/setup/status,用于识别 LLM、Agent、自选股、通知和本地存储配置缺口;该接口不会重载运行时、写入.env或创建数据库文件。
改进
- 问股页面支持组合选择多个 Agent 策略。
修复
- Bot
/market命令复用get_open_markets_today()/compute_effective_region()做交易日过滤:结果作为override_region透传给run_market_review;若结果为空字符串则跳过复盘并推送“今日相关市场休市”,与 CLI/调度入口行为一致。 - 问股 Agent 在未配置可用 LLM 时保留后端真实错误原因并维持
done.success=false失败语义,避免前端把配置缺失误当成成功回答。 - Agent 模式未生成有效决策仪表盘时保留本地趋势分析的评分、趋势和操作建议,并将强买/强卖 fallback 归一到兼容的
buy/sell决策类型,避免首页结果被50 / 观望 / 未知缺省值覆盖。 - 持仓快照现价缺失时不再静默回退为持仓成本;当天快照优先使用历史收盘价,仅在缺失时使用实时价 fallback,缺价持仓不再污染市值与未实现盈亏汇总,并为持仓明细返回价格来源、日期、stale 与缺价状态。
- 分析 Prompt 在注入
trend_analysis前按最终trend_status/ma_alignment清洗互斥理由:空头结构移除看多理由、多头结构移除空头结构风险,并在事件/技术冲突与异常放量(>10 倍)时强制提示“事件先行、技术待确认”与量能降权。 - LLM 返回非 JSON 响应时同样触发备用模型切换:主模型成功返回但无法解析 JSON 时,不再立即降级为纯文本 fallback,而是依次尝试
LITELLM_FALLBACK_MODELS中的备用模型;所有模型均无法返回合法 JSON 时,再降级为文本 fallback。 - LiteLLM 内部 DEBUG 日志默认压低到 WARNING,避免流式生成时 token 级日志污染
stock_analysis_debug_*.log;如需排查 LiteLLM 内部细节,可临时设置LITELLM_LOG_LEVEL=DEBUG(Fixes #1156)。
文档
- 补充 LLM 配置指南与 FAQ,明确问股 Agent 对
LITELLM_CONFIG/LLM_CHANNELS/ legacyGEMINI_*OPENAI_*ANTHROPIC_*的兼容优先级、回退路径与“不静默迁移旧配置”的结论。
测试
- 新增
tests/test_bot_market_command.py,覆盖MARKET_REVIEW_REGION=both+ open markets{"cn","us"}/{"cn","hk"}的override_region透传断言,并覆盖全市场休市跳过与关闭交易日检查路径;新增tests/test_yfinance_hk_indices.py覆盖港股指数符号映射与部分/全部失败降级路径。 - 补齐
task_queue轻量导入 stub 的股票代码规范化函数,恢复tests/test_task_queue_config_sync.py收集与运行。
[3.14.1] - 2026-04-26
- [测试] 修正大盘复盘 prompt 测试对“明日交易计划”标题的断言,并同步桌面端版本号,恢复发布 gate。
[3.14.0] - 2026-04-26
发布亮点
- 📊 大盘复盘升级为盘后工作台式结构 — A 股复盘固定输出盘面温度、指数明细、板块 Top 表、新闻催化、明日交易计划和风险提示,减少纯文字复盘的重复与空泛。
- 🖥️ 桌面端新增 GitHub Release 更新提醒 — Windows/macOS 桌面端启动后自动检测新版本,也可从设置页手动检查并跳转下载页。
- 🤖 Pipeline Agent 数据加载大幅降噪 — K 线工具改为 DB-first 并预热 240 天历史数据,避免同一只股票重复 HTTP 请求。
- 🐳 Docker 发布链路整理 — 发布工作流收敛为正式发布与手动补发两条路径,官方 Docker Hub 镜像名统一为
zhulinsen/daily_stock_analysis。 - 🔧 LLM 渠道与 DeepSeek V4 配置补强 — GitHub Actions 定时分析补齐多渠道变量透传,DeepSeek 官方渠道预设与示例同步到 V4。
- 🧩 桌面端静态资源一致性校验 — 打包链路和运行时都能更早发现静态资源错配,降低 Release 包白屏排查成本。
新功能
- 🏠 Web 首页历史报告区新增重新分析入口 — 支持基于原始 prompt 重做同一只股票同日期的分析。
- 🖥️ Windows/macOS 桌面端新增 GitHub Release 更新提醒 — 启动后自动检测新版本,并支持从设置页手动检查后跳转下载页。
改进
- 📊 A 股大盘复盘报告改为结构化盘后工作台版式 — 固定输出盘面温度、指数明细、板块 Top 表、新闻催化和明日交易计划。
- 🐳 Docker 发布工作流收敛 — 更清晰地区分正式发布与手动补发链路,并统一官方 Docker Hub 镜像名为
zhulinsen/daily_stock_analysis。 - 🤖 Agent 日线工具优先复用本地缓存 — 同时持久化新获取的日线与新闻情报,减少重复数据源调用。
修复
- 🤖 Pipeline Agent K 线工具 DB-first 加载 —
get_daily_history/analyze_trend/calculate_ma/get_volume_analysis/analyze_pattern改为优先读取本地 DB,消除同一只股票 9x5=45 次重复 HTTP 请求(Fixes #1066)。 - 🤖 Pipeline Agent 执行前按需预热 240 天 K 线历史到 DB — 正常情况下 K 线工具调用无需重复网络请求。
- 🕒 冻结
target_date并通过 ContextVar 透传到 Pipeline Agent K 线工具线程 — 消除跨收盘边界时间漂移。 - 🪟 Windows 桌面端后端日志转抄编码修复 — 转抄 stdout/stderr 时优先使用 UTF-8,并兼容本地代码页回退,避免中文日志乱码。
- ⚙️ GitHub Actions 每日分析工作流补齐 LLM 渠道变量透传 — 支持
LLM_CHANNELS、多 Key 与常用LLM_<NAME>_*,避免本地可用的多模型配置在云端定时任务中失效(Fixes #1063, #872)。 - 📈 历史报告详情接口修正
change_pct取值 — 使用is None判断避免把 0.0(平盘)当作缺失值丢弃,移除错误的change_60d兜底,并在缺失时回退到原始实时行情字段(Fixes #1084)。 - 🔧 DeepSeek 官方渠道预设与示例配置同步到 V4 — 保留 legacy
deepseek-chat默认值并增加废弃提示,同时修正模型发现后旧运行时选择导致保存失败的问题(Fixes #1108, #1109)。 - 🧩 桌面端打包链路新增静态资源一致性检查 —
scripts/check_static_assets.py会在源static/与 PyInstaller 产物中校验index.html引用的资源是否真实存在,运行时也会在错配时写入明确日志,避免重现 Release 包打开后白屏(Refs #1064 / #1065 / #1050)。 - 🧩 后端
/assets/*改为显式路由托管 — 资源缺失时返回与请求扩展名匹配的text/javascript/text/css404,减少默认 JSON 错误响应带来的排查误导(Refs #1064)。 - 🌙
kimi-k2.6自动使用固定温度 — 主分析、大盘复盘和 Agent 调用该模型时自动使用temperature=1.0,避免模型拒绝默认温度请求(Fixes #1102)。
文档
- 🐳 补充官方 Docker 镜像使用说明 — 增加镜像拉取、
docker run用法与.env/ 数据目录映射说明,不再只覆盖 Compose 部署路径。 - 📨 修正飞书自定义机器人 Webhook 示例 —
feishu_sender.py中的示例改为 interactive card JSON,并补充飞书自动化 Webhook 触发器配置教程。 - 📚 优化根 README 结构 — 保留首页级功能特性、技术栈、快速开始、推送效果、Web、Agent、赞助商和新闻源入口,将细配置、交易纪律和基本面语义收口到完整指南,并将 Docker 徽章指向官方镜像页。
- 🌐 同步英文与繁中 README 的精简入口结构 — 同时补齐完整指南中的 LLM 用量 API 与持仓管理说明。
- 🤝 调整 AI 协作与 PR 模板中的 README 维护规则 — 明确 README 非必要不更新,细节优先进入专题文档。
测试
- 🧪 稳定市场复盘相关测试的 LiteLLM stub 行为 — 避免本机安装的 LiteLLM 在测试收集顺序变化时影响市场复盘单元测试。
- 🧪 pytest 默认跳过前端依赖目录 — 本地存在
apps/dsa-web/node_modules时不再被后端测试递归扫描,避免发布前 gate 被无关目录拖慢。
[3.13.0] - 2026-04-21
发布亮点
- 🌉 长桥 OpenAPI 数据源接入 — 美股/港股行情优先使用 Longbridge,YFinance / AkShare 自动兜底;未配置时行为不变。
- 📈 Tushare 港股全链路扩展 — 港股日线通过
hk_daily获取;筹码分布对港股返回None;换算单位跟随港股口径,不再套用 A 股手/千元规则。 - 🔍 Anspire Search 语义搜索接入 — 配置
ANSPIRE_*后即可使用 Anspire Search 获取实时行情及资讯,未配置时完全透明。 - 🚀 普通分析链路支持 LLM 流式生成 — 首页任务 SSE 新增
task_progress事件,进度更细化;不支持流式的 provider 自动回退到非流式调用。 - 🤖 Web 渠道编辑器支持按需拉取可用模型列表 —
/v1/models统一模型发现入口,多选写回LLM_{CHANNEL}_MODELS,拉取失败时保留手动输入降级。 - 🛡️ Agent 稳定性与预算护栏全面补强 —
AGENT_MAX_STEPS语义统一、技能降级不中断管线、SSE 异常透传、技能加载 warning 日志补齐。 - 🛠️ SQLite 写入链路原子化 — 批量原子 upsert + WAL +
busy_timeout+ 有限写入重试,显著降低批量分析并发锁竞争。
新功能
- 🌉 集成 Longbridge OpenAPI 作为美股/港股可选数据源(fixes #981)— 配置
LONGBRIDGE_*后优先使用长桥获取日线与实时行情,YFinance / AkShare 兜底;未配置时行为与此前一致。联调使用tests/longbridge_live_smoke.py(手动脚本,不参与 pytest 收集)。 - 📈 Tushare 支持港股日线查询 — 配置 Tushare 凭证后调用
hk_daily接口获取港股数据;权限不足时抛出异常,与原流程一致。 - 🔍 集成 Anspire Search 可选语义搜索后端 — 配置
ANSPIRE_*可使用 Anspire Search 获取实时行情及新闻资讯;未配置时行为与此前一致。联调使用tests/test_anspire_search.py(手动脚本)。 - 🚀 普通分析链路支持 LiteLLM 流式生成与更细任务进度 — 股票分析在 LLM 阶段优先尝试
stream=True并在服务端累积 chunk,首页任务 SSE 新增task_progress事件与更细的message/progress更新;仅在最终 JSON 解析成功后持久化历史报告;不支持流式的 provider 自动回退到非流式调用。 - 🤖 Web AI 模型配置支持按渠道获取可用模型列表 — 渠道编辑器支持调用
/v1/models拉取可用模型,并以多选方式写回LLM_{CHANNEL}_MODELS;拉取失败时保留手动输入作为降级路径。
改进
- 🔎 SerpAPI 正文补抓范围收敛 — 自然搜索结果不再逐条同步抓取网页正文;仅对极少数高位且摘要不足的结果做延迟补抓,优先复用 SerpAPI 已返回的结构化摘要,降低搜索链路尾延迟与慢站点放大风险。
- 🤖 LLM 接入体验简化 — 面向用户的 AI 模型接入文案统一为"主模型 / Agent 主模型 / 备选模型 / 模型渠道",不再把 LiteLLM 当作普通用户必学概念,现有
LITELLM_*/LLM_CHANNELS配置键保持兼容。 - 🧠 IntelAgent 新增公司公告搜索与主力资金流工具 — 增加上交所/深交所/cninfo 公告搜索维度与
get_capital_flow工具,修复 Agent 模式下公告和资金流数据经常缺失的问题。 - 📦 后端股票名称解析优先复用
stocks.index.json— 懒加载缓存前端静态索引,纯后端/缺失静态资源场景静默降级回STOCK_NAME_MAP与原有数据源回退链路。 - 📊 TushareFetcher 港股单位适配 —
get_chip_distribution对港股直接返回None(港股暂不支持筹码分布);_normalize_data对港股(hk_daily)不再做 A 股手→股、千元→元的缩放,与 Tushare 港股字段语义一致。 - ⏱️ Agent 超步数错误增加
AGENT_MAX_STEPS调整提示 — 帮助用户自助排查步数限制问题。 - ⚙️ GitHub Actions 分析任务超时支持
vars配置 —daily_analysis.yml任务超时从 repository variables 读取,无需修改代码即可调整运行超时上限(fixes #1014)。
修复
- 📣 大盘复盘链路接入
REPORT_LANGUAGE—REPORT_LANGUAGE=en时,A 股/合并复盘的 Prompt、章节标题、模板兜底文案与通知包装标题统一输出英文,避免英文正文搭配中文标题的混排问题。 - 📈 EfinanceFetcher 指数开盘价映射兼容(fixes #1043)—
get_main_indices()的开盘价映射改为兼容今开 → 开盘 → open,修复部分 efinance 版本下指数开盘价被读成缺失值的问题。 - 🤖 AGENT_MAX_STEPS 语义统一(fixes #1026)— 在 orchestrator 多 Agent 模式下明确为"各子 Agent 步数上限而非硬覆盖";TechnicalAgent 等高默认值 Agent 会被封顶,低默认值 Agent 保持原值;用户主动调高(>10)时统一覆盖所有子 Agent。修复了用户设置 12 但 TechnicalAgent 仍以默认 6 步运行并报 "Agent exceeded max steps" 的问题。
- 🛡️ Specialist(Skill)Agent 失败改为优雅降级 — 技能 Agent 失败不再中断整个分析管线,与 intel/risk 保持相同的降级策略。
- 🔧 MiniMax-M2.7 连接测试修复 — 修复 LLM 通道连接测试在 MiniMax-M2.7 下返回 "Empty response" 的问题;将
max_tokens上限从 8 提升至 256 以容纳思考过程,并添加content_blocks格式解析逻辑。 - 📊 移除
sentiment_score范围约束(fixes #942)— 移除HistoryItem与ReportSummary响应 Schema 中sentiment_score的ge=0/le=100约束,历史库中存储的超范围值不再触发 Pydantic ValidationError。 - 🖥️ WebUI 前端资源缺失时发出明确警告 —
webui_frontend.py在static/index.html存在但static/assets/缺失时发出 warning,避免 CSS/JS 资源缺失导致页面异常变大却无从排查(fixes #944)。 - 🔗 分析管线可选服务降级初始化 —
StockAnalysisPipeline搜索服务与社交舆情服务任一初始化异常时,记录 warning 并以禁用状态继续运行,避免外部依赖抖动阻塞主分析链路。 - 🖥️ 桌面端版本展示统一读取
package.json— 统一读取apps/dsa-desktop/package.json,移除 preload 中硬编码的0.1.0,设置页展示真实桌面端版本;修复版本号显示错误(fixes #1048)。 - 🐋 港股名称获取失败修复(fixes #940)— 修复主数据源字段缺失时无法正确回退到备用字段获取港股名称的问题。
- 🔄 SSE 任务流断开时
CancelledError正确 re-raise(fixes #967)— 修复 SSE 流中断时异常被静默吞掉导致故障无日志可查的问题。 - 🔄 Agent SSE 清理阶段后台任务异常正确上报(fixes #969)— 流结束时后台执行器异常现在正确记录并上报,避免错误无法感知。
- 🔇 技能加载异常补充
logger.warning日志(fixes #970)— 在ask.py、skills/aggregator.py、skills/router.py的静默 except 块补充日志,确保技能列表为空时有日志可查。 - 🛠️ SQLite 写入链路原子化(fixes #878)—
stock_daily(code,date)使用批量原子 upsert;文件型 SQLite 连接默认启用 WAL +busy_timeout+ 有限写入重试;"新增数"改按本次真正插入窗口计算。 - 💰 多 Agent / 单 Agent 预算护栏语义统一 — 剩余预算低于最小阈值时主动跳过并降级;已完成阶段可构建降级报告时返回
success=True并携带非空内容,否则返回success=False。 - ⚙️ GitHub Actions
daily_analysis.yml补齐REPORT_LANGUAGE注入(fixes #1013)— 修复用户在 Secrets/Variables 中配置REPORT_LANGUAGE后不生效的问题。 - 📊 任务状态 API 补齐实时价格字段(fixes #983)—
GET /api/v1/analysis/status/{task_id}从数据库回填已完成任务时补齐current_price/change_pct,修复首页报告股票名旁不显示实时价格的问题。 - 📅 非交易日数据返回最近交易日(fixes #1009)— 修复非交易日(周末/节假日)筹码分布与板块排行返回倒数第二个交易日数据的问题,现在正常返回最近交易日数据。
- 🔍 A 股资讯搜索恢复中文优先 —
search_stock_news()在首个 provider 主要返回英文资讯时继续尝试后续引擎,并将同批结果中的中文资讯排到前面;非美股查询不再默认沿用 Brave 的en/US区域语言偏好。 - 📨 飞书群机器人通知支持签名校验 — 飞书通知现在支持
FEISHU_WEBHOOK_SECRET/FEISHU_WEBHOOK_KEYWORD;Web 设置与文档明确区分 Webhook 推送模式和FEISHU_APP_ID/FEISHU_APP_SECRET应用模式,降低误配风险。 - ⚡ LLM 适配层新增
RateLimitError和ContextWindowExceeded检测 — 识别并处理速率限制与上下文窗口超出错误,提升分析链路在高负载或长文本场景下的健壮性(fixes #1002)。
测试
- 🧪 TushareFetcher 港股相关单元测试 — 新增
get_chip_distribution筹码分布获取与_normalize_data港股/A 股/ETF 单位处理的单元测试,覆盖港股特殊路径。
文档
- 📘 DEPLOY.md 补充 UI 元素异常变大排查步骤 — 新增重建 Docker 镜像或手动执行
npm run build的排查指南;deploy-webui-cloud.md同步更新。 - 📨 飞书 Webhook 配置说明补全 — 强调
FEISHU_WEBHOOK_URL是群通知必填项、签名校验须两端同时启用或关闭、FEISHU_APP_SECRET仅用于应用/Stream Bot 模式;.env.example补充内联注释;同步英文指南。 - 🤝 FAQ 补充 Ollama 连接失败排障条目(Q12c) — 覆盖服务未启动、URL 配置错误、模型前缀缺失、模型未下载、远程防火墙等 5 个检查点(fixes #854)。
- 🌉 README 补充长桥数据源使用说明 — 中/英/繁 README 明确长桥"首选 / 兜底 / 未配置不调用"边界;
docs/内相对路径链接修复;LONGBRIDGE_PRINT_QUOTE_PACKAGES配置与代码及.env.example对齐。 - 🐋 Docker 安装场景版本说明 — 补充最小化文档,明确 Docker 安装场景下应以 Git tag / 镜像 tag 判断版本(fixes #1091)。
[3.12.0] - 2026-04-01
发布亮点
- 📊 回测页新增"次日验证"视图 — 可按股票与日期范围查看 AI 预测 vs 次日实际涨跌,复用历史分析与 1 日回测结果,快速验证分析准确率。
- 🔧 LLM 接入体验简化 — 用户侧文案统一收口为"主模型 / 备选模型 / 模型渠道",不再把 LiteLLM 当作普通用户必学概念,现有配置键保持兼容。
- 🐳 Docker / WebUI 运行时稳态补强 — 修复系统设置保存后配置不生效、启动早期日志缺失、预构建静态资源复用等问题,降低容器化部署的运维摩擦。
- 🔒 安全与并发稳定性同步增强 — Discord 入站 Webhook 补齐 Ed25519 验签,修复并发执行时共享状态未加锁、单股推送模式通知并发复用等问题。
- 🖥️ 桌面端与定时任务细节打磨 — Windows 安装器支持自选安装目录,内置定时调度器感知运行中 SCHEDULE_TIME 变更,断点续传改按市场时区判断。
新功能
- 📊 回测页新增"次日验证 / 1 日窗口"视图 — 可按股票代码与分析日期范围查看 AI 预测、次日实际涨跌及筛选区间准确率,复用历史分析与 1 日回测结果实现。
- 🏷️ Web 设置页新增版本信息卡片 —
apps/dsa-web现在会在构建时注入前端包版本与构建时间,系统设置页新增只读"版本信息"区块,展示WebUI 版本 / 构建标识 / 构建时间;当package.json仍为占位版本0.0.0时,会自动回退为构建标识,方便 Docker 重建后快速确认当前静态资源是否已经生效。 - 🪟 Windows 桌面安装器支持自选安装目录 — 安装器改为支持在安装向导中自定义安装目录,安装到非默认盘符后仍沿用现有打包态目录逻辑在安装目录旁读写
.env、data/stock_analysis.db和logs/desktop.log,同时保留win-unpacked免安装分发方式。安装器仅支持当前用户安装、已禁用管理员提权(allowElevation: false),并通过 NSIS.onVerifyInstDir阻止选择系统保护目录。
改进
- 🔎 SerpAPI 正文补抓范围收敛 — 自然搜索结果不再逐条同步抓取网页正文;现在仅对极少数高位且摘要明显不足的结果,在更短超时预算内做延迟补抓,并优先复用 SerpAPI 已返回的结构化摘要,降低搜索链路尾延迟与慢站点放大风险。
- 🤖 LLM 接入体验简化 — 面向用户的 AI 模型接入文案已统一收口为"主模型 / Agent 主模型 / 备选模型 / 模型渠道 / 高级模型路由配置";Web 设置页、配置元数据、校验提示与中英文文档不再把 LiteLLM 当作普通用户默认必学概念,现有
LITELLM_*/LLM_CHANNELS配置键仍保持兼容。
修复
- 🚀 启动早期失败时暴露真实根因 —
python main.py现在通过 stderr 暴露真实根因,bootstrap 阶段不再向硬编码logs/目录写入文件日志,文件日志推迟到config.log_dir可用后创建,避免健康启动在非预期路径残留日志文件。 - 🐳 Docker WebUI 运行时优先复用预构建静态资源 —
prepare_webui_frontend_assets()现在会先检查镜像内已有的static/index.html是否可直接复用;当容器运行时不包含apps/dsa-web源码目录且未安装npm时,也不会误报"未找到前端项目,无法自动构建",从而恢复 Docker 部署后的 WebUI 打开能力。 - 🐳 Docker WebUI 系统设置保存后配置生效 — Docker 场景下 WebUI 保存
STOCK_LIST、SCHEDULE_ENABLED、SCHEDULE_TIME、SCHEDULE_RUN_IMMEDIATELY、RUN_IMMEDIATELY后,Config会优先读取持久化.env中的新值,避免被容器创建时注入的旧环境变量覆盖。 - 📈 市场复盘 LLM max_tokens 提升 — 市场复盘生成链路将 LLM
max_tokens从2048提升到8192,降低长复盘输出因MAX_TOKENS提前截断导致内容未完成的概率。 - ⏰ 内置定时调度器感知 SCHEDULE_TIME 运行时变更 — 调度器现在会在运行中感知 WebUI 保存后的
SCHEDULE_TIME变化,并在下一轮检查时重绑 daily job。 - 🪟 Windows Release 渠道编辑器保留 MiniMax 模型前缀 — 渠道模式下填写
minimax/<模型名>时,后端归一化与 Web 设置页运行时模型列表都会保留该值原样,不再误改写成openai/minimax/<模型名>。 - 🤖 Discord 入站 Webhook 补齐 Ed25519 验签 —
DiscordPlatform现在会基于X-Signature-Ed25519、X-Signature-Timestamp和原始请求体校验 Discord Interaction 签名;缺失签名头、公钥格式非法或签名不匹配时直接拒绝请求,同时对 timestamp 做 ±5 分钟时效窗口校验以防御重放攻击。 - ⚙️ STOCK_GROUP_N / EMAIL_GROUP_N 配置关系明确化 — 明确与
STOCK_LIST的关系,并在配置校验中对超出STOCK_LIST的邮件分组给出 warning。 - 🗓️ 断点续传改按市场时区和交易日历判断(fixes #880)— 股票数据存在性检查不再直接使用服务器自然日,而是按 A 股 / 港股 / 美股各自市场时区解析"最新可复用交易日"。
- 📨 单股推送模式不再并发复用共享通知实例 —
StockAnalysisPipeline.run()现在会保留个股分析并发,但把SINGLE_STOCK_NOTIFY=true下的即时通知挪到结果收集侧串行发送。 - 🔇 实时行情降级提示收口为单次告警 — 分析主流程获取股票名称时不再提前触发一次实时行情查询,只有在全部数据源都不可用时才提示已降级为历史收盘价继续分析。
- 🔍 A 股中文资讯搜索恢复中文优先 —
search_stock_news()现在会在首个 provider 主要返回英文资讯时继续尝试后续引擎,并将同批结果中的中文资讯排到前面。 - 🔒 并发执行时共享状态补齐统一加锁 — 修复并发执行时共享状态缺少统一加锁的问题,避免多线程场景下的数据竞争。
测试
- 🧪 补充设置页版本信息回归测试 — 新增 Web 设置页版本信息渲染断言,并覆盖占位版本
0.0.0自动回退为构建标识的逻辑。 - 🧪 UI 治理与关键路径回归补强 — 补充
SidebarNav、ChatPage、BacktestPage等组件测试,并新增 UI governance 守卫,持续防止交互元素重新引入原生title属性或旧input-terminal样式回流。同步更新 smoke / markdown drawer 相关验证,覆盖主题升级后的关键主链路。
[3.11.0] - 2026-03-27
发布亮点
- 🎨 Web 工作台完成一轮 UI 统一与双主题升级 — 首页、问股、回测、持仓和设置页进一步收口到统一设计 token、输入表面和状态表达;新增完整浅色主题,并支持浅色 / 深色一键切换与持久化保存。
- 🤖 Bot / Agent 能力重新补回主分支 — 恢复
/history、/strategies、/research等命令,/ask继续支持多股对比与组合视角;Deep Research、事件监控与 schedule 轮询链路重新接回主线能力。 - 🔒 安全性与运行稳态同步补强 — 修复
X-Forwarded-For限流绕过风险,恢复 LiteLLM 官方 PyPI 安装路径,Tushare 初始化不再依赖本地 SDK,降低 Docker、桌面打包和环境重建时的脆弱点。 - 🖥️ 日常使用细节继续打磨 — 修复首页港股自动补全提交、登录页首屏主题闪烁、历史长股票名重叠,以及 Telegram Markdown 解析失败时整条通知发送中断等问题。
新功能
- 🎨 全新浅色主题与双主题切换上线 — Web 工作台新增完整浅色主题,并支持在侧边栏中一键切换浅色 / 深色模式;主题选择会持久化保存,刷新页面后仍保持当前偏好。此次升级不是局部配色微调,而是对卡片层级、边界对比、输入表面、状态提示和页面背景做了一整套 light theme 重绘。
- 🤖 补回主分支缺失的 Agent / Bot 能力 —
#648/#649已重新补回main:Bot 恢复/history、/strategies、/research,/ask保留多股对比与组合视角;Deep Research 与 Event Monitor 的配置重新在 Web 设置页可见并可编辑,schedule 模式也重新接入事件告警轮询。
改进
- 🖥️ 核心页面统一到同一套工作台视觉语言 —
Home / Chat / Backtest / Portfolio / Settings进一步收口到共享设计 token、input-surface输入体系、空态/错误态表达和抽屉遮罩语义,减少页面之间的视觉割裂与局部私有样式漂移。 - 💬 问股交互可达性与反馈增强 — 问股页补强了会话导出、通知发送、消息复制、历史删除与追问上下文提示;AI 回复操作不再过度依赖 hover,触屏设备和小屏场景下也能直接触达关键按钮。
- 📊 回测与持仓页表面和状态表达继续标准化 — 回测页筛选控件、布尔状态、结果表格与汇总卡片统一到共享输入/状态原语;持仓页的导入反馈、汇率刷新提示、空态与警示信息进一步归口到共享组件,减少页面级重复实现。
- 🧭 导航与页面壳层协同优化 — 侧边栏主题切换、问股完成角标、移动端抽屉遮罩和主内容滚动契约进一步统一,首页、问股和回测在桌面端与移动端的切页体验更稳定。
测试
- 🧪 UI 治理与关键路径回归补强 — 补充
SidebarNav、ChatPage、BacktestPage等组件测试,并新增 UI governance 守卫,持续防止交互元素重新引入原生title属性或旧input-terminal样式回流。同步更新 smoke / markdown drawer 相关验证,覆盖主题升级后的关键主链路。
修复
🌗 Web 首屏默认主题预设为深色 —
apps/dsa-web/index.html现在会在 React 挂载前读取本地保存的主题偏好;若没有已保存值,则立即给<html>预设dark并同步color-scheme,避免首页和登录页首屏先闪出浅色主题。🔐 登录页独立主题层收口 — 登录页输入框、标签、切换按钮和按钮文案现在使用独立的
--login-*视觉 token,不再继承全局浅/深主题文字色;即使浏览器缓存了浅色主题,登录页仍保持稳定的深色视觉与青色密码输入表现,避免密码圆点和文案落成黑色。🖥️ 首页港股代码输入修复 — Web 首页分析输入框现在可正确接受港股代码与自动完成选中的港股项,补齐
00700.HK/HK00700等格式识别,避免提交时误报“请输入有效的股票代码或股票名称”。🔒 认证限流 X-Forwarded-For 取值修复(CWE-345)(#841 / #842)—
get_client_ip()从取X-Forwarded-For最左值改为最右值,防止攻击者通过伪造首部旋转限流桶绕过暴力破解保护;仅影响TRUST_X_FORWARDED_FOR=true且单层可信反向代理的部署场景,多级代理环境需按部署文档评估配置。📦 恢复 LiteLLM 官方 PyPI 安装并锁定安全上限 —
requirements.txt重新使用pip install litellm的官方 PyPI 安装路径,并在保留历史最低要求>=1.80.10的同时增加<1.82.7的安全上限,避免误装已被移除的1.82.7/1.82.8风险版本;Windows 桌面打包脚本也同步回退到标准pip install -r requirements.txt链路,减少特殊下载分支带来的维护成本。📨 Telegram Markdown 解析失败回退纯文本(fixes #850)—
src/notification_sender/telegram_sender.py现在会在 Telegram 返回HTTP 400且包含can't parse entities/ Markdown 解析错误时,自动去掉parse_mode后重试纯文本发送,避免*ST等正文内容直接导致整条通知失败。🔢 A 股同码实时行情保留交易所提示(fixes #852)—
DataFetcherManager与TushareFetcher现在会保留SZ000001/000001.SZ这类显式沪深提示,旧版 Tushare 实时行情降级分支不再把深市000001误判成sh000001上证指数。🎯 多 Agent 次优买点不再盲目复制理想买点(fixes #851)— 当多智能体结果缺少独立
secondary_buy时,仪表盘现在优先展示N/A而不是把 fallback 值硬拷贝成与ideal_buy完全相同,减少误导性的双买点展示。🧩 Tushare 初始化不再强依赖本地 SDK 包 —
TushareFetcher现在直接使用内置 HTTP client 访问 Tushare Pro,不再在启动阶段先import tushare才能初始化;修复了 Docker、桌面打包或环境重建后因缺少tushare包而提前报No module named 'tushare'的问题,并补充对应回归测试。⚙️
daily_analysis工作流补齐DEEPSEEK_API_KEY映射 — GitHub Actions 每日分析工作流现在会正确透传DEEPSEEK_API_KEY,避免云端任务配置了密钥却在运行时拿不到对应环境变量。🖥️ 历史列表过长股票名称截断与悬停展示(fixes #815)— 历史列表中过长的股票名称, 现在会按字符类型自动截断(英文15/中文8/混合10字符),默认显示截断结果,悬停时展示完整名称;解决 1920x1080 分辨率下股票名称与右侧状态标签文字重叠的问题。新增
stockName.ts工具函数并补充对应测试。
文档
- 🧾 README 捐赠入口更新为小红书二维码 — README 及中英文说明中的赞助入口更新为小红书二维码素材,保持展示口径一致。
[3.10.1] - 2026-03-24
新功能
- 🔔 Web 端分析推送通知开关(#808)— 首页分析按钮旁新增「推送通知」复选框,默认勾选;取消勾选时本次分析不发送 Telegram/企业微信等推送。API
POST /api/v1/analysis/analyze新增notify字段(bool,默认true),不传时行为与修改前一致,Bot 和定时任务不受影响。
改进
- 🖥️ 问股 / 回测页面布局与壳层协同优化 — 统一 Chat / Backtest 页面容器、共享 UI 状态和跟随问答交互路径,移除部分硬编码高度限制,让导航框架内的填充与滚动行为更连贯。
- 🎨 全局视觉与共享组件继续收敛 — Light theme 引入动态 HSL 阴影体系,统一侧边栏激活态、告警组件对比度和聊天气泡样式,并把部分零散内联样式收口为语义化 CSS 变量,提升一致性与可维护性。
修复
- 🖼️ 系统设置智能导入文件选择恢复 — 修复了“系统设置 > 基础设置 > 智能导入”模块中 “选择图片 / 选择文件” 两个按钮点击无响应的问题。
- 🖥️ 移动端滚动与交互层级修复 — 解决主题切换菜单在移动端被主内容遮挡的 z-index 冲突,并恢复首页长报告场景下的正常纵向滚动,不影响其他页面现有滚动行为。
- 🧾 Markdown 纯文本复制清洗增强 — 改进纯文本导出算法,复制分析报告时会更稳定地清除表格分隔符等 Markdown 痕迹,提升分享和归档内容的纯净度。
- 🧠 Trading philosophy injection 覆盖 legacy + Agent 全链路(#810)—
GeminiAnalyzer、单 Agent 模式和 skill-aware Prompt 现在共享同一套策略注入状态;只有隐式回落到内置默认bull_trend时才保留旧的趋势型提示,显式策略选择或自定义默认 skill 不再被偷偷叠加MA5>MA10>MA20多头基线。 - 🛠️ 后端 CI 依赖安装链路稳态化(#835)— 拆分 backend gate 阶段、为依赖安装增加重试,并把 CI 用的
litellm安装来源调整为更稳定的 GitHub 源,降低依赖解析抖动导致的 backend gate 偶发失败。 - 🪟 Windows 桌面发版构建恢复 LiteLLM 安装兼容性 —
scripts/build-backend.ps1现在会先过滤requirements.txt中的 LiteLLM GitHub 源包,再下载对应 tag 的 zipball 到本地移除上游可选enterprise/目录后安装,绕过 Windows runner 上 Poetry 构建 wheel 时把目录误当文件打包导致的失败;同时补上pip install退出码检查,避免依赖安装失败后只在后续python-multipart校验阶段才暴露成次生报错。
测试
- 🧪 问股 / 回测 / 智能导入回归覆盖补齐 — 同步更新 E2E 冒烟期望,补充
DashboardStateBlock、Chat 页、智能导入文件选择与相关交互回归断言,确保近期 UI 调整后的关键路径仍可稳定通过。
[3.10.0] - 2026-03-24
发布亮点
- 🔎 自动补全与索引工具扩展到三市场 — 补全索引生成链路现在同时覆盖 A 股、港股、美股,配套新增 Tushare 股票列表抓取工具与更完整的静态索引数据,让首页搜索入口从“能用”走向“更全、更稳”。
- 🖥️ Dashboard 与报告查看体验继续收口 — 首页 Dashboard 面板、状态边界、字体层级和完整报告表格密度完成一轮统一;报告详情也补齐了 Markdown/纯文本复制与更可靠的按钮交互,减少历史报告查看与分享时的摩擦。
- 🤖 Agent skill 与市场语义边界更清晰 — skill bundle、默认策略、回测汇总语义和兼容接口进一步收敛;同时分析 Prompt 不再默认写死 A 股上下文,美股和港股分析也能按各自市场规则生成更贴切的内容。
- ⏰ 定时与桌面配置能力更贴近真实使用场景 — 桌面端支持
.env导入导出;python main.py --schedule --stocks ...也不再把启动时股票快照错误带入后续计划执行,定时任务会跟随最新保存的STOCK_LIST。
新功能
- 💾 桌面端
.env备份/恢复入口(#754)— 桌面模式下的系统设置页新增导出 .env/导入 .env按钮,可直接备份当前已保存配置,或把备份文件中的键值合并恢复到当前桌面端.env;导入沿用现有config_version冲突保护与运行时重载链路,不改变现有桌面端便携模式路径。 - 📊 Tushare 股票列表获取工具 — 新增
scripts/fetch_tushare_stock_list.py,支持从 Tushare Pro 获取 A股、港股、美股列表信息并保存为 CSV,配有分页读取、智能限流、错误处理和进度提示;新增对应使用文档docs/TUSHARE_STOCK_LIST_GUIDE.md。 - 🔎 索引生成脚本多市场支持 —
generate_index_from_csv.py重构为支持 Tushare 和 AkShare 双数据源,同时覆盖 A股、港股、美股三个市场;新增按市场分类的别名映射(A股、港股常见别名,美股常用股票英文缩写);添加--source参数切换数据源、--test参数验证模式;严格过滤美股 DUMMY 记录。 - 🔎 索引生成脚本增强 —
generate_stock_index.py新增--test/-t测试模式和--verbose/-v详细输出模式,添加市场分布统计,优化 JSON 输出格式。 - 📋 首页完整报告支持双模式复制 — 历史报告详情头部新增“复制 Markdown 源码”和“复制纯文本”工具按钮;前者保留原始 Markdown 结构,后者去除常见 Markdown 格式符号,方便分享、归档和跨报告比对。复制按钮文案会跟随
REPORT_LANGUAGE保持中英文一致,避免英文报告页出现中文固定文案。 - 🧩 个股分析页补齐关联板块展示(#669)— A 股分析写路径现在会把
belong_boards一次性写入fundamental_context/fundamental_snapshot,结构化报告详情同步新增belong_boards与sector_rankings字段,Web 个股分析页首屏可直接展示所属板块及其是否命中当日板块涨跌榜;无数据时保持 fail-open 隐藏,不影响现有分析主流程。
改进
- 🖥️ Dashboard 面板统一化(PR7-2) — 新增
DashboardPanelHeader和DashboardStateBlock作为历史、报告、资讯、任务和透明度等面板的通用组件;统一了各面板标题层级、加载/空态/错误态和 CSS 变量 token。 - 🖥️ HomePage 状态边界收口(PR7-2) — 引入
useHomeDashboardStatehook,集中stockPoolStore状态选取逻辑,移除HomePage中重复的本地状态派生和回调定义。 - 🧭 Agent skill 统一到单一配置语义 — Multi-Agent runtime、API、Web chat 和配置元数据统一围绕
skill概念收敛;/api/v1/agent/skills成为主发现入口,AGENT_SKILL_*成为主配置面,内置 skill 元数据也开始声明默认启用、排序优先级、market regime tag 等信息,减少默认策略散落在代码里的隐式耦合。 - 🔎 自动补全索引数据更新 — 重新生成
stocks.index.json,涵盖 A股、港股、美股三个市场,提升自动补全覆盖率。 - 🧾 Dashboard 字体与完整报告表格密度微调 — 收敛首页侧栏、空状态、历史操作区的字体层级,并将完整 Markdown 报告表格
th/td的内边距调整到更紧凑的 4-6px 区间,让信息密度与现有 Dashboard 视觉节奏更一致。
修复
- ⏰ 定时模式不再锁定启动时 CLI 股票快照 —
python main.py --schedule --stocks ...现在不会让后续计划执行沿用启动时的旧股票列表;定时任务每次触发前都会重新读取最新保存的STOCK_LIST,确保 WebUI 或.env更新后的自选股配置能参与后续推送。 - 🌍 LLM Prompt 按股票市场动态注入上下文 — 分析链路不再把市场规则写死成 A 股;系统 Prompt 会根据股票代码识别 A 股、港股或美股,并注入对应的角色描述与交易规则提示,减少跨市场分析出现口径错位或结论失真的问题。
- 🔎 美股自动补全复用 ticker 去重 —
generate_index_from_csv.py在导入 Tushareus_basicCSV 时会先按ts_code折叠复用的美股 ticker,优先保留更可能仍在使用的记录,避免stocks.index.json出现重复canonicalCode后让 Web 自动补全展示历史名称或提交歧义代码。 - 🧾 Web 报告详情复制交互稳定性修复(#749)—
ReportDetails中“原始分析结果 / 分析快照”的复制按钮补齐可点击层级,避免被下方 JSON 内容覆盖;两个面板的复制提示也改为各自独立,不再出现复制一个后两个按钮同时显示“已复制”的误导反馈。 - 📊 Agent skill 回测与兼容接口语义收敛 —
get_skill_backtest_summary现在要求显式传入skill_id,缺失时返回明确校验提示;仓库尚未持久化真实 skill 级汇总时会返回明确的 unsupported/info 响应,并保留normalized与*_pct兼容字段,避免沿用 overall 指标误导 Agent 或用户。 - 🔧 Skill 默认选择与兼容层行为加固 —
allowed-tools会继续仅作为SKILL.mdbundle 元数据保留,不再泄露到运行时工具选择;/api/v1/agent/strategies恢复旧 payload 形状;显式传入skills: []时会清空陈旧上下文;当用户明确选择策略 skill 时不再偷偷叠加默认 bull-trend,而在AGENT_SKILLS为空时则统一只回落到单一主默认 skill。
测试
- 🧪 Dashboard 组件测试覆盖率扩展(PR7-2) — 新增
ReportNews和TaskPanel测试;对HistoryList、ReportDetails、HomePage、useDashboardLifecycle和stockPoolStore增强了断言覆盖,包括删除回退、移动端抽屉和任务生命周期等场景。 - 🧪 多市场索引生成测试补齐 — 新增
tests/test_generate_index_from_csv.py,覆盖 Tushare/AkShare 双数据源解析、多市场判断、美股 DUMMY 过滤与重复 ticker 去重等核心路径。 - 🧪 关联板块写入与 API 契约回归 — 新增
tests/test_pipeline_related_boards.py,并补充分析历史与分析接口契约测试,确保belong_boards/sector_rankings只做增量扩展且保持 fail-open。 - 🧪 定时模式股票列表语义回归测试 — 新增
tests/test_main_schedule_mode.py,覆盖定时模式忽略启动时--stocks快照、单次运行仍保留 CLI 股票覆盖的边界场景。
文档
- 📘 新增 Tushare 股票列表工具文档 — 新增
docs/TUSHARE_STOCK_LIST_GUIDE.md,说明股票列表抓取工具的使用方法、数据格式和常见问题。 - 🌍 补齐定时模式与关联板块的双语说明 —
docs/full-guide.md/docs/full-guide_EN.md现在明确说明 scheduled mode 会在每次执行前重新读取STOCK_LIST,并同步补充个股关联板块展示能力说明,减少配置预期偏差。 - 🧭 调整 Agent 术语兼容文案 — README、双语文档、设置页与问股界面继续以“策略”作为用户入口主称呼,同时补充
skill作为内部统一命名,降低迁移期理解成本。
[3.9.0] - 2026-03-20
发布亮点
- 🤖 模型链路与报告语言更灵活 — Agent 现在可以通过
AGENT_LITELLM_MODEL独立选择模型链路,普通分析与 Agent 报告也可通过REPORT_LANGUAGE=zh|en输出统一语言,减少“英文内容 + 中文壳子”这类混排问题,并允许团队分别权衡主分析与 Agent 的成本、速度和能力。 - 🔎 首页分析体验完成一轮闭环优化 — 首页新增 A 股自动补全,支持代码、中文名、拼音和别名检索;同时 Dashboard 状态收口到统一 store,历史、报告、新闻与 Markdown 抽屉的交互更稳定,“Ask AI” 追问也会优先携带当前报告上下文。
- 💬 通知与检索能力继续外扩 — 新增 Slack 一等通知渠道;SearXNG 在未配置自建实例时可以自动发现公共实例并按受控轮询降级;Tavily 时效新闻链路修复后,严格时效过滤不再错误丢光有效结果。
- 💼 持仓与市场复盘链路更稳 — A 股 market review 可选接入 TickFlow 强化指数与涨跌统计;持仓账本写入改为串行化以缩小并发超卖窗口;汇率刷新入口和禁用态提示也更加清晰,减少用户误判。
新功能
- 🔎 Web 股票自动补全 MVP — 首页分析输入框新增本地索引驱动的自动补全,支持股票代码、中文名、拼音和别名匹配;选中候选后会提交 canonical code,并透传
stock_name、original_query、selection_source到分析请求、任务状态和 SSE 事件;索引加载失败时自动退回旧输入模式,不阻断原有提交流程。同步补充了静态索引加载器、索引生成脚本和前后端契约测试。分阶段进行开发,第一阶段仅支持 A 股。 - 💬 Slack 一等通知渠道 — 新增 Slack 原生通知支持,同时支持 Bot Token 和 Incoming Webhook 两种接入方式;同时配置时优先使用 Bot API,确保文本与图片发送到同一频道;Bot Token 模式支持图片上传(raw body POST,不使用 multipart);新增
SLACK_BOT_TOKEN、SLACK_CHANNEL_ID、SLACK_WEBHOOK_URL配置项,GitHub Actions 工作流同步补齐对应 Secrets 传递。 - 🌍 报告输出语言可配置(Issue #758)— 新增
REPORT_LANGUAGE=zh|en,默认zh;语言设置会同步注入普通分析与 Agent Prompt,并覆盖 Markdown/Jinja 模板、通知 fallback、历史/APIreport_language元数据及 Web 报告页固定文案,避免“英文内容 + 中文壳子”的混合输出。 - 🚀 Agent 与普通分析模型解耦(Issue #692)— 新增
AGENT_LITELLM_MODEL(留空继承LITELLM_MODEL,无前缀按openai/<model>归一);Agent 执行链路与/api/v1/agent/models的is_primary/is_fallback标记改为基于 Agent 实际模型链路;系统配置与启动期校验补齐AGENT_LITELLM_MODEL的unknown_model/missing_runtime_source检查;Web 设置页新增 Agent 主模型选择并与渠道模式运行时配置同步。 - 🔎 SearXNG 公共实例自动发现与受控轮询(#752)— 新增
SEARXNG_PUBLIC_INSTANCES_ENABLED,在未配置SEARXNG_BASE_URLS时默认从searx.space拉取公共实例列表,并按受控轮询顺序选择实例;同次请求内遇到超时、连接错误、HTTP 非 200 或无效 JSON 会自动切换到下一个实例。已配置自建实例的用户保持原有优先级与语义不变;daily_analysisGitHub Actions 工作流也已支持显式透传该开关并在启动日志中展示当前状态。 - 📈 TickFlow market review enhancement (#632) — 新增可选
TICKFLOW_API_KEY;配置后,A 股大盘复盘的主要指数行情优先尝试 TickFlow;若当前 TickFlow 套餐支持标的池查询,市场涨跌统计也会优先尝试 TickFlow。失败或权限不足时立即回退到现有AkShare / Tushare / efinance链路;板块涨跌榜回退顺序保持不变。接入层同时适配了真实 SDK 契约:主指数查询按单次请求上限分批拉取,并将 TickFlow 返回的比例型change_pct/amplitude统一转换为项目内部的百分比口径。
改进
- Dashboard state slice and workspace closure — moved Home / Dashboard state into
stockPoolStore, consolidated history selection, report loading, task syncing, polling refresh, and markdown drawer handling under a single state slice. - Dashboard panel standardization — kept the current dashboard layout contract stable while unifying history, report, news, and markdown presentation with shared tokens, standardized states, and bounded in-panel scrolling for the history list.
- Dashboard-to-chat follow-up bridge — routed “Ask AI” follow-ups through report-context hydration instead of direct cross-page state coupling, while keeping chat sends usable when enriched history context is still loading.
- 💼 持仓账本并发写入串行化(#742)— 持仓源事件写入/删除现在会在 SQLite 下先获取串行化写锁,减少并发卖出把超售流水写入账本的窗口;直接持仓写接口在锁竞争时返回
409 portfolio_busy,CSV 导入保持逐条提交并把 busy 计入failed_count。 - 💱 持仓页汇率手动刷新入口补齐(#748)— Web
/portfolio页面现在会在“汇率状态”卡片中展示“刷新汇率”按钮,直接调用现有POST /api/v1/portfolio/fx/refresh接口;刷新后会仅重载快照与风险数据,并以内联摘要反馈“已更新 / 仍 stale / 刷新失败”的结果,减少用户对fxStale长时间停留的误解。
修复
- 🔎 Web 自动补全 Enter 提交语义修正 — 股票自动补全在搜索命中候选时不再默认高亮第一项;候选列表展开但用户尚未用方向键或鼠标明确选中时,按 Enter 会继续提交原始输入,避免手动输入被第一条候选静默覆盖。
- 🌍 补齐
REPORT_LANGUAGE启动解析与历史展示本地化边界 —Config在启动时继续遵循“真实环境变量优先、.env兜底”的既有语义,并在两者冲突时输出显式告警,减少REPORT_LANGUAGE来源不清带来的误判;同时/api/v1/history/{id}英文详情响应会同步本地化sentiment_label,历史 Markdown 也会正确识别英文bias_status的风险等级 emoji,避免出现乐观或🚨Safe这类中英混排/误报展示。 - 📰 Tavily 时效新闻检索发布时间映射修复(#782)— Tavily 在股票新闻和严格时效的情报维度中现在会显式使用
topic="news",并兼容published_date/publishedDate两种发布时间字段;修复了 Tavily 明明返回结果却在后续硬过滤阶段被全部记为drop_unknown丢弃的问题,同时将机构分析、业绩预期、行业分析等分析型维度恢复为宽源搜索,不再被统一压缩成新闻模式。 - 💱 持仓页汇率刷新禁用语义修正(#772)— 当
PORTFOLIO_FX_UPDATE_ENABLED=false时,POST /api/v1/portfolio/fx/refresh现在会返回显式refresh_enabled=false与disabled_reason,Web/portfolio页面会明确提示“汇率在线刷新已被禁用”,不再误报“当前范围无可刷新的汇率对”。 - 🤖 Agent timeout and config hardening —
AGENT_ORCHESTRATOR_TIMEOUT_Snow also protects the legacy single-agent ReAct loop, parallel tool batches stop waiting once the remaining budget is exhausted, and invalid numeric.envvalues fall back to safe defaults with warnings instead of crashing startup. - 🌐 CORS wildcard + credentials compatibility —
CORS_ALLOW_ALL=trueno longer combinesallow_origins=["*"]with credentialed requests, avoiding browser-side cross-origin failures in demo/development setups. - 🧭 Unavailable Agent settings hidden from Web UI — Deep Research / Event Monitor controls are now treated as compatibility-only metadata in the current branch and are removed from the Settings page to avoid exposing non-functional toggles.
文档
- 新增 Ollama 本地模型配置说明,同步更新
README.md与docs/README_EN.md(Fixes #690) - 完善 Ollama 配置说明:
docs/full-guide.md/docs/full-guide_EN.md环境变量表与 Note 补充OLLAMA_API_BASE,避免英文用户误以为 Ollama 不能作为独立配置入口;合并重复的OLLAMA_API_BASE条目为单一条目 - 明确文档同步治理边界:补充
README.md、专题文档、双语文档与交付说明之间的默认同步规则,减少后续文档漂移
[3.8.0] - 2026-03-17
发布亮点
- 🎨 Web 界面完成一轮骨架升级 — 新的 App Shell、侧边导航、主题能力、登录与系统设置流程已经串成统一体验,桌面端加载背景也完成对齐。
- 📈 分析上下文继续补强 — 美股新增社交舆情情报,A 股补齐财报与分红结构化上下文,Tushare 新接入筹码分布和行业板块涨跌数据。
- 🔒 运行稳定性与配置兼容性提升 — 退出登录会立即让旧会话失效,定时启动兼容旧配置,运行中的
MAX_WORKERS调整和新闻时效窗口反馈更清晰。 - 💼 持仓纠错链路更完整 — 超售会被前置拦截,错误交易/资金流水/公司行为可以直接删除回滚,便于修复脏数据。
新功能
- 📱 美股社交舆情情报 — 新增 Reddit / X / Polymarket 社交媒体情绪数据源,为美股分析提供实时社交热度、情绪评分和提及量等补充指标;完全可选,仅在配置
SOCIAL_SENTIMENT_API_KEY后对美股生效。 - 📊 A 股财报与分红结构化增强(Issue #710)—
fundamental_context.earnings.data新增financial_report与dividend字段;分红统一按“仅现金分红、税前口径”计算,并补充ttm_cash_dividend_per_share与ttm_dividend_yield_pct;分析/历史 API 的details追加financial_report、dividend_metrics可选字段,保持 fail-open 与向后兼容。 - 🔍 接入 Tushare 筹码与行业板块接口 — 新增筹码分布、行业板块涨跌数据获取能力,并统一纳入配置化数据源优先级;默认按上海时间区分盘中/盘后交易日取数,优先使用 Tushare 同花顺接口,必要时降级到东财。
- 🧱 Web UI 基础骨架升级 — 重建共享设计令牌与通用组件,新增 App Shell、Theme Provider、侧边导航,并同步调整 Electron 加载背景,为 Web / Desktop 的统一体验打底。
- 🔐 登录与系统设置流程重做 — 重构 Login、Settings 与 Auth 管理流程,补上显式的认证 setup-state 处理,并让 Web 端与运行时认证配置 API 行为对齐。
- 🧪 前端回归与冒烟覆盖补强 — 新增并扩展登录、首页、聊天、移动端 Shell、设置页、回测入口等关键路径的组件测试与 Playwright smoke coverage。
变更
- 🧭 页面接入新 Shell 布局契约 — Home、Chat、Settings、Backtest 已统一接入新的页面容器、抽屉和滚动约定,降低 UI 迁移期间的页面行为不一致。
- 💾 设置页状态同步更稳 — 优化草稿保留、直接保存同步与冲突处理,减少模块级保存后前后端配置状态不一致的问题。
- 🎭 登录页视觉基线回归 — 登录页恢复到既有
006分支的视觉基线,同时保留新的认证状态逻辑和统一表单交互模型。 - 🏛️ AI 协作治理资产加固 — 收敛并加强
AGENTS.md、CLAUDE.md、Copilot 指令和校验脚本的一致性约束,降低治理资产长期漂移风险。
Added
- Web UI foundation refresh — rebuilt shared design tokens and common primitives, introduced the app shell, theme provider, sidebar navigation, and Electron loading background alignment for the upgraded desktop/web experience
- Settings and auth workflow overhaul — rebuilt the Login, Settings, and Auth management flows, added explicit auth setup-state handling, and aligned the Web UI with the runtime auth configuration APIs
- UI regression coverage and smoke checks — expanded targeted frontend tests and added Playwright smoke coverage for login, home, chat, mobile shell, settings, and backtest entry flows
Changed
- Shell-driven page integration — aligned Home, Chat, Settings, and Backtest with the new shell layout contract so routing, drawer behavior, and page-level scrolling are consistent during the UI migration
- Settings state consistency — refined draft preservation, direct-save synchronization, and conflict handling so module-level saves no longer leave the page out of sync with backend config state
- Login visual baseline — restored the login page visual treatment to the established
006branch baseline while keeping the newer auth-state logic and unified form interaction model
修复
- ⏰ 定时启动立即执行兼容旧配置(Issue #726)—
SCHEDULE_RUN_IMMEDIATELY未设置时会回退读取RUN_IMMEDIATELY,修复升级后旧.env在定时模式下的兼容性问题;同时澄清.env.example/ README 中两个配置项的适用范围,并注明 Outlook / Exchange 强制 OAuth2 暂不支持。 - 🧵 运行期
MAX_WORKERS配置生效与可解释性增强(#633)— 修复异步分析队列未按MAX_WORKERS同步的问题;新增任务队列并发 in-place 同步机制(空闲即时生效、繁忙延后),并在设置保存反馈与运行日志中明确输出profile/max/effective,减少“参数未生效”误解。 - 🔐 退出登录立即失效现有会话 —
POST /api/v1/auth/logout现在会轮换 session secret,避免旧 cookie 在退出后仍可继续访问受保护接口;同浏览器标签页和并发页面会被同步登出。认证开启时,该接口也不再属于匿名白名单,未登录请求会返回401,避免匿名请求触发全局 session 失效。 - 🧮 Tushare 板块/筹码调用限流与跨日缓存修复 — 新增的
trade_cal、行业板块排行、筹码分布链路统一接入_check_rate_limit();交易日历缓存改为按自然日刷新,避免服务跨天运行后继续沿用旧交易日判断取数日期。 - 💼 持仓超售拦截与错误流水恢复(#718)—
POST /api/v1/portfolio/trades现在会在写入前校验可卖数量,超售返回409 portfolio_oversell;持仓页新增交易 / 资金流水 / 公司行为删除能力,删除后会同步失效仓位缓存与未来快照,便于从错误流水中直接恢复。 - 📧 邮件中文发件人名编码(#708)— 邮件通知现在会对包含中文的
EMAIL_SENDER_NAME自动做 RFC 2047 编码,并在异常路径补充 SMTP 连接清理,修复 GitHub Actions / QQ SMTP 下'ascii' codec can't encode characters导致的发送失败。 - 🐛 港股 Agent 实时行情去重与快速路由 — 统一
HK01810/1810.HK/01810等港股代码归一规则;港股实时行情改为直接走单次akshare_hk路径,避免按 A 股 source priority 重复触发同一失败接口;Agent 运行期对显式retriable=false的工具失败增加短路缓存,减少同轮分析中的重复失败调用。 - 📰 新闻时效硬过滤与策略分窗(#697)— 新增
NEWS_STRATEGY_PROFILE(ultra_short/short/medium/long)并与NEWS_MAX_AGE_DAYS统一计算有效窗口;搜索结果在返回后执行发布时间硬过滤(时间未知剔除、超窗剔除、未来仅容忍 1 天),并在历史 fallback 链路追加相同约束,避免旧闻再次进入“最新动态/风险警报”。
文档
- ☁️ 新增云服务器 Web 界面部署与访问教程(Fixes #686)— 补充从云端部署到外部访问的落地说明,降低远程自托管门槛。
- 🌍 补齐英文文档索引与协作文档 — 新增英文文档索引、贡献指南、Bot 命令文档,并补充中英双语 issue / PR 模板,方便中英文协作与外部贡献者理解项目入口。
- 🏷️ 本地化 README 补充 Trendshift badge — 在多语言 README 中同步补上新版能力入口标识,减少中英文说明面不一致。
[3.7.0] - 2026-03-15
新功能
💼 持仓管理 P0 全功能上线(#677,对应 Issue #627)
- 核心账本与快照闭环:新增账户、交易、现金流水、企业行为、持仓缓存、每日快照等核心数据模型与 API 端点;支持 FIFO / AVG 双成本法回放;同日事件顺序固定为
现金 → 企业行为 → 交易;持仓快照写入采用原子事务。 - 券商 CSV 导入:支持华泰 / 中信 / 招商首批适配,含列名别名兼容;两阶段接口(解析预览 + 确认提交);
trade_uid优先、key-field hash 兜底的幂等去重;前导零股票代码完整保留。 - 组合风险报告:集中度风险(Top Positions + A 股板块口径)、历史回撤监控(支持回填缺失快照)、止损接近预警;多币种统一换算 CNY 口径;汲取失败时回退最近成功汇率并标记 stale。
- Web 持仓页(
/portfolio):组合总览、持仓明细、集中度饼图、风险摘要、全组合 / 单账户切换;手工录入交易 / 资金流水 / 企业行为;内嵌账户创建入口;CSV 解析 + 提交闭环与券商选择器。 - Agent 持仓工具:新增
get_portfolio_snapshot数据工具,默认紧凑摘要,可选持仓明细与风险数据。 - 事件查询 API:新增
GET /portfolio/trades、GET /portfolio/cash-ledger、GET /portfolio/corporate-actions,支持日期过滤与分页。 - 可扩展 Parser Registry:应用级共享注册,支持运行时注册新券商;新增
GET /portfolio/imports/csv/brokers发现接口。
- 核心账本与快照闭环:新增账户、交易、现金流水、企业行为、持仓缓存、每日快照等核心数据模型与 API 端点;支持 FIFO / AVG 双成本法回放;同日事件顺序固定为
🎨 前端设计系统与原子组件库(#662)
- 引入渐进式双主题架构(HSL 变量化设计令牌),清理历史 Legacy CSS;重构 Button / Card / Badge / Collapsible / Input / Select 等 20+ 核心组件;新增
clsx+tailwind-merge类名合并工具;提升历史记录、LLM 配置等页面可读性。
- 引入渐进式双主题架构(HSL 变量化设计令牌),清理历史 Legacy CSS;重构 Button / Card / Badge / Collapsible / Input / Select 等 20+ 核心组件;新增
⚡ 分析 API 异步契约与启动优化(#656)
- 规范
POST /api/v1/analysis/analyze异步请求的返回契约;优化服务启动辅助逻辑;修复前端报告类型联合定义与后端响应对齐问题。
- 规范
修复
- 🔔 Discord 环境变量向后兼容(#659):运行时新增
DISCORD_CHANNEL_ID→DISCORD_MAIN_CHANNEL_ID的 fallback 读取;历史配置用户无需修改即可恢复 Discord Bot 通知;全部相关文档与.env.example对齐。 - 🔧 GitHub Actions Node 24 升级(#665):将所有 GitHub 官方 actions 升级至 Node 24 兼容版本,消除 CI 日志中的 Node.js 20 deprecation warning(影响 2026-06-02 强制升级窗口)。
- 📅 持仓页默认日期本地化:手工录入表单默认日期改用本地时间(
getFullYear/Month/Date),修复 UTC-N 时区用户在当天晚间出现日期偏移的问题。 - 🔁 CSV 导入去重逻辑加固:dedup hash 纳入行序号作为区分因子,确保同字段合法分笔成交不被误折叠;同时在
trade_uid存在时也持久化 hash,防止混合来源重复写入。
变更
POST /api/v1/portfolio/trades在同账户内trade_uid冲突时返回409。- 持仓风险响应新增
sector_concentration字段(增量扩展),原有concentration字段保持不变。 - 分析 API
analyze接口异步行为契约文档化;前端报告类型联合更新。
测试
- 新增持仓核心服务测试(FIFO / AVG 部分卖出、同日事件顺序、重复
trade_uid返回 409、快照 API 契约)。 - 新增 CSV 导入幂等性、合法分笔成交不误去重、去重边界、风险阈值边界、汇率降级行为测试。
- 新增 Agent
get_portfolio_snapshot工具调用测试。 - 新增分析 API 异步契约回归测试。
[3.6.0] - 2026-03-14
Added
📊 Web UI Design System — implemented dual-theme architecture and terminal-inspired atomic UI components
📊 UI Components Refactoring — integrated
clsxandtailwind-mergefor robust class composition across Web UI🗑️ History batch deletion — Web UI now supports multi-selection and batch deletion of analysis history; added
POST /api/v1/history/batch-deleteendpoint andConfirmDialogcomponent.🔐 Auth settings API — new
POST /api/v1/auth/settingsendpoint to enable or disable Web authentication at runtime and set the initial admin password when neededopenclaw Skill 集成指南 — 新增 docs/openclaw-skill-integration.md,说明如何通过 openclaw Skill 调用 DSA API
⚙️ LLM channel protocol/test UX —
.envand Web settings now share the same channel shape (LLM_CHANNELS+LLM_<NAME>_PROTOCOL/BASE_URL/API_KEY/MODELS/ENABLED); settings page adds per-channel connection testing, primary/fallback/vision model selection, and protocol-aware model prefixing🤖 Agent architecture Phase 0+1 — shared protocols (
AgentContext,AgentOpinion,StageResult), extractedrun_agent_loop()runner,AGENT_ARCHswitch (single/multi), config registry entries🔍 Bot NL routing — two-layer natural-language routing: cheap regex pre-filter (stock codes + finance keywords) → lightweight LLM intent parsing; controlled by
AGENT_NL_ROUTING=true; supports multi-stock and strategy extraction💬
/askmulti-stock analysis — comma orvsseparated codes (max 5), parallel thread execution with 150s timeout (preserves partial results), Markdown comparison summary table at top📋
/historycommand — per-user session isolation via{platform}_{user_id}:{scope}format (colon delimiter prevents prefix collision); lists both/chatand/asksessions; view detail or clear📊
/strategiescommand — lists available strategy YAML files grouped by category (趋势/形态/反转/框架) with ✅/⬜ activation status🔧 Backtest summary tools —
get_strategy_backtest_summaryandget_stock_backtest_summaryregistered as read-only Agent tools⚙️ Agent auto-detection —
is_agent_available()auto-detects fromLITELLM_MODEL; explicitAGENT_MODE=true/falsetakes full precedence🏗️ Multi-Agent orchestrator (Phase 2) —
AgentOrchestratorwith 4 modes (quick/standard/full/strategy); drop-in replacement forAgentExecutorviaAGENT_ARCH=multi;BaseAgentABC with tool subset filtering, cached data injection, and structuredAgentOpinionoutput🧩 Specialised agents (Phase 2-4) —
TechnicalAgent(8 tools, trend/MA/MACD/volume/pattern analysis),IntelAgent(news & sentiment, risk flag propagation),DecisionAgent(synthesis into Decision Dashboard JSON),RiskAgent(7 risk categories, two-level severity with soft/hard override)📈 Strategy system (Phase 3) —
StrategyAgent(per-strategy evaluation from YAML skills),StrategyRouter(rule-based regime detection → strategy selection),StrategyAggregator(weighted consensus with backtest performance factor)🔬 Deep Research agent (Phase 5) —
ResearchAgentwith 3-phase approach (decompose → research sub-questions → synthesise report); token budget tracking; new/researchbot command with aliases (/深研,/deepsearch)🧠 Memory & calibration (Phase 6) —
AgentMemorywith prediction accuracy tracking, confidence calibration (activates after minimum sample threshold), strategy auto-weighting based on historical win rate📊 Portfolio Agent (Phase 7) —
PortfolioAgentfor multi-stock portfolio analysis (position sizing, sector concentration, correlation risk, cross-market linkage, rebalance suggestions)🔔 Event-driven alerts (Phase 7) —
EventMonitorwithPriceAlert,VolumeAlert,SentimentAlertrules; async checking, callback notifications, serializable persistence⚙️ New config entries —
AGENT_ORCHESTRATOR_MODE,AGENT_RISK_OVERRIDE,AGENT_DEEP_RESEARCH_BUDGET,AGENT_MEMORY_ENABLED,AGENT_STRATEGY_AUTOWEIGHT,AGENT_STRATEGY_ROUTING— all registered inconfig.py+config_registry.py(WebUI-configurable)
Changed
- 🔐 Auth password state semantics — stored password existence is now tracked independently from auth enablement; when auth is disabled,
/api/v1/auth/statusreturnspasswordSet=falsewhile preserving the saved password for future re-enable - 🔐 Auth settings re-enable hardening — re-enabling auth with a stored password now requires
currentPassword, and failed session creation rolls back the auth toggle to avoid lockout - ♻️ AgentExecutor refactored —
_run_loopdelegates to sharedrunner.run_agent_loop(); removed duplicated serialization/parsing/thinking-label code - ♻️ Unified agent switch — Bot, API, and Pipeline all use
config.is_agent_available()instead of divergentconfig.agent_modechecks - 📖 README.md — expanded Bot commands section (ask/chat/strategies/history), added NL routing note, updated agent mode description
- 📖 .env.example — added
AGENT_ARCHandAGENT_NL_ROUTINGconfiguration documentation - 🔌 Analysis API async contract —
POST /api/v1/analysis/analyzenow documents distinct async202payloads for single-stock vs batch requests, andreport_type=fullis treated consistently with the existing full-report behavior
Fixed
- 🐛 Analysis API blank-code guardrails —
POST /api/v1/analysis/analyzenow drops whitespace-only entries before batch enqueue and returns400when no valid stock code remains - 🐛 Bare
/apiSPA fallback — unknown API paths now return JSON404consistently for both/api/...and the exact/apipath - 🎮 Discord channel env compatibility — runtime now accepts legacy
DISCORD_CHANNEL_IDas a fallback forDISCORD_MAIN_CHANNEL_ID, and the docs/examples now use the same variable name as the actual workflow/config implementation - 🐛 Session secret rotation on Windows — use atomic replace so auth toggles invalidate existing sessions even when
.session_secretalready exists - 🐛 Auth toggle atomicity — persist
ADMIN_AUTH_ENABLEDbefore rotating session secret; on rotation failure, roll back to the previous auth state - 🔧 LLM runtime selection guardrails — YAML 模式下渠道编辑器不再覆盖
LITELLM_MODEL/ fallback / Vision;系统配置校验补上全部渠道禁用后的运行时来源检查,并修复vertexai/...这类协议别名模型被重复加前缀的问题 - 🐛 Multi-stock
/askfollow-up regressions — portfolio overlay now shares the same timeout budget as the per-stock phase and is skipped on timeout instead of blocking the bot reply;/historynow stores the readable per-stock summary instead of raw dashboard JSON; condensed multi-stock output now renders numericsniper_pointsvalues - 🐛 Decision dashboard enum compatibility — multi-agent
DecisionAgentnow keepsdecision_typewithin the legacybuy|hold|sellcontract and normalizes straystrong_*outputs before risk override, pipeline conversion, and downstream统计/通知汇总 - 🛟 Multi-Agent partial-result fallback —
IntelAgentnow caches parsed intel for downstream reuse, shared JSON parsing tolerates lightly malformed model output, and the orchestrator preserves/synthesizes a minimal dashboard on timeout or mid-pipeline parse failure instead of always collapsing to50/观望/未知 - 🐛 Shared LiteLLM routing restored — bot NL intent parsing and
ResearchAgentplanning/synthesis now reuse the same LiteLLM adapter / Router / fallback /api_baseinjection path as the main Agent flow, soLLM_CHANNELS/LITELLM_CONFIG/ OpenAI-compatible deployments behave consistently - 🐛 Bot chat session backward compatibility —
/chatnow keeps using the legacy{platform}_{user_id}session id when old history already exists, and/historycan still list / view / clear those pre-migration sessions alongside the new{platform}_{user_id}:chatformat - 🐛 EventMonitor unsupported rule rejection — config validation/runtime loading now reject or skip alert types the monitor cannot actually evaluate yet, so schedule mode no longer silently accepts permanent no-op rules
- 🐛 P0 基本面聚合稳定性修复 (#614) — 修复
get_stock_info板块语义回归(新增belong_boards并保留boards兼容别名)、引入基本面上下文精简返回以控制 token、为基本面缓存增加最大条目淘汰,并补齐 ETF 总体状态聚合与 NaN 板块字段过滤,保证 fail-open 与最小入侵。 - 🔧 GitHub Actions 搜索引擎环境变量补充 — 工作流新增
MINIMAX_API_KEYS、BRAVE_API_KEYS、SEARXNG_BASE_URLS环境变量映射,使 GitHub Actions 用户可配置 MiniMax、Brave、SearXNG 搜索服务(此前 v3.5.0 已添加 provider 实现但缺少工作流配置) - 🤖 Multi-Agent runtime consistency —
AGENT_MAX_STEPSnow propagates to each orchestrated sub-agent; added cooperativeAGENT_ORCHESTRATOR_TIMEOUT_Sbudget to stop overlong pipelines before they cascade further - 🔌 Multi-Agent feature wiring —
AGENT_RISK_OVERRIDEnow actively downgrades final dashboards on hard risk findings;AGENT_MEMORY_ENABLEDnow injects recent analysis memory + confidence calibration into specialised agents; multi-stock/asknow runsPortfolioAgentto add portfolio-level allocation and concentration guidance - 🔔 EventMonitor runtime wiring — schedule mode can now load alert rules from
AGENT_EVENT_ALERT_RULES_JSON, poll them atAGENT_EVENT_MONITOR_INTERVAL_MINUTES, and send triggered alerts through the existing notification service - 🛠️ Follow-up stability fixes — multi-stock
/asknow falls back to usable text output when dashboard JSON parsing fails; EventMonitor skips semantically invalid rules instead of aborting schedule startup; background alert polling now runs independently of the main scheduled analysis loop - 🧪 Multi-Agent regression coverage — added orchestrator execution tests for
run(),chat(), critical-stage failure, graceful degradation, and timeout handling - 🧹 PortfolioAgent cleanup —
post_process()now reuses shared JSON parsing and removed stale unused imports - 🚦 Bot async dispatch —
CommandDispatchernow exposesdispatch_async(); NL intent parsing and default command execution are offloaded from the event loop, DingTalk stream awaits async handlers directly, and Feishu stream processing is moved off the SDK callback thread - 🌐 Async webhook handler — new
handle_webhook_async()function inbot/handler.pyfor use from async contexts (e.g. FastAPI); callsdispatch_async()directly without thread bridging - 🧵 Feishu stream ThreadPoolExecutor — replaced unbounded per-message
Threadspawning with a cappedThreadPoolExecutor(max_workers=8)to prevent thread explosion under message bursts - 🔒 EventMonitor safety —
_check_volume()now safely handlesget_daily_datareturningNone(no tuple-unpacking crash);on_triggercallbacks support both sync and async callables viaasyncio.to_thread/await - 🧹 ResearchAgent dedup —
_filtered_registry()now delegates toBaseAgent._filtered_registry()instead of duplicating the filtering logic - 🧹 Bot trailing whitespace cleanup — removed W291/W293 whitespace issues across
bot/handler.py,bot/dispatcher.py,bot/commands/base.py,bot/platforms/feishu_stream.py,bot/platforms/dingtalk_stream.py - 🐛 Dispatcher
_parse_intent_via_llmsafety — replaced fragile'raw' in dir()with'raw' in locals()for undefined-variable guard inJSONDecodeErrorhandler - 🐛 筹码结构 LLM 未填写时兜底补全 (#589) — DeepSeek 等模型未正确填写
chip_structure时,自动用数据源已获取的筹码数据补全,保证各模型展示一致;普通分析与 Agent 模式均生效 - 🐛 历史报告狙击点位显示原始文本 (#452) — 历史详情页现优先展示
raw_result.dashboard.battle_plan.sniper_points中的原始字符串,避免analysis_history数值列把区间、说明文字或复杂点位压缩成单个数字;保留原有数值列作为回退 - 🐛 Session prefix collision — user ID
123could see sessions of user1234viastartswith; fixed with colon delimiter in session_id format - 🐛 NL pre-filter false positives —
re.IGNORECASEcaused[A-Z]{2,5}to match common English words like "hello"; removed global flag, use inline(?i:...)only for English finance keywords - 🐛 Dotted ticker in strategy args —
_get_strategy_args()didn't recognizeBRK.Bas a stock code, leaving it in strategy text; now acceptsTICKER.CLASSformat - ⏱️ efinance 长调用挂起修复 (#660) — 为所有 efinance API 调用引入
_ef_call_with_timeout()包装(默认 30 秒,可通过EFINANCE_CALL_TIMEOUT配置);使用executor.shutdown(wait=False)确保超时后不再阻塞主线程,彻底消除 81 分钟挂起问题 - 🛡️ 类型安全内容完整性检查 (#660) —
check_content_integrity()现在将非字符串类型的operation_advice/analysis_summary视为缺失字段,避免下游get_emoji()因dict.strip()崩溃 - 📄 报告保存与通知解耦 (#660) —
_save_local_report()不再依赖send_notification标志触发,--no-notify模式下本地报告照常保存 - 🔄 operation_advice 字典归一化 (#660) — Pipeline 和 BacktestEngine 现在将 LLM 返回的
dict格式operation_advice通过decision_type(不区分大小写)映射为标准字符串,防止因模型输出格式变化导致崩溃 - 🛡️ runner.py usage None 防护 (#660) —
response.usage为None时不再抛出AttributeError,回退为 0 token 计数 - 📋 orchestrator 静默失败改为日志警告 (#660) —
IntelAgent/RiskAgent阶段失败现在记录WARNING而非静默跳过,便于诊断
Notes
- ⚠️ Multi-worker auth toggles — runtime auth updates are process-local; multi-worker deployments must restart/roll workers to keep auth state consistent
[3.5.0] - 2026-03-12
Added
- 📊 Web UI full report drawer (Fixes #214) — history page adds "Full Report" button to display the complete Markdown analysis report in a side drawer; new
GET /api/v1/history/{record_id}/markdownendpoint - 📊 LLM cost tracking — all LLM calls (analysis, agent, market review) recorded in
llm_usagetable; newGET /api/v1/usage/summary?period=today|month|allendpoint returns aggregated token usage by call type and model - 🔍 SearXNG search provider (Fixes #550) — quota-free self-hosted search fallback; priority: Bocha > Tavily > Brave > SerpAPI > MiniMax > SearXNG
- 🔍 MiniMax web search provider —
MiniMaxSearchProviderwith circuit breaker (3 failures → 300s cooldown) and dual time-filtering; configured viaMINIMAX_API_KEYS - 🤖 Agent models discovery API —
GET /api/v1/agent/modelsreturns available model deployments (primary/fallback/source/api_base) for Web UI model selector - 🤖 Agent chat export & send (#495) — export conversation to .md file; send to configured notification channels; new
POST /api/v1/agent/chat/send - 🤖 Agent background execution (#495) — analysis continues when switching pages; badge notification on completion; auto-cancel in-progress stream on session switch
- 📝 Report Engine P0 — Pydantic schema validation for LLM JSON; Jinja2 templates (markdown/wechat/brief) with legacy fallback; content integrity checks with retry; brief mode (
REPORT_TYPE=brief); history signal comparison - 📦 Smart import — multi-source import from image/CSV/Excel/clipboard; Vision LLM extracts code+name+confidence; name→code resolver (local map + pinyin + AkShare); confidence-tiered confirmation
- ⚙️ GitHub Actions LiteLLM config — workflow supports
LITELLM_CONFIG/LITELLM_CONFIG_YAMLfor flexible AI provider configuration - ⚙️ Config engine refactor & system API (#602) — unified config registry, validation and API exposure
- 📖 LLM configuration guide — new
docs/LLM_CONFIG_GUIDE.mdcovering 3-tier config, quick start, Vision/Agent/troubleshooting
Fixed
- 🐛 analyze_trend always reports No historical data (#600) — now fetches from DB/DataFetcher instead of broken
get_analysis_context - 🐛 Chip structure fallback when LLM omits it (#589) — auto-fills from data source chip data for consistent display across models
- 🐛 History sniper points show raw text (#452) — prioritizes original strings over compressed numeric values
- 🐛 GitHub Actions ENABLE_CHIP_DISTRIBUTION configurable (#617) — no longer hardcoded, supports vars/secrets override
- 🐛
.envsave preserves comments and blank lines — Web settings no longer destroys.envformatting - 🐛 Agent model discovery fixes — legacy mode includes LiteLLM-native providers; source detection aligned with runtime; fallback deployments no longer expanded per-key
- 🐛 Stooq US stock previous close semantics — no longer misuses open price as previous close
- 🐛 Stock name prefetch regression — prioritizes local
STOCK_NAME_MAPbefore remote queries - 🐛 AkShare limit-up/down calculation (#555) — fixed market analysis statistics
- 🐛 AkShare Tencent source field index & ETF quote mapping (#579)
- 🐛 Pytdx stock name cache pagination (#573) — prevents cache overflow
- 🐛 PushPlus oversized report chunking (#489) — auto-segments long content
- 🐛 Agent chat cancel & switch (#495) — cancel no longer misreports as failure; fast switch no longer overwrites stream state
- 🐛 MiniMax search status in
/statuscommand (#587) - 🐛 config_registry duplicate BOCHA_API_KEYS — removed duplicate dict entry that silently overwrote config
Changed
- 🔎 Fetcher failure observability — logs record start/success/failure with elapsed time, failover transitions; Efinance/Akshare include upstream endpoint and classified failure categories
- ♻️ Data source resilience & cleanup (#602) — fallback chain optimization
- ♻️ Image extract API response extension — new
itemsfield (code/name/confidence);codespreserved for backward compatibility - ♻️ Import parse error messages — specific failure reasons for Excel/CSV; improved logging with file type and size
Docs
- 📖 LLM config guide refactored for clarity (#583)
- 📖
image-extract-prompt.mdwith full prompt documentation - 📖 AkShare fallback cache TTL documentation
[3.4.10] - 2026-03-07
Fixed
- 🐛 EfinanceFetcher ETF OHLCV data (#541, #527) — switch
_fetch_etf_datafromef.fund.get_quote_history(NAV-only, no OHLCV, nobeg/endparams) toef.stock.get_quote_history; ETFs now return proper open/high/low/close/volume/amount instead of zeros; remove obsolete NAV column mappings from_normalize_data - 🐛 tiktoken 0.12.0
Unknown encoding cl100k_base(#537) — pintiktoken>=0.8.0,<0.12.0in requirements.txt to avoid plugin-registration regression introduced in 0.12.0 - 🐛 Web UI API error classification (#540) — frontend no longer treats every HTTP 400 as the same "server/network" failure; now distinguishes Agent disabled / missing params / model-tool incompatibility / upstream LLM errors / local connection failures
- 🐛 北交所代码识别失败 (#491, #533) — 8/4/92 开头的 6 位代码现正确识别为北交所;Tushare/Akshare/Yfinance 等数据源支持 .BJ 或 bj 前缀;Baostock/Pytdx 对北交所代码显式切换数据源;避免误判上海 B 股 900xxx
- 🐛 狙击点位解析错误 (#488, #532) — 理想买入/二次买入等字段在无「元」字时误提取括号内技术指标数字;现先截去第一个括号后内容再提取
Added
- Markdown-to-image for dashboard report (#455, #535) — 个股日报汇总支持 markdown 转图片推送(Telegram、WeChat、Custom、Email),与大盘复盘行为一致
- markdown-to-file engine (#455) —
MD2IMG_ENGINE=markdown-to-file可选,对 emoji 支持更好,需npm i markdown-to-file - PREFETCH_REALTIME_QUOTES (#455) — 设为
false可禁用实时行情预取,避免 efinance/akshare_em 全市场拉取 - Stock name prefetch (#455) — 分析前预取股票名称,减少报告中「股票xxxxx」占位符
- 📊 分析报告模型标记 (#528, #534) — 在分析报告 meta、报告末尾、推送内容中展示
model_used(完整 LLM 模型名);Agent 多轮调用时记录并展示每轮实际使用的模型(支持 fallback 切换)
Changed
- Enhanced markdown-to-image failure warning (#455) — 转图失败时提示具体依赖(wkhtmltopdf 或 m2f)
- WeChat-only image routing optimization (#455) — 仅配置企业微信图片时,不再对完整报告做冗余转图,避免误导性失败日志
- Stock name prefetch lightweight mode (#455) — 名称预取阶段跳过 realtime quote 查询,减少额外网络开销
[3.4.9] - 2026-03-06
Added
- 🧠 Structured config validation —
ConfigIssuedataclass andvalidate_structured()with severity-aware logging;CONFIG_VALIDATE_MODE=strictaborts startup on errors - 🖼️ Vision model config —
VISION_MODELandVISION_PROVIDER_PRIORITYfor image stock extraction; provider fallback (Gemini → Anthropic → OpenAI → DeepSeek) when primary fails - 🚀 CLI init wizard —
python -m dsa init3-step interactive bootstrap (model → data source → notification), 9 provider presets, incremental merge by default - 🔧 Multi-channel LLM support with visual channel editor (#494)
Changed
- ♻️ Vision extraction — migrated from gemini-3 hardcode to
litellm.completion()with configurable model and provider fallback;OPENAI_VISION_MODELdeprecated in favor ofVISION_MODEL - ♻️ Market analyzer — uses
Analyzer.generate_text()for LLM calls; fixes bypass and AnthropicAttributeErrorwhen using non-Router path - ♻️ Config validation refinements — test_env output format syncs with
validate_structured(severity-aware ✓/✗/⚠/·); Vision key warning whenVISION_MODELset but no provider API key; market_analyzer test coversgenerate_market_reviewfallback whengenerate_textreturns None - ⚙️ Auto-tag workflow defaults to NO tag — only tags when commit message explicitly contains
#patch,#minor, or#major - ♻️ Formatter and notification refactor (#516)
Fixed
- 🐛 STOCK_LIST not refreshed on scheduled runs —
.envor WebUI changes toSTOCK_LISTnow hot-reload before each scheduled analysis (#529) - 🐛 WebUI fails to load with MIME type error — SPA fallback route now resolves correct
Content-Typefor JS/CSS files (#520) - 🐛 AstrBot sender docstring misplaced —
import timeplaced before docstring in_send_astrbot, causing it to become dead code - 🐛 Telegram Markdown link escaping —
_convert_to_telegram_markdownescaped[]()characters, breaking all Markdown links in reports - 🐛 Duplicate
discord_bot_statusfield in Config dataclass — second declaration silently shadowed the first - 🧹 Unused imports — removed
shutil/subprocessfrommain.py - 🔧 Config validation and Vision key check (#525)
Docs
- 📝 Clarified GitHub Actions non-trading-day manual run controls (
TRADING_DAY_CHECK_ENABLED+force_run) for Issue #461 / PR #466
[3.4.8] - 2026-03-02
Fixed
- 🐛 Desktop exe crashes on startup with
FileNotFoundError— PyInstaller build was missing litellm's JSON data files (e.g.model_prices_and_context_window_backup.json). Added--collect-data litellmto both Windows and macOS build scripts so the files are correctly bundled in the executable.
CI
- 🔧 Cache Electron binaries on macOS CI runners to prevent intermittent EOF download failures when fetching
electron-vX.Y.Z-darwin-*.zipfrom GitHub CDN - 🔧 Fix macOS DMG
hdiutil Resource busyerror during desktop packaging
Docs
- 📝 Clarify non-trading-day manual run controls for GitHub Actions (
TRADING_DAY_CHECK_ENABLED+force_run) (#474)
[3.4.7] - 2026-02-28
Added
- 🧠 CN/US Market Strategy Blueprint System (#395) — market review prompt injects region-specific strategy blueprints with position sizing and risk trigger recommendations
Fixed
- 🐛
TRADING_DAY_CHECK_ENABLEDenv var and--force-runfor GitHub Actions (#466) - 🐛 Agent pipeline preserved resolved stock names (#464) — placeholder names no longer leak into reports
- 🐛 Code cleanup (#462, Fixes #422)
- 🐛 WebUI auto-build on startup (#460)
- 🐛 ARCH_ARGS unbound variable (#458)
- 🐛 Time zone inconsistency & right panel flash (#439)
Docs
- 📝 Clarify potential ambiguities in code (#343)
- 📝 ENABLE_EASTMONEY_PATCH guidance for Issue #453 (#456)
[3.4.0] - 2026-02-27
Added
- 📡 LiteLLM Direct Integration + Multi API Key Support (#454, Fixes #421 #428)
- Removed native SDKs (google-generativeai, google-genai, anthropic); unified through
litellm>=1.80.10 - New config:
LITELLM_MODEL,LITELLM_FALLBACK_MODELS,GEMINI_API_KEYS,ANTHROPIC_API_KEYS,OPENAI_API_KEYS - Multi-key auto-builds LiteLLM Router (simple-shuffle) with 429 cooldown
- Breaking:
.envGEMINI_MODEL(no prefix) only for fallback; explicit config must include provider prefix
- Removed native SDKs (google-generativeai, google-genai, anthropic); unified through
Changed
- ♻️ Notification Refactoring (#435) — extracted 10 sender classes into
src/notification_sender/
Fixed
- 🐛 LLM NoneType crash, history API 422, sniper points extraction
- 🐛 Auto-build frontend on WebUI startup —
WEBUI_AUTO_BUILDenv var (defaulttrue) - 🐛 Docker explicit project name (#448)
- 🐛 Bocha search SSL retry (#445, #446) — transient errors retry up to 3 times
- 🐛 Gemini google-genai SDK migration (Fixes #440, #444)
- 🐛 Mobile home page scrolling (Fixes #419, #433)
- 🐛 History list scroll reset (#431)
- 🐛 Settings save button false positive (fixes #417, #430)
[3.3.22] - 2026-02-26
Added
- 💬 Chat History Persistence (Fixes #400, #414) —
/chatpage survives refresh, sidebar session list - 🎨 Project VI Assets — logo icon set, PSD, vector, banner (#425)
- 🚀 Desktop CI Auto-Release (#426) — Windows + macOS parallel builds
Fixed
- 🐛 Agent Reasoning 400 & LiteLLM Proxy (fixes #409, #427)
- 🐛 Discord chunked sending (#413) —
DISCORD_MAX_WORDSconfig - 🐛 yfinance shared DataFrame (#412)
- 🐛 sniper_points parsing (#408)
- 🐛 Agent framework category missing (#406)
- 🐛 Date inconsistency & query id (fixes #322, #363)
[3.3.12] - 2026-02-24
Added
- 📈 Intraday Realtime Technical Indicators (Issue #234, #397) — MA calculated from realtime price, config:
ENABLE_REALTIME_TECHNICAL_INDICATORS - 🤖 Agent Strategy Chat (#367) — full ReAct pipeline, 11 YAML strategies, SSE streaming, multi-turn chat
- 📢 PushPlus Group Push —
PUSHPLUS_TOPIC(#402) - 📅 Trading Day Check (Issue #373, #375) —
TRADING_DAY_CHECK_ENABLED,--force-run
Fixed
- 🐛 DeepSeek reasoning mode (Issue #379, #386)
- 🐛 Agent news intel persistence (Fixes #396, #405)
- 🐛 Bare except clauses replaced with
except Exception(#398) - 🐛 UUID fallback for HTTP non-secure context (fixes #377, #381)
- 🐛 Docker DNS resolution (Fixes #372, #374)
- 🐛 Agent session/strategy bugs — multiple follow-up fixes for #367
- 🐛 yfinance parallel download data filtering
Changed
- Market review strategy consistency — unified cn/us template
- Agent test assertions updated (
6 -> 11)
[3.2.11] - 2026-02-23
修复(#patch)
- 🐛 StockTrendAnalyzer 从未执行 (Issue #357)
- 根因:
get_analysis_context仅返回 2 天数据且无raw_data,pipeline 中raw_data in context始终为 False - 修复:Step 3 直接调用
get_data_range获取 90 日历天(约 60 交易日)历史数据用于趋势分析 - 改善:趋势分析失败时用
logger.warning(..., exc_info=True)记录完整 traceback
- 根因:
[3.2.10] - 2026-02-22
新增
- ⚙️ 支持
RUN_IMMEDIATELY配置项,设为true时定时任务触发后立即执行一次分析,无需等待首个定时点
修复
- 🐛 修复 Web UI 页面居中问题
- 🐛 修复 Settings 返回 500 错误
[3.2.9] - 2026-02-22
修复
- 🐛 ETF 分析仅关注指数走势(Issue #274)
- 美股/港股 ETF(如 VOO、QQQ)与 A 股 ETF 不再纳入基金公司层面风险(诉讼、声誉等)
- 搜索维度:ETF/指数专用 risk_check、earnings、industry 查询,避免命中基金管理人新闻
- AI 提示:指数型标的分析约束,
risk_alerts不得出现基金管理人公司经营风险
[3.2.8] - 2026-02-21
修复
- 🐛 BOT 与 WEB UI 股票代码大小写统一(Issue #355)
- BOT
/analyze与 WEB UI 触发分析的股票代码统一为大写(如aapl→AAPL) - 新增
canonical_stock_code(),在 BOT、API、Config、CLI、task_queue 入口处规范化 - 历史记录与任务去重逻辑可正确识别同一股票(大小写不再影响)
- BOT
[3.2.7] - 2026-02-20
新增
- 🔐 Web 页面密码验证(Issue #320, #349)
- 支持
ADMIN_AUTH_ENABLED=true启用 Web 登录保护 - 首次访问在网页设置初始密码;支持「系统设置 > 修改密码」和 CLI
python -m src.auth reset_password重置
- 支持
[3.2.6] - 2026-02-20
⚠️ 破坏性变更(Breaking Changes)
- 历史记录 API 变更 (Issue #322)
- 路由变更:
GET /api/v1/history/{query_id}→GET /api/v1/history/{record_id} - 参数变更:
query_id(字符串) →record_id(整数) - 新闻接口变更:
GET /api/v1/history/{query_id}/news→GET /api/v1/history/{record_id}/news - 原因:
query_id在批量分析时可能重复,无法唯一标识单条历史记录。改用数据库主键id确保唯一性 - 影响范围:使用旧版历史详情 API 的所有客户端需同步更新
- 路由变更:
修复
- 修复美股(如 ADBE)技术指标矛盾:akshare 美股复权数据异常,统一美股历史数据源为 YFinance(Issue #311)
- 🐛 历史记录查询和显示问题 (Issue #322)
- 修复历史记录列表查询中日期不一致问题:使用明天作为 endDate,确保包含今天全天的数据
- 修复服务器 UI 报告选择问题:原因是多条记录共享同一
query_id,导致总是显示第一条。现改用analysis_history.id作为唯一标识 - 历史详情、新闻接口及前端组件已全面适配
record_id - 新增后台轮询(每 30s)与页面可见性变更时静默刷新历史列表,确保 CLI 发起的分析完成后前端能及时同步,使用
silent模式避免触发 loading 状态
- 🐛 美股指数实时行情与日线数据 (Issue #273)
- 修复 SPX、DJI、IXIC、NDX、VIX、RUT 等美股指数无法获取实时行情的问题
- 新增
us_index_mapping模块,将用户输入(如 SPX)映射为 Yahoo Finance 符号(如 ^GSPC) - 美股指数与美股股票日线数据直接路由至 YfinanceFetcher,避免遍历不支持的数据源
- 消除重复的美股识别逻辑,统一使用
is_us_stock_code()函数
优化
- 🎨 首页输入栏与 Market Sentiment 布局对齐优化
- 股票代码输入框左缘与历史记录 glass-card 框左对齐
- 分析按钮右缘与 Market Sentiment 外框右对齐
- Market Sentiment 卡片向下拉伸填满格子,消除与 STRATEGY POINTS 之间的空隙
- 窄屏时输入栏填满宽度,响应式对齐保持一致
[3.2.5] - 2026-02-19
新增
- 🌍 大盘复盘可选区域(Issue #299)
- 支持
MARKET_REVIEW_REGION环境变量:cn(A股)、us(美股)、both(两者) - us 模式使用 SPX/纳斯达克/道指/VIX 等指数;both 模式可同时复盘 A 股与美股
- 默认
cn,保持向后兼容
- 支持
[3.2.4] - 2026-02-18
修复
- 🐛 统一美股数据源为 YFinance(Issue #311)
- akshare 美股复权数据异常,统一美股历史数据源为 YFinance
- 修复 ADBE 等美股股票技术指标矛盾问题
[3.2.3] - 2026-02-18
修复
- 🐛 标普500实时数据缺失(Issue #273)
- 修复 SPX、DJI、IXIC、NDX、VIX、RUT 等美股指数无法获取实时行情的问题
- 新增
us_index_mapping模块,将用户输入(如 SPX)映射为 Yahoo Finance 符号(如^GSPC) - 美股指数与美股股票日线数据直接路由至 YfinanceFetcher,避免遍历不支持的数据源
[3.2.2] - 2026-02-16
新增
- 📊 PE 指标支持(Issue #296)
- AI System Prompt 增加 PE 估值关注
- 📰 新闻时效性筛查(Issue #296)
NEWS_MAX_AGE_DAYS:新闻最大时效(天),默认 3,避免使用过时信息
- 📈 强势趋势股乖离率放宽(Issue #296)
BIAS_THRESHOLD:乖离率阈值(%),默认 5.0,可配置- 强势趋势股(多头排列且趋势强度 ≥70)自动放宽乖离率到 1.5 倍
[3.2.1] - 2026-02-16
新增
- 🔧 东财接口补丁可配置开关
- 支持
EFINANCE_PATCH_ENABLED环境变量开关东财接口补丁(默认true) - 补丁不可用时可降级关闭,避免影响主流程
- 支持
[3.2.0] - 2026-02-15
新增
- 🔒 CI 门禁统一(P0)
- 新增
scripts/ci_gate.sh作为后端门禁单一入口 - 主 CI 改为
backend-gate、docker-build、web-gate三段式 - CI 触发改为所有 PR,避免 Required Checks 因路径过滤缺失而卡住合并
web-gate支持前端路径变更按需触发- 新增
network-smoke工作流承载非阻断网络场景回归
- 新增
- 📦 发布链路收敛(P0)
docker-publish调整为 tag 主触发,并增加发布前门禁校验- 手动发布增加
release_tag输入与 semver/changelog 强校验 - 发布前新增 Docker smoke(关键模块导入)
- 📝 PR 模板升级(P0)
- 增加背景、范围、验证命令与结果、回滚方案、Issue 关联等必填项
- 🤖 AI 审查覆盖增强(P0)
pr-review纳入.github/workflows/**范围- 新增
AI_REVIEW_STRICT开关,可选将 AI 审查失败升级为阻断
[3.1.13] - 2026-02-15
新增
- 📊 仅分析结果摘要(Issue #262)
- 支持
REPORT_SUMMARY_ONLY环境变量,设为true时只推送汇总,不含个股详情 - 默认
false,多股时适合快速浏览
- 支持
[3.1.12] - 2026-02-15
新增
- 📧 个股与大盘复盘合并推送(Issue #190)
- 支持
MERGE_EMAIL_NOTIFICATION环境变量,设为true时将个股分析与大盘复盘合并为一次推送 - 默认
false,减少邮件数量、降低被识别为垃圾邮件的风险
- 支持
[3.1.11] - 2026-02-15
新增
- 🤖 Anthropic Claude API 支持(Issue #257)
- 支持
ANTHROPIC_API_KEY、ANTHROPIC_MODEL、ANTHROPIC_TEMPERATURE、ANTHROPIC_MAX_TOKENS - AI 分析优先级:Gemini > Anthropic > OpenAI
- 支持
- 📷 从图片识别股票代码(Issue #257)
- 上传自选股截图,通过 Vision LLM 自动提取股票代码
- API:
POST /api/v1/stocks/extract-from-image;支持 JPEG/PNG/WebP/GIF,最大 5MB - 支持
OPENAI_VISION_MODEL单独配置图片识别模型
- ⚙️ 通达信数据源手动配置(Issue #257)
- 支持
PYTDX_HOST、PYTDX_PORT或PYTDX_SERVERS配置自建通达信服务器
- 支持
[3.1.10] - 2026-02-15
新增
- ⚙️ 立即运行配置(Issue #332)
- 支持
RUN_IMMEDIATELY环境变量,true时定时任务启动后立即执行一次
- 支持
- 🐛 修复 Docker 构建问题
[3.1.9] - 2026-02-14
新增
- 🔌 东财接口补丁机制
- 新增
patch/eastmoney_patch.py修复 efinance 上游接口变更 - 不影响其他数据源的正常运行
- 新增
[3.1.8] - 2026-02-14
新增
- 🔐 Webhook 证书校验开关(Issue #265)
- 支持
WEBHOOK_VERIFY_SSL环境变量,可关闭 HTTPS 证书校验以支持自签名证书 - 默认保持校验,关闭存在 MITM 风险,仅建议在可信内网使用
- 支持
[3.1.7] - 2026-02-14
修复
- 🐛 修复包导入错误(package import error)
[3.1.6] - 2026-02-13
修复
- 🐛 修复
news_intel中query_id不一致问题
[3.1.5] - 2026-02-13
新增
- 📷 Markdown 转图片通知(Issue #289)
- 支持
MARKDOWN_TO_IMAGE_CHANNELS配置,对 Telegram、企业微信、自定义 Webhook(Discord)、邮件发送图片格式报告 - 邮件为内联附件,增强对不支持 HTML 客户端的兼容性
- 需安装
wkhtmltopdf和imgkit
- 支持
[3.1.4] - 2026-02-12
新增
- 📧 股票分组发往不同邮箱(Issue #268)
- 支持
STOCK_GROUP_N+EMAIL_GROUP_N配置,不同股票组报告发送到对应邮箱 - 大盘复盘发往所有配置的邮箱
- 支持
[3.1.3] - 2026-02-12
修复
- 🐛 修复 Docker 内运行时通过页面修改配置报错
[Errno 16] Device or resource busy的问题
[3.1.2] - 2026-02-11
修复
- 🐛 修复 Docker 一致性问题,解决关键批次处理与通知 Bug
[3.1.1] - 2026-02-11
变更
- ♻️
API_HOST→WEBUI_HOST:Docker Compose 配置项统一
[3.1.0] - 2026-02-11
新增
- 📊 ETF 支持增强与代码规范化
- 统一各数据源 ETF 代码处理逻辑
- 新增
canonical_stock_code()统一代码格式,确保数据源路由正确
[3.0.5] - 2026-02-08
修复
- 🐛 修复信号 emoji 与建议不一致的问题(复合建议如"卖出/观望"未正确映射)
- 🐛 修复
*ST股票名在微信/Dashboard 中 markdown 转义问题 - 🐛 修复
idx.amount为 None 时大盘复盘 TypeError - 🐛 修复分析 API 返回
report=None及 ReportStrategy 类型不一致问题 - 🐛 修复 Tushare 返回类型错误(dict → UnifiedRealtimeQuote)及 API 端点指向
新增
- 📊 大盘复盘报告注入结构化数据(涨跌统计、指数表格、板块排名)
- 🔍 搜索结果 TTL 缓存(500 条上限,FIFO 淘汰)
- 🔧 Tushare Token 存在时自动注入实时行情优先级
- 📰 新闻摘要截断长度 50→200 字
优化
- ⚡ 补充行情字段请求限制为最多 1 次,减少无效请求
[3.0.4] - 2026-02-07
新增
- 📈 回测引擎 (PR #269)
- 新增基于历史分析记录的回测系统,支持收益率、胜率、最大回撤等指标评估
- WebUI 集成回测结果展示
[3.0.3] - 2026-02-07
修复
- 🐛 修复狙击点位数据解析错误问题 (PR #271)
[3.0.2] - 2026-02-06
新增
- ✉️ 可配置邮件发送者名称 (PR #272)
- 🌐 外国股票支持英文关键词搜索
[3.0.1] - 2026-02-06
修复
- 🐛 修复 ETF 实时行情获取、市场数据回退、企业微信消息分块问题
- 🔧 CI 流程简化
[3.0.0] - 2026-02-06
移除
- 🗑️ 移除旧版 WebUI
- 删除基于
http.server.ThreadingHTTPServer的旧版 WebUI(web/包) - 旧版 WebUI 的功能已完全被 FastAPI(
api/)+ React 前端替代 --webui/--webui-only命令行参数标记为弃用,自动重定向到--serve/--serve-onlyWEBUI_ENABLED/WEBUI_HOST/WEBUI_PORT环境变量保持兼容,自动转发到 FastAPI 服务webui.py保留为兼容入口,启动时直接调用 FastAPI 后端- Docker Compose 中移除
webui服务定义,统一使用server服务
- 删除基于
变更
- ♻️ 服务层重构
- 将
web/services.py中的异步任务服务迁移至src/services/task_service.py - Bot 分析命令(
bot/commands/analyze.py)改为使用src.services.task_service - Docker 环境变量
WEBUI_HOST/WEBUI_PORT更名为API_HOST/API_PORT(旧名仍兼容)
- 将
[2.3.0] - 2026-02-01
新增
- 🇺🇸 增强美股支持 (Issue #153)
- 实现基于 Akshare 的美股历史数据获取 (
ak.stock_us_daily()) - 实现基于 Yfinance 的美股实时行情获取(优先策略)
- 增加对不支持数据源(Tushare/Baostock/Pytdx/Efinance)的美股代码过滤和快速降级
- 实现基于 Akshare 的美股历史数据获取 (
修复
- 🐛 修复 AMD 等美股代码被误识别为 A 股的问题 (Issue #153)
[2.2.5] - 2026-02-01
新增
- 🤖 AstrBot 消息推送 (PR #217)
- 新增 AstrBot 通知渠道,支持推送到 QQ 和微信
- 支持 HMAC SHA256 签名验证,确保通信安全
- 通过
ASTRBOT_URL和ASTRBOT_TOKEN配置
[2.2.4] - 2026-02-01
新增
- ⚙️ 可配置数据源优先级 (PR #215)
- 支持通过环境变量(如
YFINANCE_PRIORITY=0)动态调整数据源优先级 - 无需修改代码即可优先使用特定数据源(如 Yahoo Finance)
- 支持通过环境变量(如
[2.2.3] - 2026-01-31
修复
- 📦 更新 requirements.txt,增加
lxml_html_clean依赖以解决兼容性问题
[2.2.2] - 2026-01-31
修复
- 🐛 修复代理配置区分大小写问题 (fixes #211)
[2.2.1] - 2026-01-31
修复
- 🐛 YFinance 兼容性修复 (PR #210, fixes #209)
- 修复新版 yfinance 返回 MultiIndex 列名导致的数据解析错误
[2.2.0] - 2026-01-31
新增
- 🔄 多源回退策略增强
- 实现了更健壮的数据获取回退机制 (feat: multi-source fallback strategy)
- 优化了数据源故障时的自动切换逻辑
修复
- 🐛 修复 analyzer 运行后无法通过改 .env 文件的 stock_list 内容调整跟踪的股票
[2.1.14] - 2026-01-31
文档
- 📝 更新 README 和优化 auto-tag 规则
[2.1.13] - 2026-01-31
修复
- 🐛 Tushare 优先级与实时行情 (Fixed #185)
- 修复 Tushare 数据源优先级设置问题
- 修复 Tushare 实时行情获取功能
[2.1.12] - 2026-01-30
修复
- 🌐 修复代理配置在某些情况下的区分大小写问题
- 🌐 修复本地环境禁用代理的逻辑
[2.1.11] - 2026-01-30
优化
- 🚀 飞书消息流优化 (PR #192)
- 优化飞书 Stream 模式的消息类型处理
- 修改 Stream 消息模式默认为关闭,防止配置错误运行时报错
[2.1.10] - 2026-01-30
合并
- 📦 合并 PR #154 贡献
[2.1.9] - 2026-01-30
新增
- 💬 微信文本消息支持 (PR #137)
- 新增微信推送的纯文本消息类型支持
- 添加
WECHAT_MSG_TYPE配置项
[2.1.8] - 2026-01-30
修复
- 🐛 修正日志中 API 提供商显示错误 (PR #197)
[2.1.7] - 2026-01-30
修复
- 🌐 禁用本地环境的代理设置,避免网络连接问题
[2.1.6] - 2026-01-29
新增
- 📡 Pytdx 数据源 (Priority 2)
- 新增通达信数据源,免费无需注册
- 多服务器自动切换
- 支持实时行情和历史数据
- 🏷️ 多源股票名称解析
- DataFetcherManager 新增
get_stock_name()方法 - 新增
batch_get_stock_names()批量查询 - 自动在多数据源间回退
- Tushare 和 Baostock 新增股票名称/列表方法
- DataFetcherManager 新增
- 🔍 增强搜索回退
- 新增
search_stock_price_fallback()用于数据源全部失败时 - 新增搜索维度:市场分析、行业分析
- 最大搜索次数从 3 增加到 5
- 改进搜索结果格式(每维度 4 条结果)
- 新增
改进
- 更新搜索查询模板以提高相关性
- 增强
format_intel_report()输出结构
[2.1.5] - 2026-01-29
新增
- 📡 新增 Pytdx 数据源和多源股票名称解析功能
[2.1.4] - 2026-01-29
文档
- 📝 更新赞助商信息
[2.1.3] - 2026-01-28
文档
- 📝 重构 README 布局
- 🌐 新增繁体中文翻译 (README_CHT.md)
修复
- 🐛 修复 WebUI 无法输入美股代码问题
- 输入框逻辑改成所有字母都转换成大写
- 支持
.的输入(如BRK.B)
[2.1.2] - 2026-01-27
修复
- 🐛 修复个股分析推送失败和报告路径问题 (fixes #166)
- 🐛 修改 CR 错误,确保微信消息最大字节配置生效
[2.1.1] - 2026-01-26
新增
- 🔧 添加 GitHub Actions auto-tag 工作流
- 📡 添加 yfinance 兜底数据源及数据缺失警告
修复
- 🐳 修复 docker-compose 路径和文档命令
- 🐳 Dockerfile 补充 copy src 文件夹 (fixes #145)
[2.1.0] - 2026-01-25
新增
- 🇺🇸 美股分析支持
- 支持美股代码直接输入(如
AAPL,TSLA) - 使用 YFinance 作为美股数据源
- 支持美股代码直接输入(如
- 📈 MACD 和 RSI 技术指标
- MACD:趋势确认、金叉死叉信号(零轴上金叉⭐、金叉✅、死叉❌)
- RSI:超买超卖判断(超卖⭐、强势✅、超买⚠️)
- 指标信号纳入综合评分系统
- 🎮 Discord 推送支持 (PR #124, #125, #144)
- 支持 Discord Webhook 和 Bot API 两种方式
- 通过
DISCORD_WEBHOOK_URL或DISCORD_BOT_TOKEN+DISCORD_MAIN_CHANNEL_ID配置
- 🤖 机器人命令交互
- 钉钉机器人支持
/分析 股票代码命令触发分析 - 支持 Stream 长连接模式
- 钉钉机器人支持
- 🌡️ AI 温度参数可配置 (PR #142)
- 支持自定义 AI 模型温度参数
- 🐳 Zeabur 部署支持
- 添加 Zeabur 镜像部署工作流
- 支持 commit hash 和 latest 双标签
重构
- 🏗️ 项目结构优化
- 核心代码移至
src/目录,根目录更清爽 - 文档移至
docs/目录 - Docker 配置移至
docker/目录 - 修复所有 import 路径,保持向后兼容
- 核心代码移至
- 🔄 数据源架构升级
- 新增数据源熔断机制,单数据源连续失败自动切换
- 实时行情缓存优化,批量预取减少 API 调用
- 网络代理智能分流,国内接口自动直连
- 🤖 Discord 机器人重构为平台适配器架构
修复
- 🌐 网络稳定性增强
- 自动检测代理配置,对国内行情接口强制直连
- 修复 EfinanceFetcher 偶发的
ProtocolError - 增加对底层网络错误的捕获和重试机制
- 📧 邮件渲染优化
- 修复邮件中表格不渲染问题 (#134)
- 优化邮件排版,更紧凑美观
- 📢 企业微信推送修复
- 修复大盘复盘推送不完整问题
- 增强消息分割逻辑,支持更多标题格式
- 增加分批发送间隔,避免限流丢失
- 👷 CI/CD 修复
- 修复 GitHub Actions 中路径引用的错误
[2.0.0] - 2026-01-24
新增
- 🇺🇸 美股分析支持
- 支持美股代码直接输入(如
AAPL,TSLA) - 使用 YFinance 作为美股数据源
- 支持美股代码直接输入(如
- 🤖 机器人命令交互 (PR #113)
- 钉钉机器人支持
/分析 股票代码命令触发分析 - 支持 Stream 长连接模式
- 支持选择精简报告或完整报告
- 钉钉机器人支持
- 🎮 Discord 推送支持 (PR #124)
- 支持 Discord Webhook 推送
- 添加 Discord 环境变量到工作流
修复
- 🐳 修复 WebUI 在 Docker 中绑定 0.0.0.0 (fixed #118)
- 🔔 修复飞书长连接通知问题
- 🐛 修复
analysis_delay未定义错误 - 🔧 启动时 config.py 检测通知渠道,修复已配置自定义渠道情况下仍然提示未配置问题
改进
- 🔧 优化 Tushare 优先级判断逻辑,提升封装性
- 🔧 修复 Tushare 优先级提升后仍排在 Efinance 之后的问题
- ⚙️ 配置 TUSHARE_TOKEN 时自动提升 Tushare 数据源优先级
- ⚙️ 实现 4 个用户反馈 issue (#112, #128, #38, #119)
[1.6.0] - 2026-01-19
新增
- 🖥️ WebUI 管理界面及 API 支持(PR #72)
- 全新 Web 架构:分层设计(Server/Router/Handler/Service)
- 核心 API:支持
/analysis(触发分析),/tasks(查询进度),/health(健康检查) - 交互界面:支持页面直接输入代码并触发分析,实时展示进度
- 运行模式:新增
--webui-only模式,仅启动 Web 服务 - 解决了 #70 的核心需求(提供触发分析的接口)
- ⚙️ GitHub Actions 配置灵活性增强(#79)
- 支持从 Repository Variables 读取非敏感配置(如 STOCK_LIST, GEMINI_MODEL)
- 保持对 Secrets 的向下兼容
修复
- 🐛 修复企业微信/飞书报告截断问题(#73)
- 移除 notification.py 中不必要的长度硬截断逻辑
- 依赖底层自动分片机制处理长消息
- 🐛 修复 GitHub Workflow 环境变量缺失(#80)
- 修复
CUSTOM_WEBHOOK_BEARER_TOKEN未正确传递到 Runner 的问题
- 修复
[1.5.0] - 2026-01-17
新增
- 📲 单股推送模式(#55)
- 每分析完一只股票立即推送,不用等全部分析完
- 命令行参数:
--single-notify - 环境变量:
SINGLE_STOCK_NOTIFY=true
- 🔐 自定义 Webhook Bearer Token 认证(#51)
- 支持需要 Token 认证的 Webhook 端点
- 环境变量:
CUSTOM_WEBHOOK_BEARER_TOKEN
[1.4.0] - 2026-01-17
新增
- 📱 Pushover 推送支持(PR #26)
- 支持 iOS/Android 跨平台推送
- 通过
PUSHOVER_USER_KEY和PUSHOVER_API_TOKEN配置
- 🔍 博查搜索 API 集成(PR #27)
- 中文搜索优化,支持 AI 摘要
- 通过
BOCHA_API_KEYS配置
- 📊 Efinance 数据源支持(PR #59)
- 新增 efinance 作为数据源选项
- 🇭🇰 港股支持(PR #17)
- 支持 5 位代码或 HK 前缀(如
hk00700、hk1810)
- 支持 5 位代码或 HK 前缀(如
修复
- 🔧 飞书 Markdown 渲染优化(PR #34)
- 使用交互卡片和格式化器修复渲染问题
- ♻️ 股票列表热重载(PR #42 修复)
- 分析前自动重载
STOCK_LIST配置
- 分析前自动重载
- 🐛 钉钉 Webhook 20KB 限制处理
- 长消息自动分块发送,避免被截断
- 🔄 AkShare API 重试机制增强
- 添加失败缓存,避免重复请求失败接口
改进
- 📝 README 精简优化
- 高级配置移至
docs/full-guide.md
- 高级配置移至
[1.3.0] - 2026-01-12
新增
- 🔗 自定义 Webhook 支持
- 支持任意 POST JSON 的 Webhook 端点
- 自动识别钉钉、Discord、Slack、Bark 等常见服务格式
- 支持配置多个 Webhook(逗号分隔)
- 通过
CUSTOM_WEBHOOK_URLS环境变量配置
修复
- 📝 企业微信长消息分批发送
- 解决自选股过多时内容超过 4096 字符限制导致推送失败的问题
- 智能按股票分析块分割,每批添加分页标记(如 1/3, 2/3)
- 批次间隔 1 秒,避免触发频率限制
[1.2.0] - 2026-01-11
新增
- 📢 多渠道推送支持
- 企业微信 Webhook
- 飞书 Webhook(新增)
- 邮件 SMTP(新增)
- 自动识别渠道类型,配置更简单
改进
- 统一使用
NOTIFICATION_URL配置,兼容旧的WECHAT_WEBHOOK_URL - 邮件支持 Markdown 转 HTML 渲染
[1.1.0] - 2026-01-11
新增
- 🤖 OpenAI 兼容 API 支持
- 支持 DeepSeek、通义千问、Moonshot、智谱 GLM 等
- Gemini 和 OpenAI 格式二选一
- 自动降级重试机制
[1.0.0] - 2026-01-10
新增
- 🎯 AI 决策仪表盘分析
- 一句话核心结论
- 精确买入/止损/目标点位
- 检查清单(✅⚠️❌)
- 分持仓建议(空仓者 vs 持仓者)
- 📊 大盘复盘功能
- 主要指数行情
- 涨跌统计
- 板块涨跌榜
- AI 生成复盘报告
- 🔍 多数据源支持
- AkShare(主数据源,免费)
- Tushare Pro
- Baostock
- YFinance
- 📰 新闻搜索服务
- Tavily API
- SerpAPI
- 💬 企业微信机器人推送
- ⏰ 定时任务调度
- 🐳 Docker 部署支持
- 🚀 GitHub Actions 零成本部署
技术特性
- Gemini AI 模型(gemini-3-flash-preview)
- 429 限流自动重试 + 模型切换
- 请求间延时防封禁
- 多 API Key 负载均衡
- SQLite 本地数据存储
CONTRIBUTING (`docs/CONTRIBUTING.md`)
贡献指南
感谢你对本项目的关注!欢迎任何形式的贡献。
🐛 报告 Bug
- 先搜索 Issues 确认问题未被报告
- 使用 Bug Report 模板创建新 Issue
- 提供详细的复现步骤和环境信息
💡 功能建议
- 先搜索 Issues 确认建议未被提出
- 使用 Feature Request 模板创建新 Issue
- 详细描述你的使用场景和期望功能
🔧 提交代码
开发环境
# 克隆仓库
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git
cd daily_stock_analysis
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# 安装依赖
pip install -r requirements.txt
# 配置环境变量
cp .env.example .env提交流程
- Fork 本仓库
- 创建特性分支:
git checkout -b feature/your-feature - 提交改动:
git commit -m 'feat: add some feature' - 推送分支:
git push origin feature/your-feature - 创建 Pull Request
Commit 规范
使用 Conventional Commits 规范:
feat: 新功能
fix: Bug 修复
docs: 文档更新
style: 代码格式(不影响功能)
refactor: 重构
perf: 性能优化
test: 测试相关
chore: 构建/工具相关示例:
feat: 添加钉钉机器人支持
fix: 修复 429 限流重试逻辑
docs: 更新 README 部署说明代码规范
- Python 代码遵循 PEP 8
- 函数和类需要添加 docstring
- 重要逻辑添加注释
- 新功能需要更新相关文档
CI 自动检查
提交 PR 后,CI 会自动运行以下检查:
仅修改普通 docs/**、非治理类 Markdown 或 LICENSE 时,CI 保留轻量的变更检测、治理校验和门禁汇总,但跳过 3 个后端测试 shard、Docker、Web 与桌面打包。被离线测试直接读取的契约文档、静态 API 规格与 Markdown fixture 仍会触发后端测试。AGENTS.md、.github/instructions/**、.claude/skills/** 等治理资产仍会由 ai-governance 校验;配置、依赖、工作流等会影响运行的文本文件仍执行对应完整检查。
| 检查项 | 说明 | 必须通过 |
|---|---|---|
| backend-gate | scripts/ci_gate.sh(py_compile + flake8 严重错误 + 本地核心脚本 + offline pytest) |
✅ |
| docker-build | Docker 镜像构建与关键模块导入 smoke | ✅ |
| web-gate | 前端变更时执行 npm run lint + npm run build |
✅(触发时) |
| pr-review | 暂停 PR 自动触发,仅保留维护者通过 workflow_dispatch 按 PR 编号手动运行;通过 GitHub API 读取 PR 元数据和 diff,不检出或执行 fork PR 代码 |
❌(辅助项) |
| network-smoke | 定时/手动执行 pytest -m network + scripts/test.sh quick(非阻断) |
❌(观测项) |
本地运行检查:
# backend gate(推荐)
pip install -r requirements.txt
pip install flake8 pytest
./scripts/ci_gate.sh
# 前端 gate(如修改了 apps/dsa-web)
cd apps/dsa-web
npm ci
npm run lint
npm run build📋 优先贡献方向
查看 README 了解项目当前的核心能力与主要入口:
- 🔔 新通知渠道(钉钉、飞书、Telegram)
- 🤖 新 AI 模型支持(GPT-4、Claude)
- 📊 新数据源接入
- 🐛 Bug 修复和性能优化
- 📖 文档完善和翻译
❓ 问题解答
如有任何问题,欢迎:
- 创建 Issue 讨论
- 查看已有 Issue 和 Discussion
再次感谢你的贡献! 🎉
CONTRIBUTING EN (`docs/CONTRIBUTING_EN.md`)
Contributing Guide
Thank you for your interest in contributing! All kinds of contributions are welcome.
🐛 Reporting Bugs
- Search Issues first to check if it has already been reported.
- Create a new Issue using the Bug Report template.
- Provide detailed reproduction steps and environment information.
💡 Suggesting Features
- Search Issues to make sure the suggestion hasn't already been raised.
- Create a new Issue using the Feature Request template.
- Describe your use case and expected behavior in detail.
🔧 Submitting Code
Setting Up the Development Environment
# Clone the repository
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git
cd daily_stock_analysis
# Create a virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Configure environment variables
cp .env.example .env
# Edit .env and fill in the required API keysContribution Workflow
- Fork this repository.
- Create a feature branch:
git checkout -b feature/your-feature - Commit your changes:
git commit -m 'feat: add some feature' - Push the branch:
git push origin feature/your-feature - Open a Pull Request against
main.
Commit Message Convention
This project follows Conventional Commits:
feat: New feature
fix: Bug fix
docs: Documentation update
style: Code formatting (no logic change)
refactor: Code refactoring
perf: Performance improvement
test: Test-related changes
chore: Build / tooling changesExamples:
feat: add DingTalk bot support
fix: handle 429 rate-limit with retry backoff
docs: update README deployment sectionCode Style
- Python code follows PEP 8 (line length: 120).
- Add docstrings to functions and classes.
- Add comments for non-obvious logic.
- Update relevant documentation when adding new features.
CI Checks
After opening a PR, CI will automatically run the following PR checks:
PRs that only change ordinary docs/**, non-governance Markdown, or LICENSE keep lightweight change detection, governance validation, and gate summarization, while skipping the three backend test shards, Docker, Web, and desktop packaging. Contract documentation, static API specifications, and Markdown fixtures read directly by offline tests still trigger backend tests. Governance assets such as AGENTS.md, .github/instructions/**, and .claude/skills/** are still validated by ai-governance; runtime-affecting text files such as configuration, dependency, and workflow files still run their corresponding full checks.
| Check | Description | Required |
|---|---|---|
backend-gate |
scripts/ci_gate.sh — py_compile + flake8 critical errors + ./scripts/test.sh code + ./scripts/test.sh yfinance + offline pytest |
✅ |
docker-build |
Docker image build and key module import smoke test | ✅ |
web-gate |
npm run lint + npm run build (triggered when apps/dsa-web/ changes) |
✅ (when triggered) |
pr-review |
Automatic PR triggering is temporarily paused. Maintainers can still run it by PR number through workflow_dispatch; it reads PR metadata and diff through the GitHub API and never checks out or executes fork PR code. |
❌ (advisory) |
Separately, the repository also has a non-blocking network-smoke workflow in .github/workflows/network-smoke.yml, but it is only triggered by schedule and workflow_dispatch, not by pull requests.
Running checks locally:
# Backend gate (recommended)
pip install -r requirements.txt
pip install flake8 pytest
./scripts/ci_gate.sh
# Frontend gate (only if you changed apps/dsa-web/)
cd apps/dsa-web
npm ci
npm run lint
npm run buildDocumentation Sync Rule
When modifying a Chinese-language core document (e.g., docs/full-guide.md), your PR description must state whether the corresponding English document has been updated. If not updated, explain why.
📋 Priority Areas for Contribution
- 🔔 New notification channels (e.g., Slack, Matrix)
- 🤖 New AI model integrations
- 📊 New data source adapters
- 🐛 Bug fixes and performance improvements
- 📖 Documentation improvements and translations
❓ Questions
Feel free to:
- Open an Issue for discussion.
- Browse existing Issues and Discussions.
Thank you for contributing! 🎉
DEPLOY (`docs/DEPLOY.md`)
🚀 部署指南
本文档介绍如何将 A股自选股智能分析系统部署到服务器。
📋 部署方案对比
| 方案 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| Docker Compose ⭐ | 一键部署、环境隔离、易迁移、易升级 | 需要安装 Docker | 推荐:大多数场景 |
| 直接部署 | 简单直接、无额外依赖 | 环境依赖、迁移麻烦 | 临时测试 |
| Systemd 服务 | 系统级管理、开机自启 | 配置繁琐 | 长期稳定运行 |
| Supervisor | 进程管理、自动重启 | 需要额外安装 | 多进程管理 |
结论:推荐使用 Docker Compose,迁移最快最方便!
🐳 方案一:Docker Compose 部署(推荐)
1. 安装 Docker
# Ubuntu/Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# CentOS
sudo yum install -y docker docker-compose
sudo systemctl start docker
sudo systemctl enable docker2. 准备配置文件
# 克隆代码(或上传代码到服务器)
git clone <your-repo-url> /opt/stock-analyzer
cd /opt/stock-analyzer
# 复制并编辑配置文件
cp .env.example .env
vim .env # 填入真实的 API Key 等配置3. 一键启动
# 构建并启动(同时包含定时分析和 Web 界面服务)
docker-compose -f ./docker/docker-compose.yml up -d
# 查看日志
docker-compose -f ./docker/docker-compose.yml logs -f
# 查看运行状态
docker-compose -f ./docker/docker-compose.yml ps启动成功后,在浏览器输入 http://服务器公网IP:8000 即可打开 Web 管理界面。如果打不开,记得先在云服务器控制台的「安全组」里放行 8000 端口。
不知道怎么访问?→ 云服务器 Web 界面访问指南
3.1 资源建议
默认 docker/docker-compose.yml 为每个服务设置 limits.memory: 1G、reservations.memory: 512M,这是完整分析场景的推荐起点。
- 最低可尝试:
512M,仅适合轻量 Web/API、单股、低并发场景,建议设置MAX_WORKERS=1。 - 推荐:
1G,适合单独运行server或analyzer的常规分析。 - 高负载:
2G+,适合同时启动server + analyzer、多股票、默认MAX_WORKERS=3、大盘复盘、新闻扩展、图片报告或选股。
如果只能使用 512M,请避免同时启动 server 和 analyzer,并关闭非必要的大盘复盘、新闻扩展和图片报告能力。
3.2 避免重复定时推送
建议只保留一个自动调度入口:独立 analyzer,或开启 SCHEDULE_ENABLED=true 的 server。排查 NAS 上遗留的 --schedule 容器及重复部署;两个实例使用独立数据库时无法去重。
CLI 与 Web/API 的每日定时任务会在 DATABASE_PATH 指向的同一个 SQLite 文件中原子认领本次计划时间。相同到期时刻、自选股/持仓来源、分析/推送选项和报告口径只自动派发一次,即使第二个进程在第一个结束后才触发。不同时间点、不同自选股或不同分析/推送选项仍独立执行。到期时间按调度器所在进程的时区换算为 UTC;同一部署应保持 TZ 一致。官方 Compose 的两个服务默认共享 data/,因此共享认领记录。
这是“自动派发至多一次”,不是失败自动重试或通知送达保证。认领后进程退出、超时、分析失败(可能已发出部分通知)都不会让另一个定时器重复整轮任务;确认结果后可用 Web 的“立即运行”手动补跑。认领发生在实际执行边界,并固定自选股快照,避免保存设置与异步启动之间的竞态;因忙碌而未启动的 Web 任务不会认领。启动时立即运行、手动 CLI/API 分析及“立即运行”不参与此去重,也不会被它阻止。记录自动保留约 7 天;数据库不可写或锁超时会记录错误并跳过本轮,Web 调度状态会显示认领错误。
本机制依赖共享 SQLite 文件的可靠文件锁,仅覆盖同主机进程及使用同一个本地 bind mount 的容器;不承诺独立数据库、NFS 或跨主机部署的分布式去重。无需新增配置或修改历史分析数据。
4. 常用管理命令
# 停止服务
docker-compose -f ./docker/docker-compose.yml down
# 重启服务
docker-compose -f ./docker/docker-compose.yml restart
# 更新代码后重新部署
git pull
docker-compose -f ./docker/docker-compose.yml build --no-cache
docker-compose -f ./docker/docker-compose.yml up -d
# 进入容器调试
docker-compose -f ./docker/docker-compose.yml exec -u dsa stock-analyzer bash
# 手动执行一次分析
docker-compose -f ./docker/docker-compose.yml exec -u dsa stock-analyzer python main.py --no-notify5. 数据持久化
数据自动保存在宿主机目录:
./data/- 数据库文件./logs/- 日志文件./reports/- 分析报告
6. 权限说明
Docker 镜像启动入口会自动创建并修复 ./data、./logs、./reports 对应挂载目录的权限,然后降权为非 root 用户 (dsa, UID 1000) 运行应用。普通部署不需要手动 chown / chmod。
如果你显式指定了 --user / Compose user:,或使用只读挂载、rootless Docker、NFS 等不允许容器修复属主的环境,请确保实际运行用户对这些目录具备写入权限。
🖥️ 方案二:直接部署
1. 安装 Python 环境
# 安装 Python 3.10+
sudo apt update
sudo apt install -y python3.10 python3.10-venv python3-pip
# 创建虚拟环境
python3.10 -m venv /opt/stock-analyzer/venv
source /opt/stock-analyzer/venv/bin/activate2. 安装依赖
cd /opt/stock-analyzer
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple3. 配置环境变量
cp .env.example .env
vim .env # 填入配置4. 运行
# 单次运行
python main.py
# 定时任务模式(前台运行)
python main.py --schedule
# 后台运行(使用 nohup)
nohup python main.py --schedule > /dev/null 2>&1 &
# 启动 Web 管理界面(云服务器需先在 .env 中设置 WEBUI_HOST=0.0.0.0)
python main.py --webui-only
# 启动 Web 界面(启动时执行一次分析;需每日定时请加 --schedule 或设 SCHEDULE_ENABLED=true)
python main.py --webui不知道怎么访问?→ 云服务器 Web 界面访问指南
🔧 方案三:Systemd 服务
创建 systemd 服务文件实现开机自启和自动重启:
1. 创建服务文件
sudo vim /etc/systemd/system/stock-analyzer.service内容:
[Unit]
Description=A股自选股智能分析系统
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/stock-analyzer
Environment="PATH=/opt/stock-analyzer/venv/bin"
ExecStart=/opt/stock-analyzer/venv/bin/python main.py --schedule
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target2. 启动服务
# 重载配置
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start stock-analyzer
# 开机自启
sudo systemctl enable stock-analyzer
# 查看状态
sudo systemctl status stock-analyzer
# 查看日志
journalctl -u stock-analyzer -f⚙️ 配置说明
必须配置项
| 配置项 | 说明 | 获取方式 |
|---|---|---|
ANSPIRE_API_KEYS / AIHUBMIX_KEY / GEMINI_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY |
AI 模型至少配置一个;推荐优先 Anspire 或 AIHubMix | 对应服务商控制台 |
STOCK_LIST |
自选股列表 | 逗号分隔的股票代码;已登记指数显式形态(如 sh000016、930606.CSI、sz399365)经一次性 --stocks 或 GitHub Actions 入口支持(本地 .env/Docker 无参数默认运行保持股票语义,指数需配 --stocks),规则见 指数自选股配置 |
| 通知渠道 | 至少配置一个,如企业微信、飞书、Telegram 或邮件 | 对应通知平台 |
可选配置项
| 配置项 | 默认值 | 说明 |
|---|---|---|
SCHEDULE_ENABLED |
false |
是否启用定时任务 |
SCHEDULE_TIME |
18:00 |
每日执行时间 |
MARKET_REVIEW_ENABLED |
true |
是否启用大盘复盘 |
ANSPIRE_API_KEYS |
- | Anspire 大模型与新闻搜索(推荐) |
AIHUBMIX_KEY |
- | AIHubMix 一 Key 多模型(推荐) |
SERPAPI_API_KEYS |
- | SerpAPI 实时金融新闻搜索(推荐) |
TAVILY_API_KEYS |
- | Tavily 新闻搜索(可选) |
MINIMAX_API_KEYS |
- | MiniMax 搜索(可选) |
🌐 代理配置
如果服务器在国内,访问 Gemini API 需要代理:
Docker 方式
编辑 docker-compose.yml:
environment:
- http_proxy=http://your-proxy:port
- https_proxy=http://your-proxy:port直接部署方式
编辑 main.py 顶部:
os.environ["http_proxy"] = "http://your-proxy:port"
os.environ["https_proxy"] = "http://your-proxy:port"📊 监控与维护
日志查看
# Docker 方式
docker-compose -f ./docker/docker-compose.yml logs -f --tail=100
# 直接部署
tail -f /opt/stock-analyzer/logs/stock_analysis_*.log健康检查
# 检查进程
ps aux | grep main.py
# 检查最近的报告
ls -la /opt/stock-analyzer/reports/定期维护
# 清理旧日志(保留7天)
find /opt/stock-analyzer/logs -mtime +7 -delete
# 清理旧报告(保留30天)
find /opt/stock-analyzer/reports -mtime +30 -delete❓ 常见问题
1. Docker 构建失败
# 清理缓存重新构建
docker-compose -f ./docker/docker-compose.yml build --no-cache2. API 访问超时
检查代理配置,确保服务器能访问 Gemini API。
3. 数据库锁定
# 停止服务后删除 lock 文件
rm /opt/stock-analyzer/data/*.lock4. 内存不足
默认 Compose 已推荐 1G。如果仍出现 OOM 或平台杀掉容器,请提高 docker-compose.yml 中的内存限制;同时跑 server + analyzer、多股票、大盘复盘、图片报告或选股时建议 2G+:
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M低配环境只能使用 512M 时,建议设置 MAX_WORKERS=1,只启动 server 或 analyzer 其中一个服务,并减少非必要的大盘复盘、新闻扩展和图片报告任务。
5. WebUI 打开后 UI 元素异常变大 / 布局错乱
症状:能访问 8000 端口,但页面上的文字、按钮、卡片异常放大,没有正常布局。
根因:static/index.html 存在,但 CSS/JS 资源文件缺失(static/assets/ 为空或不存在),浏览器无法加载样式与脚本,导致裸 HTML 渲染。
解决方法:
Docker 部署:执行以下命令重新构建镜像(确保前端已正确打包进镜像):
bashdocker-compose -f ./docker/docker-compose.yml down docker-compose -f ./docker/docker-compose.yml build --no-cache docker-compose -f ./docker/docker-compose.yml up -d构建完成后刷新浏览器缓存(
Ctrl+Shift+R)再访问。直接部署(pip + python):先构建前端,再启动服务:
bash# 安装 Node.js 18+(推荐 20+,如尚未安装) # 构建前端 cd apps/dsa-web npm ci npm run build cd ../.. # 启动服务 python main.py --webui-only
验证:用浏览器开发者工具(F12 → Network)检查是否有 /assets/index-*.js 和 /assets/index-*.css 的 404 错误;如有,说明资源缺失,按上述步骤重新构建即可。
🔄 快速迁移
从一台服务器迁移到另一台:
# 源服务器:打包
cd /opt/stock-analyzer
tar -czvf stock-analyzer-backup.tar.gz .env data/ logs/ reports/
# 目标服务器:部署
mkdir -p /opt/stock-analyzer
cd /opt/stock-analyzer
git clone <your-repo-url> .
tar -xzvf stock-analyzer-backup.tar.gz
docker-compose -f ./docker/docker-compose.yml up -d☁️ 方案四:GitHub Actions 部署(免服务器)
最简单的方案! 无需服务器,利用 GitHub 免费计算资源。
优势
- ✅ 完全免费(每月 2000 分钟)
- ✅ 无需服务器
- ✅ 自动定时执行
- ✅ 零维护成本
限制
- ⚠️ 无状态(每次运行是新环境)
- ⚠️ 定时可能有几分钟延迟
- ⚠️ 无法提供 HTTP API
部署步骤
1. 创建 GitHub 仓库
# 初始化 git(如果还没有)
cd /path/to/daily_stock_analysis
git init
git add .
git commit -m "Initial commit"
# 创建 GitHub 仓库并推送
# 在 GitHub 网页上创建新仓库后:
git remote add origin https://github.com/你的用户名/daily_stock_analysis.git
git branch -M main
git push -u origin main2. 配置 Secrets(重要!)
打开仓库页面 → Settings → Secrets and variables → Actions → New repository secret
添加以下 Secrets:
| Secret 名称 | 说明 | 必填 |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire Open API Key(一 Key 启用大模型与搜索) | 推荐 |
AIHUBMIX_KEY |
AIHubMix API Key(一 Key 多模型) | 推荐 |
ANTHROPIC_API_KEY |
Anthropic API Key | 可选 |
GEMINI_API_KEY |
Gemini AI API Key | 可选 |
OPENAI_API_KEY |
OpenAI 兼容 API Key | 可选 |
WECHAT_WEBHOOK_URL |
企业微信机器人 Webhook | 可选* |
FEISHU_WEBHOOK_URL |
飞书机器人 Webhook | 可选* |
TELEGRAM_BOT_TOKEN |
Telegram Bot Token | 可选* |
TELEGRAM_CHAT_ID |
Telegram Chat ID | 可选* |
TELEGRAM_MESSAGE_THREAD_ID |
Telegram Topic ID | 可选* |
EMAIL_SENDER |
发件人邮箱 | 可选* |
EMAIL_PASSWORD |
邮箱授权码 | 可选* |
SERVERCHAN3_SENDKEY |
Server酱³ Sendkey | 可选* |
CUSTOM_WEBHOOK_URLS |
自定义 Webhook(多个逗号分隔) | 可选* |
STOCK_LIST |
自选股列表,如 600519,300750 |
✅ |
SERPAPI_API_KEYS |
SerpAPI Key | 推荐 |
TAVILY_API_KEYS |
Tavily 搜索 API Key | 可选 |
BOCHA_API_KEYS |
博查搜索 API Key | 可选 |
BRAVE_API_KEYS |
Brave Search API Key | 可选 |
MINIMAX_API_KEYS |
MiniMax Coding Plan Web Search | 可选 |
SEARXNG_BASE_URLS |
SearXNG 自建实例(无配额兜底,需在 settings.yml 启用 format: json);留空时仅在显式启用公共实例发现后使用 searx.space |
可选 |
SEARXNG_PUBLIC_INSTANCES_ENABLED |
是否在 SEARXNG_BASE_URLS 为空时自动从 searx.space 获取公共实例(默认 false) |
可选 |
TUSHARE_TOKEN |
Tushare Token | 可选 |
GEMINI_MODEL |
模型名称(默认 gemini-2.0-flash) | 可选 |
*注:通知渠道至少配置一个,支持多渠道同时推送
3. 验证 Workflow 文件
确保 .github/workflows/00-daily-analysis.yml 文件存在且已提交:
git add .github/workflows/00-daily-analysis.yml
git commit -m "Add GitHub Actions workflow"
git push4. 手动测试运行
- 打开仓库页面 → Actions 标签
- 选择 "每日股票分析" workflow
- 点击 "Run workflow" 按钮
- 选择运行模式:
full- 完整分析(股票+大盘)market-only- 仅大盘复盘stocks-only- 仅股票分析
- 点击绿色 "Run workflow" 按钮
5. 查看执行日志
- Actions 页面可以看到运行历史
- 点击具体的运行记录查看详细日志
- 分析报告会作为 Artifact 保存 30 天
定时说明
默认配置:周一到周五,北京时间 18:00 自动执行
修改时间:编辑 .github/workflows/00-daily-analysis.yml 中的 cron 表达式:
schedule:
- cron: '0 10 * * 1-5' # UTC 时间,+8 = 北京时间常用 cron 示例:
| 表达式 | 说明 |
|---|---|
'0 10 * * 1-5' |
周一到周五 18:00(北京时间) |
'30 7 * * 1-5' |
周一到周五 15:30(北京时间) |
'0 10 * * *' |
每天 18:00(北京时间) |
'0 2 * * 1-5' |
周一到周五 10:00(北京时间) |
修改自选股
方法一:修改仓库 Secret STOCK_LIST
方法二:直接修改代码后推送:
# 修改 .env.example 或在代码中设置默认值
git commit -am "Update stock list"
git push常见问题
Q: 为什么定时任务没有执行?
A: GitHub Actions 定时任务可能有 5-15 分钟延迟,且仅在仓库有活动时才触发。长时间无 commit 可能导致 workflow 被禁用。
Q: 如何查看历史报告?
A: Actions → 选择运行记录 → Artifacts → 下载 analysis-reports-xxx
Q: 免费额度够用吗?
A: 每次运行约 2-5 分钟,一个月 22 个工作日 = 44-110 分钟,远低于 2000 分钟限制。
🌐 云服务器上部署了,但不知道怎么用浏览器访问?
详见 → 云服务器 Web 界面访问指南
涵盖:直接部署和 Docker 两种方式的启动与访问、安全组/防火墙配置、常见问题排查、Nginx 反向代理(可选)。
祝部署顺利!🎉
DEPLOY EN (`docs/DEPLOY_EN.md`)
Deployment Guide
This document explains how to deploy the AI Stock Analysis System to a server.
Deployment Options Comparison
| Option | Pros | Cons | Recommended For |
|---|---|---|---|
| Docker Compose ⭐ | One-click deploy, isolated environment, easy migration, easy upgrade | Requires Docker installation | Recommended: Most scenarios |
| Direct Deployment | Simple, no extra dependencies | Environment dependencies, migration difficulties | Temporary testing |
| Systemd Service | System-level management, auto-start on boot | Complex configuration | Long-term stable operation |
| Supervisor | Process management, auto-restart | Requires additional installation | Multi-process management |
Conclusion: Docker Compose is recommended for the fastest and most convenient migration!
Option 1: Docker Compose Deployment (Recommended)
1. Install Docker
# Ubuntu/Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# CentOS
sudo yum install -y docker docker-compose
sudo systemctl start docker
sudo systemctl enable docker2. Prepare Configuration Files
# Clone code (or upload code to server)
git clone <your-repo-url> /opt/stock-analyzer
cd /opt/stock-analyzer
# Copy and edit configuration file
cp .env.example .env
vim .env # Fill in real API Keys and configuration3. One-Click Start
# Build and start
docker-compose -f ./docker/docker-compose.yml up -d
# View logs
docker-compose -f ./docker/docker-compose.yml logs -f
# View running status
docker-compose -f ./docker/docker-compose.yml ps3.1 Resource Recommendations
The default docker/docker-compose.yml sets limits.memory: 1G and reservations.memory: 512M for each service. Treat this as the recommended starting point for full analysis workloads.
- Minimum trial:
512M, only for lightweight Web/API usage, single-stock runs, and low concurrency. SetMAX_WORKERS=1. - Recommended:
1G, suitable for normal analysis when running eitherserveroranalyzer. - Heavy workloads:
2G+, suitable when runningserver + analyzertogether, multi-stock analysis, defaultMAX_WORKERS=3, market review, news expansion, image reports, or screening.
If you can only use 512M, avoid starting both server and analyzer, and disable non-essential market review, news expansion, and image report features.
3.2 Avoid Duplicate Scheduled Notifications
Prefer one automatic scheduler: either the standalone analyzer, or server with SCHEDULE_ENABLED=true. Check for old NAS --schedule containers and duplicate deployments. Instances with separate databases cannot deduplicate each other.
Daily CLI and Web/API jobs atomically claim their due occurrence in the same SQLite file configured by DATABASE_PATH. The same due instant, watchlist/portfolio source, analysis/delivery options, and report settings dispatch once, including when a second process triggers after the first finishes. Different slots, watchlists, or analysis/delivery options remain independent. Due times use the scheduler process's timezone and are normalized to UTC; keep TZ consistent within a deployment. Both official Compose services share data/ by default, including the claim records.
This is at-most-once automatic dispatch, not automatic failure retry or guaranteed notification delivery. A crash, timeout, or analysis failure after claiming (possibly after partial notification delivery) does not cause another scheduler to repeat the whole run. Inspect the outcome, then use the Web “Run now” action to retry manually. Claiming happens at the actual execution boundary and freezes the watchlist snapshot, avoiding races with settings edits during asynchronous startup. Web dispatches rejected as busy never acquire a claim. Immediate startup runs, manual CLI/API analysis, and “Run now” bypass this deduplication. Records are retained for approximately seven days. An unwritable/busy database logs an error and skips that occurrence; Web scheduler status reports claim errors.
This relies on reliable SQLite file locking and covers same-host processes and containers sharing one local bind mount. It does not promise distributed deduplication across separate databases, NFS, or multiple hosts. No new setting or historical analysis-data migration is required.
4. Common Management Commands
# Stop services
docker-compose -f ./docker/docker-compose.yml down
# Restart services
docker-compose -f ./docker/docker-compose.yml restart
# Redeploy after code update
git pull
docker-compose -f ./docker/docker-compose.yml build --no-cache
docker-compose -f ./docker/docker-compose.yml up -d
# Enter container for debugging
docker-compose -f ./docker/docker-compose.yml exec -u dsa stock-analyzer bash
# Manually run analysis once
docker-compose -f ./docker/docker-compose.yml exec -u dsa stock-analyzer python main.py --no-notify5. Data Persistence
Data is automatically saved to host directories:
./data/- Database files./logs/- Log files./reports/- Analysis reports
6. Permissions
The Docker image startup entrypoint automatically creates and fixes ownership for the mounted ./data, ./logs, and ./reports directories, then drops privileges to the non-root dsa user (UID 1000). Normal deployments do not require manual host-side chown / chmod.
If you explicitly set --user / Compose user:, or use read-only mounts, rootless Docker, NFS, or another environment that prevents the container from fixing ownership, make sure the actual runtime user can write to these directories.
Option 2: Direct Deployment
1. Install Python Environment
# Install Python 3.10+
sudo apt update
sudo apt install -y python3.10 python3.10-venv python3-pip
# Create virtual environment
python3.10 -m venv /opt/stock-analyzer/venv
source /opt/stock-analyzer/venv/bin/activate2. Install Dependencies
cd /opt/stock-analyzer
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple3. Configure Environment Variables
cp .env.example .env
vim .env # Fill in configuration4. Run
# Single run
python main.py
# Scheduled task mode (foreground)
python main.py --schedule
# Background run (using nohup)
nohup python main.py --schedule > /dev/null 2>&1 &Option 3: Systemd Service
Create systemd service file for auto-start on boot and auto-restart:
1. Create Service File
sudo vim /etc/systemd/system/stock-analyzer.serviceContents:
[Unit]
Description=AI Stock Analysis System
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/stock-analyzer
Environment="PATH=/opt/stock-analyzer/venv/bin"
ExecStart=/opt/stock-analyzer/venv/bin/python main.py --schedule
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target2. Start Service
# Reload configuration
sudo systemctl daemon-reload
# Start service
sudo systemctl start stock-analyzer
# Enable auto-start on boot
sudo systemctl enable stock-analyzer
# View status
sudo systemctl status stock-analyzer
# View logs
journalctl -u stock-analyzer -fConfiguration Guide
Required Configuration
| Config Item | Description | How to Get |
|---|---|---|
ANSPIRE_API_KEYS / AIHUBMIX_KEY / GEMINI_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY |
Configure at least one AI model key; Anspire or AIHubMix is recommended first | Provider console |
STOCK_LIST |
Watchlist | Comma-separated stock codes; registered-index explicit forms (e.g. sh000016, 930606.CSI, sz399365) are supported via the one-shot --stocks argument or the GitHub Actions entry (local .env/Docker no-arg default runs keep stock semantics; use --stocks there for indices) — see Index watchlist configuration |
| Notification channel | Configure at least one, such as WeChat Work, Feishu, Telegram, or email | Notification provider |
Optional Configuration
| Config Item | Default | Description |
|---|---|---|
SCHEDULE_ENABLED |
false |
Enable scheduled tasks |
SCHEDULE_TIME |
18:00 |
Daily execution time |
MARKET_REVIEW_ENABLED |
true |
Enable market review |
ANSPIRE_API_KEYS |
- | Anspire LLM and news search (recommended) |
AIHUBMIX_KEY |
- | AIHubMix one-key multi-model access (recommended) |
SERPAPI_API_KEYS |
- | SerpAPI realtime financial news search (recommended) |
TAVILY_API_KEYS |
- | Tavily news search (optional) |
MINIMAX_API_KEYS |
- | MiniMax search (optional) |
Proxy Configuration
If server is in mainland China, accessing Gemini API requires proxy:
Docker Method
Edit docker-compose.yml:
environment:
- http_proxy=http://your-proxy:port
- https_proxy=http://your-proxy:portDirect Deployment Method
Edit top of main.py:
os.environ["http_proxy"] = "http://your-proxy:port"
os.environ["https_proxy"] = "http://your-proxy:port"Monitoring & Maintenance
View Logs
# Docker method
docker-compose -f ./docker/docker-compose.yml logs -f --tail=100
# Direct deployment
tail -f /opt/stock-analyzer/logs/stock_analysis_*.logHealth Check
# Check process
ps aux | grep main.py
# Check recent reports
ls -la /opt/stock-analyzer/reports/Routine Maintenance
# Clean old logs (keep 7 days)
find /opt/stock-analyzer/logs -mtime +7 -delete
# Clean old reports (keep 30 days)
find /opt/stock-analyzer/reports -mtime +30 -deleteFAQ
1. Docker build failed
# Clear cache and rebuild
docker-compose -f ./docker/docker-compose.yml build --no-cache2. API access timeout
Check proxy configuration, ensure server can access Gemini API.
3. Database locked
# Stop service then delete lock file
rm /opt/stock-analyzer/data/*.lock4. Insufficient memory
The default Compose recommendation is already 1G. If the container still hits OOM or is killed by the platform, raise the memory limit in docker-compose.yml; use 2G+ when running server + analyzer together, multi-stock analysis, market review, image reports, or screening:
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512MFor a constrained 512M deployment, set MAX_WORKERS=1, start only one of server or analyzer, and reduce non-essential market review, news expansion, and image report tasks.
Quick Migration
Migrate from one server to another:
# Source server: Package
cd /opt/stock-analyzer
tar -czvf stock-analyzer-backup.tar.gz .env data/ logs/ reports/
# Target server: Deploy
mkdir -p /opt/stock-analyzer
cd /opt/stock-analyzer
git clone <your-repo-url> .
tar -xzvf stock-analyzer-backup.tar.gz
docker-compose -f ./docker/docker-compose.yml up -dOption 4: GitHub Actions Deployment (Serverless)
The simplest option! No server needed, leverages GitHub's free compute resources.
Advantages
- ✅ Completely free (2000 minutes/month)
- ✅ No server needed
- ✅ Auto-scheduled execution
- ✅ Zero maintenance cost
Limitations
- ⚠️ Stateless (fresh environment each run)
- ⚠️ Scheduled timing may have few minutes delay
- ⚠️ Cannot provide HTTP API
Deployment Steps
1. Create GitHub Repository
# Initialize git (if not already)
cd /path/to/daily_stock_analysis
git init
git add .
git commit -m "Initial commit"
# Create GitHub repo and push
# After creating new repo on GitHub web:
git remote add origin https://github.com/your-username/daily_stock_analysis.git
git branch -M main
git push -u origin main2. Configure Secrets (Important!)
Go to repo page → Settings → Secrets and variables → Actions → New repository secret
Add these Secrets:
| Secret Name | Description | Required |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire Open API Key (one key for LLM and search) | Recommended |
AIHUBMIX_KEY |
AIHubMix API Key (one key for multiple model families) | Recommended |
ANTHROPIC_API_KEY |
Anthropic API Key | Optional |
GEMINI_API_KEY |
Gemini AI API Key | Optional |
OPENAI_API_KEY |
OpenAI-compatible API Key | Optional |
WECHAT_WEBHOOK_URL |
WeChat Work Bot Webhook | Optional* |
FEISHU_WEBHOOK_URL |
Feishu Bot Webhook | Optional* |
TELEGRAM_BOT_TOKEN |
Telegram Bot Token | Optional* |
TELEGRAM_CHAT_ID |
Telegram Chat ID | Optional* |
TELEGRAM_MESSAGE_THREAD_ID |
Telegram Topic ID | Optional* |
EMAIL_SENDER |
Sender email | Optional* |
EMAIL_PASSWORD |
Email authorization code | Optional* |
SERVERCHAN3_SENDKEY |
ServerChan v3 Sendkey | Optional* |
CUSTOM_WEBHOOK_URLS |
Custom Webhook (comma-separated for multiple) | Optional* |
STOCK_LIST |
Watchlist, e.g., 600519,300750 |
✅ |
SERPAPI_API_KEYS |
SerpAPI Key | Recommended |
TAVILY_API_KEYS |
Tavily Search API Key | Optional |
BOCHA_API_KEYS |
Bocha Search API Key | Optional |
BRAVE_API_KEYS |
Brave Search API Key | Optional |
MINIMAX_API_KEYS |
MiniMax Coding Plan Web Search | Optional |
TUSHARE_TOKEN |
Tushare Token | Optional |
GEMINI_MODEL |
Model name (default gemini-2.0-flash) | Optional |
*Note: Configure at least one notification channel, multiple channels supported for simultaneous push
3. Verify Workflow File
Ensure .github/workflows/00-daily-analysis.yml file exists and is committed:
git add .github/workflows/00-daily-analysis.yml
git commit -m "Add GitHub Actions workflow"
git push4. Manual Test Run
- Go to repo page → Actions tab
- Select "Daily Stock Analysis" workflow
- Click "Run workflow" button
- Select run mode:
full- Full analysis (stocks + market)market-only- Market review onlystocks-only- Stock analysis only
- Click green "Run workflow" button
5. View Execution Logs
- Actions page shows run history
- Click specific run record to view detailed logs
- Analysis reports are saved as Artifacts for 30 days
Schedule Details
Default configuration: Monday to Friday, 18:00 Beijing Time auto-execution
Modify time: Edit cron expression in .github/workflows/00-daily-analysis.yml:
schedule:
- cron: '0 10 * * 1-5' # UTC time, +8 = Beijing timeCommon cron examples:
| Expression | Description |
|---|---|
'0 10 * * 1-5' |
Mon-Fri 18:00 (Beijing) |
'30 7 * * 1-5' |
Mon-Fri 15:30 (Beijing) |
'0 10 * * *' |
Daily 18:00 (Beijing) |
'0 2 * * 1-5' |
Mon-Fri 10:00 (Beijing) |
Modify Watchlist
Method 1: Modify repo Secret STOCK_LIST
Method 2: Modify code directly then push:
# Modify .env.example or set default value in code
git commit -am "Update stock list"
git pushFAQ
Q: Why isn't the scheduled task running?
A: GitHub Actions scheduled tasks may have 5-15 minute delays, and only trigger when repo has activity. Long periods without commits may cause workflow to be disabled.
Q: How to view historical reports?
A: Actions → Select run record → Artifacts → Download analysis-reports-xxx
Q: Is the free quota enough?
A: Each run takes about 2-5 minutes, 22 workdays per month = 44-110 minutes, well below the 2000 minute limit.
Wishing you a smooth deployment!
FAQ (`docs/FAQ.md`)
❓ 常见问题解答 (FAQ)
本文档整理了用户在使用过程中遇到的常见问题及解决方案。
📊 数据相关
Q1: 美股代码(如 AMD, AAPL)分析时价格显示不正确?
现象:输入美股代码后,显示的价格明显不对(如 AMD 显示 7.33 元),或被误识别为 A 股。
原因:早期版本代码匹配逻辑优先尝试国内 A 股规则,导致代码冲突。
解决方案:
- 已在 v2.3.0 修复,系统现在支持美股代码自动识别
- 如仍有问题,可在
.env中设置:这将优先使用 Yahoo Finance 数据源获取美股数据bashYFINANCE_PRIORITY=0
📌 相关 Issue: #153
Q2: 报告中"量比"字段显示为空或 N/A?
现象:分析报告中量比数据缺失,影响 AI 对缩放量的判断。
原因:默认的某些实时行情源(如新浪接口)不提供量比字段。
解决方案:
- 已在 v2.3.0 修复,腾讯接口现已支持量比解析
- 推荐配置实时行情源优先级:bash
REALTIME_SOURCE_PRIORITY=tencent,akshare_sina,efinance,akshare_em - 系统已内置 5 日均量计算作为兜底逻辑
📌 相关 Issue: #155
Q3: Tushare 获取数据失败,提示 Token 不对?
现象:日志显示 Tushare 获取数据失败: 您的token不对,请确认
解决方案:
- 无 Tushare 账号:无需配置
TUSHARE_TOKEN,系统会自动使用免费数据源(AkShare、Efinance) - 有 Tushare 账号:确认 Token 是否正确,可在 Tushare Pro 个人中心查看
- 本项目所有核心功能均可在无 Tushare 的情况下正常运行
Q4: 数据获取被限流或返回为空?
现象:日志显示 熔断器触发 或数据返回 None,或出现 RemoteDisconnected、push2his.eastmoney.com 连接被关闭等
原因:免费数据源(东方财富、新浪等)有反爬机制,短时间大量请求会被限流。
解决方案:
- 系统已内置多数据源自动切换和熔断保护
- 减少自选股数量,或增加请求间隔
- 避免频繁手动触发分析
- 若东财接口频繁失败,可设置
ENABLE_EASTMONEY_PATCH=true启用东财补丁(注入 NID 令牌与随机 User-Agent,降低被限流概率) - 将
MAX_WORKERS=1改为串行获取,减少对东财的并发压力
⚙️ 配置相关
Q5: GitHub Actions 运行失败,提示找不到环境变量?
现象:Actions 日志显示 GEMINI_API_KEY 或 STOCK_LIST 未定义
原因:GitHub 区分 Secrets(加密)和 Variables(普通变量),配置位置不对会导致读取失败。
解决方案:
- 进入仓库
Settings→Secrets and variables→Actions - Secrets(点击
New repository secret):存放敏感信息GEMINI_API_KEYOPENAI_API_KEYTELEGRAM_BOT_TOKEN- 各类 Webhook URL
- Variables(点击
Variables标签):存放非敏感配置STOCK_LISTGEMINI_MODELREPORT_TYPE
兼容说明:每日分析 workflow 也会绑定名为
STOCK_LIST的 Environment,因此误把STOCK_LIST填到该 Environment variables 中也能被读取;但推荐位置仍是 Repository variables。除非你希望每日任务等待人工审批,否则不要给该 Environment 配置 required reviewers、wait timer 或部署分支限制。
Q6: 修改 .env 文件后配置没有生效?
解决方案:
- 确保
.env文件位于项目根目录 - Docker 部署 / WebUI 系统设置:
--env-file .env/ Composeenv_file只会把宿主机.env作为启动环境变量注入容器,不会自动创建或回写容器内/app/.env- WebUI 设置页会在当前活跃
.env文件缺少某些键时展示启动注入的同名环境变量作为兜底;但“导出.env”仍只导出当前活跃配置文件内容 - WebUI 保存后的
STOCK_LIST、SCHEDULE_ENABLED、SCHEDULE_TIME、SCHEDULE_TIMES、SCHEDULE_RUN_IMMEDIATELY、RUN_IMMEDIATELY会写回容器内的.env - WebUI 保存后会触发当前进程的配置重载;运行中的读取路径会同步使用最新写回的
.env,例如定时任务会继续热读取保存后的STOCK_LIST - 如果容器启动命令里传入了这些同名环境变量(如
--env-file .env、docker run -e ...或 Composeenvironment:),后续重启时仍可能以启动环境变量为准;要让 WebUI 保存值接管,请同步更新或移除这些同名 override - 如需持久化 WebUI 保存的配置,请将
ENV_FILE指向/app/data/runtime.env等可写数据卷文件,不要把宿主机.env单文件挂载到/app/.env SCHEDULE_ENABLED、SCHEDULE_TIME、SCHEDULE_TIMES保存后会让 WebUI/API/Desktop 长运行进程按新配置启停或重建 runtime scheduler;重启--serve-only或 Desktop 进程时会恢复已启用的 daily jobs,但不会在启动时立即执行分析SCHEDULE_RUN_IMMEDIATELY与RUN_IMMEDIATELY仍属于启动期/一次性运行配置,保存后不会立即触发一次分析
- Docker 手工改
.env后:修改后仍建议重启容器bashdocker-compose down && docker-compose up -d - GitHub Actions:
.env文件不生效,必须在 Secrets/Variables 中配置 - 检查是否有多个
.env文件(如.env.local)导致覆盖
Q7: 如何配置代理访问 Gemini/OpenAI API?
解决方案:
在 .env 中配置:
USE_PROXY=true
PROXY_HOST=127.0.0.1
PROXY_PORT=10809⚠️ 注意:代理配置仅对本地运行生效,GitHub Actions 环境无需配置代理。
LLM 配置常见问题
完整说明见 LLM 配置指南。
Q: 配置了 GEMINI_API_KEY 和 LLM_CHANNELS,为什么只用渠道?
系统按优先级只取一种:高级模型路由 YAML(LITELLM_CONFIG)> LLM_CHANNELS > legacy keys。但 YAML 仅在文件可正常解析且产出了有效 model_list 时才生效;如果 YAML 路径无效或内容为空,系统会自动回退到 LLM_CHANNELS 或 legacy keys。一旦某一层级实际生效,更低优先级的配置不参与解析。
Q: check_env 输出“未配置可用 AI 模型”怎么办?
默认先选一种服务商并填写对应 API Key;如果需要固定主模型,再补 LITELLM_MODEL=provider/model;如果要多模型切换,再配置 LLM_CHANNELS 或高级模型路由 YAML。运行 python scripts/check_env.py --config 校验配置,python scripts/check_env.py --llm 实际调用 API 测试。
Q: 如何同时使用多个模型(如 AIHubmix + DeepSeek + Gemini)?
使用渠道模式:设置 LLM_CHANNELS=aihubmix,deepseek,gemini,并配置各渠道的 LLM_{NAME}_BASE_URL、LLM_{NAME}_API_KEY、LLM_{NAME}_MODELS。也可在 Web 设置页 → AI 模型 → AI 模型接入 中可视化配置。
Q: 问股/Agent 提示未配置可用 LLM,但我只有旧的 GEMINI_* / OPENAI_* / ANTHROPIC_* 配置,怎么办?
先确认当前是否启用了 LITELLM_CONFIG 或 LLM_CHANNELS;如果启用了,上层配置会覆盖 legacy keys。若你没有启用这两层,且 AGENT_LITELLM_MODEL 为空,问股 Agent 仍会自动继承 legacy provider 模型:GEMINI_MODEL、OPENAI_MODEL、ANTHROPIC_MODEL 分别映射到对应 provider 前缀的 LiteLLM 模型名。此次修复不会静默迁移或清空旧配置,只是把“真实缺失原因”直接返回到前端,便于你判断到底是缺 key、缺模型名,还是被上层配置覆盖。完整兼容语义见 LLM 配置指南 中“问股 Agent / LiteLLM 配置兼容说明”。
📱 推送相关
Q8: 机器人推送失败,提示消息过长?
现象:分析成功但未收到推送,日志显示 400 错误或 Message too long
原因:不同平台消息长度限制不同:
- 企业微信:4KB
- 飞书:20KB
- 钉钉:20KB
解决方案:
- 自动分块:最新版本已实现长消息自动切割
- 单股推送模式:设置
SINGLE_STOCK_NOTIFY=true,每分析完一只股票立即推送 - 精简报告:设置
REPORT_TYPE=simple使用精简格式 - 仅落盘本地报告:即使未配置任何通知渠道,
SINGLE_STOCK_NOTIFY=true仍会把单股报告保存到reports/report_YYYYMMDD_<股票代码>.md
Q8.1: 分析结束了,但 `reports/` 里没有生成报告文件?
常见原因:
STOCK_LIST为空,且本轮未启用大盘复盘- 股票列表非空,但个股分析全部失败,最终没有成功结果
- 个股结果已生成,但写入
reports/时失败(如目录权限或挂载问题)
现在的行为:
- CLI 启动分析会对上述场景显式记录失败原因
- 非
--serve的独立运行模式会返回非零退出码,避免工作流把“未生成报告”误判为成功 - 若是单股推送模式且通知未配置,仍会继续保存本地 Markdown 报告用于排查
Q9: Telegram 推送收不到消息?
解决方案:
- 确认
TELEGRAM_BOT_TOKEN和TELEGRAM_CHAT_ID都已配置 - 获取 Chat ID 方法:
- 给 Bot 发送任意消息
- 访问
https://api.telegram.org/bot<TOKEN>/getUpdates - 在返回的 JSON 中找到
chat.id
- 确保 Bot 已被添加到目标群组(如果是群聊)
- 本地运行时需要能访问 Telegram API(可能需要代理)
Q10: 企业微信 Markdown 格式显示不正常?
解决方案:
- 企业微信对 Markdown 支持有限,可尝试设置:bash
WECHAT_MSG_TYPE=text - 这将发送纯文本格式的消息
🤖 AI 模型相关
Q11: Gemini API 返回 429 错误(请求过多)?
现象:日志显示 Resource has been exhausted 或 429 Too Many Requests
解决方案:
- Gemini 免费版有速率限制(约 15 RPM)
- 减少同时分析的股票数量
- 增加请求延迟:bash
GEMINI_REQUEST_DELAY=5 ANALYSIS_DELAY=10 - 或切换到 OpenAI 兼容 API 作为备选
Q12: 如何使用 DeepSeek 等国产模型?
配置方法:
# 不需要配置 GEMINI_API_KEY
OPENAI_API_KEY=sk-xxxxxxxx
OPENAI_BASE_URL=https://api.deepseek.com
OPENAI_MODEL=deepseek-v4-flash
# deepseek-chat / deepseek-reasoner 仍兼容,但官方已标记为 2026/07/24 后废弃支持的模型服务:
- DeepSeek:
https://api.deepseek.com - 通义千问:
https://dashscope.aliyuncs.com/compatible-mode/v1 - Moonshot:
https://api.moonshot.cn/v1
Q12b: 如何使用 Ollama 本地模型?
配置方法:使用 OLLAMA_API_BASE + LITELLM_MODEL,或渠道模式(LLM_CHANNELS=ollama + LLM_OLLAMA_BASE_URL + LLM_OLLAMA_MODELS)。
避坑:不要使用 OPENAI_BASE_URL 配置 Ollama,否则系统会错误拼接 URL(如 404、api/generate/api/show)。详见 LLM 配置指南 示例 4 与渠道示例。
Q12c: 运行时报 `OllamaException / APIConnectionError`(All LLM models failed)怎么办?
症状:日志出现 litellm.APIConnectionError: OllamaException 或 Analysis failed: All LLM models failed (tried 1 model(s))。
逐项排查以下 5 个检查点:
Ollama 服务是否已启动
bash# 查看进程 pgrep -a ollama # 若无输出则先启动 ollama serve确认服务正在监听:
curl http://localhost:11434,应返回Ollama is running。OLLAMA_API_BASE是否配置正确- ✅ 正确:
OLLAMA_API_BASE=http://localhost:11434 - ❌ 错误:把 Ollama 地址填到
OPENAI_BASE_URL,会导致 URL 路径拼错(如…/api/generate/api/show)。
- ✅ 正确:
模型名称是否加了
ollama/前缀- ✅ 正确:
LITELLM_MODEL=ollama/qwen3:8b - ❌ 错误:
LITELLM_MODEL=qwen3:8b(缺少前缀,litellm 无法路由到 Ollama)
- ✅ 正确:
模型是否已下载到本地
bashollama list # 查看已有模型 ollama pull qwen3:8b # 如无则先拉取远程部署 / Docker 时的网络与防火墙
- 若 Ollama 和程序不在同一主机,需将
OLLAMA_API_BASE改为实际 IP,如http://192.168.1.100:11434。 - 确认防火墙已放行 11434 端口,且 Ollama 启动时绑定了正确地址(
OLLAMA_HOST=0.0.0.0:11434)。
- 若 Ollama 和程序不在同一主机,需将
完整配置示例见 LLM 配置指南 → 示例 4(Ollama)。
🐳 Docker 相关
Q13: Docker 容器启动后立即退出?
解决方案:
- 查看容器日志:bash
docker logs <container_id> - 常见原因:
- 环境变量未正确配置
.env文件格式错误(如有多余空格)- 依赖包版本冲突
Q14: Docker 中 API 服务无法访问?
解决方案:
- 确保启动命令包含
--host 0.0.0.0(不能是 127.0.0.1) - 检查端口映射是否正确:yaml
ports: - "8000:8000"
Q14.1: Docker 中网络/DNS 解析失败(如 api.tushare.pro、searchapi.eastmoney.com 无法解析)?
现象:日志显示 Temporary failure in name resolution 或 NameResolutionError,股票数据 API 和大模型 API 均无法访问。
原因:自定义 bridge 网络下,容器使用 Docker 内置 DNS,在旁路由、特定网络环境时可能解析失败。
解决方案(按优先级尝试):
显式配置 DNS:在
docker/docker-compose.yml的x-common下添加:yamldns: - 223.5.5.5 - 119.29.29.29 - 8.8.8.8然后执行
docker-compose down和docker-compose up -d --force-recreate重新创建容器。改用 host 网络模式:若上述仍无效,可在
server服务下添加network_mode: host,并移除ports映射。使用 host 模式时,ports无效,端口由command中的--port指定。若宿主机默认端口已占用,可修改为其他端口(如.env中设置API_PORT=8080),访问对应http://localhost:8080。
📌 相关 Issue: #372
Q14.2: Docker 安装时,软件版本号写在哪个文件里?
结论:对 Docker 用户来说,最权威的版本不是某个 Python 源文件常量,而是你实际使用的镜像 tag。
为什么:
- 仓库的 Docker 发布由
.github/workflows/docker-publish.yml触发,只有推送v*.*.*形式的 Git tag(例如v3.12.0)时才会生成对应发布镜像。 - 这意味着 Docker 镜像版本本质上跟随 GitHub Release / Git tag,而不是写死在
main.py、server.py或其他后端源码里。 apps/dsa-web/package.json里的version当前是占位值0.0.0,WebUI “版本信息”卡片更适合用来确认静态资源是否已重建,不应当作 Docker 发布版本。- 桌面端版本是单独维护的,写在
apps/dsa-desktop/package.json的version字段;它只代表 Electron 桌面端,不代表 Docker 镜像版本。
怎么查当前 Docker 版本:
- 先看部署命令或 Compose 文件里的镜像 tag:例如
ghcr.io/zhulinsen/daily_stock_analysis:v3.12.0,其中v3.12.0就是当前部署版本。 - 如果你拉的是
latest:请回看当时的docker pull/docker-compose.yml/ 部署脚本,或对照 GitHub Releases 确认对应发布记录。 - 如果只是想确认前端是否更新到新构建:可以打开 WebUI 的“系统设置”页查看
构建标识/构建时间;这能帮助确认静态资源是否刷新,但不等同于 Docker 镜像发布版本。
建议:如果你想避免重复更新,部署时尽量固定使用明确的版本 tag(如 v3.12.0),不要长期依赖 latest。
🖥️ 桌面端相关
Q15: macOS 提示“应用已损坏”或无法打开桌面端?
原因:当前 macOS DMG 尚未使用 Apple Developer 证书签名和公证。从浏览器下载后,macOS Gatekeeper 可能给应用添加 quarantine(下载隔离)属性并阻止启动。
解决方案:
只从项目的 GitHub Releases 下载附件,并确认安装包架构与 Mac 一致。不要对第三方转载或来源不明的应用绕过 Gatekeeper。
将
Daily Stock Analysis拖入“应用程序”,先在“系统设置 → 隐私与安全性”中尝试“仍要打开”。如果仍无法启动,并且已经确认文件来自项目官方 Release,可在终端只针对该应用移除 quarantine 属性并启动:
bashxattr -dr com.apple.quarantine "/Applications/Daily Stock Analysis.app" open "/Applications/Daily Stock Analysis.app"
如果应用不在 /Applications,请替换为实际 .app 路径。不要对整个 /Applications 目录执行 xattr。该命令只是临时放行受信任的 unsigned 应用,不等同于签名或公证;完整排查说明见 桌面端打包与发布。
📌 相关 Issue: #2113
🔧 其他问题
Q16: 如何只运行大盘复盘,不分析个股?
方法:
# 本地运行
python main.py --market-only
# GitHub Actions
# 手动触发时选择 mode: market-onlyQ17: 分析结果中买入/观望/卖出数量统计不对?
原因:早期版本使用正则匹配统计,可能与实际建议不一致。
解决方案:已在最新版本中修复,AI 模型现在会直接输出 decision_type 字段用于准确统计。
Q18: 为什么周末在 GitHub Actions 手动触发仍显示“非交易日跳过”?
现象:已经配置了 TRADING_DAY_CHECK_ENABLED 或希望手动运行,但日志仍提示“今日所有相关市场均为非交易日,跳过执行”。
解决方案:
- 打开
Actions → 每日股票分析 → Run workflow - 手动触发时将
force_run设为true(单次强制运行) - 如果希望长期关闭交易日检查,在
Settings → Secrets and variables → Actions中设置:bashTRADING_DAY_CHECK_ENABLED=false
规则说明:
TRADING_DAY_CHECK_ENABLED=true且force_run=false:非交易日跳过(默认)force_run=true:本次即使非交易日也执行TRADING_DAY_CHECK_ENABLED=false:定时和手动都不做交易日检查
💬 还有问题?
如果以上内容没有解决你的问题,欢迎:
- 查看 完整配置指南
- 搜索或提交 GitHub Issue
- 查看 更新日志 了解最新修复
最后更新:2026-08-03
FAQ EN (`docs/FAQ_EN.md`)
Frequently Asked Questions (FAQ)
This document compiles common issues encountered by users and their solutions.
Data Related
Q1: US stock codes (e.g., AMD, AAPL) show incorrect prices during analysis?
Symptom: After entering US stock codes, displayed prices are clearly wrong (e.g., AMD showing 7.33 yuan), or being misidentified as A-shares.
Cause: Earlier version code matching logic prioritized A-share rules, causing code conflicts.
Solution:
- Fixed in v2.3.0, system now supports automatic US stock code recognition
- If issues persist, set in
.env:This prioritizes Yahoo Finance data source for US stock databashYFINANCE_PRIORITY=0
Related Issue: #153
Q2: "Volume Ratio" field shows empty or N/A in reports?
Symptom: Volume ratio data missing in analysis reports, affecting AI's judgment on volume changes.
Cause: Some default real-time quote sources (e.g., Sina interface) don't provide volume ratio field.
Solution:
- Fixed in v2.3.0, Tencent interface now supports volume ratio parsing
- Recommended real-time quote source priority:bash
REALTIME_SOURCE_PRIORITY=tencent,akshare_sina,efinance,akshare_em - System has built-in 5-day average volume calculation as fallback
Related Issue: #155
Q3: Tushare data fetch failed, showing Token error?
Symptom: Log shows Tushare data fetch failed: Your token is incorrect, please verify
Solution:
- No Tushare account: No need to configure
TUSHARE_TOKEN, system will automatically use free data sources (AkShare, Efinance) - Have Tushare account: Verify Token is correct, check in Tushare Pro personal center
- All core features of this project work normally without Tushare
Q4: Data fetch rate-limited or returning empty?
Symptom: Log shows Circuit breaker triggered or data returns None
Cause: Free data sources (Eastmoney, Sina, etc.) have anti-scraping mechanisms, high-frequency requests get rate-limited.
Solution:
- System has built-in multi-source auto-switching and circuit breaker protection
- Reduce watchlist size, or increase request intervals
- Avoid frequently manually triggering analysis
Configuration Related
Q5: GitHub Actions run failed, showing environment variable not found?
Symptom: Actions log shows GEMINI_API_KEY or STOCK_LIST undefined
Cause: GitHub distinguishes Secrets (encrypted) and Variables (regular variables), wrong configuration location causes read failure.
Solution:
- Go to repo
Settings→Secrets and variables→Actions - Secrets (click
New repository secret): Store sensitive informationGEMINI_API_KEYOPENAI_API_KEYTELEGRAM_BOT_TOKEN- Various Webhook URLs
- Variables (click
Variablestab): Store non-sensitive configurationSTOCK_LISTGEMINI_MODELREPORT_TYPE
Compatibility note: the daily analysis workflow also binds the
STOCK_LISTenvironment, so aSTOCK_LISTvalue mistakenly added under that Environment's variables can still be read. Repository variables remain the recommended location. Unless you want the daily job to wait for manual approval, do not add required reviewers, wait timers, or deployment branch restrictions to this Environment.
Q6: Configuration not taking effect after modifying .env file?
Solution:
- Ensure
.envfile is in project root directory - Docker deployment / WebUI Settings:
--env-file .env/ Composeenv_fileonly injects the host.envas startup environment variables; it does not create or write back to/app/.envinside the container- When the active
.envfile does not contain a key, the WebUI Settings page falls back to showing the same key from startup-injected environment variables; the raw.envexport still contains only the active config file content - WebUI saves
STOCK_LIST,SCHEDULE_ENABLED,SCHEDULE_TIME,SCHEDULE_TIMES,SCHEDULE_RUN_IMMEDIATELY, andRUN_IMMEDIATELYback into the container's.env - Saving from WebUI triggers a config reload for the current process, and runtime reads continue from the latest persisted
.env; for example, scheduled runs keep hot-reading the savedSTOCK_LIST - If you pass the same keys as startup env vars (
--env-file .env,docker run -e ..., or Composeenvironment:), those startup values can still win on later restarts; update or remove the same-name overrides if you want the WebUI-saved.envvalues to take over - To persist WebUI-saved config, point
ENV_FILEat a writable data-volume file such as/app/data/runtime.env; do not bind-mount the host.envas a single file over/app/.env - Saving
SCHEDULE_ENABLED,SCHEDULE_TIME, orSCHEDULE_TIMESstarts, stops, or rebuilds the runtime scheduler in long-running WebUI/API/Desktop processes; restarting a--serve-onlyor Desktop process restores enabled daily jobs without immediately running an analysis at startup SCHEDULE_RUN_IMMEDIATELYandRUN_IMMEDIATELYremain startup/one-shot settings; saving them does not immediately trigger an analysis run
- Manual
.envedits in Docker: Restart the container after changesbashdocker-compose down && docker-compose up -d - GitHub Actions:
.envfile doesn't work, must configure in Secrets/Variables - Check if there are multiple
.envfiles (e.g.,.env.local) causing override
Q7: How to configure proxy to access Gemini/OpenAI API?
Solution:
Configure in .env:
USE_PROXY=true
PROXY_HOST=127.0.0.1
PROXY_PORT=10809Note: Proxy configuration only works for local runs, GitHub Actions environment doesn't need proxy.
LLM Configuration
Full details: LLM Config Guide.
Q: Configured both GEMINI_API_KEY and LLM_CHANNELS, why does it only use channels?
The system uses exactly one mode by priority: advanced YAML routing (LITELLM_CONFIG) > LLM_CHANNELS > legacy keys. However, YAML routing only takes effect when the file can be parsed successfully and yields a non-empty model_list; if the YAML path is invalid or the content is empty, the system automatically falls back to LLM_CHANNELS or legacy keys. Once a tier is active, lower-priority tiers are not used.
Q: check_env says no usable AI model is configured, what should I do?
Start with one provider and its API key. If you want to pin a primary model, add LITELLM_MODEL=provider/model. If you need multi-model switching, configure LLM_CHANNELS or advanced YAML routing. Run python scripts/check_env.py --config to validate config and python scripts/check_env.py --llm to actually call the API.
Q: How to use multiple models at once (e.g. AIHubmix + DeepSeek + Gemini)?
Use channel mode: set LLM_CHANNELS=aihubmix,deepseek,gemini and configure each channel's LLM_{NAME}_BASE_URL, LLM_{NAME}_API_KEY, LLM_{NAME}_MODELS. You can also configure this visually in Web Settings → AI Model → AI Model Access.
Q: The ask-stock / Agent page says no usable LLM is configured, but I only use legacy GEMINI_* / OPENAI_* / ANTHROPIC_* settings. What should I check?
First confirm whether LITELLM_CONFIG or LLM_CHANNELS is active, because either of those tiers overrides legacy keys. If neither tier is active and AGENT_LITELLM_MODEL is empty, the ask-stock Agent still inherits legacy provider models automatically: GEMINI_MODEL, OPENAI_MODEL, and ANTHROPIC_MODEL are mapped to LiteLLM provider-prefixed model names for the corresponding runtime. This fix does not silently migrate or clear old settings; it only returns the real backend reason to the frontend so you can see whether the issue is a missing key, a missing model name, or an upper-tier config taking precedence. Full compatibility details are documented in the LLM Config Guide under “Ask-Stock Agent / LiteLLM compatibility notes”.
Push Notification Related
Q8: Bot push failed, showing message too long?
Symptom: Analysis succeeded but no notification received, log shows 400 error or Message too long
Cause: Different platforms have different message length limits:
- WeChat Work: 4KB
- Feishu: 20KB
- DingTalk: 20KB
Solution:
- Auto-chunking: Latest version implements automatic long message splitting
- Single stock push mode: Set
SINGLE_STOCK_NOTIFY=true, push immediately after each stock analysis - Brief report: Set
REPORT_TYPE=simplefor simplified format - Local file fallback: Even with no notification channel configured,
SINGLE_STOCK_NOTIFY=truestill saves each stock report toreports/report_YYYYMMDD_<stock_code>.md
Q8.1: Analysis finished, but no report file was created under `reports/`?
Common causes:
STOCK_LISTis empty and market review was not enabled for this run- The stock list is non-empty, but every stock analysis failed so no successful result was produced
- Stock results were produced, but writing to
reports/failed (for example due to permissions or mount issues)
Current behavior:
- CLI-triggered analysis now logs the exact failure reason for these cases
- Standalone non-
--serveruns now return a non-zero exit code so workflows do not treat “no report generated” as success - In single-stock push mode, local Markdown report saving still happens even when notifications are not configured
Q9: Not receiving Telegram push messages?
Solution:
- Confirm both
TELEGRAM_BOT_TOKENandTELEGRAM_CHAT_IDare configured - How to get Chat ID:
- Send any message to the Bot
- Visit
https://api.telegram.org/bot<TOKEN>/getUpdates - Find
chat.idin the returned JSON
- Ensure Bot has been added to target group (if group chat)
- When running locally, need to be able to access Telegram API (may need proxy)
Q10: WeChat Work Markdown format not displaying correctly?
Solution:
- WeChat Work has limited Markdown support, try setting:bash
WECHAT_MSG_TYPE=text - This will send plain text format messages
AI Model Related
Q11: Gemini API returns 429 error (too many requests)?
Symptom: Log shows Resource has been exhausted or 429 Too Many Requests
Solution:
- Gemini free tier has rate limits (about 15 RPM)
- Reduce number of stocks analyzed simultaneously
- Increase request delay:bash
GEMINI_REQUEST_DELAY=5 ANALYSIS_DELAY=10 - Or switch to OpenAI-compatible API as backup
Q12: How to use DeepSeek and other Chinese models?
Configuration method:
# No need to configure GEMINI_API_KEY
OPENAI_API_KEY=sk-xxxxxxxx
OPENAI_BASE_URL=https://api.deepseek.com
OPENAI_MODEL=deepseek-v4-flash
# deepseek-chat / deepseek-reasoner remain compatible, but DeepSeek marks them deprecated after 2026/07/24Supported model services:
- DeepSeek:
https://api.deepseek.com - Qwen (Tongyi Qianwen):
https://dashscope.aliyuncs.com/compatible-mode/v1 - Moonshot:
https://api.moonshot.cn/v1
Q12b: How to use Ollama local models?
Configuration: Use OLLAMA_API_BASE + LITELLM_MODEL, or channel mode (LLM_CHANNELS=ollama + LLM_OLLAMA_BASE_URL + LLM_OLLAMA_MODELS).
Pitfall: Do not use OPENAI_BASE_URL for Ollama, or the system will concatenate URLs incorrectly (e.g. 404, api/generate/api/show). See LLM Config Guide Example 4 and channel examples.
Q12c: Getting `OllamaException / APIConnectionError` (All LLM models failed)?
Symptom: Log shows litellm.APIConnectionError: OllamaException or Analysis failed: All LLM models failed (tried 1 model(s)).
Work through the following 5 checkpoints in order:
Is the Ollama service running?
bash# Check process pgrep -a ollama # If no output, start it first ollama serveVerify it is listening:
curl http://localhost:11434should returnOllama is running.Is
OLLAMA_API_BASEset correctly?- ✅ Correct:
OLLAMA_API_BASE=http://localhost:11434 - ❌ Wrong: Putting the Ollama address in
OPENAI_BASE_URLcauses the URL path to be mangled (e.g.…/api/generate/api/show).
- ✅ Correct:
Does the model name include the
ollama/prefix?- ✅ Correct:
LITELLM_MODEL=ollama/qwen3:8b - ❌ Wrong:
LITELLM_MODEL=qwen3:8b(missing prefix — litellm cannot route to Ollama)
- ✅ Correct:
Has the model been pulled locally?
bashollama list # list downloaded models ollama pull qwen3:8b # pull if missingNetwork / firewall for remote or Docker deployments
- If Ollama runs on a different host, set
OLLAMA_API_BASEto its actual IP, e.g.http://192.168.1.100:11434. - Make sure port 11434 is open and Ollama binds the right address (
OLLAMA_HOST=0.0.0.0:11434).
- If Ollama runs on a different host, set
See LLM Config Guide → Example 4 (Ollama) for a complete configuration example.
Docker Related
Q13: Docker container exits immediately after starting?
Solution:
- View container logs:bash
docker logs <container_id> - Common causes:
- Environment variables not correctly configured
.envfile format error (e.g., extra spaces)- Dependency package version conflicts
Q14: API service inaccessible in Docker?
Solution:
- Ensure startup command includes
--host 0.0.0.0(cannot be 127.0.0.1) - Check port mapping is correct:yaml
ports: - "8000:8000"
Q14.1: Where is the software version stored when I install with Docker?
Short answer: For Docker users, the authoritative version is the image tag you actually deployed, not a hardcoded constant in a Python source file.
Why:
- Docker publishing is driven by
.github/workflows/docker-publish.yml, which only publishes release images for Git tags matchingv*.*.*(for example,v3.12.0). - So the Docker image version follows the GitHub Release / Git tag, rather than a fixed value in
main.py,server.py, or another backend module. - The
versionfield inapps/dsa-web/package.jsonis currently a placeholder0.0.0. The WebUI version/build card is useful for checking whether frontend assets were rebuilt, but it is not the Docker release version. - The desktop app has its own version in
apps/dsa-desktop/package.json, and that only applies to the Electron desktop build, not the Docker image.
How to check your current Docker version:
- Check the image tag in your deploy command or Compose file. For example, in
ghcr.io/zhulinsen/daily_stock_analysis:v3.12.0, the deployed version isv3.12.0. - If you used
latest, check your originaldocker pull,docker-compose.yml, or deployment script, then compare with GitHub Releases. - If you only want to confirm the frontend was refreshed, open WebUI → Settings and inspect
Build ID/Build Time; that confirms static asset freshness, not the Docker release version.
Recommendation: To avoid repeated updates, prefer a pinned version tag such as v3.12.0 instead of relying on latest.
Desktop App
Q15: macOS says the desktop app is damaged or cannot be opened?
Cause: The current macOS DMG is not signed and notarized with an Apple Developer certificate. After a browser download, macOS Gatekeeper may attach the quarantine attribute and block startup.
Solution:
Download only from the project's GitHub Releases, and select the package matching your Mac architecture. Do not bypass Gatekeeper for third-party copies or files from an untrusted source.
Drag
Daily Stock Analysisinto Applications, then first try System Settings → Privacy & Security → Open Anyway.If it still does not start and you have verified that it came from the official project Release, remove quarantine only from this app and launch it:
bashxattr -dr com.apple.quarantine "/Applications/Daily Stock Analysis.app" open "/Applications/Daily Stock Analysis.app"
Replace the path if the app is not in /Applications. Never run xattr against the entire /Applications directory. This is a temporary way to allow a trusted unsigned app; it is not a substitute for signing or notarization. See the desktop packaging guide for the complete troubleshooting notes.
Related Issue: #2113
Other Issues
Q16: How to run only market review, without stock analysis?
Method:
# Local run
python main.py --market-only
# GitHub Actions
# Select mode: market-only when manually triggeringQ17: Buy/Hold/Sell counts in analysis results are incorrect?
Cause: Earlier versions used regex matching for statistics, may not match actual recommendations.
Solution: Fixed in latest version, AI model now directly outputs decision_type field for accurate statistics.
Still Have Questions?
If the above content doesn't solve your issue, welcome to:
- Check Complete Configuration Guide
- Search or submit GitHub Issue
- Check Changelog for latest fixes
Last updated: 2026-08-03
FXMACRODATA (`docs/FXMACRODATA.md`)
FXMacroData 宏观数据工具
股票分析与聊天 Agent 的工具注册表已接入 FXMacroData。USD 数据目录、最近 90 天的指标历史与发布日历可免费使用,无需密钥、账户或信用卡。
如需可选的授权数据,在运行应用的环境中配置 FXMACRODATA_API_KEY;使用 GitHub Actions 定时分析时,将其保存为仓库 Secret FXMACRODATA_API_KEY,00-daily-analysis.yml 会把它映射到分析步骤环境。密钥不会进入工具参数、模型上下文或响应。启用 Agent 模式后,可查询 fxmacrodata_data_catalogue(currency=USD)、fxmacrodata_indicator_history(currency=USD, indicator=policy_rate)及 fxmacrodata_release_calendar。
多 Agent 模式下,情报 Agent 的原生工具允许列表提供固定客户端 v0.1.0 快照中的全部 72 个操作;风险和组合 Agent 提供与发布、宏观及跨市场风险相关的操作。各专业 Agent 仍遵守原有的工具访问限制。Codex 聊天后端只提供标记为可安全取消的工具,FXMacroData 工具不在其中,因此目前通过其他聊天后端和报告 Agent 使用。
集成保留公开 API 原始数据、发布时间、来源及推定时间标记。records 是用于展示的附加视图。市场共识、官方预测和 FXMacroData 生成的预测保持区分。数据不可用时返回明确状态,不填入模拟数据。
固定客户端 v0.1.0 内置快照中的 23 个 REST 操作及 49 个 MCP 工具的操作表、参数、安装及测试说明见 English guide。这两份文档同步介绍相同功能;完整操作名称保持英文。
FXMACRODATA EN (`docs/FXMACRODATA_EN.md`)
FXMacroData for Daily Stock Analysis
The stock report and chat agent can call native macroeconomic tools. Each tool retains the complete public input schema, original payload, release timestamps and source provenance. The factory adds these tools to the same registry as the existing market tools.
The core integration uses FXMacroData always-free public USD endpoints and requires no API key, account or credit card. Public indicator history currently covers the most recent 90 days; catalogue and release-calendar access also work without a key. Optional authenticated coverage follows the API contract.
Installation
The integration is included in the application dependencies. Install them using the project's normal command:
python -m pip install -r requirements.txtPublic USD data requires no API key. Optional authorization is configured below.
Use
The optional FXMACRODATA_API_KEY environment variable uses the host process configuration. For the scheduled GitHub Actions analysis, store it as the repository secret FXMACRODATA_API_KEY; 00-daily-analysis.yml maps that secret into the analysis step environment. The key never enters a tool schema, model invocation or response. The application must be in Agent mode for these report/chat tools to be used. No report layout changes are required. The Codex chat backend only exposes tools registered as cancellation-safe, and the FXMacroData tools are not, so they are currently available through the other chat backends and the report agents.
In multi-agent mode, the Intelligence specialist can discover and execute all 72 operations in the pinned client v0.1.0 snapshot through its native tool allowlist. Risk and Portfolio specialists also receive the operations relevant to release, macro and cross-market risk. Existing specialist tool restrictions remain in force.
Start with data_catalogue and parameters {"currency":"USD"}, then indicator_history with {"currency":"USD","indicator":"policy_rate","limit":5} or release_calendar with {"currency":"USD"}. Agent tool names have an fxmacrodata_ prefix. The operation catalogue includes exact required parameters and supported options.
The data field preserves the original public response; records is an additive table view. Keep source fields, assumed-time flags and timezone offsets when using the data. The API contract distinguishes official forecasts, market consensus and FXMacroData-generated outputs. Historical observation filters do not by themselves establish point-in-time vintage safety. Streaming is bounded by the client; it is not a persistent subscription.
Coverage
The table below is the operation catalogue bundled with the pinned fxmacrodata-public-client v0.1.0 (23 REST operations and 49 hosted MCP tools). Operations added to the live API after that release appear here once the pinned client is updated. Every operation in this snapshot is available through the native consumer above. Access requirements depend on the operation and currency. MCP tools, non-USD data and other protected datasets may require authorization; they are not required for the public USD baseline.
| Operation | Transport | Native consumer | Status |
|---|---|---|---|
health |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
ping |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
forex |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
intraday_reference_rates |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
fx_sources |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
fx_source_universe |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
data_catalogue |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
release_calendar |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
market_sessions |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
rate_differentials |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
curves |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
financial_prices |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
press_releases |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
risk_sentiment |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
factors |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
event_predictions |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
latest_announcements |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
indicator_history |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
cot |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
latest_commodities |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
commodities |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
announcement_changes |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
stream_events |
GET | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_ping |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_mcp_capabilities |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_mcp_auth_guide |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_subscribe_for_mcp_access |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_data_catalogue |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_risk_sentiment |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_news |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_release_calendar |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_release_calendar_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_event_predictions |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_latest_announcements |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_announcement_changes |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_press_releases |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_factor |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_fx_reference_sources |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_fx_reference_universe |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_fx_intraday_reference_rates |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_rate_curve |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_rate_differentials |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_latest_commodities |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_forex |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_seasonality |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_indicator_query |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_plot_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_indicator_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_forex_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_commodities_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_cot_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_policy_rate_differential_visual_artifact |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_briefing_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_indicator_intel_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_pair_intel_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_heatmap_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_policy_scenario_modeler_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_war_room_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_event_impact_replay_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_quant_scenario_lab_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_known_at_time_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_regime_classifier_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_release_risk_score_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_portfolio_risk_engine_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_fx_trade_setup_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_fx_backtest_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_macro_research_pack_task |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_market_sessions |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_cot_data |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_commodities |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_financial_prices |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
mcp_official_dataset_family |
MCP | src/agent/tools/fxmacrodata_tools.py |
Implemented |
Validation
Offline tests cover tool registration and schema for every operation, factory registration for report and chat, the multi-agent specialist filters, and the degraded unavailable paths (client construction, transport and close failures, missing operation catalogue). They do not make real provider requests or exercise report routing end to end.
python -m pytest tests/test_fxmacrodata_tools.py -o addopts= -q -n 8 --dist loadThese focused tests do not replace the target project's full CI gate. No live credentials or private datasets are fixtures.
Attribution
Rob Tidball owns FXMacroData and maintains this adapter. Website links identify the provider; campaign parameters distinguish repository documentation visits from integration application visits. API/MCP requests have no campaign parameters, and the adapter sends no click telemetry.
INDEX EN (`docs/INDEX_EN.md`)
English Documentation Index
This is the entry point for project documentation. The README covers the project overview and quick start; detailed setup, configuration, deployment, feature usage, and troubleshooting docs are linked below.
For Chinese documentation, see docs/INDEX.md.
Choose By Goal
| I want to | Start with | Then read |
|---|---|---|
| Understand what the project does | README (EN) | Full Guide (EN) |
| Run the project for the first time | README (EN) | Full Guide (EN) |
| Configure model providers | LLM Config Guide (EN) | Provider Configuration Guide (Chinese-only) |
| Configure notifications | Notification Baseline (Chinese-only) | Full Guide (EN) |
| Deploy to a server or cloud platform | Deploy Guide (EN) | Cloud WebUI Deployment (Chinese-only), Zeabur Deployment (Chinese-only) |
| Use Bot / IM integrations | Bot Commands (EN) | Bot Platform Docs (Chinese-only) |
| Troubleshoot runtime issues | FAQ (EN) | Changelog |
| Troubleshoot data-source failures | Data-source stability diagrams (Chinese-only) | FAQ (EN) |
| Contribute code or docs | Contributing Guide (EN) | API Spec |
Getting Started
| Document | Contents |
|---|---|
| README (EN) | Project overview, key features, quick start, sample output |
| Full Guide (EN) | Environment setup, run modes, configuration, deployment paths, and common issues |
| FAQ (EN) | Common configuration, model, notification, deployment, and runtime issues |
| Data-source stability diagrams (Chinese-only) | Existing-source routing, fallback chains, and recommended configuration profiles |
| Data Center (Chinese-only) | Read-only provider capabilities, exact dataset/market support, quality states, and runtime priorities |
| Changelog | Release notes, capability changes, and migration notes |
Configuration
| Document | Contents |
|---|---|
| LLM Config Guide (EN) | Model providers, three-tier configuration, Web settings, and common model setup |
| Provider Configuration Guide (Chinese-only) | Provider presets, GitHub Actions mapping, error categories, and diagnostics |
| LiteLLM YAML Example | Example LiteLLM multi-provider configuration |
| Notification Baseline (Chinese-only) | WeChat Work, Feishu, Telegram, Discord, Slack, Email, and other notification channels |
| Tushare Stock List Guide (Chinese-only) | Tushare stock-list configuration and usage notes |
Usage Topics
| Document | Contents |
|---|---|
| Watchlist Change, State, And Next Action (Chinese-only) | Three compact signals shown for each stock in the home-page watchlist, their mapping rules, and the boundary for later extensions |
| Bot Commands (EN) | Bot commands, webhooks, platform integration, and callback behavior |
| Bot Platform Docs (Chinese-only) | Feishu, DingTalk, Discord, and related Bot configuration screenshots and notes |
| Real-Time Alert Center (Chinese-only) | EventMonitor baseline, Web rule management, notification attempts, cooldown state, and phase boundaries |
| Stock Profile API (Chinese-only) | Single stock-profile contract, per-block quality status, code normalization, and Web phase boundaries |
| DecisionSignal Topic (Chinese-only) | AI signal fields, API, Web display, alert/notification/portfolio-risk linkage, outcome evaluation, redaction, migration, and rollback |
| ETF Rotation (Chinese-only) | Rule-based dual-momentum ETF rotation via --etf-rotation: rules, config, report sections, parameter-plateau reading, and data-quality warnings |
| ResearchArtifact Contract (Chinese-only) | Structured thesis, evidence, invalidation conditions, deterministic fallback identity, and the boundary before persistence/API integration |
| Portfolio Risk And Exposure Dashboard (Chinese-only) | Portfolio-page risk flags, market/currency exposure, classified-sector handling, and price-quality aggregation |
| Screening Explanation Contract (Chinese-only) | Deterministic Why Selected / Why Now explanations, provenance, quality, and missing-value boundaries |
| Analysis Context Pack Contract, Runtime Consumption, And Visibility (Chinese-only) | AnalysisContextPack first-scope boundaries, field quality states, P1/P2 internal contracts, P3 prompt-summary consumption, P4 history/API/Web low-sensitivity visibility, P5 data-quality scoring, and P6 migration/rollback notes, plus source anchors; the full guide adds #1386 market-phase analysis, migration, and rollback entry points |
| Image Extraction Prompt (Chinese-only) | Prompt and boundaries for extracting stock information from images |
| OpenClaw Skill Integration (Chinese-only) | OpenClaw / Skill external integration notes |
| Grok Bot Integration (Chinese-only) | 2026-08-11 Grok Bot (AI teammate) vs LiteLLM xai/ provider; HTTP / Skill / DecisionSignal consumption |
Deployment And Packaging
| Document | Contents |
|---|---|
| Deploy Guide (EN) | Server deployment, Docker, systemd, Supervisor, and related options |
| Cloud WebUI Deployment (Chinese-only) | Cloud server WebUI access and deployment notes |
| Zeabur Deployment (Chinese-only) | Zeabur platform deployment |
| Desktop Packaging (Chinese-only) | Electron desktop app and Web artifact packaging |
Reference And Development
| Document | Contents |
|---|---|
| API Spec | FastAPI OpenAPI artifact |
| Contributing Guide (EN) | Issues, pull requests, tests, documentation sync, and collaboration expectations |
Languages
| Document | Contents |
|---|---|
| Chinese Documentation Index | Chinese documentation entry point |
| Traditional Chinese README | Traditional Chinese project overview and quick start |
China-Market Glossary
| Term | Meaning |
|---|---|
| A-shares | Stocks listed on the Shanghai or Shenzhen stock exchanges, denominated in CNY |
| Northbound capital flow | Net buy/sell flow from foreign investors through Stock Connect programs |
| Dragon-Tiger List | Daily SSE/SZSE disclosure of heavily traded stocks and top trading seats |
| Chip distribution | Cost-basis distribution of outstanding shares, often used to estimate support and resistance |
| Tushare | Chinese financial data API that requires a token |
| AkShare | Open-source Python market data library |
| Baostock | Free Python SDK for historical A-share data |
| WeChat Work | Tencent enterprise messaging platform with webhook notifications |
| Feishu | ByteDance enterprise collaboration platform with webhook notifications |
| PushPlus / ServerChan | Chinese mobile push notification services |
LLM CONFIG GUIDE (`docs/LLM_CONFIG_GUIDE.md`)
LLM (大模型) 配置指南
欢迎!无论你是刚接触 AI 的新手小白,还是精通各种 API 的高玩老手,这份指南都能帮你快速把大模型(LLM)跑起来。
本项目对外提供统一的 AI 模型接入体验,支持主流官方 API、OpenAI 兼容平台以及本地模型。底层由 LiteLLM 驱动,但大多数用户只需要理解“选服务商、填 API Key、选主模型/渠道”这条默认路径。为了照顾不同阶段的用户,我们设计了“三层优先级”配置,按需选择最适合你的方式即可。
如果你正在选择具体服务商、配置 GitHub Actions Secrets / Variables、排查 details.reason 错误或准备回滚配置,请优先查看 LLM 服务商配置指南。该文档集中维护 provider 预设、Actions 变量对照、运行时能力检测边界和常见错误处理建议。
本页的 provider/model/Base URL 说明本次未新增外部兼容语义,仅用于同步现网约定;实际兼容判断仍按当前仓库锁定依赖与运行时实现执行:
- 依赖边界:
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(与requirements.txt一致)。- 兼容验证入口:
tests/test_system_config_service.py、tests/test_system_config_api.py以及现有前端模型配置页回归用例。- 回退路径:优先使用
.env配置备份 +POST /api/v1/system/config/import恢复;也可在重启前手动回填旧LITELLM_MODEL/LLM_*/AGENT_LITELLM_MODEL/VISION_MODEL/LLM_TEMPERATURE/LLM_USAGE_HMAC_*。
说明:本页对 provider/model/base URL 的说明同步沿用当前依赖约束与历史约定,仅做文档补充,不引入新的运行时 provider、模型或 Base URL 行为变更。
快速导航:你应该看哪一节?
- 【新手小白】 "我只想赶紧把系统跑起来,越简单越好!" -> 指路【方式一:极简单模型配置】
- 【进阶用户】 "我有好几个 Key,想配置备用模型,还要改自定义网址(Base URL)。" -> 指路【方式二:渠道(Channels)模式配置】
- 【高玩老手】 "我要做复杂的负载均衡、请求路由、甚至多异构平台高可用!" -> 指路【方式三:YAML 高级配置】
- 【本地模型】 "我想用 Ollama 本地模型!" -> 指路【示例 4:使用 Ollama 本地模型】
- 【视觉模型】 "我想用图片识别股票代码!" -> 指路【扩展功能:看图模型(Vision)配置】
Generation Backend(Phase 4)
Generation backend 是普通分析、大盘复盘和 generate_text() 的外层运行时选择。默认仍是 litellm,零配置路径与历史行为保持一致;codex_cli / claude_code_cli / opencode_cli 是显式 opt-in 的本地 CLI backend,当前标记为 experimental/limited。
GENERATION_BACKEND=litellm
GENERATION_FALLBACK_BACKEND=litellm
GENERATION_BACKEND_TIMEOUT_SECONDS=300
GENERATION_BACKEND_MAX_OUTPUT_BYTES=1048576
GENERATION_BACKEND_MAX_CONCURRENCY=1
LOCAL_CLI_BACKEND_MAX_CONCURRENCY=1
# 可选:留空时使用本机 OpenCode 默认模型;配置时作为 --model 覆盖值传给 OpenCode。
# OPENCODE_CLI_MODEL=provider/model
AGENT_BACKEND=auto
AGENT_GENERATION_BACKEND=autoGENERATION_BACKEND=litellm|codex_cli|claude_code_cli|opencode_cli。本地 CLI backend 是 generation backend,不是 LiteLLM provider;不要写LITELLM_MODEL=codex_cli/...、LITELLM_MODEL=claude_code_cli/...或LITELLM_MODEL=opencode_cli/...。GENERATION_BACKEND=opencode_cli时默认不传--model,由本机 OpenCode 使用自身默认模型配置;OPENCODE_CLI_MODEL只是可选覆盖值,配置时才作为单个--model参数传给 OpenCode。provider 认证、账号和模型可用性由本机 OpenCode 自身配置负责;DSA 不接管这些配置。GENERATION_FALLBACK_BACKEND未配置时默认litellm;本地.env显式空值GENERATION_FALLBACK_BACKEND=表示禁用 backend-level fallback;primary 与 fallback 相同时解析为 no-op。仓库自带 GitHub Actions workflow 未配置该变量时会显式导出litellm,如果要在 Actions 中禁用 backend fallback,请把 fallback 设为 primary backend,例如GENERATION_BACKEND=codex_cli+GENERATION_FALLBACK_BACKEND=codex_cli。GENERATION_BACKEND=codex_cli|claude_code_cli且没有 Gemini/OpenAI/Anthropic/DeepSeek API Key 时,普通分析和大盘复盘仍会尝试本地 CLI backend;如果对应 executable 不存在,会返回结构化command_not_found,不会报“API Key 未配置”。- 当前
codex_clipreset 使用codex --ask-for-approval never exec --sandbox read-only --output-last-message <temp-file> -:普通分析是无人值守生成任务,固定never可避免非交互运行停在人工批准请求,同时仍由read-only保持只读边界。DSA 从临时文件读取最终响应;Codex CLI 同时打印到 stdout 的重复内容会从诊断预览和输出大小统计中剔除,不参与主分析 JSON 解析。官方依据见 Codex non-interactive mode 与 Codex CLI command line options。本仓库当前真实验证codex-cli 0.144.3,不声明更宽最低版本;如果 CLI 版本不支持 preset 参数,DSA 会返回结构化capability_unsupported/cli_contract_unsupported诊断,并在配置 backend fallback 时回退到litellm。 - 当前
claude_code_clipreset 使用claude --safe-mode --tools "" --disallowedTools "mcp__*" --strict-mcp-config --no-session-persistence --output-format json -p <static instruction>,完整 DSA prompt 通过 stdin 传入。DSA 只从 Claude JSON envelope 的result/success最终字段提取文本;如果后续启用--json-schema,schema mode 必须提取structured_output,并且仍会继续经过 DSA 现有 JSON validator、minimal parser contract、_parse_response()、integrity retry、placeholder fill 和 usage telemetry。参数依据见 Claude Code CLI reference;本 PR smoke 验证版本为claude 2.1.177 (Claude Code),不声明更宽最低版本。 - 当前
opencode_clipreset 使用opencode --pure run --format json [--model <OPENCODE_CLI_MODEL>] <static instruction> --file <temp prompt file>;只有显式配置OPENCODE_CLI_MODEL时才追加--model,完整 DSA prompt 写入权限受控的临时文件,不进入 argv。DSA 只解析 OpenCode JSON event 输出中无工具事件的text内容,并要求正常step_finish;出现tool_use、error、question、permission等事件会结构化失败。参数依据见 OpenCode CLI reference,项目配置合并语义见 OpenCode config reference;本 PR smoke 验证版本为opencode 1.17.11,不声明更宽最低版本。 - 本地 CLI backend 不支持 streaming。请求 stream 时会自动降级为 non-stream,不会因此返回
capability_unsupported。 - 本地 CLI usage 通常不可用,系统不会写入 fake 0 token、fake cost 或 fake cache telemetry。
- 本地 CLI 执行上限有硬边界:
GENERATION_BACKEND_TIMEOUT_SECONDS最大3600,GENERATION_BACKEND_MAX_OUTPUT_BYTES最大33554432,GENERATION_BACKEND_MAX_CONCURRENCY最大16,LOCAL_CLI_BACKEND_MAX_CONCURRENCY最大4。诊断 stdout/stderr 与最终响应合计超过输出上限时会返回结构化output_too_large;对--output-last-messagepreset,stdout 中重复打印的最终响应不会重复计入,也不会作为stdout_preview暴露。 - 本地 CLI 的
stdout_preview/stderr_preview在写入结构化 diagnostics 前会脱敏短凭证赋值,不依赖值长度:大写环境变量赋值沿用 child-env 的 fail-closed 敏感名称判定,JSON 与 YAML / 普通日志中的标量赋值使用更窄的凭证字段 allowlist,URL 继续使用独立的 userinfo / webhook / 敏感参数规则。token_budget、session_id、sort_key等普通排障字段不会仅因包含token、session或key子串而被删除;但未加引号的 YAML 敏感标量没有可靠的同行边界,因此从该值起到行尾会 fail-closed 脱敏,同行后续字段也不会保留。 - 本地 CLI 默认并发为 1;有效并发为
min(LOCAL_CLI_BACKEND_MAX_CONCURRENCY, GENERATION_BACKEND_MAX_CONCURRENCY),不继承MAX_WORKERS。 AGENT_GENERATION_BACKEND=auto不会继承GENERATION_BACKEND的 local CLI 值;Agent 工具调用继续使用 LiteLLM。Web 设置页仅暴露auto|litellm;手写AGENT_GENERATION_BACKEND=codex_cli|claude_code_cli|opencode_cli不实现 text-only Agent mode,会返回明确 unsupported tool-calling 诊断。- Phase 6a 的 DSA Tool Surface 仍是唯一工具 schema、权限元数据、scope guard、结构化错误和审计/脱敏边界;Phase 6 的 Codex AgentBackend 只能通过该 Tool Surface 执行工具。
codex_cli/claude_code_cli/opencode_cli仍是 generation-only,不能作为 Agent tool fallback。 - Web 设置页的生成后端快速检查只读取已保存的
.env、运行时兜底值和未保存草稿;它不会写配置、重载运行时,也不会发起真实模型请求。available只表示当前配置具备尝试运行的条件。JSON 冒烟测试是单独的显式操作,会使用服务端固定的 JSON 提示词和 schema 发起一次真实的生成后端请求,用于验证提取器、JSON 契约、超时、输出限制和 usage-unavailable 语义。 GET /api/v1/system/config/generation-backends/status只读取已保存配置;未保存草稿需调用POST /api/v1/system/config/generation-backends/status/preview或POST /api/v1/system/config/generation-backends/smoke-test。被遮罩的密钥字段会继续沿用已保存值。health_status与last_error_code/message只代表本次计算结果,不是历史持久健康状态。
问股会话状态与升级
服务端统一保存主讨论对象及会话 Skill;LiteLLM single/现有 multi 与 Codex single 接口消费同一次接受快照。省略或 null Skill 沿用已保存选择(未选择时使用配置默认);显式 [] 保存清空选择并使用内置默认,不启用配置中的非空 Skill。名称只在服务端索引唯一匹配时确认;比较不切股,模型失败/停止不撤销已接受状态。并发或会话实例冲突时,请手动刷新后重试,页面保留输入与待提交选择,不自动重发。
旧会话只恢复明确代码,不从历史名称造新事实;“分析600519 → 改看宁德时代 → 风险呢”需要再确认,不能继续沿用600519。升级自动追加状态列,不删除消息/Skill/报告。升级或回滚前停止后端,使用 SQLite backup 或停服完整备份(不能在 WAL 活跃时只复制主文件);保留原配置、数据库位置和数据目录,更新配套后端/Web 后重启,不手工删表/删列。回滚保留新增列,旧代码仍可写消息与 Skill;再次升级若发现回滚期间新增用户消息,先显示未确认,再由用户明确确认对象。旧/新写进程混跑不支持;分离部署升级先后端再 Web,回滚先 Web 再后端。
代码与名称混用时,比较范围包含两者;紧邻的明确代码可消除同名上市地歧义(如 688981中芯国际)。从600519改看300750/把600519换成300750 按操作词关联的目的地确认300750,不因当前已经是300750或重复发送而反向切回600519;每次接受仍保存消息并递增版本。明确切股只开放新对象的工具范围,并移除不匹配的旧报告提示;目的地含多个备选项时需澄清,不能任选最后一个或排除当前对象后猜选。
已有主对象时,看看近期走势/分析一下财务状况/分析风险 正常继续分析,保留原确认来源和时间;分析300750 或唯一匹配名称仍可确认新对象。单独的“分析/看看”加尾随文本不证明切股;明确但无法确认的“改看…”需澄清,不启动分析。旧历史沿用同一方向与动作判定,但只用有效明确代码建立事实:普通追问保留候选,无法确认的新切换使旧候选失效,后续追问不能复活它;后续明确代码可重新建立。历史名称仅作为目标变化证据,不补造确认事实;读取恢复结果不写回股票或递增版本。
否定、询问、条件或引用中的切换不作为肯定确认:不要改看300750,继续分析600519 维持600519;是否改看300750? 保留对象并澄清,不开放300750。代码和名称按实际出现位置解释;旧历史只有目的地位置的有效代码能确认,句子另一处同码不能替历史名称造事实。首次单独输入有效代码/唯一名称仍可确认,但任意文本中的唯一提及不自动确认。
如果600519下跌,就改看300750 的相邻条件与后件即使被逗号分隔,也只保存问题并澄清,不立即切股或启动分析;旧历史恢复同样不把它当成新确认。被否定的强/弱/选择比较不贡献参与对象,例如 不要研究600519和300750的区别,继续分析600519 仍正常分析600519,工具范围只有600519。独立分句中的肯定请求不被整条消息的条件/否定屏蔽。
问股个股回测工具的汇总、明细、总数与分页共同使用 guard 已验证的市场身份;合法前缀/后缀与现有 CN 写入器的数字存储键均可读取,不重新按裸码索引选择其他市场。数字键仅由已验证 SH/SZ/BJ 对象按原写入规范生成;明细须关联同对象分析来源,来源中的明确其他市场不被认领。数字汇总须有同一回测窗口/引擎的同对象明细依据,存在冲突数字来源时不认领该汇总;缺失可证明归属的数据正常返回无数据。没有市场字段的正常旧 CN 分析沿用原裸码存储约定,不一律排除数字键。未提供严格目标的原 service/repository 入口保持 legacy 行为,不迁移或改写历史数据。此内部目标参数仅用于现有工具的汇总/明细读取,不支持日期/phase 动态聚合;显式请求不支持的组合会报错,不丢弃身份约束。
普通追问中的顺带提及不扩展工具范围。严格 Chat 的现有标的工具共用身份、scope 与适用性检查,使用本次工具调用验证好的目标读取缓存及选择数据源;日线、技术分析和实时行情保留股票/指数区分,缓存缺失不改查同码股票。执行前必要身份失效只拒绝受影响对象,其他仍可确认的 scope 成员不连带禁用;验证后同一次调用不再读取注册表重新分类。筹码、公司资料、资金流和个股回测不支持指数,明确返回不支持;原市场与 ETF 条件保留。严格自定义工具未声明适用类型时只拒绝该调用。新闻对象由已确认身份/名称决定,不采用模型猜测的另一股票名称。
同名会话的当前 Bot 与 Web 可以继续交替提问:Web 已确认对象后,Bot 普通追问使用该对象的严格工具范围,合法比较只为本轮增加比较对象;两者都保留 Web 主对象及其确认来源/时间,不递增股票状态版本。Bot 不独立持久化新主对象;明确切股或无法安全维持旧对象时,Web 状态进入待重新确认,不能继续声称旧对象可靠。真正旧代码/回滚期间的未知用户写入仍要求重新确认,不能当作当前状态感知 Bot。当前 Bot 提交校验准备快照的实例、版本和最新用户消息锚点;期间已有新接受则拒绝旧提交,不追加旧问题或自动重试模型。回复、provider trace 和摘要仍绑定该次读取的会话实例与真实消息来源,删除后重建不会接收旧实例的迟到写入。Web 的迟到初始列表可以更新列表展示,不能清空新请求/新接受实例或取消其 stream;正常的无会话恢复、切换和新建仍使用既有逻辑。
这不改变模型路由、Phase 2 或 Codex 工具权限。Gate 0B 的受影响真实 Codex 路径仍为 BLOCKED;受控执行端的状态验证不能证明真实运行层或所有平台升级通过。
Codex 本地 Agent(Phase 6 实验原型)
AGENT_BACKEND 只决定现有问股 Chat 的运行方式,不影响普通报告、定时分析、大盘复盘、普通 Agent 分析 pipeline、LiteLLM Multi Agent 或 Deep Research:
# auto(推荐)不会自动启用实验性的 Codex;auto 与 litellm 均保持原有默认模型路径。
AGENT_BACKEND=auto
# 显式启用时:
# AGENT_BACKEND=codex_app_server
# AGENT_ARCH=singleWeb 启用步骤:打开「设置 → Agent 设置 → 问股生成方式」,选择「Codex 本地 Agent(实验)」,确认架构为「单 Agent」,并将 Agent 整体时限设置为大于 0 后保存。设置页只检查当前配置、本机 Codex 命令和所需 App Server 协议是否允许尝试,不登录、不调用模型,也不读取股票数据。保存后可直接回到问股页提问;第一次问题就是第一次真实执行。要恢复原有行为,选择「自动(推荐)」并保存。
- Codex 必须安装并登录在运行 DSA 后端的设备上;DSA 不读取或保存 Codex 凭据,App Server 进程使用 Codex 自身登录态。Docker、远程服务器和 Desktop 的 PATH / 登录态彼此独立。在 Desktop 中从 Finder/Dock 启动时,后端只继承 Desktop 构造的真实 PATH;若状态提示找不到 Codex,请将 Codex CLI 安装到后端 PATH 可见位置并完全重启 DSA,不要只在另一个终端窗口验证。
- Phase 6 的 Codex App Server Agent 当前支持 macOS、Linux,以及 DSA 后端完整运行于 WSL 的环境;原生 Windows 后端会在状态检查和 transport 启动前明确拒绝。此限制不影响 Phase 2
GENERATION_BACKEND=codex_cli已有的 Windows 生成能力。 - Codex 当前只开放已保存分析上下文、全局回测汇总和策略回测汇总的只读查询。本期只验证并承诺这三个工具的独立进程、停止、超时和回收闭环;实时行情、新闻、市场热点、技术指标重算、个股回测明细和持仓工具未纳入本期验证,因此不会出现在 Codex 的工具列表中。需要这些能力时,请在 Web 中选择「默认模型」。明确股票代码或 Web 已选择的唯一股票只会为已开放的历史分析上下文工具建立股票范围;遇到同名歧义时不会猜测。
- 该能力不是离线模型。股票代码、问题、新闻、持仓上下文和脱敏后的工具结果可能由 Codex 自身配置的服务处理。
- Codex 问股会传递当前激活的交易 Skill 指令。省略选择时沿用会话已保存选择(新会话使用默认技能),显式空列表清除会话选择并回到默认技能。Skill 只用于解读三个只读工具返回的已保存数据,不能增加工具权限;所需数据或能力不足时必须说明缺失项,不能声称完整执行了技能。
- 当前只支持 single-agent Chat;不支持 Codex Multi Agent 或 Codex Deep Research。现有 LiteLLM Multi Agent 与 Deep Research 不受影响。
- 每次 Chat 都创建新的 ephemeral App Server thread;DSA 继续保存原有可见会话历史,并在下一轮注入,但不会注入 LiteLLM provider trace。Web 客户端不会收到 chain-of-thought、原始 JSON-RPC、stderr 或完整工具参数/结果;Codex 只接收完成该轮分析所需的脱敏工具结果。
- 用户停止问股时,页面会先显示“正在停止”。DSA 会中断 Codex turn,并结束、回收本轮独立运行的工具进程;只有确认 Codex 与工具进程均已退出后,原问股请求才返回最终“已停止”。超时和客户端断开也遵守同一清理边界,不会在后台仍有本轮任务时提前宣告结束。该契约仅作用于 Codex,不改变默认 LiteLLM Agent 的执行方式。
- Codex 必须使用大于 0 的
AGENT_ORCHESTRATOR_TIMEOUT_S,以保证成功、失败、超时或停止都在明确时间内结束;0关闭时限的旧语义只保留给默认 LiteLLM 路径,选择 Codex 时会在保存与运行前明确拒绝,不会静默替换成 600 秒。 - 快速状态只检查配置、可执行文件、版本和生产路径实际依赖的 App Server schema 能力,不发模型请求,也不证明 Codex 已登录、模型可用或真实工具闭环。状态只表达“可以尝试”;真实命令、协议、登录、模型和工具错误在用户发送问题时按原始类别返回。Chat 每次加载检查一次,失败后由用户手动重新检查,不自动重试。
- 正式 Chat 由服务端选择实际 backend。服务端完成上下文准备并保存用户问题后,SSE 才发出唯一
accepted事件,然后开始模型执行。Web 在accepted前保留输入、股票范围、追问上下文和技能选择;因此环境或保存失败不会产生页面中的幽灵消息。accepted返回的实际 backend 决定停止方式。 - 每次问股共享一份整轮资源预算:App Server 的累计输出和事件数有界,工具调用总数服从现有
AGENT_MAX_STEPS,完成后立即清理本轮状态。工具进程按原始 UTF-8 结果字节计量,不会因 JSON 转义把合法结果误报为 handler 崩溃;超限统一返回明确错误并回收本轮进程,不重试或切换后端。 - 当前依据 Codex App Server v2 文档 使用 JSONL stdio、ephemeral
thread/start、turn/start/turn/interrupt与 experimental dynamic tools。2026-07-15 的本地验收版本为codex-cli 0.144.3;本项目不据此猜测通用最低版本。设置页兼容性检查只决定是否允许尝试,最终可用性以用户问题的真实执行结果为准。
Local CLI 本地 backend 隐私与边界
- 本地 CLI Backend 不等于离线模型;Codex / Claude Code / OpenCode 背后的服务可能处理股票代码、新闻、持仓上下文、分析 prompt、报告草稿等内容。
- Docker、云服务器、CI 不天然拥有你本机的 CLI 登录态。
- GitHub Actions 只负责透传配置值,不安装或登录本地 CLI;如果在 Actions 中 opt-in local CLI backend,runner 上缺少可执行文件或登录态时应看到结构化失败。
- DSA 不读取 Codex/Claude/OpenCode credential 文件,但子进程可能读取 CLI 自身登录态。
- macOS 从 Finder/Dock 启动桌面端时不继承 shell PATH;打包桌面端会在启动后端时补入常见 Homebrew 路径(如
/opt/homebrew/bin、/usr/local/bin)。如果设置检查仍提示找不到 CLI 可执行文件,请完全退出并重开 DSA;打开 CLI 交互窗口不会改变已运行后端的 PATH。 - DSA 默认只继承最小运行环境,并拒绝通配继承
CLAUDE_*、ANTHROPIC_*、OPENCODE_*、OPENAI_*、GOOGLE_*、GEMINI_*、AWS_*、AZURE_*、VERTEX_*、*_API_KEY、*_AUTH_TOKEN、*_ACCESS_TOKEN、*_SECRET、*_PASSWORD,降低 DSA API keys、provider tokens 和 webhook tokens 泄漏风险。CODEX_HOME是为兼容既有 Codex CLI 登录目录保留的精确例外;不会恢复CODEX_CLI_*通配。 opencode_cli会在临时 cwd 写入最小项目opencode.json以关闭分享、自动更新、快照和常见工具权限,但 OpenCode resolved config 仍可能包含用户本机全局配置;运行时安全边界同时依赖--pure、env denylist、prompt file 权限和 event extractor fail-closed。- Web 设置页只暴露安全 preset,不允许提交任意 command / argv / shell string。
codex_cli/claude_code_cli/opencode_cli仍标记为 experimental/limited;如果你的 CLI 版本不支持本仓库已验证的非交互输出契约,DSA 会返回结构化capability_unsupported、cli_contract_unsupported、invalid_json、schema_validation_failed或对应 backend error,并在配置 backend fallback 时回退到litellm。无法接受该版本漂移风险时,请保持GENERATION_BACKEND=litellm。opencode_cli不支持 OpenCode serve / web / ACP / MCP / attach /--dangerously-skip-permissions;DSA 不把 OpenCode final text 当成 Agent tool success。
方式一:极简单模型配置(适合新手)
目标: 只要记得填入 API Key 和对应的模型名就能立刻用。不需要折腾复杂概念。
如果你只打算用一种模型,这是最快捷的办法。打开项目根目录下的 .env 文件(如果没有,复制一份 .env.example 并重命名为 .env)。
Modelflare 使用标准 Channels 配置,见下方 Modelflare 示例。本节保留单模型 legacy 配置,现有配置无需迁移。
示例 1:使用通用第三方平台(兼容 OpenAI 格式,推荐)
现在市面上绝大多数第三方聚合平台(例如硅基流动、AIHubmix、阿里百炼、智谱等)都兼容 OpenAI 的接口格式。只要平台提供了 API Key 和 Base URL,你都可以按照以下格式无脑配置:
# 填入平台提供给你的 API Key
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
# 填入平台的接口地址 (非常重要:结尾通常必须带有 /v1)
OPENAI_BASE_URL=https://api.siliconflow.cn/v1
# 填入该平台上具体的模型名称(非常重要:注意前面必须加上 openai/ 前缀帮系统识别)
LITELLM_MODEL=openai/deepseek-ai/DeepSeek-V3示例 2:使用 DeepSeek 官方接口
# 填入你在 DeepSeek 官方平台申请的 API Key
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx兼容提示:仅填这一行时,系统仍会默认使用 deepseek/deepseek-chat 并在日志提示迁移。deepseek-chat / deepseek-reasoner 仍可用于兼容旧配置,但 DeepSeek 官方已标记为 2026/07/24 后废弃;新配置建议通过 Web 快速渠道或显式 LITELLM_MODEL=deepseek/deepseek-v4-flash 迁移到 deepseek-v4-flash / deepseek-v4-pro。
示例 3:使用 Gemini 免费 API
# 填入你获取的 Google Gemini Key
GEMINI_API_KEY=AIzac...示例 4:使用 Ollama 本地模型
# Ollama 无需 API Key,本地运行 ollama serve 后即可使用
OLLAMA_API_BASE=http://localhost:11434
LITELLM_MODEL=ollama/qwen3:8b重要:Ollama 必须使用
OLLAMA_API_BASE配置,不要使用OPENAI_BASE_URL,否则系统会错误拼接 URL(如 404、api/generate/api/show)。远程 Ollama 时,将OLLAMA_API_BASE设为实际地址(如http://192.168.1.100:11434)。当前依赖约束为litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(与 requirements.txt 一致)。
Anspire Open legacy 兼容示例
现有 Anspire 搜索与 LLM 共用 Key 的兼容路径继续保留。以下为配置示例,模型与网关可用性以账号权限和 Anspire 控制台为准;可在 Web 设置页点击“测试连接”确认。
# Anspire Open API Keys(支持多个,逗号分隔)
# 获取: https://open.anspire.cn/dsa?share_code=QFBC0FYC
# 满足默认优先级条件时,系统会复用该 Key 处理搜索与 LLM(仅限示例兜底路径)。
# 示例模型:Doubao-Seed-2.0-lite;示例网关:https://open-gateway.anspire.cn/v6
ANSPIRE_API_KEYS=sk-xxxxxxxxxxxxxxxx
# 可选:按控制台可用性切换模型或网关
# ANSPIRE_LLM_MODEL=Doubao-Seed-2.0-pro
# ANSPIRE_LLM_BASE_URL=https://open-gateway.anspire.ai/v6恭喜!小白读到这里就可以去运行程序了!
想测测看通没通?在主目录打开命令行输入:python scripts/check_env.py --llm
方式二:渠道(Channels)模式配置(适合进阶/多模型)
目标: 我有多个不同平台的 Key 想要混着用,如果主模型卡了/网络挂了,我希望它能自动切换到备用模型。
网页端可以直接配: 你可以启动程序后,在 Web UI 的“系统设置 -> AI 模型 -> AI 模型接入” 中非常直观地进行可视化配置!
新版编辑体验补充:对于 DeepSeek、阿里百炼(DashScope)以及其他兼容 OpenAI
/v1/models的渠道,设置页现在支持直接点击“获取模型”,从{base_url}/models拉取可用模型并多选;底层仍会保存为原来的LLM_{CHANNEL}_MODELS=model1,model2逗号格式。若渠道不支持该接口、鉴权失败或暂时不可达,仍可继续手动填写模型列表,不影响保存。
首次启动配置状态
后端提供只读状态接口 GET /api/v1/system/config/setup/status,用于判断首次启动闭环中最基础的几类配置是否已经就绪:LLM 主渠道、Agent 渠道、自选股、通知渠道和本地存储。这个接口只读取已保存的 .env 与当前进程环境变量,不会重载运行时配置、写入 .env、测试真实模型或创建数据库文件;前端向导和后续 smoke run 可以基于该接口逐步接入。
Web 渠道编辑器的兼容性 / 迁移 / 回退规则
- 预设里的 provider / API Surface / Base URL / 示例模型只用于初始化表单;真正落盘时仍是你当前输入的
LLM_{CHANNEL}_PROTOCOL、LLM_{CHANNEL}_API_SURFACE、LLM_{CHANNEL}_BASE_URL、LLM_{CHANNEL}_MODELS、LLM_{CHANNEL}_API_KEY(S),不会在后台偷偷改成别的 provider 名、Surface 或 URL。 LLM_{CHANNEL}_API_SURFACE可选chat_completions(默认)或responses;Responses 当前只支持 OpenAI-compatible 协议,而且渠道内每个模型的实际 LiteLLM provider 都必须是openai。系统从当前安装的 LiteLLM provider registry 识别直连前缀,并通过GET /api/v1/system/config的llm_model_providers返回给 Web 编辑器;前后端不再各自维护 provider 表。因此anthropic/claude-*、xai/grok-*以及未来 LiteLLM 新增的直连 provider 不会因本地白名单滞后而被错误包装成 OpenAI route;冲突配置会在保存、启动、状态诊断和筛选入口一致拒绝,不会生成anthropic/responses/...。deepseek-ai/DeepSeek-V3、Qwen/...这类不在 registry 中的网关自有模型 ID 会规范化为openai/<网关模型 ID>。同一渠道中的模型必须使用同一种 Surface,并且同一个规范化 route alias 不能跨渠道混用 Chat 与 Responses,否则 Router 可能把同一次公开 alias 调度到错误 endpoint;需要两种 Surface 时必须使用不同别名。设置页的“获取模型”仍只调用{base_url}/models;“测试连接”会按选中的 Surface 对模型列表首项发起一次最小请求,并展示resolved_model与resolved_api_surface,不会在失败后静默重试另一 endpoint。若返回details.reason=model_access_denied(例如 Issue #1208 中已观测到的 SiliconFlow / OpenAI Compatible 经 LiteLLM 返回Model disabled),请把它视为基于 provider 文案的 best-effort 模型可用性诊断,优先确认该模型是否已在当前账号/key 下开通,必要时调整模型顺序或移除不可用模型后重试;未覆盖或语义不同的 provider 文案会继续走兜底诊断。可选的“运行时能力检测”必须由用户显式选择后触发,会额外发起 JSON / tools / stream / vision smoke 请求,结果仅代表当前账号、模型和 endpoint 的一次 best-effort 检测。上述检测返回的结构化诊断字段不会写回.env,也不会阻止保存。- 若返回
details.reason=provider_blocked,表示服务商或中转网关明确拦截了本次请求;它区别于本地网络 / TLS 异常和model_access_denied,应优先检查账号风控、地域或请求来源限制、模型权限、代理商网关策略和内容安全策略。 - 运行时能力检测会产生真实 LLM 请求,可能带来 token / 图像输入费用、RPM/TPM 限流、余额不足或超时。检测失败可能来自账号权限、模型未开通、endpoint 区域、余额、服务商兼容层或 LiteLLM 转换路径,不等于该 provider 全局不支持对应能力。P3 未对所有真实 provider 做在线 smoke;兼容依据来自当前依赖约束
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0下的 LiteLLMcompletion()/ OpenAI I/O format / streaming / exception mapping,以及 OpenAI Chat Completions 的 JSON mode、tool calling、streaming 和 vision input 形状。 - 相关外部来源:LiteLLM Python SDK / OpenAI I/O format / streaming / exception mapping:https://docs.litellm.ai/;LiteLLM OpenAI-compatible 路由:https://docs.litellm.ai/docs/providers/openai_compatible;OpenAI Chat Completions:https://platform.openai.com/docs/api-reference/chat/create;JSON mode:https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat;tool calling:https://platform.openai.com/docs/guides/function-calling?api-mode=chat;streaming:https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat;vision input:https://platform.openai.com/docs/guides/images-vision?api-mode=chat。
- 保存渠道时,只会更新这次提交的 key;不会因为切换渠道模式而静默迁移整个旧配置。唯一会被同步清理的是运行时模型引用:如果
LITELLM_MODEL、AGENT_LITELLM_MODEL、VISION_MODEL或LITELLM_FALLBACK_MODELS指向了当前已启用渠道里已经不存在的模型,设置页会在保存前把这些失效引用清空/移除,避免运行时继续指向无效模型;即使当前启用渠道没有任何可选模型,也会清理缺少 legacy Key 支撑的托管 provider 旧值。cohere/*、google/*、xai/*这类直连模型仅用于说明历史direct-env兼容保留语义,不等于可用性承诺,是否可用请按各厂商官方模型/API 文档再做实际验证。 - 后端一致性依据:配置校验链路在
SystemConfigService._validate_llm_runtime_selection(src/services/system_config_service.py)中通过_uses_direct_env_provider(src/config.py)判断运行时来源;当前仅gemini、vertex_ai、anthropic、openai、deepseek属于托管 key provider,cohere、google、xai不在该白名单中,因此会保留为直连模型。 - 回退方式也保持最小:把对应渠道模型列表改回去后重新选择主模型 / fallback,或直接用桌面端导出备份 / 手动
.env还原之前的LLM_*、LITELLM_MODEL、AGENT_LITELLM_MODEL、VISION_MODEL、LLM_TEMPERATURE、LLM_USAGE_HMAC_*即可,不需要额外跑迁移脚本。Web 端如需恢复配置,也可在启用管理员鉴权(ADMIN_AUTH_ENABLED=true)后通过POST /api/v1/system/config/import回滚。 - 当前仓库对此链路的依赖约束是
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(见requirements.txt);回归覆盖包括tests/test_system_config_service.py、tests/test_system_config_api.py和apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx。
外部 provider 示例模型说明:
cohere/*、google/*、xai/*等 provider 前缀值仅用于说明当前保存清理语义,不代表该依赖约束内的逐型号可用性保证。文档或测试中的具体模型名都是配置保留行为样例,不是生产推荐;实际可用性请以对应官方模型文档为准,并结合仓库依赖约束litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0复核。
回退与兼容性证据
- 依赖约束与静默清理范围:在
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0下,保存仅清理失效的 runtime 模型引用(LITELLM_MODEL、AGENT_LITELLM_MODEL、VISION_MODEL、LITELLM_FALLBACK_MODELS),cohere/*、google/*、xai/*等非渠道直连模型会被保留。 - 回退方式:可直接用桌面端导出备份后通过
POST /api/v1/system/config/import恢复;也可手动把.env中历史LITELLM_* / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE / LLM_USAGE_HMAC_*回填后重启生效。Web 端执行导入前请先开启管理员鉴权(ADMIN_AUTH_ENABLED=true)。 - 回退回归证据:
tests/test_system_config_service.py::test_import_desktop_env_restores_runtime_models_after_cleanup覆盖“清理后用桌面导出备份恢复 runtime 引用”。 - 直连 provider 回归证据:
tests/test_system_config_service.py::SystemConfigServiceTestCase::test_validate_accepts_minimax_model_as_direct_env_provider、test_validate_accepts_cohere_model_as_direct_env_provider、test_validate_accepts_google_model_as_direct_env_provider、test_validate_accepts_xai_model_as_direct_env_provider覆盖直连 provider 保留语义。 - 前端回归命令:
cd apps/dsa-web && npm run lint && npm run build && npm run test -- src/components/settings/__tests__/LLMChannelEditor.test.tsx。 - 建议回退操作链路(含设置页刷新):先导出桌面备份,
POST /api/v1/system/config/import导入后,再通过GET /api/v1/system/config刷新页面配置,再确认LITELLM_MODEL / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE / LLM_USAGE_HMAC_*与模型列表一致后再继续使用。
常用官方文档来源(用于核对预设 provider / Base URL / 模型命名)
- Modelflare:快速开始、接入端点、模型与分组
- OpenAI Compatible 规范(LiteLLM):https://docs.litellm.ai/docs/providers/openai_compatible
- OpenAI 官方:https://platform.openai.com/docs/api-reference/chat
- DeepSeek 官方:https://api-docs.deepseek.com/
- Anspire Open:https://open.anspire.cn/dsa?share_code=QFBC0FYC
- 阿里百炼 DashScope 兼容模式:https://help.aliyun.com/zh/model-studio/compatibility-of-openai-with-dashscope
- Moonshot / Kimi 官方:https://platform.moonshot.ai/docs/guide/compatibility
- Anthropic 官方:https://docs.anthropic.com/en/api/messages
- Gemini 官方:https://ai.google.dev/gemini-api/docs/openai
- Cohere 官方:https://docs.cohere.com/
- Cohere API 参考:https://docs.cohere.com/reference/
- Cohere LiteLLM Provider:https://docs.litellm.ai/docs/providers/cohere
- Google Gemini API 与模型:https://ai.google.dev/gemini-api/docs/openai、https://ai.google.dev/gemini-api/docs/models
- Google LiteLLM Provider:https://docs.litellm.ai/docs/providers/gemini
- xAI 官方:https://docs.x.ai/docs
- xAI LiteLLM Provider:https://docs.litellm.ai/docs/providers/xai
- Ollama 官方:https://github.com/ollama/ollama/blob/main/docs/api.md
如果不方便用网页版,在 .env 文件中配置也非常丝滑,它能让你同时管理多个第三方平台。规则如下:
- 先声明你有几个渠道:
LLM_CHANNELS=渠道名称1,渠道名称2 - 给每个渠道分别填写配置(注意全大写):
LLM_{渠道名}_XXX
示例:Modelflare
注册 Modelflare,在控制台创建 API Key 后配置:
MODELFLARE_KEY=sk-xxx只需填写 MODELFLARE_KEY,后端自动启用 Modelflare,默认地址为 https://modelflare.dev/v1、模型为 openai/gpt-6.1-sol,无需填写渠道名、地址或模型。显式 YAML 或 LLM_CHANNELS 优先,Modelflare 显式渠道 Key 优先于此简写;清空 Key 或禁用渠道即可停用。推导不改写已保存配置。
只声明 modelflare 一个渠道且未填渠道模型列表时,未配置主模型时默认使用 openai/gpt-6.1-sol;配置主模型后,后端复用 LITELLM_MODEL 和带 openai/ 前缀的备用模型;主模型使用 openai/<模型名>。显式 LLM_MODELFLARE_MODELS 优先,多渠道仍需分别声明模型。推导不改写已保存配置或模型选择。
渠道名为 modelflare 时,未填写或留空的接入地址自动使用 https://modelflare.dev/v1;显式 LLM_MODELFLARE_BASE_URL 优先。该地址默认识别为 openai 协议,接口默认为 chat_completions;未设置 LITELLM_MODEL 时使用首个渠道模型。默认模型仍需账号有访问权限,不自动补充 API Key。
gpt-6.1-sol 是本项目的 Modelflare 默认模型。请按账号权限选择实际可用模型,并通过 Web「获取模型」和「测试连接」确认;Base URL 不要附加 /chat/completions。Modelflare 是新建渠道的首选模板,已保存的渠道和主模型不会因模板顺序调整而改变。Anspire Search 仍通过 ANSPIRE_API_KEYS 独立配置。
客户端快速添加按钮及渠道说明中的「Modelflare 专属注册」都使用 app UTM,README 使用 github UTM。新建渠道尚未填写 Key,或主模型/备用模型不属于当前启用渠道时,状态预览会提示「配置需要调整」并列出具体字段;补填 Key、选择可用模型或清空无效的备用模型后重试,预览不会自动改写已保存配置。
兼容示例:Anspire Responses API(GPT-5.6 名称为观测样本)
LLM_CHANNELS=anspire
LLM_ANSPIRE_PROTOCOL=openai
LLM_ANSPIRE_API_SURFACE=responses
LLM_ANSPIRE_BASE_URL=https://open-gateway.anspire.cn/v6
LLM_ANSPIRE_API_KEY=sk-xxx
LLM_ANSPIRE_MODELS=gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna
LITELLM_MODEL=openai/gpt-5.6-sol
LITELLM_FALLBACK_MODELS=openai/gpt-5.6-terra,openai/gpt-5.6-lunaAnspire 官方接入页公开了 https://open-gateway.anspire.cn/v6/responses 调用方式:https://open.anspire.cn/model?link=sample&tab=models。Responses 路由不按模型名写死;上面的 GPT-5.6 名称只是当前网关观测样本,不作为长期模型清单承诺。模型、Surface 与账号权限请以渠道实时 /models 返回、服务商说明和连接测试为准。
示例:同时配置 DeepSeek 和某中转平台,并设置备用切换
# 1. 开启渠道模式,声明这里有两个渠道:deepseek 和 aihubmix
LLM_CHANNELS=deepseek,aihubmix
# 2. 渠道一:配置 DeepSeek 官方
LLM_DEEPSEEK_BASE_URL=https://api.deepseek.com
LLM_DEEPSEEK_API_KEY=sk-1111111111111
LLM_DEEPSEEK_MODELS=deepseek-v4-flash,deepseek-v4-pro
# 3. 渠道二:配置一个常用的聚合中转 API
LLM_AIHUBMIX_BASE_URL=https://api.aihubmix.com/v1
LLM_AIHUBMIX_API_KEY=sk-2222222222222
LLM_AIHUBMIX_MODELS=gpt-5.5,claude-sonnet-4-6
# 4. 【关键】指定主模型和备用模型列表
# 平时首选用 deepseek 这款模型:
LITELLM_MODEL=deepseek/deepseek-v4-flash
# 可选:Agent 问股单独指定主模型(留空则继承主模型)
AGENT_LITELLM_MODEL=deepseek/deepseek-v4-pro
# 主模型崩了立刻挨个尝试下面这俩备用模型:
LITELLM_FALLBACK_MODELS=openai/gpt-5.4-mini,anthropic/claude-sonnet-4-6示例:Ollama 渠道模式(本地模型,无需 API Key)
# 1. 开启渠道模式,声明 ollama 渠道
LLM_CHANNELS=ollama
# 2. 配置 Ollama 地址(本地默认 11434 端口)
LLM_OLLAMA_BASE_URL=http://localhost:11434
LLM_OLLAMA_MODELS=qwen3:8b,llama3.2
# 3. 指定主模型
LITELLM_MODEL=ollama/qwen3:8b示例:Hermes 本地 HTTP Generation(Phase 3)
LLM_CHANNELS=hermes
LLM_HERMES_PROTOCOL=openai
LLM_HERMES_BASE_URL=http://127.0.0.1:8642/v1
LLM_HERMES_API_KEY=sk-local-hermes
LLM_HERMES_MODELS=hermes-agent
LITELLM_MODEL=openai/hermes-agentHermes 是保留渠道名,只支持本机 loopback /v1 OpenAI-compatible generation。Phase 3 只验证普通分析与 JSON 输出;不支持 Stream/SSE、Tools、Vision、Agent tools、远程 Hermes 或进程生命周期管理。Hermes API Key 只能使用单个 LLM_HERMES_API_KEY,不要配置 LLM_HERMES_API_KEYS 或 LLM_HERMES_EXTRA_HEADERS。如果 Hermes 配置非法,系统会阻止 legacy provider silent fallback,避免错误地改用外部模型。Web 设置页保存 reserved Hermes 渠道时,会显式清空旧的 LLM_HERMES_API_KEYS / LLM_HERMES_EXTRA_HEADERS 并返回 warning;如需恢复旧值,请从 .env 备份、Git 历史或桌面端导出备份手动还原,但 Phase 3 仍会拒绝非空的多 Key / Extra Headers 配置。
MiniMax 渠道模型填写说明
- 如果你通过 OpenAI Compatible 渠道接 MiniMax,请在渠道模型里直接填写
minimax/<模型名>,例如minimax/MiniMax-M1。 - Web 设置页里的主模型、Agent 主模型、Fallback、Vision 下拉会保留这个值原样展示,不会再错误改写成
openai/minimax/<模型名>。
默认模型问股 / LiteLLM 配置兼容说明
AGENT_BACKEND=auto|litellm时,问股 Agent 沿用与普通分析相同的三层优先级:LITELLM_CONFIG(LiteLLM YAML)>LLM_CHANNELS> legacy provider keys。只要上层配置有效生效,下层配置就不会再参与本次请求;Codex 不使用这套模型路由。- YAML 模式下,Agent 直接复用 LiteLLM
model_list/model_name路由语义;渠道模式下,优先读取AGENT_LITELLM_MODEL,留空时继承LITELLM_MODEL,再按LITELLM_FALLBACK_MODELS继续 fallback。 - 如果你没有启用 YAML / Channels,且
AGENT_LITELLM_MODEL也留空,但本地仍保留 legacy 环境变量,问股 Agent 依然会继承旧配置:GEMINI_API_KEY + GEMINI_MODEL->gemini/<model>,OPENAI_API_KEY + OPENAI_MODEL->openai/<model>,ANTHROPIC_API_KEY + ANTHROPIC_MODEL->anthropic/<model>。 - 该兼容逻辑只增强“失败时保留后端真实错误原因”和“未配置 LLM 时给出更具体诊断”,不会静默删除、清空、迁移或改写你现有的
GEMINI_*/OPENAI_*/ANTHROPIC_*/LITELLM_*配置。 - 如果当前环境没有任何有效 Agent 模型链路,问股页面会继续按失败语义返回,并直接展示后端真实配置诊断;补齐任一有效模型来源后即可恢复,无需额外执行配置迁移脚本。
- 推荐的新配置方式仍然是显式设置
LITELLM_MODEL/AGENT_LITELLM_MODEL或使用LLM_CHANNELS;legacy provider keys 目前保留为兼容回退路径,方便旧.env、本地 macOS 开发环境和历史部署平滑继续运行。
问股可见对话上下文压缩
默认情况下,问股仍按历史行为只注入最近 20 条可见对话。以下 LLM 压缩只适用于「默认模型」问股:Codex Agent 始终使用最近 20 条用户可见对话,不会调用 AGENT_LITELLM_MODEL 生成历史摘要。切换到 Codex 不会清空已保存的压缩配置,切回「默认模型」后会继续生效。需要为默认模型长会话省 token 时,可开启:
AGENT_CONTEXT_COMPRESSION_ENABLED=true
AGENT_CONTEXT_COMPRESSION_PROFILE=balanced
# 留空则跟随 profile preset
AGENT_CONTEXT_COMPRESSION_TRIGGER_TOKENS=
AGENT_CONTEXT_PROTECTED_TURNS=压缩只处理 session_id 下用户可见的 user / assistant 文本历史,不处理 provider trace、thinking blocks、tool calls 或 tool results,也不会改变同轮工具调用透传。三档 preset 分别是 cost(6000 tokens / 保护 2 轮)、balanced(12000 / 4)和 long_context_raw_first(24000 / 6);trigger / protected 留空时跟随当前 profile,显式填写时覆盖 profile。
问股 single-agent 路径会额外维护一条 provider-aware trace 分轨,用于 DeepSeek V4 thinking + tool-call 的跨轮协议回放:只有同一轮同时出现 tool_calls 与 reasoning_content 时才会按当前 session_id + provider + model 保存最近 3 条最小协议材料,并在下一轮按原始时序插回对应可见 assistant 回复之前。该 trace 只能原样保留或整段丢弃,不参与摘要、不写入 Web 会话消息、不新增 .env 配置;model/provider 不匹配、锚点已被 summary 覆盖或预算不足时会整段跳过。Claude extended thinking 本轮只覆盖 adapter/storage 级 opaque thinking / redacted_thinking / signature blocks plumbing 与离线 fixture,不声明生产端到端支持;multi-agent trace 注入仍是 follow-up。外部协议依据包括 DeepSeek thinking mode 文档(https://api-docs.deepseek.com/guides/thinking_mode)和 Anthropic Claude extended thinking 文档(https://platform.claude.com/docs/en/docs/build-with-claude/extended-thinking),LiteLLM 兼容窗口仍以 requirements.txt 的 litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0 为准。
严格 temperature 模型兼容说明
- Moonshot 官方说明 Kimi API 兼容 OpenAI 接口,Base URL 使用
https://api.moonshot.ai/v1:https://platform.kimi.ai/docs/guide/kimi-k2-6-quickstart - LiteLLM 官方要求 OpenAI Compatible 渠道模型名使用
openai/前缀:https://docs.litellm.ai/docs/providers/openai_compatible - Moonshot 官方兼容性文档区分两种固定值:thinking 模式固定
1.0,non-thinking 模式固定0.6;传其它值会被接口拒绝:https://platform.moonshot.ai/docs/guide/compatibility#parameters-differences-in-request-body - OpenAI Chat Completions 规范中
temperature是可选参数;对 GPT-5 / o 系列等只接受默认温度的模型,本项目会在请求层省略temperature,让服务端使用默认值,而不是改写你的LLM_TEMPERATURE:https://platform.openai.com/docs/api-reference/chat/create - 当前仓库的运行时依赖约束是
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(见requirements.txt);本次兼容逻辑按该约束回归验证了主分析、大盘复盘、Agent 直连 LiteLLM,以及系统设置页的渠道连通性测试。 - 因此本项目会在请求发出前按实际请求模式归一化
kimi-k2.6及其kimi-k2.6-*变体:默认 / thinking 路径使用temperature=1.0;如果你的 LiteLLM YAML 路由别名里显式写了litellm_params.extra_body.thinking.type: disabled(或等价 non-thinking 配置),则自动切到temperature=0.6。你在.env或 Web 设置里保存的LLM_TEMPERATURE不会被改写。 - 如果兼容平台对未收录的新模型返回明确的参数错误(例如
temperature不支持、只能使用默认1.0、top_p不支持),运行时会对当前请求做一次参数修正并重试;只有重试成功后才把该策略缓存在当前进程内。该缓存不会写回.env,服务重启后会重新按配置与适配规则判断。 - 对已经产生部分内容的流式响应,系统不会在半截输出后切换参数;仍沿用原有“同模型非流式重试 / fallback 模型”的稳定路径,避免拼接出不一致的回答。
SystemConfigService在 Web 设置保存 / 桌面端.env导入时只更新你提交的 key,不会因为切到严格 temperature 模型静默清空、迁移或重写已有LLM_TEMPERATURE;渠道测试请求里的临时参数策略也不会回写到配置文件。- 非严格主模型、非严格 fallback 以及切回普通模型后的请求,仍继续使用你配置的温度;也就是说旧配置无需迁移,切换模型即可自动恢复原行为。
- 本仓库兼容性回归覆盖见:
tests/test_llm_channel_config.py、tests/test_market_analyzer_generate_text.py、tests/test_agent_pipeline.py、tests/test_system_config_service.py。 - 最小回滚方式:直接回退本次 LLM 参数适配相关改动,无需单独迁移已有
LLM_TEMPERATURE配置。
兼容性与回退复核清单(按 PR 审核口径)
- 运行时依赖约束:
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(与requirements.txt一致)。 - 回归验证入口:
- 渠道模型发现与连接:
tests/test_llm_channel_config.py - 运行时源清理与恢复(含桌面导出备份链路):
tests/test_system_config_service.py - 接口校验与问题面向字段:
tests/test_system_config_api.py - 设置页交互与保存后提示:
apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx
- 渠道模型发现与连接:
- 旧配置回退路径:
桌面端导出备份 -> /api/v1/system/config/import,或手动恢复LLM_* / LITELLM_* / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE / LLM_USAGE_HMAC_*;Web 导入备份前同样要求ADMIN_AUTH_ENABLED=true,否则会返回 403。
致命避坑说明:如果你启用了
LLM_CHANNELS,那么你直接写在外面的DEEPSEEK_API_KEY或OPENAI_API_KEY将全部失效(系统一律无视)!二者选其一即可,千万不要既写了新手模式又写了渠道模式结果产生冲突。
Docker 注意:如果你在docker compose environment:或docker run -e中显式传入LITELLM_MODEL、LLM_CHANNELS、LLM_DEEPSEEK_MODELS等变量,容器重启后这些环境变量会覆盖 Web 设置页写入的.env,需要同步修改部署配置。
兼容依据与回退审计说明(本次 PR 适配说明)
- 官方与运行时兼容依据采用两层:第一层为官方接口语义(LiteLLM OpenAI-compatible 路由、OpenAI Chat Completions、Moonshot/Kimi 文档与官方模型说明);第二层为本仓库当前运行时语义(
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0)下的实际错误归类。 - 本次兼容恢复只使用“本地运行时错误归类 + 单请求修正重试 + 进程内缓存”策略,不写入
.env、不做配置迁移,仅在执行路径上动态规避不支持参数(temperature、top_p、presence_penalty、frequency_penalty、seed)。若要回退,不需要额外迁移命令,恢复旧值即可。 - 回归与证据:
tests/test_llm_param_recovery.py、tests/test_system_config_service.py、tests/test_llm_channel_config.py、tests/test_system_config_api.py、tests/test_market_analyzer_generate_text.py、tests/test_agent_pipeline.py;桌面导入与运行时清理回退另有test_import_desktop_env_restores_runtime_models_after_cleanup直接覆盖。
LLM usage HMAC 遥测
P0a usage telemetry 会为实际发送的 message 生成 HMAC-SHA256 指纹,用于后续判断相同 prompt/message 前缀是否稳定。该能力只写入本地 llm_usage 记录,不改变 prompt、provider 参数、cache hint、模型输出或 fallback 顺序。
Usage 来源按三层读取:
- 优先读取 provider / LiteLLM 公开响应字段
usage。 - 其次读取 LiteLLM 公开响应字段
usage_metadata。 - 最后才读取
_hidden_params["usage"],这是 LiteLLM private/internal 的 best-effort fallback,不是稳定公共契约;缺失时只代表 usage/cache telemetry 可能不完整,不代表模型请求失败。
Cache token 归一化只做 allowlisted best-effort normalization。外部字段依据和运行时边界如下,避免把官方稳定契约、LiteLLM 当前归一化行为和本仓库兼容 allowlist 混为一谈:
| Provider / 来源 | 读取字段 | 依据与边界 | 覆盖情况 |
|---|---|---|---|
| OpenAI | usage.prompt_tokens_details.cached_tokens |
官方 Prompt Caching 文档说明 1024 tokens 以下也会返回 cached_tokens=0:https://developers.openai.com/api/docs/guides/prompt-caching |
unit/mock 覆盖;本 PR 未做 OpenAI live smoke |
| Anthropic | cache_creation_input_tokens / cache_read_input_tokens / input_tokens |
官方 Prompt Caching 文档定义 total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens:https://platform.claude.com/docs/en/build-with-claude/prompt-caching |
unit/mock 覆盖;本 PR 未做 Anthropic live smoke |
| Gemini / Vertex AI | 官方字段为 UsageMetadata.cachedContentTokenCount;运行时消费 LiteLLM 暴露的 snake_case / normalized 字段,如 cached_content_token_count、cache_read_input_tokens 或 prompt_tokens_details.cached_tokens |
Gemini UsageMetadata 官方字段见 https://ai.google.dev/api/generate-content#UsageMetadata;本仓库不新增 native camelCase runtime fallback,运行时边界以 litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0 为准 |
unit/mock 覆盖;本 PR 未做 Gemini / Vertex live smoke |
| DeepSeek | prompt_cache_hit_tokens / prompt_cache_miss_tokens |
DeepSeek Chat Completion 文档说明 prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens:https://api-docs.deepseek.com/api/create-chat-completion |
unit/mock 覆盖;本 PR 只做一次脱敏 DeepSeek smoke,不保存完整响应 |
| GLM / OpenAI-compatible / StepFun 等兼容平台 | 已建模 token/cache count allowlist 中能映射到统一字段的值 | 不声明官方稳定 cache telemetry contract;仅表示在当前 LiteLLM / OpenAI-compatible shape 下做 best-effort normalization,未建模 metadata 不持久化 | unit/fixture/mock 覆盖;本 PR 未做这些 provider 的 live smoke |
| LiteLLM public response shape | usage / usage_metadata |
按当前依赖窗口 litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0 的 response / Usage object shape 消费;不作为 LiteLLM 2.x 兼容承诺 |
Analyzer / Agent / usage tests 覆盖 |
| LiteLLM private fallback | _hidden_params["usage"] |
private/internal best-effort fallback,不是 LiteLLM 稳定公共契约;仅在 public usage zero-only/no-signal 等窄场景补足 streaming usage,不改变 provider 请求参数 | unit/mock 覆盖;缺失时只影响 telemetry 完整性,不代表模型请求失败 |
LLM_USAGE_HMAC_SECRET=
LLM_USAGE_HMAC_KEY_VERSION=local-v1LLM_USAGE_HMAC_SECRET留空时,系统会在数据目录生成.llm_usage_hmac_secret,适合单部署本地比较。- 只有需要跨部署比较 HMAC 时,才显式配置同一个高熵随机密钥;建议使用
openssl rand -hex 32生成。 .llm_usage_hmac_secret是本地 secret artifact,已在.gitignore中按文件名忽略。- 轮换密钥时同步更新
LLM_USAGE_HMAC_KEY_VERSION,避免不同密钥生成的 HMAC 被误比较。 - 不要复用登录 session secret,也不要把真实密钥提交到版本控制或暴露在 issue、日志、截图中。
Provider prompt cache 配置(P1 / P1.5)
Prompt cache 配置只控制本项目是否记录 cache usage / diagnostics,以及主分析路径是否主动发送已验证的 provider-specific hint;它不控制 OpenAI、Gemini、DeepSeek 等 provider 的 implicit / provider-managed cache。
LLM_PROMPT_CACHE_TELEMETRY_ENABLED=true
LLM_PROMPT_CACHE_HINTS_ENABLED=false
LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL=offLLM_PROMPT_CACHE_TELEMETRY_ENABLED=false时,不持久化 provider raw usage JSON、normalized cache fields 和 cache decision diagnostics;基础 token usage 记录保持兼容。LLM_PROMPT_CACHE_HINTS_ENABLED=true只允许主分析 / analyzer LiteLLM 路径向 registry 中已验证或 smoke-tested 的 provider / route 发送prompt_cache_key、cache_control、user_id等 hint。问股 Agent 路径当前只记录 capability / usage diagnostics,不主动发送 provider-specific hints。未知 OpenAI-compatible gateway 默认 telemetry only。LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL=basic只在 debug 日志和测试可观察对象中提供 provider、api surface、verification status、hint applied / disabled reason 等非敏感枚举。debug在同一范围内额外提供 HMAC-derived route/cache diagnostics 和 matched caps id,但仍禁止 raw prompt、request body、message content、股票/用户原文、webhook 或 API key;这些诊断不是公开 Usage API 或普通设置页输出。- Provider Cache Capability Registry 是
src/llm/provider_cache.py中的 code-level 手工能力表。条目带doc_sources、last_verified_at和verification_status;新增 provider 或升级 LiteLLM 后应同步更新条目与测试。 - Prompt cache key、route key 和 DeepSeek session isolation 复用
LLM_USAGE_HMAC_SECRET/.llm_usage_hmac_secret做 domain-separated HMAC,不新增 prompt-cache 专用 secret。
Legacy message stability audit(P0.5a)
P0.5a 在普通个股分析路径为 legacy [system, user] message 追加内部稳定性审计字段,继续写入本地 llm_usage。它复用上面的 message HMAC,不修改 prompt 内容、message 顺序、provider 请求参数、cache hint、模型输出、fallback 顺序,也不扩展公开 Usage API 或 Web 页面。
新增字段只用于维护者诊断:
language、market_group、analysis_mode、legacy_prompt_mode、provider、transport、message_count描述本次普通个股分析调用的低敏路由上下文。skill_config_hmac是基于已解析 skill prompt 片段、默认 skill 策略和 legacy prompt 模式生成的 HMAC-SHA256,用于判断 system message 是否随 skill configuration 变化;不会保存 skill 原文。known_dynamic_marker_positions是 JSON string,只记录marker_name、message_role、char_offset;不会保存股票代码、股票名称、日期、新闻正文、行情值、headers、response text 或 prompt 片段。estimated_total_prompt_tokens、approx_common_prefix_chars、approx_common_prefix_tokens基于项目内稳定 canonical render 估算:按 message 顺序拼接role + "\n" + content,并用固定分隔符连接。该口径不声称等同 provider 真实 wire bytes。char_offset是 marker 在对应 messagecontent内的位置;approx_common_prefix_chars是 canonical render 起点到第一个已知动态 marker 之前的字符数。没有 marker 时 common-prefix 字段为NULL。- token 估算使用
ceil(chars / 3),只作 diagnostics,不替代 provider usage,也不参与 cache threshold 判定;中文场景可能偏低。
P0.5a 不引入 PromptBlock IR、block_id、stability_class、static_prefix_hash 或 dynamic_context_hash。Agent、research 与 market review 路径暂不接入该审计。
方式三:YAML 高级配置(适合老手自定义)
目标: 我不在乎学习门槛,我要最高控制权,我要用原生规则做企业级高可用!
这一层会直接映射到底层 LiteLLM 路由能力,支持高并发、自动重试、按 RPM/TPM 负载均衡等操作。
本地运行 / Docker 部署模式配置说明
- 在
.env中只保留一行指向声明:envLITELLM_CONFIG=./litellm_config.yaml - 在项目根目录创建一个
litellm_config.yaml(可以参考自带的docs/examples/litellm_config.example.yaml)。
示例 litellm_config.yaml:
model_list:
- model_name: my-smart-model
litellm_params:
model: deepseek/deepseek-v4-flash
api_base: https://api.deepseek.com
api_key: "os.environ/MY_CUSTOM_SECRET_KEY" # 从环境变量读取 Key,安全防泄漏
# Ollama 本地模型(无需 api_key)
- model_name: ollama/qwen3:8b
litellm_params:
model: ollama/qwen3:8b
api_base: http://localhost:11434GitHub Actions配置说明
Settings→Secrets and variables→Actions。非敏感配置(如模型名、开关、Base URL)可以放在Secret或Variables;凡是*_API_KEY/*_API_KEYS以及LLM_<NAME>_API_KEY/LLM_<NAME>_API_KEYS这类密钥字段,请统一放在Secret标签页的New repository secret按下表配置,只有全部必填配置正确配置,YAML 高级配置模式才可以生效,YAML配置文件的写法,可以参考自带的
docs/examples/litellm_config.example.yaml
| Secret 名称 | 说明 | 必填 |
|---|---|---|
LITELLM_CONFIG |
高级模型路由配置文件路径,通常配置 ./litellm_config.yaml |
必填 |
LITELLM_MODEL |
默认主模型名称或路由别名 | 必填 |
LITELLM_CONFIG_YAML |
存放 YAML 配置文件内容,可不在仓库中提交实体文件 | 可选 |
LITELLM_API_KEY |
用于存储API Key,可在配置文件中引用(环境变量引用方式)。由于GitHub Actions必须要指定导入的环境变量,因此你不能像本地运行模式那样自由命名环境变量 | 可选,必须配置到repository secret中 |
ANTHROPIC_API_KEY |
如果要多个API Key,这个变量名称也能拿来用 | 可选,必须配置到repository secret中 |
OPENAI_API_KEY |
同上,可以用来存储API Key | 可选,必须配置到repository secret中 |
渠道模式无需上传 YAML 文件。仓库自带 00-daily-analysis.yml 已显式透传以下常用字段:
- 运行时选择:
GENERATION_BACKEND、GENERATION_FALLBACK_BACKEND、GENERATION_BACKEND_TIMEOUT_SECONDS、GENERATION_BACKEND_MAX_OUTPUT_BYTES、GENERATION_BACKEND_MAX_CONCURRENCY、LOCAL_CLI_BACKEND_MAX_CONCURRENCY、AGENT_GENERATION_BACKEND、LLM_CHANNELS、LITELLM_MODEL、LITELLM_FALLBACK_MODELS、AGENT_LITELLM_MODEL、VISION_MODEL、VISION_PROVIDER_PRIORITY、LLM_TEMPERATURE、LLM_USAGE_HMAC_SECRET、LLM_USAGE_HMAC_KEY_VERSION、LLM_PROMPT_CACHE_TELEMETRY_ENABLED、LLM_PROMPT_CACHE_HINTS_ENABLED、LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL - 多 Key:
GEMINI_API_KEYS、ANTHROPIC_API_KEYS、OPENAI_API_KEYS、DEEPSEEK_API_KEYS(当前 workflow 仅从 repository secrets 导入,不会读取同名 Variables) - 常用渠道名:
modelflare、primary、secondary、aihubmix、anspire、deepseek、dashscope、zhipu、moonshot、minimax、volcengine、siliconflow、openrouter、requesty、gemini、anthropic、openai、ollama、hermes
例如在 GitHub Actions 中配置 LLM_CHANNELS=primary,deepseek 时,需同步配置 LLM_PRIMARY_* / LLM_DEEPSEEK_*。其中 LLM_<NAME>_API_KEY / LLM_<NAME>_API_KEYS 当前也仅从 repository secrets 导入;如果你把这些值放在 Variables,运行时不会生效。若使用自定义渠道名(如 my_proxy),GitHub Actions 还必须在 workflow env: 中显式新增对应的 LLM_MY_PROXY_* 映射;本地 .env 和 Docker 不受这个限制。
三层配置互斥准则:YAML 优先级最高!只要配置了 YAML,渠道模式 和 新手极简模式 统统被忽略。系统优先级为:
YAML配置 > 渠道模式 > 极简单模型。
扩展功能:看图模型 (Vision) 配置
系统中有些特定功能(比如上传股票软件截图,让 AI 提取出截图里的股票代码并放入自选股池)必须用到具备“视觉能力”的模型。你需在 .env 单独给它指派一个懂图片的模型。
# 指定你看图专用的模型名
VISION_MODEL=openai/gpt-5.5
# 别忘了填写它对应提供商的 API KEY,如果是 OpenAI 兼容渠道就提供 OPENAI_API_KEY:
# OPENAI_API_KEY=xxx备用看图机制: 为了防止偶尔罢工,系统内置了切换策略。如果主视觉模型调用失败,它会按照下方的顺位尝试寻找是否有其他看图模型的 Key:
# 默认的备用顺序:
VISION_PROVIDER_PRIORITY=gemini,anthropic,openai检测与排错 (Troubleshooting)
配好了之后心惊胆战不知道对不对?在命令行(Terminal)里敲入下面代码帮你挂号问诊:
python scripts/check_env.py --config:纯检测.env配置文件里的逻辑写得对不对,是不是少写了什么。(秒出结果,不调用网络,纯检查本地文本拼写)python scripts/check_env.py --llm:系统会真的发一句问候语给大模型,让你亲眼看到他的回答。这能彻底测出你的网络通不通、账号有没有欠费。
常见踩坑答疑台
| 遇到了什么诡异报错? | 罪魁祸首可能是啥? | 该怎么收拾它? |
|---|---|---|
| 界面提示主模型未配置 | 系统不知道你到底想用哪家的哪个模型 | 在 .env 中写上一句明白话:LITELLM_MODEL=provider/你的模型名。比如 openai/gpt-5.5 |
| 我写了好几家的Key,为什么死活只有一个生效?修改还没用? | 你把 极简模式 和 渠道模式 混着写了! | 想好一条路走到黑——只要简单就删掉 LLM_CHANNELS 开头的;想要丰富备用切换就要全部转投到 LLM_CHANNELS 下的编制里。 |
| 错误码报 400 或 401 或 Invalid API Key | API Key 填错、少复制了一截、账号充值没到账、或者模型名字敲错(极度常见)。 | 1. 检查复制的 Key 前后是否有误填空格。 2. 检查 Base URL 最后是不是少了一个 /v1。 3. 检查模型名是否少写了 openai/ 之类的前缀! |
Kimi K2.6 报 invalid temperature(可能提示只允许 1.0 或 0.6) |
该模型按 thinking / non-thinking 模式要求不同固定 temperature;旧配置或调用入口可能还在传 0.7。 |
升级后系统会对 kimi-k2.6 默认 / thinking 请求自动使用 temperature=1.0;如果你在 LiteLLM YAML 路由里显式关闭 thinking,则自动改用 0.6。模型名建议写成 openai/kimi-k2.6 并配合 Moonshot / 聚合平台的 OpenAI 兼容 Base URL 与 API Key。非 Kimi fallback 仍会继续使用你配置的 LLM_TEMPERATURE。 |
GPT-5 / o 系列报 temperature 不支持或只允许默认值 |
这类模型只接受服务端默认采样参数,但旧调用入口会显式传 0.7。 |
升级后请求层会省略 temperature,让服务端使用默认值;.env / Web 设置中的 LLM_TEMPERATURE 不会被改写,切回普通模型后仍按原值发送。 |
| 转圈转不停,最后报 Timeout / ConnectionRefused 等 | 1. 在国内使用国外原版(像 Google、OpenAI),没开代理被墙了。2. 你买的云服务器压根不能出境。 | 非常推荐使用国内官方(如DeepSeek、阿里)或者各种兼容 OpenAI 的聚合中转接口。因为中转站把网络问题帮你解决好了。 |
Ollama 报 404、Could not get model info 或 api/generate/api/show |
误用 OPENAI_BASE_URL 配置 Ollama,系统会错误拼接 URL |
改用 OLLAMA_API_BASE=http://localhost:11434 或渠道模式(LLM_CHANNELS=ollama + LLM_OLLAMA_BASE_URL) |
进阶老手的叮嘱:如果你开启了 Agent (深度思考网络搜索问股) 模式,这里有个经验之谈,推荐选用如 deepseek-v4-pro 这种逻辑推导能力更强的大模型。如果为了省钱用小微模型跑 Agent,它逻辑能力大概率跟不上,不仅达不到预期,还会白跑一堆空流程。
LLM CONFIG GUIDE EN (`docs/LLM_CONFIG_GUIDE_EN.md`)
LLM Configuration Guide
Welcome! Whether you are a beginner newly exposed to AI or a veteran skilled with various APIs, this guide will help you set up Large Language Models (LLMs) quickly.
This project exposes a unified AI model access flow that supports official APIs, OpenAI-compatible platforms, and local models. Under the hood it is powered by LiteLLM, but most users only need to think in terms of picking a provider, adding an API key, and optionally choosing a primary model or channels. To cater to different experience levels, we provide a three-tier configuration hierarchy. Choose the method that fits you best.
If you are choosing a concrete provider, setting up GitHub Actions Secrets / Variables, troubleshooting a details.reason error, or rolling back an LLM configuration, start with the Provider Configuration Guide. It is the maintained reference for provider presets, Actions variable mapping, runtime capability-check boundaries, and common error handling.
Quick Navigation: Which section should you read?
- [Beginners] "I just want to get the system running ASAP, keep it as simple as possible!" -> Go to Method 1: Simple Model Config
- [Advanced Users] "I have several Keys, want to configure fallback models, and define custom Base URLs." -> Go to Method 2: Channels Mode Config
- [Veterans] "I want complex load balancing, request routing, and enterprise-level high availability!" -> Go to Method 3: Advanced YAML Config
- [Local Models] "I want to use Ollama local models!" -> Go to Example 4: Using Ollama Local Models
- [Vision Models] "I want to extract stock codes from images!" -> Go to Vision Model Config
Generation Backend (Phase 4)
The generation backend is the outer runtime selector for regular stock analysis, market review, and generate_text(). The default remains litellm with zero regression. codex_cli / claude_code_cli / opencode_cli are explicit opt-in local CLI backends and are currently experimental/limited.
GENERATION_BACKEND=litellm
GENERATION_FALLBACK_BACKEND=litellm
GENERATION_BACKEND_TIMEOUT_SECONDS=300
GENERATION_BACKEND_MAX_OUTPUT_BYTES=1048576
GENERATION_BACKEND_MAX_CONCURRENCY=1
LOCAL_CLI_BACKEND_MAX_CONCURRENCY=1
# Optional: leave empty to use the local OpenCode default model; set it only to pass a --model override.
# OPENCODE_CLI_MODEL=provider/model
AGENT_BACKEND=auto
AGENT_GENERATION_BACKEND=autoGENERATION_BACKEND=litellm|codex_cli|claude_code_cli|opencode_cli. Local CLI backends are generation backends, not LiteLLM providers; do not setLITELLM_MODEL=codex_cli/...,LITELLM_MODEL=claude_code_cli/..., orLITELLM_MODEL=opencode_cli/....- With
GENERATION_BACKEND=opencode_cli, DSA does not pass--modelby default and lets local OpenCode use its own default model configuration.OPENCODE_CLI_MODELis only an optional override; when set, DSA passes it as one OpenCode--modelargument. Provider authentication, account state, and model availability are handled by your local OpenCode setup. - If
GENERATION_FALLBACK_BACKENDis unset, it defaults tolitellm. In local.env, an explicit empty value disables backend-level fallback. A fallback equal to the primary backend is treated as no-op. The bundled GitHub Actions workflow explicitly exportslitellmwhen this variable is not configured; to disable backend fallback there, set the fallback to the primary backend, for exampleGENERATION_BACKEND=codex_cli+GENERATION_FALLBACK_BACKEND=codex_cli. - With
GENERATION_BACKEND=codex_cli|claude_code_cli, regular analysis and market review do not require Gemini/OpenAI/Anthropic/DeepSeek API keys. If the corresponding executable is missing, DSA returns structuredcommand_not_foundinstead of “API key not configured”. - The current
codex_clipreset usescodex --ask-for-approval never exec --sandbox read-only --output-last-message <temp-file> -. Regular analysis is unattended generation, soneverprevents a non-interactive run from stopping for human approval whileread-onlyretains the filesystem boundary. DSA reads the final response from the temporary file; the duplicate response printed to stdout is removed from diagnostics previews and output-size accounting and is never used for main-analysis JSON parsing. Official references: Codex non-interactive mode and Codex CLI command line options. This repository has live-verifiedcodex-cli 0.144.3and does not claim a wider minimum version range; if the installed CLI does not support a preset argument, DSA returns structuredcapability_unsupported/cli_contract_unsupporteddiagnostics and falls back tolitellmwhen backend fallback is configured. - The current
claude_code_clipreset usesclaude --safe-mode --tools "" --disallowedTools "mcp__*" --strict-mcp-config --no-session-persistence --output-format json -p <static instruction>, with the full DSA prompt passed through stdin. DSA only extracts the final text from Claude'sresult/successJSON envelope. If--json-schemais enabled later, schema mode must extractstructured_output, and the output still goes through DSA's existing JSON validator, minimal parser contract,_parse_response(), integrity retry, placeholder fill, and usage telemetry. The CLI flags are based on the Claude Code CLI reference. This PR smoke-testedclaude 2.1.177 (Claude Code)and does not claim a wider minimum version range. - The current
opencode_clipreset usesopencode --pure run --format json [--model <OPENCODE_CLI_MODEL>] <static instruction> --file <temp prompt file>. DSA only appends--modelwhenOPENCODE_CLI_MODELis explicitly set. The full DSA prompt is written to a permission-restricted temporary file and is not placed in argv. DSA only extracts text from OpenCode JSON event output that has no tool events and ends with a normalstep_finish;tool_use,error,question, orpermissionevents fail structurally. The CLI flags are based on the OpenCode CLI reference, and project config merge semantics are documented in the OpenCode config reference. This PR smoke-testedopencode 1.17.11and does not claim a wider minimum version range. - Local CLI backends do not support streaming. Stream requests degrade to non-stream and do not return
capability_unsupported. - Local CLI usage is normally unavailable. DSA does not persist fake 0-token, fake cost, or fake cache telemetry.
- Local CLI execution has hard caps:
GENERATION_BACKEND_TIMEOUT_SECONDSmax3600,GENERATION_BACKEND_MAX_OUTPUT_BYTESmax33554432,GENERATION_BACKEND_MAX_CONCURRENCYmax16, andLOCAL_CLI_BACKEND_MAX_CONCURRENCYmax4. Diagnostic stdout/stderr plus the final response are counted together; for--output-last-messagepresets, the final response duplicated to stdout is not counted twice and is not exposed instdout_preview. - Local CLI
stdout_preview/stderr_previewredact short credential assignments before diagnostics are emitted, regardless of value length. Uppercase environment assignments reuse the child-env fail-closed sensitive-name contract; scalar assignments in JSON, YAML, and ordinary logs use a narrower credential-field allowlist; URLs keep separate userinfo, webhook, and sensitive-parameter rules. Ordinary troubleshooting fields such astoken_budget,session_id, andsort_keyare not removed merely because their names containtoken,session, orkey; however, an unquoted sensitive YAML scalar has no reliable same-line boundary, so redaction fails closed from that value through the end of the line and does not preserve later fields on that line. - Local CLI default concurrency is 1. Effective local CLI concurrency is
min(LOCAL_CLI_BACKEND_MAX_CONCURRENCY, GENERATION_BACKEND_MAX_CONCURRENCY)and does not inheritMAX_WORKERS. AGENT_GENERATION_BACKEND=autodoes not inherit local CLI values fromGENERATION_BACKEND; Agent tool calling remains on LiteLLM. The Web settings page only exposesauto|litellm; a hand-writtenAGENT_GENERATION_BACKEND=codex_cli|claude_code_cli|opencode_clidoes not enable Agent text-only mode and returns an explicit unsupported tool-calling diagnostic.- The Phase 6a DSA Tool Surface remains the only tool-schema, permission, scope-guard, structured-error, audit, and redaction boundary. The Phase 6 Codex AgentBackend can execute tools only through this surface.
codex_cli/claude_code_cli/opencode_cliremain generation-only and are never Agent tool fallbacks. - The Web settings generation-backend quick check only reads saved
.env, runtime defaults, and unsaved drafts. It does not write config, reload runtime, or send a real model request;availableonly means the current config can be attempted. JSON smoke test is a separate explicit button that sends one real generation-backend request with a server-owned fixed JSON prompt/schema to verify extractor behavior, JSON contract, timeout, output limits, and usage-unavailable semantics. GET /api/v1/system/config/generation-backends/statusonly reads saved config. Unsaved drafts usePOST /api/v1/system/config/generation-backends/status/previeworPOST /api/v1/system/config/generation-backends/smoke-test; masked secrets preserve saved values.health_statusandlast_error_code/messagedescribe only the current computation, not persisted historical health.
Chat Session State And Upgrades
The server owns the main discussion object and saved Skills. LiteLLM single/existing multi and the Codex single interface consume the same accepted snapshot. Omitted/null Skills inherit saved choices (configuration defaults when unset); explicit [] saves a cleared selection and uses built-in defaults, not a non-empty configured selection. Only unique server-index names establish identity. Comparisons do not switch the main object; execution failure/stopping does not undo acceptance. On a state/instance conflict, refresh and retry manually; Web keeps the input and draft selection and does not resend automatically.
Legacy recovery uses explicit codes, not historical names. Analyze 600519 → switch to CATL by name → risks? requires confirmation rather than reusing 600519. Startup adds state columns without deleting messages, Skills or reports. Stop the backend before upgrading/rolling back; use SQLite backup or a complete stopped-server backup, not a main-file-only copy with active WAL. Keep configuration, database location and data directory; update matching backend/Web and restart without deleting tables/columns. Rollback preserves added columns and old-code message/Skill writes. Re-upgrade after intervening user messages shows an unconfirmed object until explicitly reconfirmed. Concurrent old/new writers are unsupported. For separate deployments, upgrade backend before Web and roll back Web before backend.
Comparisons that mix codes and names include both identities. An adjacent explicit code disambiguates a matching listing name (for example, 688981中芯国际). 从600519改看300750 / 把600519换成300750 bind the destination to the switch operation: the destination remains 300750 even if it is already current or the instruction is repeated. Each acceptance still saves the message and increments the version. Explicit switching permits only the new object's tool scope and removes mismatched report hints. Multiple destination alternatives require clarification; neither the last mention nor exclusion of the current object selects the destination.
With a confirmed object, 看看近期走势 / 分析一下财务状况 / 分析风险 continue analysis while preserving confirmation source and time. 分析300750 or a uniquely matched name can still confirm a new object. An analysis verb followed by arbitrary prose is not evidence of switching; an explicit but unconfirmed 改看… requires clarification without analysis execution. Legacy history shares direction/action rules but establishes facts only from valid explicit codes: ordinary follow-ups preserve a candidate, an unconfirmed new switch invalidates it, and later follow-ups cannot revive it; a later explicit code can establish a new fact. Historical names provide change evidence only, never a new confirmation fact. Reading recovered state does not persist the object or increment the version.
Negated, questioned, conditional or quoted switches do not confirm a target: 不要改看300750,继续分析600519 maintains 600519; 是否改看300750? preserves the object and asks for clarification without permitting 300750. Codes and names use actual occurrence positions. Legacy confirmation requires a valid code at the destination itself, not an equal code elsewhere in the sentence. A standalone valid code/unique name remains supported; a lone incidental mention in arbitrary prose does not automatically confirm it.
An adjacent conditional antecedent/consequent such as 如果600519下跌,就改看300750 remains conditional across the comma: save the question and clarify without switching or starting analysis. Legacy recovery does not treat it as a new confirmation either. Negated strong, weak and choice comparisons contribute no participants; 不要研究600519和300750的区别,继续分析600519 still analyzes 600519 with only that stock in scope. Independent affirmative clauses are not disabled by a condition or negation elsewhere in the message.
The ask-stock backtest tool uses the guard-validated market identity for its summary, recent rows, count and pagination. Valid prefix/suffix aliases and the current CN writer's numeric storage keys remain readable, without bare-code index-based market discovery. Numeric keys are generated only from validated SH/SZ/BJ targets using the existing writer normalization. Numeric details require a same-object analysis source, with no contrary persisted market. Numeric rollups require same-object results in their evaluation-window/engine bucket and no conflicting numeric source; unprovable ownership returns normal no-data information. Normal legacy CN sources without a market field retain the existing bare-code storage convention: numeric keys are not categorically excluded. Service/repository callers without a strict target keep legacy behavior; no historical data is migrated. This internal target seam supports the tool's existing summary/recent reads, not date/phase dynamic aggregation: unsupported combinations fail explicitly rather than dropping identity constraints.
Incidental mentions in ordinary follow-ups do not expand tool scope. Existing strict Chat stock tools share identity, scope and applicability checks, then carry that call's validated target into cache/provider routing. Daily history, technical analysis and realtime quotes distinguish stocks from indices; cache misses do not fetch a colliding stock. Loss of required identity evidence before execution rejects only the affected object, not independently valid scope members. A validated call does not reclassify through a later registry read. Chip, company information, capital-flow and per-stock backtest tools explicitly reject indices; existing market/ETF restrictions remain. A strict custom tool without an applicability declaration rejects only that call. News queries use the confirmed identity/name, not another stock name guessed by the model.
Current Bot and Web may alternate within the same session instance. After Web confirms an object, an ordinary Bot follow-up uses its strict tool scope; a valid comparison adds participants only for that turn. Both preserve the Web main object, confirmation source/time and state version. Bot does not independently persist a new main object: an explicit switch or an unsafe continuation leaves Web pending reconfirmation rather than claiming the old object remains reliable. Unknown user writes from genuinely old/rollback code still require reconfirmation and are not treated as current state-aware Bot turns. Current Bot commits check the prepared generation, version and latest user anchor; a newer acceptance rejects the stale commit without appending its question or automatically retrying the model. Replies, provider traces and summaries remain bound to the captured instance and actual message source. Delete/recreate rejects late writes from the old instance. A late initial Web list may update presentation but cannot clear a new request/accepted instance or cancel its stream; ordinary missing-session restoration, switching and creation retain their existing behavior.
This does not change model routing, Phase 2 or Codex tool permissions. Affected real Codex paths remain Gate 0B BLOCKED; controlled execution tests do not establish real-runtime safety or upgrades on every platform.
Codex Local Agent (Phase 6 Experimental Prototype)
AGENT_BACKEND selects only the runtime for the existing ask-stock Chat. It does not change regular reports, scheduled analysis, market review, the regular Agent analysis pipeline, LiteLLM Multi Agent, or Deep Research:
# Auto (recommended) never enables experimental Codex; auto and litellm keep the existing route.
AGENT_BACKEND=auto
# Explicit opt-in:
# AGENT_BACKEND=codex_app_server
# AGENT_ARCH=singleWeb setup: open Settings → Agent → Ask-stock method, choose Codex local Agent (experimental), keep Single Agent, set the overall Agent timeout to a value greater than 0, and save. Settings checks only whether the current configuration, local Codex command, and required App Server protocol allow an attempt; it does not sign in, call a model, or read stock data. Return to Chat and ask directly after saving. The first question is the first real execution. To restore the original behavior, select Auto (recommended) and save.
- Codex must be installed and signed in on the device running the DSA backend. DSA does not read or store Codex credentials; the App Server process uses Codex's own sign-in state. Docker, remote servers, and Desktop have separate PATH and sign-in environments. A Desktop backend launched from Finder/Dock only sees the PATH constructed for that process; install Codex CLI where that PATH can find it and fully restart DSA.
- The Phase 6 Codex App Server Agent currently supports macOS, Linux, and environments where the complete DSA backend runs inside WSL. Native Windows backends are rejected before status probing or transport startup. This does not change the existing Windows generation support for Phase 2
GENERATION_BACKEND=codex_cli. - Codex currently exposes read-only access only to saved analysis context, overall backtest summaries, and strategy backtest summaries. This phase validates and promises the independent-process, stop, timeout, and cleanup lifecycle only for those three tools. Live quotes, news, market hotspots, technical-indicator recalculation, per-stock backtest details, and portfolio tools were not validated in this phase and are therefore omitted from the Codex tool list; choose Default model when you need them. An explicit symbol or one uniquely selected Web stock only establishes the stock scope for the exposed historical-analysis-context tool; DSA does not guess across ambiguous listings.
- This is not an offline model. Services configured in Codex may process stock symbols, questions, news, position context, and redacted tool results.
- Codex Chat receives the active trading Skill instructions. Omitting selection inherits the saved session selection (new sessions use defaults); an explicit empty list clears the saved selection and returns to defaults. Skills only interpret saved data from the three read-only tools and cannot expand tool permissions. Missing data or capabilities must be disclosed without claiming complete Skill execution.
- Only single-agent Chat is supported. Codex Multi Agent and Codex Deep Research are not supported. Existing LiteLLM Multi Agent and Deep Research remain unchanged.
- Each Chat request creates a new ephemeral App Server thread. DSA retains the existing visible session history and injects it into the next request, but it does not inject LiteLLM provider traces. Web clients do not receive chain-of-thought, raw JSON-RPC, stderr, or complete tool arguments/results; Codex receives only the redacted tool results needed for that turn.
- When the user stops ask-stock Chat, the page first shows Stopping. DSA interrupts the Codex turn and terminates and reaps the independently running tool process for that turn. The original Chat request reports Stopped only after both Codex and the tool process have exited. Timeouts and client disconnects use the same cleanup boundary, so a turn is never reported as finished while its background work is still running. This contract applies only to Codex and does not change the default LiteLLM Agent execution path.
- Codex requires
AGENT_ORCHESTRATOR_TIMEOUT_Sgreater than 0 so success, failure, timeout, and stop all reach a bounded end. The existing0-disables-timeout behavior remains available only to the default LiteLLM path. Codex rejects 0 during configuration and before execution instead of silently replacing it with 600 seconds. - Cheap status checks configuration, executable visibility, version, and the App Server schema capabilities required by the production path without a model request. It does not prove that Codex is signed in, a model is available, or tools can complete. The status says only Can try; command, protocol, sign-in, model, and tool failures are classified when the user sends a question. Chat checks status once when loaded and retries only when the user asks it to check again.
- The server selects the actual backend for each Chat request. It prepares context and persists the user question before emitting the single SSE
acceptedevent, and only then starts model execution. Web preserves the input, stock scope, follow-up context, and skill selection untilaccepted, preventing ghost messages when preparation fails. The actual backend inaccepteddetermines how Stop works. - One bounded turn budget covers cumulative App Server output and event counts. Tool calls use the existing
AGENT_MAX_STEPSbudget, and per-turn state is removed at completion. Tool-process IPC measures raw UTF-8 result bytes rather than JSON-escaped size. A limit breach returns a structured error and reclaims the turn without retrying or switching backends. - The integration follows the Codex App Server v2 documentation for JSONL stdio, ephemeral
thread/start,turn/start/turn/interrupt, and experimental dynamic tools. Local acceptance on 2026-07-15 usedcodex-cli 0.144.3; DSA does not infer a universal minimum version from that observation. Settings decides only whether an attempt is allowed; the user's real question determines final availability.
Local CLI Privacy And Boundaries
- A local CLI backend is not an offline model. The service behind Codex / Claude Code / OpenCode may process stock symbols, news, position context, analysis prompts, and report drafts.
- Docker, cloud servers, and CI do not automatically have your local CLI login state.
- GitHub Actions only passes configuration values through; it does not install or log in local CLIs. If you opt into a local CLI backend in Actions, a runner without the executable or login state should return a structured failure.
- DSA does not read Codex/Claude/OpenCode credential files, but the subprocess may use the CLI's own login state.
- On macOS, desktop apps launched from Finder/Dock do not inherit the shell PATH. The packaged desktop app adds common Homebrew directories such as
/opt/homebrew/binand/usr/local/binwhen starting the backend. If setup checks still cannot find the CLI executable, fully quit and reopen DSA; opening an interactive CLI window does not change the already-running backend PATH. - DSA only inherits a minimal child environment and denies wildcard inheritance of
CLAUDE_*,ANTHROPIC_*,OPENCODE_*,OPENAI_*,GOOGLE_*,GEMINI_*,AWS_*,AZURE_*,VERTEX_*,*_API_KEY,*_AUTH_TOKEN,*_ACCESS_TOKEN,*_SECRET, and*_PASSWORD, reducing the risk of leaking DSA API keys, provider tokens, or webhook tokens.CODEX_HOMEis the exact-name exception retained for existing Codex CLI login-directory compatibility;CODEX_CLI_*wildcard inheritance is not restored. opencode_cliwrites a minimal projectopencode.jsonin the temporary cwd to disable sharing, autoupdate, snapshots, and common tool permissions, but OpenCode's resolved config may still include local global settings. Runtime safety also relies on--pure, the env denylist, prompt-file permissions, and the event extractor failing closed.- The Web settings page only exposes safe presets; it does not accept arbitrary command, argv, or shell strings.
codex_cli/claude_code_cli/opencode_cliremain experimental/limited. If your CLI version does not support the non-interactive output contract verified by this repository, DSA returns structuredcapability_unsupported,cli_contract_unsupported,invalid_json,schema_validation_failed, or the corresponding backend error, and falls back tolitellmwhen backend fallback is configured. If that version-drift risk is unacceptable, keepGENERATION_BACKEND=litellm.opencode_clidoes not support OpenCode serve / web / ACP / MCP / attach /--dangerously-skip-permissions, and DSA never treats OpenCode final text as Agent tool success.
Method 1: Simple Model Config (For Beginners)
Goal: Just paste your API Key and the model name to start using it immediately. No need to mess with complex concepts.
If you only plan to use one single model, this is the fastest way. Open the .env file in the project's root directory (if it doesn't exist, copy .env.example and rename it to .env).
Modelflare uses standard Channels configuration; see the Modelflare example below. This section retains single-model legacy configuration, and existing setups do not require migration.
Example 1: Using a Third-party OpenAI-Compatible Platform (Highly Recommended)
Most third-party relay platforms and local API providers support the OpenAI interface format. As long as the platform provides an API Key and a Base URL, you can configure it easily using the following pattern:
# Fill in the API Key provided by your platform
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
# Fill in the platform's API Base URL (Very Important: Usually must end with /v1)
OPENAI_BASE_URL=https://api.siliconflow.cn/v1
# Fill in the specific model name (Very Important: You must add the "openai/" prefix so the system recognizes it)
LITELLM_MODEL=openai/deepseek-ai/DeepSeek-V3Example 2: Using the Official DeepSeek API
# Fill in the API Key requested from the official DeepSeek platform
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxCompatibility note: with only this line, the system still defaults to deepseek/deepseek-chat and logs a migration warning.deepseek-chat / deepseek-reasoner still work for compatibility with old configs, but DeepSeek marks them deprecated after 2026/07/24. New configs should migrate through the Web quick channel or explicitly set LITELLM_MODEL=deepseek/deepseek-v4-flash for deepseek-v4-flash / deepseek-v4-pro.
Example 3: Using the Free Gemini API
# Fill in your Google Gemini Key
GEMINI_API_KEY=AIzac...Example 4: Using Ollama Local Models
# Ollama requires no API Key; works after running ollama serve locally
OLLAMA_API_BASE=http://localhost:11434
LITELLM_MODEL=ollama/qwen3:8bImportant: Ollama must be configured with
OLLAMA_API_BASE. Do not useOPENAI_BASE_URL, or the system will concatenate URLs incorrectly (e.g. 404,api/generate/api/show). For remote Ollama, setOLLAMA_API_BASEto the actual address (e.g.http://192.168.1.100:11434). Current dependency constraint islitellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(matches requirements.txt).
Anspire Open legacy compatibility example
Existing Anspire setups that share a key between search and LLMs remain supported. These are configuration examples whose availability depends on your account and the Anspire console; confirm them with the Web "Test connection" action.
# Anspire Open API keys (multiple keys supported, separated by commas)
# Get your key at: https://open.anspire.cn/dsa?share_code=QFBC0FYC
# When no higher-priority OpenAI-compatible source is set, this key is reused for Anspire search + LLM path (example fallback behavior only).
# Example model: Doubao-Seed-2.0-lite; example gateway: https://open-gateway.anspire.cn/v6
ANSPIRE_API_KEYS=sk-xxxxxxxxxxxxxxxx
# Optional: switch example model or gateway according to your Anspire account and official docs.
# ANSPIRE_LLM_MODEL=Doubao-Seed-2.0-pro
# ANSPIRE_LLM_BASE_URL=https://open-gateway.anspire.ai/v6Congratulations! If you're a beginner, you can stop reading here and run the program!
Want to test the connection? Open your terminal in the root directory and run:python scripts/check_env.py --llm
Method 2: Channels Mode Config (Advanced/Multi-model)
Goal: I have Keys from multiple different platforms and want to use them together. If my primary model fails or the network drops, I want it to automatically switch to fallback models.
Configure via Web UI directly: After starting the application, you can do this visually under System Settings -> AI Model -> AI Model Access in the Web UI.
New editor behavior: For DeepSeek, DashScope, and other OpenAI-compatible providers that expose
/v1/models, the settings page can now fetch models directly from{base_url}/modelsand let you select multiple entries visually. The underlying storage format is still the existing comma-separatedLLM_{CHANNEL}_MODELS=model1,model2value. If a provider does not support/models, authentication fails, or the endpoint is temporarily unavailable, you can still type the model list manually and save normally.
First-run Setup Status
The backend exposes a read-only status endpoint at GET /api/v1/system/config/setup/status. It reports whether the minimum first-run pieces are present: primary LLM, Agent model inheritance/configuration, stock list, optional notification channel, and local storage. The endpoint only reads the saved .env plus the current process environment; it does not reload runtime config, write .env, test a real model, or create a database file. Frontend onboarding and later smoke-run flows can build on this endpoint incrementally.
Web channel editor: compatibility, migration, and rollback rules
- The preset provider / API surface / Base URL / sample models are form defaults only. What gets persisted is exactly what you submit in
LLM_{CHANNEL}_PROTOCOL,LLM_{CHANNEL}_API_SURFACE,LLM_{CHANNEL}_BASE_URL,LLM_{CHANNEL}_MODELS, andLLM_{CHANNEL}_API_KEY(S); the editor does not silently rewrite them to a different provider, surface, or URL. LLM_{CHANNEL}_API_SURFACEacceptschat_completions(default) orresponses; Responses currently requires the OpenAI-compatible protocol, and every model's actual LiteLLM provider must beopenai. Direct prefixes come from the installed LiteLLM provider registry and are returned to the Web editor asllm_model_providersbyGET /api/v1/system/config, so the frontend and backend do not maintain separate provider tables. This preventsanthropic/claude-*,xai/grok-*, and providers added by future LiteLLM versions from being miswrapped as OpenAI routes because of a stale client allow-list. Conflicts are rejected consistently by save validation, runtime loading, status diagnostics, and screening instead of producinganthropic/responses/.... Gateway-owned IDs such asdeepseek-ai/DeepSeek-V3orQwen/...that are absent from the registry are normalized underopenai/<gateway model ID>. All models in one channel must use the same surface, and one normalized route alias cannot mix Chat and Responses across channels because Router load balancing could select the wrong endpoint; use distinct aliases when both surfaces are required. "Discover models" still calls{base_url}/models; "Test connection" sends one minimal request through the selected surface and reportsresolved_modelplusresolved_api_surface. It never retries another endpoint after failure. If the response includesdetails.reason=model_access_denied(for example, the observed Issue #1208 SiliconFlow / OpenAI Compatible sample returnedModel disabledthrough LiteLLM), treat it as a best-effort availability diagnostic: confirm model entitlement for the current key, then adjust the model order or remove unavailable models. Optional capability checks send additional real requests and remain best-effort. Their structured diagnostic fields are never persisted into.envand do not block saving.- If the response includes
details.reason=provider_blocked, the provider or relay gateway explicitly blocked this request. This is distinct from local network / TLS failures andmodel_access_denied; first check account risk controls, region or request-source restrictions, model entitlement, relay gateway policy, and content-safety policy. - Runtime capability checks send real LLM requests and may incur token / image-input cost, RPM/TPM rate limiting, insufficient balance errors, or timeouts. A failed check may come from account permissions, model entitlement, endpoint region, balance, provider compatibility layers, or LiteLLM translation behavior; it does not prove that the provider globally lacks that capability. P3 does not include online smoke coverage for every real provider. Its compatibility basis is the repository dependency constraint
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0, LiteLLMcompletion()/ OpenAI I/O format / streaming / exception mapping, and the OpenAI Chat Completions shapes for JSON mode, tool calling, streaming, and vision input. - External references: LiteLLM Python SDK / OpenAI I/O format / streaming / exception mapping: https://docs.litellm.ai/; LiteLLM OpenAI-compatible routing: https://docs.litellm.ai/docs/providers/openai_compatible; OpenAI Chat Completions: https://platform.openai.com/docs/api-reference/chat/create; JSON mode: https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat; tool calling: https://platform.openai.com/docs/guides/function-calling?api-mode=chat; streaming: https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat; vision input: https://platform.openai.com/docs/guides/images-vision?api-mode=chat.
- Saving channels only updates the keys submitted in that save operation; there is no whole-config silent migration when you switch channel settings. The one deliberate cleanup is runtime model references: if
LITELLM_MODEL,AGENT_LITELLM_MODEL,VISION_MODEL, orLITELLM_FALLBACK_MODELSpoint to models that no longer exist in the currently enabled channels, the editor clears/removes those stale references before saving so runtime calls do not keep targeting invalid models. Even when enabled channels expose no selectable models, stale managed-provider values without a matching legacy key are cleaned.cohere/*,google/*, andxai/*are kept as explicit direct-env compatibility examples for legacy retention behavior only, and are not a runtime availability guarantee. - Backend consistency basis: runtime validation in
SystemConfigService._validate_llm_runtime_selection(src/services/system_config_service.py) relies on_uses_direct_env_provider(src/config.py). Onlygemini,vertex_ai,anthropic,openai, anddeepseekare treated as managed key-backed providers;cohere,google, andxaiare not in that allowlist, so they remain valid direct provider runtime entries. - Rollback stays minimal: restore the previous channel model list and re-select the runtime models, or restore the previous
LLM_*,LITELLM_MODEL,AGENT_LITELLM_MODEL,VISION_MODEL,LLM_TEMPERATURE, andLLM_USAGE_HMAC_*values from your desktop export / manual.envbackup. No extra migration script is required. - The current dependency constraint for this flow in the repository is
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(seerequirements.txt). Regression coverage for it lives intests/test_system_config_service.py,tests/test_system_config_api.py, andapps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx.
External provider model examples notice:
cohere/*,google/*, andxai/*provider-prefixed values are included here only to describe current runtime retention behavior and are not a global availability guarantee. Specific model names in docs or tests are configuration-retention examples, not production recommendations. Check the provider's official model/API docs and validate against the repository dependency constraintlitellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0before production use.
Rollback & compatibility evidence
- Scope and cleanup behavior under
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0: only runtime references (LITELLM_MODEL,AGENT_LITELLM_MODEL,VISION_MODEL,LITELLM_FALLBACK_MODELS) are sanitized during save; non-channel direct providers such ascohere/*,google/*, andxai/*are preserved. - Rollback path: export desktop config, then restore the backup through
POST /api/v1/system/config/import; or manually restore historical.enventries (LITELLM_*,AGENT_LITELLM_MODEL,VISION_MODEL,LLM_TEMPERATURE,LLM_USAGE_HMAC_*) and restart. - Rollback evidence:
tests/test_system_config_service.py::test_import_desktop_env_restores_runtime_models_after_cleanupcovers restore from exported desktop backup after runtime cleanup. - Direct-provider evidence:
tests/test_system_config_service.py::SystemConfigServiceTestCase::test_validate_accepts_minimax_model_as_direct_env_provider,test_validate_accepts_cohere_model_as_direct_env_provider,test_validate_accepts_google_model_as_direct_env_provider, andtest_validate_accepts_xai_model_as_direct_env_providercover the preserved direct-provider behavior. - Frontend regression commands:
cd apps/dsa-web && npm run lint && npm run build && npm run test -- src/components/settings/__tests__/LLMChannelEditor.test.tsx. - Recommended rollback sequence (including UI reload): export desktop backup, restore via
POST /api/v1/system/config/import, then callGET /api/v1/system/configto refresh the settings page and verifyLITELLM_MODEL/AGENT_LITELLM_MODEL/VISION_MODEL/LLM_TEMPERATUREbefore continuing.
Official references for provider presets / Base URLs / model naming
- Modelflare: Quickstart, Endpoints, Models and groups
- OpenAI-compatible routing in LiteLLM: https://docs.litellm.ai/docs/providers/openai_compatible
- OpenAI official API docs: https://platform.openai.com/docs/api-reference/chat
- DeepSeek official API docs: https://api-docs.deepseek.com/
- Anspire Open: https://open.anspire.cn/dsa?share_code=QFBC0FYC
- DashScope OpenAI-compatible mode: https://help.aliyun.com/zh/model-studio/compatibility-of-openai-with-dashscope
- Moonshot / Kimi official compatibility docs: https://platform.moonshot.ai/docs/guide/compatibility
- Anthropic official Messages API: https://docs.anthropic.com/en/api/messages
- Gemini official OpenAI compatibility docs: https://ai.google.dev/gemini-api/docs/openai
- Cohere official: https://docs.cohere.com/
- Cohere API reference: https://docs.cohere.com/reference/
- Cohere LiteLLM provider page: https://docs.litellm.ai/docs/providers/cohere
- Google Gemini API and model list: https://ai.google.dev/gemini-api/docs/openai, https://ai.google.dev/gemini-api/docs/models
- Google LiteLLM provider page: https://docs.litellm.ai/docs/providers/gemini
- xAI official: https://docs.x.ai/docs
- xAI LiteLLM provider page: https://docs.litellm.ai/docs/providers/xai
- Ollama API docs: https://github.com/ollama/ollama/blob/main/docs/api.md
If you prefer modifying files, configuring this in the .env file is also very smooth. It allows you to manage multiple platforms simultaneously. The rules are:
- Declare your channels first:
LLM_CHANNELS=channel_name_1,channel_name_2 - Provide configurations for each channel (Note the uppercase):
LLM_{CHANNEL_NAME}_XXX
Example: Modelflare
Register with Modelflare, create an API key in the console, and configure:
MODELFLARE_KEY=sk-xxxWith MODELFLARE_KEY alone, the backend enables Modelflare and uses https://modelflare.dev/v1 and openai/gpt-6.1-sol. No channel, endpoint or model variables are required. Explicit YAML or LLM_CHANNELS takes precedence; explicit Modelflare channel keys override this shorthand. Clear the key or disable the channel to stop using it. Inference does not rewrite saved configuration.
When modelflare is the only declared channel and its model list is blank, the backend defaults to openai/gpt-6.1-sol if no primary model is configured; otherwise it reuses LITELLM_MODEL and OpenAI-prefixed fallback routes. Use openai/<model> for the primary model. An explicit channel list takes precedence; multi-channel setups must declare each channel's models. Inference does not rewrite saved configuration or change model selection.
For the channel named modelflare, an omitted or blank Base URL defaults to https://modelflare.dev/v1; an explicit LLM_MODELFLARE_BASE_URL takes precedence. Protocol defaults to openai for this endpoint and the API surface to chat_completions. When LITELLM_MODEL is unset, the first channel model is selected. The default model still requires account access; no API key is supplied automatically.
gpt-6.1-sol is this project's Modelflare default. Select models available to your account and confirm them with the Web editor's "Discover models" and "Test connection" actions; do not append /chat/completions to the Base URL. Modelflare is the initially selected template for new channels. Reordering templates does not change saved channels or primary models. Anspire Search continues to use ANSPIRE_API_KEYS independently.
Both the client's quick-add registration button and the exclusive registration link in channel details use the app UTM; README links use the github UTM. If a new channel has no key yet, or the primary/fallback model is not declared by an enabled channel, status preview shows the affected configuration fields. Supply the key, select available models or clear invalid fallback selections, then retry. Preview does not rewrite saved settings.
Compatibility example: Anspire Responses API (observed GPT-5.6 model names)
LLM_CHANNELS=anspire
LLM_ANSPIRE_PROTOCOL=openai
LLM_ANSPIRE_API_SURFACE=responses
LLM_ANSPIRE_BASE_URL=https://open-gateway.anspire.cn/v6
LLM_ANSPIRE_API_KEY=sk-xxx
LLM_ANSPIRE_MODELS=gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna
LITELLM_MODEL=openai/gpt-5.6-sol
LITELLM_FALLBACK_MODELS=openai/gpt-5.6-terra,openai/gpt-5.6-lunaAnspire's official integration page documents https://open-gateway.anspire.cn/v6/responses: https://open.anspire.cn/model?link=sample&tab=models. Responses routing is not hard-coded to model names; the GPT-5.6 names above are current gateway observations rather than a durable model-catalog promise. Treat the live /models response, provider guidance, and connection test as authoritative for the model, Surface, availability, and account access.
Example: Configuring DeepSeek and a Third-party Relay with Fallbacks
# 1. Enable channel mode, declare two channels here: deepseek and aihubmix
LLM_CHANNELS=deepseek,aihubmix
# 2. Channel 1: Configure Official DeepSeek
LLM_DEEPSEEK_BASE_URL=https://api.deepseek.com
LLM_DEEPSEEK_API_KEY=sk-1111111111111
LLM_DEEPSEEK_MODELS=deepseek-v4-flash,deepseek-v4-pro
# 3. Channel 2: Configure a common relay/proxy API
LLM_AIHUBMIX_BASE_URL=https://api.aihubmix.com/v1
LLM_AIHUBMIX_API_KEY=sk-2222222222222
LLM_AIHUBMIX_MODELS=gpt-5.5,claude-sonnet-4-6
# 4. [Key Step] Specify the primary model and fallback list
# Set your primary model:
LITELLM_MODEL=deepseek/deepseek-v4-flash
# Optional: set an Agent-only primary model (empty = inherit the primary model)
AGENT_LITELLM_MODEL=deepseek/deepseek-v4-pro
# If the primary model crashes, try these fallbacks sequentially:
LITELLM_FALLBACK_MODELS=openai/gpt-5.4-mini,anthropic/claude-sonnet-4-6Example: Ollama Channel Mode (Local Models, No API Key)
# 1. Enable channel mode, declare ollama channel
LLM_CHANNELS=ollama
# 2. Configure Ollama address (default local port 11434)
LLM_OLLAMA_BASE_URL=http://localhost:11434
LLM_OLLAMA_MODELS=qwen3:8b,llama3.2
# 3. Specify primary model
LITELLM_MODEL=ollama/qwen3:8bExample: Hermes Local HTTP Generation (Phase 3)
LLM_CHANNELS=hermes
LLM_HERMES_PROTOCOL=openai
LLM_HERMES_BASE_URL=http://127.0.0.1:8642/v1
LLM_HERMES_API_KEY=sk-local-hermes
LLM_HERMES_MODELS=hermes-agent
LITELLM_MODEL=openai/hermes-agenthermes is a reserved channel name for local loopback /v1 OpenAI-compatible generation. Phase 3 only verifies regular analysis and JSON output. It does not support Stream/SSE, tools, Vision, Agent tools, remote Hermes, or process lifecycle management. Use exactly one LLM_HERMES_API_KEY; do not configure LLM_HERMES_API_KEYS or LLM_HERMES_EXTRA_HEADERS. If enabled Hermes config is invalid, DSA blocks legacy provider silent fallback so requests do not unexpectedly switch to an external model. When the Web settings page saves the reserved Hermes channel, it explicitly clears stale LLM_HERMES_API_KEYS / LLM_HERMES_EXTRA_HEADERS values and returns a warning. To recover previous values, restore them from a .env backup, Git history, or a desktop export backup, but Phase 3 will still reject non-empty multi-key or extra-header Hermes settings.
MiniMax Model Naming in Channel Mode
- If you access MiniMax through an OpenAI-compatible channel, enter the model as
minimax/<model-name>in the channel model list, for exampleminimax/MiniMax-M1. - The Web settings page now keeps that value unchanged in Primary, Agent Primary, Fallback, and Vision selectors instead of rewriting it to
openai/minimax/<model-name>.
Default-Model Ask-Stock / LiteLLM Compatibility Notes
- With
AGENT_BACKEND=auto|litellm, ask-stock follows the same three-tier runtime priority as the regular analyzer:LITELLM_CONFIG(LiteLLM YAML) >LLM_CHANNELS> legacy provider keys. Once an upper tier is valid and active, lower tiers are ignored for that request; Codex does not use this model route. - In YAML mode, the Agent reuses LiteLLM
model_list/model_namerouting semantics directly. In channel mode, it first readsAGENT_LITELLM_MODEL; when that is empty it inheritsLITELLM_MODEL, then continues throughLITELLM_FALLBACK_MODELS. - If you do not use YAML or Channels, leave
AGENT_LITELLM_MODELempty, and still rely on legacy provider env vars, the ask-stock Agent continues to inherit them:GEMINI_API_KEY + GEMINI_MODEL->gemini/<model>,OPENAI_API_KEY + OPENAI_MODEL->openai/<model>, andANTHROPIC_API_KEY + ANTHROPIC_MODEL->anthropic/<model>. - This fix only improves two things: preserving the backend's real failure reason and returning a more specific diagnostic when no usable Agent LLM is configured. It does not silently delete, clear, migrate, or rewrite your existing
GEMINI_*,OPENAI_*,ANTHROPIC_*, orLITELLM_*settings. - If the current environment has no valid Agent model path at all, the ask-stock page still returns a failure and now surfaces the backend's real configuration diagnosis. As soon as you restore any valid model source, the flow recovers without running any migration step.
- The recommended forward path is still to configure
LITELLM_MODEL/AGENT_LITELLM_MODELexplicitly or move toLLM_CHANNELS; legacy provider keys remain a compatibility fallback for older.envfiles, local macOS development, and existing deployments.
Ask-Stock Visible Chat Context Compression
By default, ask-stock injects only the 20 most recent visible messages. The following LLM compression applies only to Default model ask-stock: Codex Agent always uses the 20 most recent user-visible messages and never calls AGENT_LITELLM_MODEL to summarize history. Switching to Codex does not clear the saved compression setting; it takes effect again after switching back to Default model.
AGENT_CONTEXT_COMPRESSION_ENABLED=true
AGENT_CONTEXT_COMPRESSION_PROFILE=balanced
# Blank values follow the selected profile preset
AGENT_CONTEXT_COMPRESSION_TRIGGER_TOKENS=
AGENT_CONTEXT_PROTECTED_TURNS=Compression processes only visible user / assistant text under the same session_id. It does not process provider traces, thinking blocks, tool calls, or tool results, and it does not change same-turn tool passthrough. The presets are cost (6000 tokens / 2 protected turns), balanced (12000 / 4), and long_context_raw_first (24000 / 6). Blank trigger/protected values follow the selected profile; explicit values override it.
For the single-agent ask-stock path, the backend also keeps a provider-aware trace track for DeepSeek V4 thinking + tool-call roundtrip. A trace is persisted only when the same run has both tool_calls and reasoning_content; the last 3 minimal protocol slices per session_id + provider + model are spliced back into the next request before the anchored visible assistant reply. Provider trace is either preserved exactly or dropped as a whole; it is never summarized, never returned by Web session-history APIs, and adds no .env setting. Model/provider mismatch, summarized anchors, or insufficient budget drop the whole trace. Claude extended thinking is limited in this PR to adapter/storage-level opaque thinking / redacted_thinking / signature block plumbing with offline fixtures; production end-to-end Claude and multi-agent trace injection remain follow-ups. Protocol references: DeepSeek thinking mode (https://api-docs.deepseek.com/guides/thinking_mode) and Anthropic Claude extended thinking (https://platform.claude.com/docs/en/docs/build-with-claude/extended-thinking). The LiteLLM compatibility window remains litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0 from requirements.txt.
Strict Temperature Model Compatibility Notes
- Moonshot officially documents Kimi as an OpenAI-compatible API, with
https://api.moonshot.ai/v1as the base URL: https://platform.kimi.ai/docs/guide/kimi-k2-6-quickstart - LiteLLM officially requires the
openai/prefix for OpenAI-compatible model routing: https://docs.litellm.ai/docs/providers/openai_compatible - Moonshot's compatibility docs distinguish two fixed values: thinking mode must use
1.0, while non-thinking mode must use0.6; other values are rejected by the API: https://platform.moonshot.ai/docs/guide/compatibility#parameters-differences-in-request-body - The OpenAI Chat Completions API treats
temperatureas optional. For GPT-5 / o-series style models that only accept the provider default temperature, this project omitstemperatureat request time instead of rewriting your savedLLM_TEMPERATURE: https://platform.openai.com/docs/api-reference/chat/create - The current runtime dependency constraint in this repository is
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(seerequirements.txt); this compatibility fix is regression-covered under that constraint across the main analyzer, market review, direct Agent LiteLLM calls, and the system-settings channel connectivity test path. - This repository therefore normalizes
kimi-k2.6andkimi-k2.6-*right before dispatch based on the actual request mode: default / thinking requests usetemperature=1.0; if your LiteLLM YAML route alias explicitly setslitellm_params.extra_body.thinking.type: disabled(or an equivalent non-thinking override), it automatically switches totemperature=0.6. Your savedLLM_TEMPERATUREvalue in.envor the Web settings is not rewritten. - If a compatible platform returns an explicit parameter error for a not-yet-profiled model, such as unsupported
temperature, default-only1.0, or unsupportedtop_p, the runtime repairs the current request and retries once. The strategy is cached only in the current process after the retry succeeds; it is never written back to.env, and a service restart re-evaluates the configured rules normally. - For streaming responses that already produced partial content, the runtime does not switch parameters mid-output. It keeps the existing same-model non-stream retry / fallback-model path to avoid stitching inconsistent answers together.
SystemConfigServiceonly updates keys that you actually submit when saving from the Web settings page or importing a desktop.env; switching to a strict-temperature model does not silently clear, migrate, or rewrite an existingLLM_TEMPERATURE. Temporary request-time parameter strategies are not persisted back into the config file.- Non-strict primary models, non-strict fallbacks, and any request after switching back to a regular model still use your configured temperature. Existing configs do not need migration; changing the model restores the original behavior automatically.
- Repository-side compatibility coverage lives in
tests/test_llm_channel_config.py,tests/test_market_analyzer_generate_text.py,tests/test_agent_pipeline.py, andtests/test_system_config_service.py. - Minimal rollback: revert only the LLM generation-parameter adaptation change set; no separate
LLM_TEMPERATUREmigration is required.
Critical Warning: If you enable
LLM_CHANNELS, any standardDEEPSEEK_API_KEYorOPENAI_API_KEYdeclared independently will be completely ignored. Use only one mode to prevent configuration conflicts.
Docker note: IfLITELLM_MODEL,LLM_CHANNELS,LLM_DEEPSEEK_MODELS, or related variables are explicitly passed throughdocker compose environment:ordocker run -e, they will override the.envwritten by the Web settings page after a container restart. Update the deployment environment at the same time.
Compatibility evidence and rollback audit notes (for this recovery change)
- Compatibility is validated in two layers: first-party provider/API contract references (LiteLLM OpenAI-compatible routing, OpenAI Chat Completions, Moonshot/Kimi docs and model notes), and second the current runtime implementation in this repository under
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0. - This recovery path is runtime-only and intentionally local: exception classification + one in-request repair retry + in-process cache. It does not rewrite
.env, migrate saved config keys, or alter legacy values; it only omits/adjusts request parameters (temperature,top_p,presence_penalty,frequency_penalty,seed) for the current call. Rolling back requires no migration; restore previous settings and model/provider selection. - Regression evidence for this path is in
tests/test_llm_param_recovery.py,tests/test_system_config_service.py,tests/test_llm_channel_config.py,tests/test_system_config_api.py,tests/test_market_analyzer_generate_text.py,tests/test_agent_pipeline.py; desktop backup import restore is directly covered bytest_import_desktop_env_restores_runtime_models_after_cleanup.
Method 3: Advanced YAML Config (Expert Setup)
Goal: I want maximum control and origin-level routing rules for enterprise-grade high availability.
This layer maps directly to the underlying LiteLLM routing capabilities, including high concurrency, automatic retries, and TPM/RPM-based load balancing.
- Keep only one declaration line in your
.env:envLITELLM_CONFIG=./litellm_config.yaml - Create a
litellm_config.yamlin the project root directory (you can refer todocs/examples/litellm_config.example.yaml).
Example litellm_config.yaml:
model_list:
- model_name: my-smart-model
litellm_params:
model: deepseek/deepseek-v4-flash
api_base: https://api.deepseek.com
api_key: "os.environ/MY_CUSTOM_SECRET_KEY" # Fetch from environment vars for security
# Ollama local model (no api_key needed)
- model_name: ollama/qwen3:8b
litellm_params:
model: ollama/qwen3:8b
api_base: http://localhost:11434Priority Rule: YAML is king! If YAML is configured, both Channels Mode and Simple Mode are entirely ignored. Hierarchy:
YAML > Channels > Simple.
LLM usage HMAC telemetry
P0a usage telemetry creates HMAC-SHA256 fingerprints for the actual messages sent to the model. This only writes local llm_usage telemetry. It does not change prompts, provider parameters, cache hints, model output, or fallback order.
Usage is read in three tiers:
- Prefer the provider / LiteLLM public
usageresponse field. - Then read the LiteLLM public
usage_metadataresponse field. - Only then read
_hidden_params["usage"], which is a LiteLLM private/internal best-effort fallback rather than a stable public contract. If it is absent, usage/cache telemetry may be incomplete; the model request itself has not failed for that reason.
Cache-token normalization is allowlisted best-effort normalization only. The external field evidence and runtime boundaries are separated below so provider contracts, current LiteLLM normalization behavior, and repository-specific compatibility allowlists are not treated as the same thing:
| Provider / source | Fields read | Evidence and boundary | Coverage |
|---|---|---|---|
| OpenAI | usage.prompt_tokens_details.cached_tokens |
The official Prompt Caching docs state that requests below 1024 tokens still expose cached_tokens=0: https://developers.openai.com/api/docs/guides/prompt-caching |
Covered by unit/mock tests; this PR does not include OpenAI live smoke |
| Anthropic | cache_creation_input_tokens / cache_read_input_tokens / input_tokens |
The official Prompt Caching docs define total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens: https://platform.claude.com/docs/en/build-with-claude/prompt-caching |
Covered by unit/mock tests; this PR does not include Anthropic live smoke |
| Gemini / Vertex AI | Official source field: UsageMetadata.cachedContentTokenCount; runtime consumes LiteLLM-exposed snake_case / normalized fields such as cached_content_token_count, cache_read_input_tokens, or prompt_tokens_details.cached_tokens |
Gemini UsageMetadata official field: https://ai.google.dev/api/generate-content#UsageMetadata. This repository does not add native camelCase runtime fallback; runtime compatibility is bounded to litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0 |
Covered by unit/mock tests; this PR does not include Gemini / Vertex live smoke |
| DeepSeek | prompt_cache_hit_tokens / prompt_cache_miss_tokens |
DeepSeek Chat Completion docs state that prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens: https://api-docs.deepseek.com/api/create-chat-completion |
Covered by unit/mock tests; this PR includes one redacted DeepSeek smoke only and does not store the full response |
| GLM / OpenAI-compatible / StepFun and similar compatible platforms | Values from the modeled token/cache count allowlist that can be normalized to common fields | No stable official cache telemetry contract is claimed here; this is best-effort normalization under the current LiteLLM / OpenAI-compatible shape. Unmodeled metadata is not persisted | Covered by unit/fixture/mock tests; this PR does not include live smoke for these providers |
| LiteLLM public response shape | usage / usage_metadata |
Consumed according to the response / Usage object shape in the current dependency window litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0; this is not a LiteLLM 2.x compatibility guarantee |
Covered by Analyzer / Agent / usage tests |
| LiteLLM private fallback | _hidden_params["usage"] |
Private/internal best-effort fallback, not a stable LiteLLM public contract. It only fills narrow streaming telemetry gaps such as public zero-only/no-signal usage and does not change provider request parameters | Covered by unit/mock tests; absence only affects telemetry completeness, not model request success |
LLM_USAGE_HMAC_SECRET=
LLM_USAGE_HMAC_KEY_VERSION=local-v1- When
LLM_USAGE_HMAC_SECRETis empty, the backend creates.llm_usage_hmac_secretin the data directory for local deployment-scoped comparisons. - Set the same high-entropy random secret only when multiple deployments intentionally need comparable HMACs; generate one with
openssl rand -hex 32. .llm_usage_hmac_secretis a local secret artifact and is ignored by filename in.gitignore.- When rotating the secret, update
LLM_USAGE_HMAC_KEY_VERSIONso old and new fingerprints are not compared as if they used the same key. - Do not reuse the login session secret and do not commit or expose the real secret in version control, issues, logs, or screenshots.
Provider prompt cache configuration (P1 / P1.5)
Prompt-cache settings only control whether this project records cache usage / diagnostics and whether the main analysis path actively sends verified provider-specific hints. They do not control implicit or provider-managed cache behavior in OpenAI, Gemini, DeepSeek, or other providers.
LLM_PROMPT_CACHE_TELEMETRY_ENABLED=true
LLM_PROMPT_CACHE_HINTS_ENABLED=false
LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL=off- When
LLM_PROMPT_CACHE_TELEMETRY_ENABLED=false, provider raw usage JSON, normalized cache fields, and cache-decision diagnostics are not persisted. Basic token usage remains compatible. LLM_PROMPT_CACHE_HINTS_ENABLED=trueonly allows the main analysis / analyzer LiteLLM path to sendprompt_cache_key,cache_control,user_id, and similar hints for provider / route entries that are verified or smoke-tested in the registry. The ask-stock Agent path currently records capability / usage diagnostics only and does not actively send provider-specific hints. Unknown OpenAI-compatible gateways stay telemetry-only.LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL=basicprovides non-sensitive enum decisions such as provider, API surface, verification status, hint applied, and disabled reason only through debug logs and test-observable objects.debugadds HMAC-derived route/cache diagnostics and matched caps id on the same surfaces, but still must not include raw prompts, request bodies, message content, raw stock/user values, webhooks, or API keys. These diagnostics are not public Usage API or ordinary settings-page output.- The Provider Cache Capability Registry is a code-level manual registry in
src/llm/provider_cache.py. Entries includedoc_sources,last_verified_at, andverification_status; update them with tests when adding providers or upgrading LiteLLM. - Prompt cache keys, route keys, and DeepSeek session isolation reuse
LLM_USAGE_HMAC_SECRET/.llm_usage_hmac_secretwith domain-separated HMACs. No prompt-cache-specific secret is introduced.
Legacy message stability audit (P0.5a)
P0.5a adds internal stability-audit fields for the ordinary stock-analysis legacy [system, user] message path. The fields are written only to local llm_usage records. They reuse the message HMAC pipeline above and do not change prompt text, message order, provider request parameters, cache hints, model output, fallback order, the public Usage API, or Web pages.
The added fields are for maintainer diagnostics only:
language,market_group,analysis_mode,legacy_prompt_mode,provider,transport, andmessage_countdescribe low-sensitivity routing context for the stock-analysis call.skill_config_hmacis an HMAC-SHA256 over the resolved skill prompt fragments, default skill policy, and legacy prompt mode. It lets maintainers tell whether the system message changes with skill configuration without storing raw skill text.known_dynamic_marker_positionsis a JSON string. Each entry stores onlymarker_name,message_role, andchar_offset; it does not store stock codes, stock names, dates, news body text, quote values, headers, response text, or prompt snippets.estimated_total_prompt_tokens,approx_common_prefix_chars, andapprox_common_prefix_tokensuse the repository's stable canonical render: messages are concatenated in order asrole + "\n" + contentwith a fixed separator. This is not claimed to match provider wire bytes.char_offsetis measured inside the matching messagecontent.approx_common_prefix_charsis the character count from canonical-render start to the first known dynamic marker. When no marker is found, common-prefix fields stayNULL.- Token estimates use
ceil(chars / 3). They are diagnostics only, do not replace provider usage, and are not used for cache-threshold decisions; Chinese text can be underestimated.
P0.5a does not introduce PromptBlock IR, block_id, stability_class, static_prefix_hash, or dynamic_context_hash. Agent, research, and market-review paths are not wired into this audit yet.
GitHub Actions Notes
The bundled 00-daily-analysis.yml explicitly passes the common LLM runtime fields to the job environment:
- Runtime selection:
GENERATION_BACKEND,GENERATION_FALLBACK_BACKEND,GENERATION_BACKEND_TIMEOUT_SECONDS,GENERATION_BACKEND_MAX_OUTPUT_BYTES,GENERATION_BACKEND_MAX_CONCURRENCY,LOCAL_CLI_BACKEND_MAX_CONCURRENCY,AGENT_GENERATION_BACKEND,LLM_CHANNELS,LITELLM_MODEL,LITELLM_FALLBACK_MODELS,AGENT_LITELLM_MODEL,VISION_MODEL,VISION_PROVIDER_PRIORITY,LLM_TEMPERATURE,LLM_USAGE_HMAC_SECRET,LLM_USAGE_HMAC_KEY_VERSION,LLM_PROMPT_CACHE_TELEMETRY_ENABLED,LLM_PROMPT_CACHE_HINTS_ENABLED,LLM_PROMPT_CACHE_DIAGNOSTICS_LEVEL - Multiple keys:
GEMINI_API_KEYS,ANTHROPIC_API_KEYS,OPENAI_API_KEYS,DEEPSEEK_API_KEYS(the current workflow imports these from repository Secrets only, not from same-named Variables) - Common channel names:
modelflare,primary,secondary,aihubmix,anspire,deepseek,dashscope,zhipu,moonshot,minimax,volcengine,siliconflow,openrouter,requesty,gemini,anthropic,openai,ollama,hermes
For example, if you set LLM_CHANNELS=primary,deepseek in GitHub Actions, also configure the corresponding LLM_PRIMARY_* and LLM_DEEPSEEK_* entries. The LLM_<NAME>_API_KEY / LLM_<NAME>_API_KEYS fields are also imported from repository Secrets only right now, so storing them in Variables will not work at runtime. If you use a custom channel name such as my_proxy, GitHub Actions must explicitly add matching LLM_MY_PROXY_* mappings in the workflow env: block. Local .env and Docker runs do not have this limitation.
Advanced Feature: Vision Model Config
Certain specific features in our system (like uploading a stock chart screenshot to extract the stock code) require models capable of computer vision. You need to assign a dedicated vision model in your .env.
# Specify your dedicated vision model name
VISION_MODEL=openai/gpt-5.5
# Make sure to provide its corresponding provider API KEY (e.g., OPENAI_API_KEY):
# OPENAI_API_KEY=xxxVision Fallback Mechanism: To prevent unexpected failures, the system has a built-in fallback strategy. If the primary vision model fails, it will attempt to use alternative vision-capable provider keys in the following order:
# Default fallback sequence:
VISION_PROVIDER_PRIORITY=gemini,anthropic,openaiTroubleshooting
Afraid you got the config wrong? Type the following commands in your terminal to diagnose:
python scripts/check_env.py --config: Only verifies if the logic in your.envis structurally correct. (Provides instant results, no network calls, strictly checks for syntax omissions).python scripts/check_env.py --llm: Sends a real greeting to the LLM to test the actual endpoint. This thoroughly verifies if your network is working and if your account has sufficient balance.
Common Pitfalls
| Weird Error You Got? | Likely Culprit | How to Fix It? |
|---|---|---|
| The UI says the primary model is not configured | The system doesn't know which provider/model you want to use. | Add a clear instruction in .env: LITELLM_MODEL=provider/your_model_name. Example: openai/gpt-5.5. |
| I added multiple provider Keys, why is only one working? | You mixed the Simple Mode and Channels Mode! | Choose one path. For simple setups, delete anything starting with LLM_CHANNELS. To use multi-model fallbacks, migrate all your Keys into the LLM_CHANNELS setup. |
| Returns 400, 401, or Invalid API Key | The API Key is wrong, copied incompletely, account lacks credits, or you mistyped the model name (extremely common). | 1. Ensure there are no spaces at the start/end of your Key. 2. Ensure your Base URL ends with /v1. 3. Check if you forgot the openai/ prefix on the model name! |
Kimi K2.6 returns invalid temperature (it may say only 1.0 or 0.6 is allowed) |
The model requires different fixed temperatures for thinking vs non-thinking mode, while older config or call paths may still pass 0.7. |
After this fix, default / thinking kimi-k2.6 requests automatically use temperature=1.0; if you explicitly disable thinking in a LiteLLM YAML route, the request automatically uses 0.6 instead. Prefer openai/kimi-k2.6 with your Moonshot or relay OpenAI-compatible Base URL and API key. Non-Kimi fallbacks still keep your configured LLM_TEMPERATURE. |
GPT-5 / o-series returns that temperature is unsupported or only the default is allowed |
These models only accept the provider default sampling parameters, while older call paths may still send 0.7. |
The request layer now omits temperature so the provider default is used. Your .env / Web LLM_TEMPERATURE is not rewritten, and regular models keep using it after you switch back. |
| Spins endlessly, eventually hits Timeout/ConnectionRefused | You are using restricted APIs (like Google/OpenAI) in a blocked region without a proxy, or your cloud server lacks external internet access. | Highly recommend using official regional APIs (like DeepSeek) or OpenAI-compatible relay platforms. Third-party platforms bypass these network constraints. |
Ollama returns 404, Could not get model info, or api/generate/api/show |
Using OPENAI_BASE_URL for Ollama makes the system concatenate URLs incorrectly |
Use OLLAMA_API_BASE=http://localhost:11434 or channel mode (LLM_CHANNELS=ollama + LLM_OLLAMA_BASE_URL) instead |
*Veteran's Tip: If you enable Agent Mode (Deep-thinking & web-search), experience shows you should use a stronger model like deepseek-v4-pro. Trying to save money by using weak mini-models for agents will likely result in infinite loops or missed objectives.*
README CHT (`docs/README_CHT.md`)
💖 贊助商 (Sponsors)
🖥️ 產品預覽
✨ 功能特性
| 能力 | 覆蓋內容 |
|---|---|
| AI 決策報告 | 核心結論、評分、趨勢、買賣點位、風險警報、催化因素、操作檢查清單 |
| 多市場數據聚合 | 覆蓋 A股、港股、美股、日股、韓股、台股和 ETF,支援行情、K 線、技術指標、新聞、公告、基本面與報告輔助數據;不同市場的數據源和能力邊界見 市場支持邊界 |
| Web / 桌面工作台 | 手動分析、任務進度、歷史報告、完整 Markdown、回測、持倉、配置管理、淺色 / 深色主題 |
| Agent 策略問股 | 多輪追問,支援均線、纏論、波浪、趨勢、熱點、事件、成長、預期等 15 種內建策略,覆蓋 Web/Bot/API |
| 智能匯入與補全 | 圖片、CSV/Excel、剪貼簿匯入;股票代碼/名稱/拼音/別名補全 |
| 自動化與推送 | GitHub Actions、Docker、本地定時任務、FastAPI 服務和企業微信/飛書/Telegram/Discord/Slack/郵件推送 |
功能細節、欄位契約、基本面 P0 超時語義、交易紀律、數據源優先級、Web/API 行為請看 完整配置與部署指南。
技術棧與數據來源
| 類型 | 支援 |
|---|---|
| AI 模型 | Modelflare、AIHubMix、Gemini、OpenAI 兼容、DeepSeek、通義千問、Claude、Ollama 本地模型等 |
| 行情數據 | AkShare、Tushare、Pytdx、Baostock、YFinance、Longbridge |
| 新聞搜尋 | Anspire、SerpAPI、Tavily、Bocha、Brave、MiniMax、SearXNG |
| 社交輿情 | Stock Sentiment API(Reddit / X / Polymarket,僅美股,可選) |
專案預設內建 AkShare、Baostock、YFinance 等免費行情源,可零配置執行;免費源受上游限流、介面變動和網路波動影響,穩定性不保證。長期定時、批量分析或更穩定行情建議配置 Tushare、Longbridge 等 token 型數據源,適用市場、Actions 映射和 fallback 規則見 數據源配置。
🚀 快速開始
方式一:[GitHub Actions(推薦)](https://www.bilibili.com/video/BV11FEb66EXG/)
5 分鐘完成部署,零成本,無需伺服器。
1. Fork 本倉庫
點擊右上角 Fork 按鈕(順便點個 Star 支援一下)。
2. 配置 Secrets
Settings -> Secrets and variables -> Actions -> New repository secret
AI 模型配置(至少配置一個)
預設先選一個模型服務商並填寫 API Key;需要多模型、圖片識別、本地模型或高級路由時,再參考 LLM 配置指南。
| Secret 名稱 | 說明 | 必填 |
|---|---|---|
MODELFLARE_KEY |
Modelflare API Key,低成本接入全球熱門大模型 | 推薦 |
AIHUBMIX_KEY |
AIHubMix API Key,一 Key 切換使用全系模型,本項目可享 10% 優惠 | 推薦 |
GEMINI_API_KEY |
Google Gemini API Key | 可選 |
ANTHROPIC_API_KEY |
Anthropic Claude API Key | 可選 |
OPENAI_API_KEY |
OpenAI 兼容 API Key(支援 DeepSeek、通義千問等) | 可選 |
OPENAI_BASE_URL / OPENAI_MODEL |
使用 OpenAI 兼容服務時填寫 | 可選 |
Ollama 更適合本地 / Docker 部署,GitHub Actions 推薦使用雲端 API。
通知渠道配置(至少配置一個)
| Secret 名稱 | 說明 |
|---|---|
WECHAT_WEBHOOK_URL |
企業微信機器人 |
FEISHU_WEBHOOK_URL |
飛書機器人 |
TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID |
Telegram |
DISCORD_WEBHOOK_URL |
Discord Webhook |
SLACK_BOT_TOKEN + SLACK_CHANNEL_ID |
Slack Bot |
EMAIL_SENDER + EMAIL_PASSWORD |
郵件推送 |
更多渠道、簽名校驗、分組郵件、Markdown 轉圖片等配置見 通知渠道詳細配置。
自選股配置(必填)
| Secret 名稱 | 說明 | 必填 |
|---|---|---|
STOCK_LIST |
自選股代碼,如 600519,hk00700,AAPL,7203.T,005930.KS,2330.TW;已登記指數顯式代碼(如 sh000016、930606.CSI)經一次性 --stocks 或 GitHub Actions 入口支持,規則見 指數自選股配置 |
✅ |
新聞源配置(推薦)
新聞源會顯著影響輿情、公告、事件和催化因素品質,建議至少配置一個搜尋服務。
| Secret 名稱 | 說明 | 必填 |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire AI Search:匯聚全球輿情信息,適配 A股、美股、港股等新聞和輿情檢索;同一 Key 可複用大模型服務;本項目新用戶可獲得 30 元等額的免費點數 | 推薦 |
SERPAPI_API_KEYS |
SerpAPI:搜尋引擎結果補強,適合即時金融新聞 | 推薦 |
TAVILY_API_KEYS |
Tavily:通用新聞搜尋 API | 可選 |
BOCHA_API_KEYS |
博查搜尋:中文搜尋優化,支援 AI 摘要 | 可選 |
BRAVE_API_KEYS |
Brave Search:隱私優先,美股資訊補強 | 可選 |
MINIMAX_API_KEYS |
MiniMax:結構化搜尋結果 | 可選 |
SEARXNG_BASE_URLS |
SearXNG 自建實例:無配額兜底,適合私有部署 | 可選 |
更多搜尋源、社交輿情和降級規則見 搜尋服務配置。
行情數據源配置(可選)
預設使用 AkShare、Baostock、YFinance 等免費數據源,日誌中「未配置」的提示不影響執行。
如需更穩定的行情,可按市場配置以下 Secret:
| Secret 名稱 | 適用市場 | 說明 |
|---|---|---|
TUSHARE_TOKEN |
A 股 | 提升歷史行情穩定性 |
LONGBRIDGE_OAUTH_CLIENT_ID + LONGBRIDGE_OAUTH_TOKEN_CACHE_B64 |
港股/美股 | 補齊量比、換手率、PE 等欄位 |
詳見 數據源配置。
3. 啟用 Actions
Actions 標籤 -> I understand my workflows, go ahead and enable them
4. 手動測試
Actions -> 每日股票分析 -> Run workflow -> Run workflow
完成
預設每個工作日 18:00(北京時間)自動執行,也可手動觸發。預設非交易日(含 A/H/US 節假日)不執行;強制運行、交易日檢查、斷點續傳等規則見 完整指南。
方式二:[客戶端配置教程](https://www.bilibili.com/video/BV11FEb66Eyr/) / 本地運行 / Docker 部署
# 克隆項目
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git && cd daily_stock_analysis
# 安裝依賴
pip install -r requirements.txt
# 配置環境變數
cp .env.example .env && vim .env
# 運行分析
python main.py常用命令:
python main.py --debug
python main.py --dry-run
python main.py --stocks 600519,hk00700,AAPL,2330.TW
python main.py --market-review
python main.py --schedule
python main.py --serve-only📱 推送效果
決策儀表盤
🎯 2026-02-08 決策儀表盤
共分析3隻股票 | 🟢買入:0 🟡觀望:2 🔴賣出:1
📊 分析結果摘要
🟡 中鎢高新(000657): 觀望 | 評分 65 | 看多
🟡 永鼎股份(600105): 觀望 | 評分 48 | 震盪
🔴 新萊應材(300260): 賣出 | 評分 35 | 看空
🚨 風險警報:
風險點1:主力資金出現明顯流出,需警惕短期拋壓。
風險點2:籌碼集中度偏高,拉升阻力可能較大。
✨ 利好催化:
利好1:公司被市場定位為 AI 供應鏈核心標的。
利好2:近期業績增長為股價提供基本面支撐。大盤復盤
🎯 2026-01-10 大盤復盤
📊 主要指數
- 上證指數: 3250.12 (🟢+0.85%)
- 深證成指: 10521.36 (🟢+1.02%)
- 創業板指: 2156.78 (🟢+1.35%)
📈 市場概況
上漲: 3920 | 下跌: 1349 | 漲停: 155 | 跌停: 3
🔥 板塊表現
領漲: 互聯網服務、文化傳媒、小金屬
領跌: 保險、航空機場、光伏設備⚙️ 配置說明
完整環境變數、模型渠道、通知渠道、數據源優先級、交易紀律、基本面 P0 語義和部署說明請參考 完整配置指南。
🖥️ Web 介面
Web 工作台提供配置管理、任務監控、手動分析、歷史報告、完整 Markdown 報告、Agent 問股、回測、持倉管理、智能匯入和淺色 / 深色主題。啟動方式:
python main.py --webui
python main.py --webui-only訪問 http://127.0.0.1:8000 即可使用。認證、智能匯入、搜尋補全、歷史報告複製、雲端伺服器訪問等細節見 本地 WebUI 管理介面。
🤖 Agent 策略問股
配置任意可用 AI API Key 後,Web /chat 頁面即可使用策略問股;如需顯式關閉可設定 AGENT_MODE=false。
- 支援均線金叉、纏論、波浪理論、多頭趨勢、熱點題材、事件驅動、成長品質、預期重估等內建策略
- 支援即時行情、K 線、技術指標、新聞和風險資訊調用
- 支援多輪追問、會話匯出、發送到通知渠道和後台執行
- 支援自訂策略文件與多 Agent 編排(實驗性)
🧩 相關項目 (Related Projects)
DSA 聚焦日常分析報告;內建選股實作參考 AlphaSift,AlphaEvo 用於策略驗證與進化。
| 項目 | 定位 |
|---|---|
| AlphaSift | DSA 內建選股引擎的參考專案 |
| AlphaEvo | 策略回測與自我進化,用於驗證策略規則,並透過迭代探索策略參數與組合 |
📬 聯繫與合作
| 合作郵箱 | [email protected]
項目諮詢、部署支援與功能擴展 |
掃碼關注小紅書 |
| --- | --- | --- |
| 小紅書 | 歡迎關注小紅書 | |
| 問題反饋 | 提交 Issue | |
📄 License
MIT License © 2026 ZhuLinsen
如果你在項目中使用或基於本項目進行二次開發,非常歡迎在 README 或文檔中註明來源並附上本倉庫鏈接。
⚠️ 免責聲明
本項目僅供學習和研究使用,不構成任何投資建議。股市有風險,投資需謹慎。作者不對使用本項目產生的任何損失負責。
README EN (`docs/README_EN.md`)
💖 Sponsors
🖥️ Product Preview
✨ Key Features
| Capability | Coverage |
|---|---|
| AI decision reports | Core conclusion, score, trend, entry/exit levels, risk alerts, catalysts, and action checklist |
| Multi-market data | Covers A-shares, Hong Kong, US, Japanese, Korean, Taiwan stocks, and ETFs, with quotes, K-lines, technical indicators, news, announcements, fundamentals, and report context. Data-source coverage and market boundaries are documented in market boundaries |
| Web / desktop workspace | Manual analysis, task progress, history, full Markdown reports, backtest, portfolio, settings, and light/dark themes |
| Agent strategy chat | Multi-turn Q&A with 15 built-in strategies across Web/Bot/API |
| Smart import & autocomplete | Image, CSV/Excel, clipboard import; code/name/pinyin/alias autocomplete |
| Automation & notifications | GitHub Actions, Docker, local scheduler, FastAPI service, and WeChat Work / Feishu / Telegram / Discord / Slack / Email delivery |
Detailed fields, fundamental P0 timeout semantics, trading rules, data-source priority, Web/API behavior, and troubleshooting live in the Full Guide.
Tech Stack & Data Sources
| Type | Supported |
|---|---|
| AI Models | Modelflare, AIHubMix, Gemini, OpenAI-compatible providers, DeepSeek, Qwen, Claude, Ollama |
| Market Data | AkShare, Tushare, Pytdx, Baostock, YFinance, Longbridge |
| News Search | Anspire, SerpAPI, Tavily, Bocha, Brave, MiniMax, SearXNG |
| Social Sentiment | Stock Sentiment API for Reddit / X / Polymarket, US stocks only |
The project includes free market-data sources such as AkShare, Baostock, and YFinance and can run without extra data-source credentials. These free sources can be rate-limited, change upstream contracts, or fluctuate by network condition, so stability is not guaranteed. For scheduled runs, batch analysis, or steadier quotes, configure token-based sources such as Tushare or Longbridge; market coverage, Actions mappings, and fallback rules are documented in Data Source Configuration.
🚀 Quick Start
Option 1: GitHub Actions (Recommended)
Deploy in about 5 minutes, with no server and no infrastructure cost.
1. Fork this repository
Click Fork in the upper-right corner. A star is very welcome if this project helps you.
2. Configure Secrets
Open your forked repository, then go to Settings -> Secrets and variables -> Actions -> New repository secret.
AI model configuration (configure at least one)
Start with one provider and one API key. For multi-model routing, image recognition, local models, or advanced routing, see the LLM Config Guide.
| Secret Name | Description | Required |
|---|---|---|
MODELFLARE_KEY |
Modelflare API key for leading global models at lower cost | Recommended |
AIHUBMIX_KEY |
AIHubMix API key, one key for multiple model families and a 10% top-up discount for this project | Recommended |
GEMINI_API_KEY |
Google Gemini API key | Optional |
ANTHROPIC_API_KEY |
Anthropic Claude API key | Optional |
OPENAI_API_KEY |
OpenAI-compatible API key, including DeepSeek and Qwen-compatible services | Optional |
OPENAI_BASE_URL / OPENAI_MODEL |
Fill these when using an OpenAI-compatible provider | Optional |
Ollama is better suited for local or Docker deployment. GitHub Actions is usually smoother with a cloud API.
Notification channels (configure at least one)
| Secret Name | Description |
|---|---|
WECHAT_WEBHOOK_URL |
WeChat Work bot |
FEISHU_WEBHOOK_URL |
Feishu bot |
TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID |
Telegram |
DISCORD_WEBHOOK_URL |
Discord webhook |
SLACK_BOT_TOKEN + SLACK_CHANNEL_ID |
Slack bot |
EMAIL_SENDER + EMAIL_PASSWORD |
Email push |
More channels, signatures, email groups, and Markdown-to-image settings are in Notification Configuration.
Watchlist (required)
| Secret Name | Description | Required |
|---|---|---|
STOCK_LIST |
Watchlist codes, such as 600519,hk00700,AAPL,7203.T,005930.KS,2330.TW; registered-index explicit codes (e.g. sh000016, 930606.CSI) are supported via the one-shot --stocks argument or the GitHub Actions entry — see Index watchlist configuration |
✅ |
News sources (recommended)
News search strongly improves sentiment, announcements, events, and catalyst quality. Configure at least one search provider if possible.
| Secret Name | Description | Required |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire AI Search, global market-sentiment aggregation tuned for news and sentiment search across A-share, US, and Hong Kong markets; the same key also works for the Anspire LLM service; new users of this project receive free credits worth CNY 30 | Recommended |
SERPAPI_API_KEYS |
SerpAPI, search-engine results for realtime financial news | Recommended |
TAVILY_API_KEYS |
Tavily, general news search API | Optional |
BOCHA_API_KEYS |
Bocha, Chinese search with AI summaries | Optional |
BRAVE_API_KEYS |
Brave Search, privacy-first search and US-stock news enrichment | Optional |
MINIMAX_API_KEYS |
MiniMax, structured search results | Optional |
SEARXNG_BASE_URLS |
Self-hosted SearXNG instances for quota-free fallback | Optional |
More search providers, social sentiment, and fallback behavior are in Search Configuration.
Market data sources (optional)
Free sources like AkShare, Baostock, and YFinance are used by default. "Not configured" messages in the logs are informational and do not affect execution.
For more stable data, configure the following secrets per market:
| Secret Name | Market | Description |
|---|---|---|
TUSHARE_TOKEN |
A-shares | Improves historical data stability |
LONGBRIDGE_OAUTH_CLIENT_ID + LONGBRIDGE_OAUTH_TOKEN_CACHE_B64 |
HK/US stocks | Fills in volume ratio, turnover rate, P/E, and other fields |
3. Enable Actions
Open the Actions tab and click I understand my workflows, go ahead and enable them.
4. Manual Test
Actions -> Daily Stock Analysis -> Run workflow -> Run workflow.
Done
By default, the workflow runs every weekday at 18:00 Beijing time and skips non-trading days. Forced runs, trading-day checks, and resume rules are covered in the Full Guide.
Option 2: Local / Docker Deployment
# Clone the project
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git && cd daily_stock_analysis
# Install dependencies
pip install -r requirements.txt
# Configure environment variables
cp .env.example .env && vim .env
# Run analysis
python main.pyCommon commands:
python main.py --debug
python main.py --dry-run
python main.py --stocks 600519,hk00700,AAPL,2330.TW
python main.py --market-review
python main.py --schedule
python main.py --serve-onlyDocker deployment, scheduling, and cloud-server WebUI access are documented in the Full Guide.
📱 Sample Output
Decision Dashboard
🎯 2026-02-08 Decision Dashboard
Analyzed 3 stocks | 🟢 Buy:0 🟡 Watch:2 🔴 Sell:1
📊 Summary
🟡 000657: Watch | Score 65 | Bullish
🟡 600105: Watch | Score 48 | Range-bound
🔴 300260: Sell | Score 35 | Bearish
🚨 Risk Alerts:
Risk 1: Main-force funds showed notable outflow.
Risk 2: Chip concentration suggests short-term resistance.
✨ Positive Catalysts:
Catalyst 1: AI-server supply-chain exposure remains a market focus.
Catalyst 2: Recent earnings growth provides fundamental support.Market Review
🎯 2026-01-10 Market Review
📊 Major Indices
- SSE Composite: 3250.12 (🟢+0.85%)
- SZSE Component: 10521.36 (🟢+1.02%)
- ChiNext: 2156.78 (🟢+1.35%)
📈 Market Breadth
Up: 3920 | Down: 1349 | Limit up: 155 | Limit down: 3
🔥 Sector Performance
Leading: Internet Services, Media & Entertainment, Minor Metals
Lagging: Insurance, Airlines & Airports, Solar Equipment⚙️ Configuration
Full environment variables, model routing, notification channels, data-source priority, trading rules, fundamental P0 semantics, and deployment details are in the Full Guide.
🖥️ Web UI
The Web workspace supports settings, task monitoring, manual analysis, history reports, full Markdown reports, Agent strategy chat, backtest, portfolio management, smart import, and light/dark themes.
python main.py --webui
python main.py --webui-onlyVisit http://127.0.0.1:8000. Authentication, smart import, autocomplete, report copying, and cloud-server access are documented in Local WebUI Management.
🤖 Agent Strategy Chat
After configuring any available AI API key, the Web /chat page can use strategy chat. Set AGENT_MODE=false only if you want to disable it explicitly.
- Built-in strategies include moving-average crossovers, Chan theory, Elliott wave, bull trend, hot themes, event-driven, growth quality, expectation repricing, and more
- Calls realtime quotes, K-line data, technical indicators, news, and risk context
- Supports follow-up questions, session export, notification sending, and background execution
- Supports custom strategy files and experimental multi-agent orchestration
Agent parameters,
skillnaming compatibility, multi-agent mode, and budget guards are covered in the Full Guide and LLM Config Guide.
🧩 Related Projects
DSA focuses on daily analysis reports. Its screening implementation references AlphaSift, while AlphaEvo covers strategy validation and evolution.
| Project | Focus |
|---|---|
| AlphaSift | Reference project for DSA's screening implementation |
| AlphaEvo | Strategy backtesting and self-evolution experiments for validating rules and iteratively exploring strategy parameters and combinations |
📞 Contact
| Email | [email protected]
Project consulting, deployment support, and feature extensions |
Follow on Xiaohongshu |
| --- | --- | --- |
| Xiaohongshu | Follow on Xiaohongshu | |
| Feedback | GitHub Issues · Discussions | |
📄 License
MIT License © 2026 ZhuLinsen
If you use or build on this project, attribution with a link back to this repository is appreciated.
⚠️ Disclaimer
This project is for informational and educational purposes only. AI-generated analysis is not investment advice. Stock market investing involves risk; do your own research and consult a licensed financial advisor when needed.
TUSHARE STOCK LIST GUIDE (`docs/TUSHARE_STOCK_LIST_GUIDE.md`)
Tushare 股票列表获取工具使用说明
功能概述
从 Tushare Pro 获取 A股、港股、美股列表信息,保存为 CSV 文件到本地。
快速开始
1. 配置 Token
在项目根目录的 .env 文件中添加 Tushare Token:
TUSHARE_TOKEN=你的tushare_token获取 Token:访问 Tushare Pro 注册并获取
2. 运行脚本
python3 scripts/fetch_tushare_stock_list.py如需针对 A 股名称状态做修正,可以加上 --a-rk,脚本会保持 stock_basic 作为基础来源,再用 rt_k 对带 XD、XR、DR、N、C 前缀的名称进行回填,并覆盖输出到 data/stock_list_a.csv:
python3 scripts/fetch_tushare_stock_list.py --a-rk3. 查看输出
数据将保存到 data/ 目录:
data/
├── stock_list_a.csv # A股列表(--a-rk 时为修正后名称)
├── stock_list_hk.csv # 港股列表
├── stock_list_us.csv # 美股列表
└── README_stock_list.md # 数据说明文档功能特性
✅ 自动分页:美股数据自动分页读取(每页5000条)
✅ 智能限流:每次请求之间随机休息5-10秒
✅ 错误处理:单个市场失败不影响其他市场
✅ 进度提示:实时显示读取进度
✅ 自动文档:生成详细的数据说明文档
市场说明
| 市场 | 接口 | 积分要求 | 数据量 |
|---|---|---|---|
| A股 | stock_basic | 2000积分 | ~5000只 |
| 港股 | hk_basic | 2000积分 | ~2000只 |
| 美股 | us_basic | 120试用/5000正式 | ~10000只 |
输出文件格式
A股(stock_list_a.csv)
执行 --a-rk 时,这个文件会写入修正后的 A 股名称。
ts_code,symbol,name,area,industry,market,exchange,list_date,...
000001.SZ,000001,平安银行,深圳,银行,主板,SZSE,19910403,...
600519.SH,600519,贵州茅台,贵州,白酒,主板,SSE,20010827,...港股(stock_list_hk.csv)
ts_code,name,fullname,market,list_date,trade_unit,curr_type,...
00700.HK,腾讯控股,腾讯控股有限公司,主板,20040616,100,HKD,...
00005.HK,汇丰控股,汇丰控股有限公司,主板,19750401,100,HKD,...美股(stock_list_us.csv)
ts_code,name,enname,classify,list_date,...
AAPL,苹果,Apple Inc.,EQT,19801212,...
TSLA,特斯拉,Tesla Inc.,EQT,20100629,...
BABA,阿里巴巴,Alibaba Group,ADR,20140919,...使用示例
Python 读取数据
import pandas as pd
# 读取 A股
a_stocks = pd.read_csv('data/stock_list_a.csv')
print(f"A股数量: {len(a_stocks)}")
# 筛选主板股票
main_board = a_stocks[a_stocks['market'] == '主板']
print(f"主板数量: {len(main_board)}")
# 查找特定股票
stock = a_stocks[a_stocks['ts_code'] == '600519.SH']
print(stock[['name', 'industry', 'list_date']])刷新股票自动补全索引
推荐直接使用一键刷新脚本,它会默认在抓取 A 股时使用 --a-rk,然后生成并同步自动补全索引:
pip install -r requirements.txt
python3 scripts/refresh_stock_index.py生成自动补全索引依赖 pypinyin 写入中文股票的完整拼音和拼音首字母字段;缺少该依赖时脚本会直接失败,避免生成无法支持拼音搜索的降级索引。
如果你只想单独更新 CSV,可以先抓取数据:
python3 scripts/fetch_tushare_stock_list.py --a-rk如果已经有新的 CSV,只想重新生成索引:
python3 scripts/generate_index_from_csv.py --test # 先测试
python3 scripts/generate_index_from_csv.py # 确认后生成本地客户端自动获取最新索引
新版客户端默认会从项目 GitHub main 分支读取最新的 apps/dsa-web/public/stocks.index.json,并缓存到本地 data/cache/stocks.index.json。前端仍访问本地 /stocks.index.json,不需要直接跨域请求 GitHub。
远程索引地址、检查频率和网络超时时间为系统内置值,不提供用户配置项;用户只需要决定是否启用:
STOCK_INDEX_REMOTE_UPDATE_ENABLED=true默认开启时,系统最多每 48 小时检查一次更新。若运行环境无法访问 GitHub raw、请求超时、返回内容不是合法股票索引,应用会保留已有缓存;如果没有远程缓存,则继续使用随应用打包的内置索引。远程更新失败不会阻断 WebUI 启动、股票自动补全或分析流程;连续失败达到系统内置阈值后,会在本进程内暂停重试直到下一轮 48 小时窗口。
注意事项
- 积分要求:确保账号积分足够(A股/港股2000,美股120试用)
- 请求限制:注意 API 的每分钟请求次数限制
- 数据更新:维护者建议每三天刷新一次并提交到仓库;本地客户端默认最多每 48 小时检查一次 GitHub
main上的索引更新。后续可通过 GitHub Actions workflow 自动化刷新与提交 PR - 网络连接:需要稳定的网络连接
常见问题
Q: 提示"未找到 TUSHARE_TOKEN"?
A: 请在 .env 文件中配置 TUSHARE_TOKEN=你的token
Q: 提示"账号积分不足"?
A:
- A股/港股需要2000积分
- 美股120积分试用,5000积分正式权限
- 访问 https://tushare.pro 查看积分获取办法
Q: 读取失败怎么办?
A:
- 检查网络连接
- 检查 Token 是否正确
- 查看账号积分是否足够
- 当前脚本不会自动重试;单次请求失败后会输出错误并结束,请排查原因后重新运行
Q: 数据更新频率?
A: 对维护者本地 CSV 与仓库索引,建议每三天更新一次并提交到仓库;遇到摘帽/更名等高影响事件可临时刷新。未来可通过 GitHub Actions workflow 自动化刷新与提交 PR。对普通本地客户端,系统默认最多每 48 小时从 GitHub main 检查一次最新索引。
Q: 无法访问 GitHub raw 会影响使用吗?
A: 不会。远程索引更新是 best-effort:失败时会继续使用已有远程缓存或随应用打包的内置索引;如果索引完全不可用,Web 自动补全会进入现有 fallback,股票代码仍可手动输入。
相关链接
Agent Stream Events (`docs/agent-stream-events.md`)
Agent Stream Progress Events
This document records the progress-event contract used by:
POST /api/v1/agent/chat/stream- Web Ask Stock chat progress rendering
- single-agent
run_agent_loop - multi-agent
AgentOrchestrator
The endpoint still streams Server-Sent Events (text/event-stream) where each
SSE payload is a JSON object with a top-level type field.
Compatibility Boundary
The event changes are additive. Existing clients can keep consuming the legacy
top-level fields:
typesteptooldisplay_namesuccessdurationmessagecontent
New clients may additionally read:
stagestatuselapsedtimeoutremainingminimumreasonmeta
Unknown event types should be ignored or displayed with a generic fallback.done and error keep their existing completion semantics.
Event Types
| Type | Producer | Meaning | Important Fields |
|---|---|---|---|
accepted |
SSE endpoint | Context preparation and the user-turn/state transaction succeeded; execution has not started yet. | backend, request_id, session_id, active_stock_context, session_state_version, session_generation, session_state |
stage_start |
single-agent loop, multi-agent orchestrator | An agent or pipeline stage has started. | stage, message |
stage_done |
single-agent loop, multi-agent orchestrator | An agent or pipeline stage has completed. | stage, status, duration |
thinking |
single-agent loop | The agent is deciding the next action. | step, message |
tool_start |
single-agent loop | A tool call has started. | step, tool, display_name |
tool_done |
single-agent loop | A tool call has completed or failed. | step, tool, success, duration, display_name |
generating |
single-agent loop | The final response is being generated. | step, message |
pipeline_timeout |
multi-agent orchestrator | The orchestrator stopped because the stage or pipeline budget expired. | stage, elapsed, timeout |
pipeline_budget_skipped |
multi-agent orchestrator | The orchestrator stopped before starting the next stage because the remaining budget was too low for useful work. | stage, elapsed, timeout, remaining, minimum, reason, message |
done |
SSE endpoint | The request completed. | success, content, error, total_steps, session_id |
error |
SSE endpoint | The request failed before normal completion, including preparation rejection before accepted. |
message, error_code, request_id, session_id |
Registered Names And Embedded Mentions
Chat target preparation treats a validated full name from the stock catalogue
as one lexical object. A shorter alias or ticker-like fragment wholly inside
that name does not introduce another target: 长电科技(600584.SH) selects600584, while standalone 长电 still selects 600900 (长江电力).
This applies to both streaming and non-streaming chat through their shared
object-decision code. Separate mentions and codes outside the name retain their
normal checks: 长电科技(600900.SH) still requires clarification, and a shared
name such as 中芯国际 still needs an unambiguous code.
The change does not add response fields, SSE event types or report formatting.
Acceptance And Session State
The endpoint checks the existing access and backend capability boundaries first.
It prepares analysis context outside the acceptance write transaction, then
commits the user message, explicit Skill selection and main discussion object
together. Only a successful commit emits accepted. Analysis progress anddone must not precede it; a terminal preparation error is allowed before it.
accepted, GET /api/v1/agent/chat/sessions/{session_id}, and the supported
non-stream POST /api/v1/agent/chat response share these state fields:
{
"active_stock_context": {
"stock_code": "sh000001",
"stock_name": "上证指数",
"canonical_id": "sh000001",
"asset_type": "index"
},
"session_state_version": 3,
"session_generation": "opaque-server-assigned-instance",
"session_state": {"selected_skill_ids": []}
}active_stock_contextmay benull;stock_namemay benull. Preservecanonical_idandasset_type, not just a bare numerical code. A comparison
can permit several tool targets while leaving the main object unchanged or null.session_generationidentifies a session instance, not authorization. It
changes when a deleted session ID is recreated. A client that knows it may
send it with its next Chat request; a stale instance is rejected.- Versions increase on accepted turns and are comparable only within one
generation. Detail version 0 is a conservative legacy-history view, not a
newly accepted turn. A genuinely empty detail can have a null generation. session_state.selected_skill_idsis the saved user selection: null means
unspecified,[]selects the built-in default, and a nonempty list selects
named Skills. Configured effective defaults are not written as user choices.- Confirmation provenance and provider traces stay private. Non-stream state
comes from that turn's acceptance snapshot, not a later detail read. - Clients must reject partially missing or malformed state fields. All three
object/version/generation fields absent indicates an old API, not version 0.
Detail reads do not repair or increment business state. If old-code writes
make an accepted object's provenance unreliable, detail may return null with
the same generation and version as an earlier accepted object. A valid current
detail takes precedence over a delayed accepted response at the same version.
Response ownership, local instance binding and generation must be checked
before comparing versions; a stale detail cannot replace a newer bound instance.
Preparation/commit conflicts emit error_code=session_state_conflict withoutaccepted, done, tool progress, or partial message/Skill/stock updates.
The non-stream API returns HTTP 409 with an error/message JSON payload.
Clients should preserve the draft and request an explicit refresh/retry, not
automatically resend. A fixed clarification is a normal accepted user turn
followed by done (total_steps=0), without analysis preparation or a model call.
Failure, timeout, stop or disconnect after commit does not undo accepted state.
Terminal writes verify the accepted session/message instance; a late reply cannot
recreate a deleted session or contaminate a same-named new one. This contract
does not enable unsupported Codex non-stream/multi combinations or change Gate 0B.
Web Behavior
The Web chat UI now recognizes stage_start, stage_done,pipeline_timeout, and pipeline_budget_skipped in addition to the existing
thinking/tool/generating events.
If a future backend event is not recognized, the UI keeps the event in the
message progress history and renders a generic fallback instead of an empty
progress row.
Runtime And Provider Scope
This event contract does not change model routing or runtime configuration.
It does not modify:
- provider selection
- model names
- Base URL handling
- LiteLLM route resolution
- API keys or credential loading
- configuration cleanup or migration semantics
Provider/model/Base URL behavior remains governed by the existing LLM
configuration docs and runtime code. Any provider/model strings used in tests
are mock identifiers only.
Validation
Recommended checks for changes to this contract:
python -m pytest tests/test_agent_stream_events.py tests/test_agent_sse_cleanup.py tests/test_agent_chat_api.py tests/test_agent_active_stock_integration.pycd apps/dsa-web
npm test -- src/stores/__tests__/agentChatStore.test.ts src/pages/__tests__/ChatPage.test.tsxThe focused tests should confirm that:
- event helper output preserves legacy fields and drops unset fields
- stage metadata is preserved
run_agent_loopemits pairedstage_start/stage_doneevents plusthinkingandgenerating- orchestrator timeout events remain separate from budget-skip events
- SSE cleanup behavior remains unchanged
- Web chat state and Chat page rendering still pass
Rollback
To roll back this event-contract change, revert the commit that introduced:
src/agent/stream_events.py- the event-helper wiring in
src/agent/runner.py - the stage-event wiring in
src/agent/orchestrator.py - the Web
ProgressStepand Chat page rendering updates
Because the change is additive and keeps done / error semantics unchanged,
existing clients can also ignore the new stage events without a migration step.
For the session-state extension, stop the new backend before rolling back the
matching backend/Web code. Keep the added nullable columns and database-level
version default; do not drop columns or rebuild the database. Do not run old and
new writing backends concurrently. Old-code user writes during rollback require
reconfirmation after re-upgrade; preserved messages and Skill choices remain
available. Platform upgrade acceptance is separate from this wire contract.
Agent Trajectory Eval (`docs/agent-trajectory-eval.md`)
Agent Trajectory 评估(最小版,Refs #1956)
定位
evals/agent_trajectory/ 提供一套离线可运行的最小轨迹评估管线:用真实 tool_calls_log + AgentResult 跑一个 golden 样例,输出结构化 JSON 报告与简短可读文本摘要。评估结果是 reporter 而非 gate —— 指标违规只反映在报告里,不会让进程失败,也不进入 CI 门禁。
- 指标层(
metrics.py)是纯函数:只消费轨迹日志与 golden 样例,不 importsrc/,不触网、不调 LLM,可离线单测。 - 入口(
run_eval.py)通过build_agent_executor()构建真实执行器,与src/core/pipeline.py使用同一个执行捕获钩子,消费真实产物。 - 入口支持单 agent 与多 Agent 运行(
AGENT_ARCH=single|multi,默认仍为 single):多 Agent 结果保留原有扁平tool_calls_log指标,并通过显式stage_trajectories快照增加阶段状态与局部/累计步数报告。没有阶段快照时,单 agent 命令与 JSON 结构保持不变。 - 入口用真实工具注册表校验 golden:
expected_tools拼错或过期会被判为无效样例(退出码 1),而不是静默按低命中继续评分。 - 本次冻结最小指标契约,股票 guard、Codex
arguments_summary等扩展语义明确留给后续 PR(见文末「不在范围」)。
快速开始
# 跑单个样例(真实 LLM,依赖本地已配置的模型)
python evals/agent_trajectory/run_eval.py --sample 600519_technical
# 跑全部样例,并输出结构化 JSON(按样例 id 键控)
python evals/agent_trajectory/run_eval.py --all --json-out eval_report.json参数:
| 参数 | 说明 |
|---|---|
--sample ID / --all |
二选一必填:跑单个 golden 样例或全部 |
--golden-path PATH |
自定义 golden JSON 路径(默认模块旁 golden_samples.json) |
--json-out PATH |
写结构化 JSON 报告(--all 时为键控对象) |
退出码:0 运行成功(含违规);1 golden 加载(含 expected_tools 不在真实工具注册表)/ 样例选择 / 工具注册表加载失败 / 执行器构建 / 运行失败(含执行器返回 success=false,如 provider 未配置、LLM 错误、超时、max_steps 耗尽、dashboard 解析失败);2 用法错误。Multi-Agent 结果若在失败时带有阶段快照,入口会先输出并写入可检查的失败轨迹报告,再以退出码 1 表示运行失败;单 Agent 失败行为不变。
冻结的最小指标契约
对每条 tool_calls_log(runner 契约:每项含 step / tool / arguments / success / duration / result_length / cached,可选 timeout / guarded),只统计:
| 指标 | 定义 |
|---|---|
expected_hit_rate / missing_expected |
期望工具按工具名命中(本版不做股票维度判定);expected_total 为去重后的期望数 |
optional_tools_used |
期望集合之外实际调用的工具;allow_optional_tools=false 时记为违规 |
redundant_calls |
同一 (tool, args-key) 对在首次出现之后的每一次出现(不论成败)。args-key = json.dumps(arguments, sort_keys=True, default=str),与运行时缓存键同思路 |
retries |
紧跟失败之后重试同一 (tool, args-key) 对;retries ⊆ redundant_calls。成功会清除该对的失败态:fail → success → success 只计 1 次 retry(后一次 success 仅计冗余) |
failed_calls |
success=false 的条目数 |
cached_calls |
cached=true 的条目数(runner 语义:复用不可重试的失败结果) |
distinct_steps / max_steps_touched |
日志 step 与 AgentResult.total_steps 取较大者(最后纯回答轮不产生工具调用,日志会低估);max_steps_touched 为 max(step) >= allowed_max_steps 的启发式 |
Multi-Agent 阶段轨迹
当执行器结果携带非空 stage_trajectories 时,入口在上述工具指标之外输出 stage_metrics。每个阶段快照是稳定的 JSON-safe 白名单对象:
| 字段 | 说明 |
|---|---|
stage_name |
阶段或 specialist 名称 |
status |
completed / failed / skipped 等阶段状态 |
total_steps |
该阶段自己的局部 agent-loop 步数,不是全局阶段计数 |
tool_calls_log |
该阶段的原始工具调用日志;空日志也会保留阶段 |
failure_reason |
stage_failure / timeout / budget_skip 等降级原因 |
评估层按快照列表顺序把局部步数累加为 cumulative_steps,因此不同阶段从 1 重新计步不会碰撞。该累计值也作为整条 Multi-Agent 轨迹的 agent-loop 步数传给顶层指标;AgentResult.total_steps 在 orchestrator 中保留原有阶段数语义,不用于轨迹 loop 预算评分。报告同时包含期望阶段命中率、缺失/额外阶段、完成/失败/跳过计数,以及每个阶段的状态、失败原因、局部/累计步数和工具调用/失败/重试指标。阶段 tool_metrics 仅统计该阶段日志中的调用数、工具名、冗余、缓存、失败和重试,不含期望工具命中率、缺失工具、期望外工具违规或 max-steps 违规。Golden 的 expected_tools、allow_optional_tools、allowed_max_steps 属于整条样例,只对汇总工具轨迹评估一次;当前不支持按阶段定义工具合规期望。关键阶段失败后,仍使用已返回的快照生成阶段报告;配置了 expected_stages 时,尚未执行的后续阶段会列入缺失阶段。specialist 并发执行后的快照由 scheduler 恢复为选中顺序,不依赖完成先后。
golden 样例可选增加 expected_stages 字段:
{
"expected_stages": ["technical", "intel", "decision"]
}未配置时仍会报告实际阶段和状态,但阶段命中率显示为未配置;这不会改变现有单 agent 样例的 JSON 字段。
Golden 样例 schema
golden_samples.json 是一个数组,字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 唯一样例 id(必填) |
task_description |
string | 交给执行器的任务文本(必填) |
stock_code |
string | 原样传入 runner context({"stock_code": ...});空串 = 无 context;不参与打分 |
expected_tools |
string[] | 期望工具名(必填,非空、无重复) |
allowed_max_steps |
int | 步数预算启发式(默认 10,>= 1) |
allow_optional_tools |
bool | 是否容忍期望外工具(默认 true) |
expected_stages |
string[] | 多 Agent 阶段期望列表(可选、非空名称且不重复);用于统计命中与缺失阶段 |
校验:加载路径(load_golden_samples)直接拒绝非法样例;直接构造路径(compute_trajectory_metrics)以 validator 完全相同的措辞逐条上报违规,两条路径的契约按构造保持一致。known_tool_names 可注入真实工具注册表做成员校验(metrics 层自身不 import src/);run_eval.py 入口会自动注入真实工具注册表。
JSON 报告 schema
单个样例:
{
"sample_id": "600519_technical",
"task_description": "…",
"stock_code": "600519",
"metrics": { "expected_hit_rate": …, "expected_total": …, "…": "… 共 11 个字段" },
"violations": ["…"]
}--all --json-out 时外层为 {sample_id: <上述对象>} 键控对象。
含阶段快照的多 Agent 报告会在上述对象中追加 run_status (completed / failed) 与 stage_metrics 字段;原有 metrics 字段继续表示扁平工具轨迹指标。运行失败但携带阶段快照时,JSON 报告仍会写出,进程退出码为 1。
不在范围(后续 PR)
- 股票维度命中判定与 guard 拦截语义(
guarded/ 越界调用违规) - Codex App Server 的
arguments_summary方言识别 - 任何
.env/ 运行时配置与 CI 门禁(本 PR 不改变现有分析流程、调度语义或 CI 阻断规则)
Alerts (`docs/alerts.md`)
实时告警中心
本文档记录 Issue #1202 告警中心的运行基线、数据契约、分阶段实现范围和兼容边界。
当前基线
当前运行时告警由 src/services/alert_worker.py 中的后台 worker 统一调度,底层规则评估复用 src/services/alert_service.py 与 src/agent/events.py 中的 EventMonitor 规则模型。
- 配置入口:
AGENT_EVENT_MONITOR_ENABLED、AGENT_EVENT_MONITOR_INTERVAL_MINUTES、AGENT_EVENT_ALERT_RULES_JSON。 - 运行入口:
main.py在 schedule 模式中注册agent_event_monitor后台任务;后台 worker 每轮读取持久化 active rules,并继续兼容 legacyAGENT_EVENT_ALERT_RULES_JSON。 - 通知投递:触发后复用
NotificationService.send(..., route_type="alert"),继续遵守通知网关的 alert 路由配置。 - Web/System 配置校验:
src/services/system_config_service.py会对AGENT_EVENT_ALERT_RULES_JSON做 JSON 与规则语义校验。
当前 runtime 支持三类规则:
alert_type |
方向字段 | 阈值字段 | 当前语义 |
|---|---|---|---|
price_cross |
direction: above / below |
price |
实时价格上破或下破固定价格 |
price_change_percent |
direction: up / down |
change_pct |
实时涨跌幅达到指定百分比 |
volume_spike |
- | multiplier |
最新成交量超过近 20 日均量的指定倍数 |
sentiment_shift、risk_flag、custom 等类型只作为未来扩展占位;当前运行时不接受这些类型作为可执行规则。
Legacy 配置兼容
规则列表现在返回只读 rule_sources:legacy_configured 为校验通过的环境规则条目数,legacy_effective 为与启用数据库规则按后台 worker 原有逻辑去重后的环境规则数。它描述未筛选的配置加载结果,不代表轮询已启动;重复环境条目可能计入前者,但只计入后者一次。无效条目沿用 worker 的跳过规则。
来源统计保留后台规则优先级、数据库与展开目标数量上限及去重语义。持仓目标通过只读账本回放获取,不查询实时行情,也不重写持仓缓存、批次或每日快照;同一次列表请求内,相同目标范围和账户的规则复用展开结果,避免重复回放。展开失败也仅尝试一次,各规则仍保留独立失败载荷和数量占位。缓存不跨请求,新请求仍读取最新持仓并重试之前的失败;列表筛选、翻页及监控开关不改变这一边界。后台轮询和规则试运行仍使用原有行情与快照加载方式。
Web 检测到有效环境规则时始终显示来源提示,即使数据库列表为空或环境规则当前全部被同条件的启用数据库规则覆盖。页面删除、禁用只操作数据库,不修改环境规则;禁用或删除数据库规则后,同条件环境规则可能重新参与轮询。需要停用环境规则时,在实际部署配置中修改 AGENT_EVENT_ALERT_RULES_JSON 并重新加载配置。本次不自动迁移配置,也不改变执行、冷却或推送语义。
视觉回归:在 apps/dsa-web 运行 npx playwright test --config playwright.fixture.config.ts(无需后台或登录口令),使用受控 API 响应验收数据库空列表时的桌面/窄屏提示并生成 test-results/fixtures/**/alerts-sources-*.png。此测试验证 UI,不替代后台真实配置加载回归;专题文档没有英文对应版本。
AGENT_EVENT_ALERT_RULES_JSON 作为 legacy 运行时规则来源继续保留,不自动迁移、删除、覆盖或改写用户已有 .env / Web 配置。
- 空字符串或空数组表示未配置 legacy 规则;schedule 模式仍会注册后台 worker,以便后续 API 创建的持久化 active rules 无需重启即可被评估。
- Web/System 配置保存时执行严格校验,JSON 无效、字段缺失、方向非法、阈值非法或 unsupported rule type 都应返回配置错误。
- 运行时加载时允许跳过单条无效规则,剩余有效规则继续工作,避免单条配置破坏整个 schedule 进程。
- 当前 worker 使用进程内 fingerprint 避免持续触发条件重复推送;这不是告警中心冷却模型,也不提供跨进程或重启后的冷却状态。
数据契约
以下契约用于后续 P1+ API、worker、Web 和存储实现对齐。P0 只定义字段和语义边界,不代表当前已经存在这些持久化实体。
`alert_rule`
可管理的告警规则。
| 字段 | 说明 |
|---|---|
id |
规则 ID;legacy JSON 规则在 P0 中没有持久化 ID |
name |
用户可读名称;没有提供时可由规则类型和目标生成 |
target_scope |
目标范围,例如 single symbol、watchlist、portfolio、market |
target |
目标标的或目标引用,例如股票代码、watchlist ID、portfolio ID |
alert_type |
规则类型;P1 初始只允许 price_cross、price_change_percent、volume_spike |
parameters |
规则参数,例如 direction、price、change_pct、multiplier |
severity |
告警等级,例如 info、warning、critical |
enabled |
是否启用 |
cooldown_policy |
冷却策略;P0 只定义字段,P4 才实现执行语义 |
notification_policy |
通知策略;默认复用 NotificationService 的 alert 路由 |
source |
创建来源,例如 legacy_env、web、api、import |
created_at / updated_at |
创建和更新时间 |
`alert_trigger`
一次真实或可记录的规则触发。
| 字段 | 说明 |
|---|---|
id |
触发记录 ID |
rule_id |
对应规则 ID;legacy env 规则可记录临时引用 |
target |
实际触发目标 |
observed_value |
观察值,例如现价、涨跌幅、成交量倍数 |
threshold |
触发阈值 |
reason |
可读触发原因 |
data_source |
数据源或 provider |
data_timestamp |
数据时间;缺失时不得伪造为当前时间 |
triggered_at |
触发时间 |
status |
触发状态,例如 triggered、skipped、degraded、failed |
diagnostics |
脱敏后的诊断信息 |
`alert_notification`
一次触发对应的通知尝试。
| 字段 | 说明 |
|---|---|
id |
通知尝试 ID |
trigger_id |
对应触发记录 ID |
channel |
通知渠道 |
attempt |
第几次尝试 |
success |
是否成功 |
error_code |
结构化错误码 |
retryable |
是否建议重试 |
latency_ms |
耗时 |
diagnostics |
脱敏后的发送诊断,不得包含 token、完整 webhook URL、邮箱密码或 bot secret |
created_at |
尝试时间 |
`alert_cooldown`
规则或目标维度的冷却状态。
| 字段 | 说明 |
|---|---|
rule_id |
对应规则 ID |
target |
冷却目标 |
severity |
可选等级维度 |
last_triggered_at |
最近触发时间 |
cooldown_until |
冷却截止时间 |
reason |
冷却原因 |
state |
当前状态,例如 active、expired |
updated_at |
更新时间 |
存储方案评估
当前仓库已有 SQLite 存储层和 repository/service 分层:
src/storage.py管理 SQLite 连接、SQLAlchemy ORM 模型和DatabaseManager。src/repositories/放置数据访问层,例如PortfolioRepository。src/services/放置业务服务层,例如PortfolioService、PortfolioRiskService。- 默认数据库路径跟随现有配置,通常落在
data/stock_analysis.db。
P1/P2 实现告警持久化时,推荐优先复用以上模式:在 storage 层定义 alert ORM 模型,在 repository 层封装 CRUD 和查询,在 service 层处理规则校验、评估状态、通知结果和冷却语义。P0 不新建表,不改变现有数据库。
如果后续 PR 需要 schema 变更,必须同时给出:
- 幂等初始化:重复启动或重复执行初始化时不得破坏已有数据。
- 向后兼容:未配置告警中心时不影响每日分析、问股、通知、大盘复盘和持仓功能。
- 回滚说明:最小回滚方式至少包括 revert PR;若创建了新表或索引,需要说明是否保留数据、如何手动清理。
- 数据迁移边界:不得自动迁移、删除或覆盖
AGENT_EVENT_ALERT_RULES_JSON,除非用户显式执行导入动作。
P1 Alert API MVP
P1 新增后端 Alert API 与 schema,锁定告警中心最小 API 契约,不接入 Web 页面或后台 worker。
- 新增 API 文件:
api/v1/endpoints/alerts.py。 - 新增 schema 文件:
api/v1/schemas/alerts.py。 - API 范围:
GET /api/v1/alerts/rulesPOST /api/v1/alerts/rulesGET /api/v1/alerts/rules/{rule_id}PATCH /api/v1/alerts/rules/{rule_id}DELETE /api/v1/alerts/rules/{rule_id}POST /api/v1/alerts/rules/{rule_id}/enablePOST /api/v1/alerts/rules/{rule_id}/disablePOST /api/v1/alerts/rules/{rule_id}/testGET /api/v1/alerts/triggersGET /api/v1/alerts/notifications
- 首版规则仍只支持
price_cross、price_change_percent、volume_spike;sentiment_shift、risk_flag、custom等未来类型返回结构化 unsupported 错误。 test接口只做一次性 dry-run 评估,不发送通知,不写入真实触发记录或通知 attempt。cooldown_policy/notification_policy在 P1 中只是保留字段:API 可存储和返回这些 opaque 配置,但不执行冷却或自定义通知语义。- API 响应必须脱敏,不回显 token、完整 webhook URL、邮箱密码、cookie、bot secret。
AGENT_EVENT_ALERT_RULES_JSON继续保留为 legacy 配置入口;P1 不自动迁移、删除、覆盖或改写 legacy 配置。
P1 不做:
- 不新增 Web 告警中心页面、路由或侧边栏入口。
- 不让 schedule worker 加载持久化 active rules,也不实现持久化规则与 legacy JSON 的合并/去重。
- 不实现真实
alert_trigger/alert_notification写入;P1 只提供查询接口和表结构。 - 不实现
alert_cooldown执行语义。 - 不实现 MACD、KDJ、CCI、RSI、持仓风险或 Market Light 告警规则。
P2 告警评估 Worker
P2 将 schedule 运行时从启动时一次性构建 legacy EventMonitor,切换为每轮后台 worker 评估持久化 active rules 与 legacy JSON 规则。
AGENT_EVENT_MONITOR_ENABLED继续作为总开关,后台任务名保持agent_event_monitor。- worker 每轮读取 DB 中
enabled=true的alert_rules,并重新解析AGENT_EVENT_ALERT_RULES_JSON;新增 API 规则不需要重启 schedule 进程。 - DB 规则与 legacy 规则按
target_scope + target + alert_type + canonical(parameters)去重,冲突时 DB 规则优先;legacy 配置不自动迁移、删除或改写。 - 每条规则独立评估,单条失败只写
failed评估状态,不影响同轮其他规则或主分析流程。 alert_triggers在 P2 用于记录最小评估历史:triggered、skipped、degraded、failed;正常not_triggered不写历史,避免轮询刷表。- 实时行情缺失、字段缺失或非可评估场景记录
skipped;日线数据不可用或结构不完整记录degraded;诊断信息会脱敏。 - 触发后仍调用
NotificationService.send(..., route_type="alert");进程内 fingerprint 只避免持续触发条件重复推送,不执行cooldown_policy。
P2 不做:
- 不新增 Web 告警中心页面、路由或侧边栏入口。
- 不写
alert_notifications,不记录 per-channel notification attempt。 - 不实现
alert_cooldown、cooldown_policy或notification_policy执行语义。 - 不实现 MACD、KDJ、CCI、RSI、持仓风险或 Market Light 告警规则。
P3 Web 告警中心 MVP
P3 在 WebUI 中新增 /alerts 告警中心入口,让用户不需要直接编辑 legacy JSON 即可管理当前三类运行时规则。
- 侧边栏新增“告警”入口,页面支持规则列表、分页、启停筛选和规则类型筛选。
- 规则创建表单只支持
single_symbol目标范围和当前已可执行的三类规则:price_cross:direction为above/below,并填写price。price_change_percent:direction为up/down,并填写change_pct。volume_spike:填写multiplier。
- 规则操作支持启用、停用、删除和一次性 dry-run 测试。
- 创建、启用、停用或删除规则期间切换筛选或分页,操作完成后的列表刷新会使用当前筛选条件;创建成功仍会返回当前筛选下的第一页。
- dry-run 测试只展示
AlertRuleTestResponse已声明字段:规则 ID、状态、是否触发、观察值和消息;threshold、data_source、data_timestamp等扩展诊断字段需要后端 schema 明确暴露后再展示。 - 触发历史展示 P2 worker 已写入的
triggered、skipped、degraded、failed记录;正常not_triggered仍不会写入历史。 - 通知尝试区域只查询现有
GET /api/v1/alerts/notifications;由于 P2 运行时不写 per-channel notification attempt,当前通常显示“暂无通知尝试记录”空态,不把触发状态推断为通知投递结果。 - Web 页面不暴露
AGENT_EVENT_ALERT_RULES_JSON编辑入口,不自动迁移、删除或改写 legacy 配置。
P3 不做:
- 不新增或修改后端 API、schema、storage 或 worker 行为。
- 不实现规则编辑、target/source 高级筛选、watchlist/portfolio 目标、技术指标规则或 Market Light 联动。
- 不执行
cooldown_policy/notification_policy,不写alert_notifications。
P4 通知结果与持久化冷却
P4 让真实告警触发具备可排障的通知结果,并让通过 Alert API 创建的持久化规则具备可重启保持的业务冷却状态。
- DB 持久化规则的
triggered历史按rule_id + target + data_source + data_timestamp做同一数据点去重:同一触发事件只保留最早一条alert_triggers,重复轮询命中会复用已有触发记录;data_timestamp缺失时不做去重,避免误合并无法证明同源的数据点。即使后续被冷却或通知降噪抑制,仍通过alert_notifications记录对应的通知尝试或 synthetic 抑制状态。 alert_notifications记录真实 per-channel notification attempt,包括channel、success、error_code、retryable、latency_ms和脱敏后的diagnostics。- 非渠道发送状态使用 synthetic channel 记录:
__cooldown__:告警业务冷却抑制,error_code="cooldown_active"。__cooldown_read_failed__:读取持久化冷却状态失败后,由 worker 进程内临时兜底抑制,error_code="cooldown_read_failed"。__noise_suppressed__:通知基础设施降噪抑制,error_code="noise_suppressed"。__no_channel__:alert 路由未命中任何可用通知渠道。__dispatch__:通知调度级 fallback 或异常。
- cooldown 分层:
- DB 持久化规则正常路径使用
alert_cooldowns作为告警业务冷却,不再由 worker 进程内 fingerprint 决定;仅当读取持久化冷却状态失败时,临时使用进程内 fingerprint 防止同一规则在 DB 异常期间每轮重复推送。 - legacy
AGENT_EVENT_ALERT_RULES_JSON规则继续使用 worker 进程内 fingerprint,不写alert_cooldowns。 notification_noise.py仍作为通知基础设施层的全局安全网;它不是告警业务 cooldown,且被其抑制时不会写入或延长alert_cooldowns。
- DB 持久化规则正常路径使用
- DB 规则的
cooldown_policy.cooldown_seconds归一为非负整数;缺失时使用默认 24 小时业务冷却,0表示关闭 DB 业务冷却。 GET /api/v1/alerts/rules会返回只读last_triggered_at/cooldown_until/cooldown_active摘要;cooldown_active由后端按同一冷却时间语义计算,Web 不在浏览器本地解析 naive ISO 字符串来推断状态。- Web 告警中心只读展示冷却状态和通知结果,不提供 cooldown policy 编辑表单。
P4 不做:
- 不新增技术指标、持仓、自选股、portfolio、watchlist 或 Market Light 告警规则。
- 不实现 target-level 跨规则合并冷却;目标级合并留到持仓/市场联动阶段。
- 不重写通知渠道网关;
NotificationService.send()继续保持布尔返回兼容,结构化结果通过新增兼容接口提供。 - 不自动迁移、删除或改写 legacy
AGENT_EVENT_ALERT_RULES_JSON。
P5 技术指标规则
P5 在现有 Alert API、Web 告警中心和 src/services/alert_worker.py 评估链路中新增日线技术指标规则。规则仍写入 alert_rules,触发、降级、失败、通知结果和持久化冷却继续复用 P2-P4 的 alert_triggers、alert_notifications 与 alert_cooldowns 语义。
P5 支持的 alert_type 与 parameters:
| alert_type | parameters | 触发语义 |
|---|---|---|
ma_price_cross |
`direction=above | below,window默认20,整数 [2,250]` |
rsi_threshold |
`direction=above | below,period默认12,整数 [2,250],threshold必填且0..100` |
macd_cross |
`direction=bullish_cross | bearish_cross,fast_period=12,slow_period=26,signal_period=9,均为 [2,250]且fast_period < slow_period` |
kdj_cross |
`direction=bullish_cross | bearish_cross,period=9,k_period=3,d_period=3,均为 [2,250]` |
cci_threshold |
`direction=above | below,period默认14,整数 [2,250],threshold` 必填且为有限数值 |
评估规则:
- 首版统一使用日线 close,不做分钟线。
- 边缘触发只比较最近两根已收盘日线;非边缘但当前 level 已满足阈值时仍返回
not_triggered,避免规则创建首日把历史状态误报为新触发。 - 边缘触发包含前一根刚好等于阈值或零轴的情况:
above/bullish_cross使用prev <= threshold < current,below/bearish_cross使用prev >= threshold > current。 - partial bar 只使用服务器本地时区启发式:当前本地时间早于 16:00 时,最后一行日期等于本地今天或日期不可判定都会保守丢弃;不区分 A 股、港股、美股市场时区或交易日历。Issue #1386 P0 的市场阶段基线暂不接入技术指标规则,告警 partial bar 精确判定留到后续阶段。
src/services/alert_indicators.py自行归一化 OHLCV 并计算 MA、RSI、MACD、KDJ、CCI,不依赖 fetcher 预计算的 MA5/MA10/MA20。- RSI 使用 Wilder's EMA / SMMA:
avg_gain = gain.ewm(alpha=1/period, adjust=False).mean(),avg_loss同理,不使用 rolling SMA。 - MACD 使用
EMA(fast_period) - EMA(slow_period)得到 DIF,DEA 为 DIF 的EMA(signal_period);金叉/死叉比较 DIF-DEA 相对 0 的边缘穿越。 - KDJ 使用最近
period日最高/最低价计算 RSV,并用alpha=1/k_period、alpha=1/d_period的 EMA 得到 K/D;金叉/死叉比较 K-D 相对 0 的边缘穿越。 - CCI 使用典型价格
(high + low + close) / 3,按period日均值和平均绝对偏差计算(TP - MA(TP)) / (0.015 * mean_deviation)。 compute_required_bars(alert_type, params)定义最少有效 closed bars:MA=window+1,RSI=period+1,MACD=slow_period+signal_period+1,KDJ=period+k_period+d_period+1,CCI=period+1。- 拉取天数使用
requested_days = min(max(required_bars * 3, required_bars + 30), 365);API 会拒绝required_bars > 365的组合周期,避免创建永久样本不足的规则;同一 worker 轮次按(stock_code, requested_days)缓存日线数据,轮次结束释放。 - 缺数据、缺列或有效样本少于
required_bars写入degraded;数据源异常沿用volume_spike语义返回evaluation_error/failed,不发送通知。
兼容边界:
AGENT_EVENT_ALERT_RULES_JSON仍是 legacy JSON 路径,只支持price_cross、price_change_percent、volume_spike三类规则;P5 技术指标只通过 Alert API / Web 创建。- 不扩展
src/agent/events.py的 legacyAlertType或_RUNTIME_SUPPORTED_ALERT_TYPES。 - P5 创建/更新参数错误沿用现有 Alert API 错误契约:HTTP 400 +
validation_error;unsupported 类型返回 HTTP 400 +unsupported_alert_type。 - Web 告警中心只扩展现有创建表单、列表展示、类型筛选和 dry-run 测试,不新增规则编辑器;dry-run 测试不写触发历史,且 API 响应仍沿用
triggered/not_triggered/evaluation_error三态,worker 写入的degraded状态通过触发历史查看。 - 回滚 P5 PR 后,数据库中已创建的技术指标规则记录会保留;旧代码在 worker 加载阶段遇到 unsupported
alert_type会 skip,不影响 legacy 三类规则继续执行。如需清理,需要维护者确认后手动删除相关alert_rules记录。
P5 不做:
- 不支持 MACD 柱体放大/收缩。
- 不支持 KDJ 超买/超卖区规则。
- 不支持 MA 与 MA 双均线交叉。
- 不支持分钟线、市场日历精确判定或多市场时区精确 partial bar。
- 不支持 legacy
AGENT_EVENT_ALERT_RULES_JSON技术指标规则。 - 不引入 DSL、规则引擎、新数据库表或分析报告 pipeline 内的技术指标规则引擎。
P6 持仓与自选股联动
P6 在现有 Alert API、Web 告警中心和 src/services/alert_worker.py 评估链路中新增 watchlist、portfolio_holdings、portfolio_account 三类目标范围。规则仍写入 alert_rules,触发、降级、失败、通知结果和持久化冷却继续复用 P2-P4 的 alert_triggers、alert_notifications 与 alert_cooldowns 语义,不新增表或迁移。
P6 scope/type 矩阵
target_scope |
target |
允许的 alert_type |
评估方式 |
|---|---|---|---|
single_symbol |
股票代码 | P1 三类价格/成交量规则 + P5 技术指标 | 单规则单标的 |
watchlist |
default |
P1 三类价格/成交量规则 + P5 技术指标 | 每轮刷新并读取当前 STOCK_LIST,按股票代码展开 |
portfolio_holdings |
all 或 active account ID |
P1 三类价格/成交量规则 + P5 技术指标 | 从持仓 snapshot 的非零持仓展开 symbol,按 symbol 去重 |
portfolio_account |
all 或 active account ID |
portfolio_stop_loss、portfolio_concentration、portfolio_drawdown、portfolio_price_stale |
账户级风险评估,不展开为单标的 |
创建/更新规则时,watchlist / portfolio_holdings 不把父级 target 当股票代码校验;portfolio_account 禁止 price/volume/技术指标类型;portfolio_holdings 和 portfolio_account 在 target= 时会校验账户存在且 active,不存在返回 HTTP 400 + validation_error。legacy AGENT_EVENT_ALERT_RULES_JSON 不支持 watchlist、portfolio 或技术指标扩展,继续仅支持 single_symbol 的 price_cross、price_change_percent、volume_spike。
Target Identity Contract
P6 将可展示目标与可持久化目标分离:
| 场景 | effective_target |
display_target |
|---|---|---|
single_symbol |
<symbol> |
<symbol> |
watchlist 展开子目标 |
<symbol> |
自选股 - <symbol> |
portfolio_holdings 展开子目标 |
<symbol> |
持仓 - <symbol> |
portfolio_account target=all |
account:all |
全部账户 |
portfolio_account target= |
account: |
账户 |
alert_triggers.target、alert_cooldowns.target、P4rule_id + target + data_source + data_timestamp去重全部使用effective_target。RuntimeAlertRule.key对展开后的子目标使用{parent_key}|{effective_target},避免 DB cooldown 读取失败时的进程内 fallback 把同一父规则下的不同子目标互相 suppress。display_target不写入alert_triggers.target,仅用于通知标题、dry-runtarget_results和 Web 展示。- P6 不做跨规则同标的通知合并;同一股票若同时命中 watchlist 子规则和独立
single_symbol规则,会按每条规则独立记录和通知。
Dry-run 聚合
POST /api/v1/alerts/rules/{rule_id}/test对批量规则返回聚合字段:evaluated_count、triggered_count、degraded_count、skipped_count、target_results。- 展开目标 soft cap 为 100;dry-run 中超过 soft cap 的目标记为
degraded聚合结果并写日志。worker 运行时只评估前 100 个展开目标并写 warning,不为 overflow 本身写alert_triggers历史。 - dry-run 使用受限并发评估,单目标超时 10 秒,总评估超时 30 秒;未完成目标记为
skipped。 - 任一目标 triggered 时顶层
status=triggered;无触发但存在成功评估、skipped 或 degraded 时顶层status=not_triggered;无法展开或全部失败时才返回evaluation_error。 - 空 watchlist / 空 holdings:dry-run 返回
not_triggered并在target_results中给出record_status=skipped;worker 会写skipped历史。 degraded_count统计全部展开评估结果中record_status=degraded的条目;target_results仅展示前 20 条,排序为 triggered 优先,其次 degraded/failed,再按 target 排序。
持仓风险规则
alert_type |
参数 | 观察值 | 触发语义 |
|---|---|---|---|
portfolio_stop_loss |
`mode=near | breach,默认 near` |
受影响标的最大 loss_pct |
portfolio_concentration |
- | concentration.top_weight_pct |
top_weight_pct >= portfolio_risk_concentration_alert_pct |
portfolio_drawdown |
- | drawdown.max_drawdown_pct |
复用 PortfolioRiskService 的 drawdown.alert;current_drawdown_pct 写 diagnostics |
portfolio_price_stale |
- | stale/missing 价格持仓数量 | 任一 position price_stale=true 或 price_available=false |
portfolio diagnostics 必含 account_id(或 all)、currency、as_of、price_stale、fx_stale、data_available、top_affected_symbols。portfolio_stop_loss、portfolio_concentration、portfolio_drawdown 复用 PortfolioRiskService.get_risk_report();portfolio_price_stale 复用 PortfolioService.get_portfolio_snapshot() 的 position price metadata。
Web 与 cooldown 摘要
- Web 创建表单新增目标范围选择;
watchlist/portfolio_holdings只显示 price/volume/P5 技术指标类型,portfolio_account只显示四类 portfolio 风险类型。 portfolio_holdings/portfolio_account加载账户列表失败时,表单保留all选项并展示错误。- 规则列表上的
cooldown_active对single_symbol和portfolio_account准确;watchlist/portfolio_holdings是父规则摘要,不代表每个子目标的冷却状态,子目标冷却以触发历史和effective_target为准。 - dry-run UI 展示聚合计数和最多 20 条
target_results明细。
P6 不做:
- 不做 P7 Market Light。
- 不做财报日前、分红除权日前提醒;这类规则需要稳定日期契约后另起 follow-up。
- 不做 sector 级集中度告警;P6 集中度使用 symbol 维度
top_weight_pct。 - 不做跨规则同标的通知合并、分钟线、多市场时区精确判定或 legacy JSON 扩展。
阶段感知与公开摘要联动(Refs #1386 P6)
本节描述 #1386 P6 的告警可见性联动,区别于上面的“P6 持仓与自选股联动”。本联动不新增告警表、不做 migration、不自动触发轻量 LLM 分析,只把触发当时可公开的 phase/pack 摘要写入既有触发历史。
AlertTriggerItem保留diagnostics字符串,并新增派生字段market_phase_summary、analysis_context_pack_overview、analysis_visibility_source。- 真实
status=triggered的 worker 记录会在 JSON diagnostics 中合并 sibling keyanalysis_visibility,包含market_phase_summary、analysis_context_pack_overview、source。旧纯文本 diagnostics 保留原文,API 派生字段返回null,source 返回legacy_text。 analysis_visibility_source取值为alert_trigger_market_context、analysis_history_snapshot、evaluator_snapshot、legacy_text或null。- symbol 目标使用
get_market_for_stock(normalize_stock_code(effective_target))构造触发时 phase;target_scope=market直接用normalize_market_region(target),不会把cn|hk|us|jp|kr当作股票代码推断;账户级无法唯一定位市场时允许 summary 落为unknown。 analysis_context_pack_overview只来自 evaluator 已带 overview 或最近 30 天内的历史 snapshot。最近历史查询复用历史服务的代码变体候选,并以 best-effort + 批内短缓存方式执行;缺失或解析失败返回null,不伪造 pack。- 告警通知只输出公开摘要:阶段标签、trigger source、partial-bar warning、数据质量等级和前两条 limitations。通知不得输出 raw context pack、Prompt、新闻正文、完整 diagnostics JSON、webhook URL、token 或持仓敏感细节。
- Web 告警历史展示 phase badge、数据质量等级和 limitations 空态;旧触发记录缺少公开摘要时不影响列表读取。
- #1390 P6 进一步复用
DecisionSignal:股票级真实触发会优先关联同标的 latest active 信号,并把低敏decision_signal_summary写入 diagnostics;无 active 信号时只创建最小source_type=alert/action=alert信号。trace_id=alert-rule-<hash>只用于同源重试的 best-effort 幂等去重,不覆盖 active 信号;新建告警信号不写market_phase,避免同一规则跨阶段重复创建。market、portfolio_account、overflow 或无法解析为具体股票的触发不会创建个股信号。
DecisionSignal 字段、脱敏、迁移与回滚边界见 DecisionSignal 决策信号专题。
#1386 P7 的用户边界:告警联动只解释触发时已经可公开的阶段和数据质量摘要,不会自动发起轻量 LLM 盘中分析,也不会新增告警表、规则类型、环境变量或 migration。需要阶段化分析时,仍应通过分析 API / Web 手动分析入口触发;告警通知只保留阶段标签、trigger source、partial-bar warning、数据质量等级和前两条 limitations。
回滚本联动只需要 revert 对 worker/API/Web 的改动;已有 diagnostics.analysis_visibility 会作为普通 JSON diagnostics 保留,旧代码不会读取该 sibling key。
P7 大盘红绿灯结构化告警
P7 在现有 Alert API、Web 告警中心和 src/services/alert_worker.py 中新增 target_scope=market,消费结构化 MarketLightSnapshot,不解析 Markdown,不扩展 legacy AGENT_EVENT_ALERT_RULES_JSON,不新增表。大盘复盘历史仍写一条 analysis_history(code=MARKET, report_type=market_review);多市场复盘通过 context_snapshot.market_light_snapshots 按 region 保存本次实际复盘的快照 map。
P7 scope/type 矩阵
target_scope |
target |
允许的 alert_type |
参数 | 触发语义 |
|---|---|---|---|---|
market |
cn / hk / us / jp / kr |
market_light_status |
statuses=["red","yellow"],只允许 red/yellow,默认 ["red","yellow"] |
当前 MarketLightSnapshot.status 命中列表时触发 |
market |
cn / hk / us / jp / kr |
market_light_score_drop |
min_drop > 0 |
prev.score - current.score >= min_drop,且 prev.trade_date < current.trade_date |
scope/type 校验是双向约束:target_scope=market 只能使用两类 Market Light 规则;market_light_* 规则也只能使用 target_scope=market。target 会 strip().lower() 后严格限定为 cn|hk|us|jp|kr,非法 target 返回 HTTP 400 + validation_error。
`MarketLightSnapshot` 契约
结构化快照字段为:region、trade_date、status、score、label、temperature_label、reasons、guidance、dimensions、data_quality。trade_date 首版固定取 MarketOverview.date;P7 不解析 provider quote as-of。
dimensions 使用 canonical scorer 单一来源,build_market_light_snapshot()、大盘复盘注入块和告警 service 不重复实现 scoring。_build_market_temperature() 只是 thin wrapper;红绿灯 status 阈值保持 60/40,temperature label 阈值保持 70/55/40。
| dimension | available=true 条件 |
fallback score |
|---|---|---|
breadth |
has_market_stats && (up_count + down_count) > 0 |
50 |
index |
indices 非空且至少一个 change_pct != None |
50 |
limit |
has_market_stats && (limit_up_count + limit_down_count) > 0 |
50 |
data_quality=unavailable 表示 index.available=false,两类 market rule 都返回 skipped 且不触发通知;partial 表示至少一个维度 fallback,ok 表示三项均 available。market_light_status 在 ok/partial 下可触发;partial 触发时 diagnostics 必含 missing_dimensions。market_light_score_drop 直接比较 canonical aggregate score;任一侧 partial 仍允许比较,但 diagnostics 必含 partial_comparison=true 和 missing_dimensions。
基线、交易日与去重
- 大盘复盘持久化必须使用与报告生成共用的同一份
MarketOverview生成MarketLightSnapshot,禁止 persist 阶段二次拉行情。 load_previous_snapshot(region, before_trade_date)扫描analysis_history(code=MARKET, report_type=market_review),跳过缺少context_snapshot.market_light_snapshots[region]的 legacy 记录,先选出小于before_trade_date的最大snapshot.trade_date,再在同一trade_date内按created_at DESC, id DESC取最新 valid 快照;更晚插入的旧交易日 backfill 不会覆盖正确基线。- 若目标
trade_date只有损坏快照,market_light_score_drop返回degraded,不会自动退回更旧交易日做 best-effort 比较。 market_light_score_drop首版只做跨交易日比较;无上一交易日基线或同日基线返回skipped,查询/解析异常返回degraded。- worker 对
target_scope=market做 region 交易日 gate,并尊重TRADING_DAY_CHECK_ENABLED/config.trading_day_check_enabled;检查关闭时允许评估,检查开启且 region 非交易日时返回skipped,不拉取当前快照。 - 触发历史写
target=<region>、observed_value=<score>、data_source=market_light、data_timestamp=,继续复用 P4 的rule_id + target + data_source + data_timestamp去重。
Web 与回滚边界
- Web 告警中心新增
marketscope、region 选择、两类 market rule 参数控件、类型筛选、region 展示和参数展示;API snake_case 映射使用statuses与min_drop。 - legacy
AGENT_EVENT_ALERT_RULES_JSON不支持 market 规则;P7 不更新.env.example,因为没有新增配置项。 - P7 不做指数跌幅、板块异动、涨跌停结构恶化、分钟线、多市场时区精确 quote as-of 解析,也不新增 DSL/规则引擎。
P8 用户配置与部署边界
P8 不新增规则类型、API、表结构或 worker 行为;它把 P0-P7 已合并能力整理成面向用户和部署者的配置说明。告警 worker 只在 schedule 模式注册,核心开关仍是 AGENT_EVENT_MONITOR_ENABLED,轮询间隔仍是 AGENT_EVENT_MONITOR_INTERVAL_MINUTES。通知渠道继续走 alert 路由,详见 通知配置 中的 NOTIFICATION_ALERT_CHANNELS 与 route_type=alert。
本地配置
本地运行 python main.py --schedule、python main.py --serve --schedule 或等价内置调度模式时,设置 AGENT_EVENT_MONITOR_ENABLED=true 后会启动后台告警 worker;AGENT_EVENT_MONITOR_INTERVAL_MINUTES 控制轮询间隔。
规则来源有两类:
- Alert API / Web 告警中心持久化规则:推荐入口,支持
single_symbol、watchlist、portfolio_holdings、portfolio_account、market,覆盖实时价、涨跌幅、成交量、日线技术指标、持仓风险与大盘红绿灯规则。 - legacy
AGENT_EVENT_ALERT_RULES_JSON:只兼容single_symbol的price_cross、price_change_percent、volume_spike三类基础规则;不支持 P5 技术指标、P6 watchlist/portfolio 或 P7 market light。系统不会自动迁移、删除或改写 legacy JSON。
Docker
仓库 docker/Dockerfile 默认命令是 python main.py --schedule,因此容器内只要配置 AGENT_EVENT_MONITOR_ENABLED=true 就会在 schedule 模式中启用告警 worker。Web/API 持久化规则依赖应用数据库;Docker 部署时需要保留 data/ 数据库卷,避免容器重建后丢失规则、触发历史、通知尝试和冷却状态。legacy JSON 仍通过环境变量注入,不是 Docker 专用配置体系。
GitHub Actions
仓库自带 .github/workflows/00-daily-analysis.yml 是一次性分析 workflow,实际调用 python main.py、python main.py --market-review 或 python main.py --no-market-review,不运行 --schedule 后台 alert worker,也没有映射 AGENT_EVENT_* 变量。仅在 repository Secrets / Variables 中新增 AGENT_EVENT_MONITOR_ENABLED 或 AGENT_EVENT_ALERT_RULES_JSON 不会让默认 Actions 开始持续轮询告警。
如需 GitHub Actions 里的告警轮询,需要后续单独 PR 明确 schedule 启动方式、env 映射、规则来源和持久化数据库策略;P8 不改变现有 workflow。
Web 与 Desktop
Web 告警中心 /alerts 是持久化规则的主要入口:可以创建、启停、删除规则,执行一次性 dry-run 测试,查看触发历史、通知尝试和只读冷却状态。批量规则的列表冷却状态是父规则摘要,子目标是否冷却以触发历史中的 target / effective_target 为准。
Desktop 不新增原生告警管理界面;桌面用户复用内置或外部 WebUI 的 /alerts 页面。Desktop 回滚不需要清理额外状态。
状态、通知与回滚
worker 会把 triggered、skipped、degraded、failed 写入 alert_triggers 作为评估历史;正常未触发不写历史。skipped 表示规则本轮没有可评估条件,例如 market 非交易日或缺少上一交易日基线;degraded 表示数据源、持仓快照、历史快照或解析过程出现异常,结果不可用于触发通知。
真实触发后会写入 alert_notifications 和 alert_cooldowns;DB 持久化规则按 rule_id + target + data_source + data_timestamp 对同一数据点做 best-effort 去重。legacy JSON 规则继续只使用进程内 fingerprint,不写持久化冷却。
回滚 P8 只需 revert 文档、配置说明和 Web 文案改动;没有数据库迁移或用户数据清理。回滚早期 Phase 时,已创建的持久化规则不会自动删除,按下方 Phase 回滚说明处理。
Phase 边界
- P0:本文档、契约、存储评估和兼容测试。
- P1:Alert API MVP,首版只覆盖现有三类 runtime 规则。
- P2:告警评估 worker 与 runtime 统一,让持久化 active rules 与 legacy JSON 共存。
- P3:Web 告警中心 MVP。
- P4:触发历史、通知结果与冷却状态。
- P5:技术指标规则。
- P6:持仓与自选股联动。
- P7:大盘红绿灯与市场联动。
- P8:文档、迁移与收口。
P0 不做
- P0 阶段不新增
api/v1/schemas/alerts.py或 Alert API。 - P0 阶段不新增 Web 告警中心页面、路由或侧边栏入口。
- P0 阶段不新增数据库表、repository 或 migration。
- P0 阶段不实现触发历史、通知结果或冷却状态写入。
- P0 阶段不自动迁移、删除或覆盖
AGENT_EVENT_ALERT_RULES_JSON。 - P0 阶段不实现 MACD、KDJ、CCI、RSI、持仓风险或 Market Light 告警规则。
- P0 阶段不重写
NotificationService或通知路由框架。
回滚
- P0 是文档和测试收口。若只回滚 P0,revert 对应 PR 即可;没有数据库、配置或用户数据迁移需要额外处理。
- P1 新增 Alert API 代码和
alert_rules/alert_triggers/alert_notificationsSQLite 表。最小回滚方式是 revert P1 PR;revert 会移除 API、service、repository、schema 和 ORM 定义,但已经由Base.metadata.create_all()创建的 SQLite 表与数据不会自动删除。如需清理,需要维护者在确认不再需要历史数据后手动删除相关表。 - P3 是 Web 和文档改动。最小回滚方式是 revert P3 PR;不会删除已有规则、触发历史或 legacy JSON 配置。
- P4 新增
alert_cooldownsSQLite 表并开始写入alert_notifications。最小回滚方式是 revert P4 PR;已经创建的alert_cooldowns、alert_triggers、alert_notifications数据不会自动删除。如需清理,需要维护者确认后手动删除对应表或记录。 - P5 新增 Alert API/Web 支持的技术指标规则。最小回滚方式是 revert P5 PR;已创建的 P5
alert_rules记录不会自动删除,旧代码会在 worker 加载阶段 skip unsupportedalert_type,不影响 legacy 三类规则执行。如需清理,需要维护者确认后手动删除相关规则记录。 - P6 新增 Alert API/Web 支持的 watchlist、portfolio holdings 与 portfolio account 规则。最小回滚方式是 revert P6 PR;没有新表或迁移,已创建的 P6
alert_rules会保留。回滚前建议 disable/delete 非single_symbol的 P6 规则;否则旧 worker 可能把watchlist/portfolio_holdings的父级target当作股票代码评估并产生 failed/skipped 噪声,portfolio 专用alert_type会在 worker 加载阶段被 skip。 - P7 新增 Alert API/Web 支持的
market规则和大盘复盘market_light_snapshots历史快照。最小回滚方式是 revert P7 PR;没有新表或迁移,已创建的 P7alert_rules会保留。回滚前建议 disable/deletetarget_scope=market规则;旧 worker 会 skip unsupportedmarket_light_*类型或因 scope/type 不识别产生配置噪声。
页面来源提示的视觉验证
运行 cd apps/dsa-web && npx playwright install chromium && npx playwright test --config playwright.fixture.config.ts,使用真实告警页面与拦截 API fixture,无需后台或登录口令。空数据库仍显示 2 条有效环境配置、1 条去重后规则及停用边界;覆盖 1440px 桌面和 390px 窄屏,无横向溢出。截图保存在 apps/dsa-web/test-results/fixtures/,由 web-gate 上传 web-ui-evidence-<head SHA> artifact,不入库。
Analysis Context Pack (`docs/analysis-context-pack.md`)
AnalysisContextPack:P0 盘点、P1/P2 契约、P3 Runtime Consumption、P4 可见性、P5 数据质量、#1386 P6 联动与 #1389 P6 迁移回滚
本页是 Issue #1389 的专题文档,用于记录当前 DSA 分析上下文的真实来源、消费路径、字段状态边界,以及 AnalysisContextPack 内部契约、builder、运行态消费、低敏可见性、数据质量评分、告警/持仓/历史/回测联动、迁移和回滚边界。P0 负责现状盘点和契约边界;P1 只新增内部 schema/envelope、block catalog、类型约定和脱敏序列化;P2 只从 pipeline 已有 artifacts 组装 pack;P3 只把低敏摘要接入普通分析和 Agent 初始 Prompt;P4 只把低敏 overview 接入历史详情、同步分析响应、completed task status 和 Web 报告页;P5 在同一 PACK_VERSION = "1.0" 内补齐数据质量评分、fetch_failed 状态、Prompt 数据限制和 overview 低敏展示;#1386 P6 复用同一公开 overview 做告警、持仓、历史、回测和通知联动,并在手动持仓分析时加入可选辅助 portfolio block;#1389 P6 只补齐文档、配置可见性、迁移和回滚说明,不新增 pack runtime、pack feature flag、DB migration 或 schema 版本。
术语与边界
当前仓库里有多种名为 context / snapshot 的数据面,P0 必须先消歧,避免把现有运行时结构误写成未来 pack。
| 术语 | 当前含义 | 当前主要消费方 | P0 边界 |
|---|---|---|---|
storage.get_analysis_context() |
src/storage.py 中从数据库最近两天 OHLCV 生成的技术面简上下文,包含 today、yesterday、volume_change_ratio、price_change_ratio、ma_status 等。当前实现接收 target_date,但实际仍取最新两天数据。 |
普通分析主链路、Agent 工具 get_analysis_context |
记录为历史技术面输入来源,不把它直接等同于未来 pack。 |
enhanced_context |
普通分析中由 src/core/pipeline.py 基于 DB 简上下文、实时行情、筹码、趋势、基本面和语言信息增强后的 prompt 上下文。 |
src/analyzer.py prompt 渲染、_build_context_snapshot() |
记录当前 prompt 输入面;P0 不改变字段名或结构。 |
analysis_history.context_snapshot |
分析完成后写入历史表的持久化快照。普通分析通常包含 enhanced_context、news_content、realtime_quote_raw、chip_distribution_raw;Agent 路径保存 initial_context。 |
历史详情、同步 analysis/status 响应、回测、部分基本面 fallback 展示 | 记录为持久化消费面;必须保留 context_snapshot.enhanced_context.date 兼容。 |
| Agent executor message context | AgentExecutor._build_user_message() 注入首轮用户消息的上下文,适用于 AGENT_ARCH=single 路径,目前包含股票代码、报告类型、输出语言、realtime_quote、chip_distribution、news_context。 |
单 Agent 首轮 LLM 消息 | 记录当前首轮可见字段;P0 不补 runtime 注入。 |
Agent orchestrator AgentContext |
AgentOrchestrator._build_context() 写入多 Agent 共享上下文,适用于 AGENT_ARCH=multi 路径,可预注入 realtime_quote、daily_history、chip_distribution、trend_result、news_context。 |
Technical / Intel / Risk / Decision 多 Agent 链路 | 记录为 orchestrator 内部共享数据面;不预注入 fundamental_context,trend_result 是否存在取决于 caller 是否传入。 |
P0 范围与非目标
P0 的目标是让后续 P1/P2/P3 可以基于真实仓库边界设计 AnalysisContextPack,而不是提前改造运行时。
- P0 覆盖普通分析、Agent、告警、持仓、回测、历史、通知七条路径的上下文盘点。
- P0 固定字段质量状态词;P1 已新增
AnalysisContextPack内部 schema,但仍不新增 builder、不接入 runtime、不公开完整 pack。 - P0 不新增 builder,不新增配置项,不新增数据库字段,不改变 API、报告、历史或通知 payload。
- P0 不接入 runtime,不改
src/分析、Agent、告警、持仓、回测或通知逻辑。 - P0 不 pack 化
market_review、market_light或大盘红绿灯专题快照;这些只作为历史快照中的其他report_kind/ 专题消费边界记录。 - P0 当时不把
fetch_failed加入字段质量状态词;P5 已在同一 1.0 umbrella 内追加该状态,用于明确区分“不支持”和“本次抓取失败”。 - P0 不在 README 扩写实现细节;本页作为专题文档,由
docs/INDEX.md/docs/INDEX_EN.md入口发现。
P1 内部契约
P1 落地 src/schemas/analysis_context_pack.py,只定义内部 schema/envelope,方便 P2 builder 和 P3 runtime 消费时复用同一结构。P1 不填充运行时数据、不新增 fetcher、不改变 Prompt、不写入 history/task/report metadata,也不把完整 pack 暴露到 API、Web、Bot、Desktop 或通知。
P1 schema 包含:
PACK_VERSION = "1.0",并通过AnalysisContextPack.pack_version标记契约版本。ContextFieldStatus:P1 首版只允许available、missing、not_supported、fallback、stale、estimated、partial;P5 已追加fetch_failed,表示字段或数据块本次抓取明确失败,不代表整次分析失败。AnalysisSubject:顶层身份槽,只包含code、stock_name、market;exchange、currency、industry留给后续扩展,P2 builder 不扩 P1 schema,也不重复新增identityblock。AnalysisContextItem:字段级输入项,包含status、value、source、timestamp、fallback_from、missing_reason、warnings、metadata。AnalysisContextBlock:数据块级分组,包含status、items、source、timestamp、warnings、metadata,其中items是Dict[str, AnalysisContextItem]。DataQuality:P1 只保留warnings与metadata容器;P5 已追加overall_score、level、block_scores、limitations,仍保持低敏,不承载 raw payload。AnalysisContextPack:顶层 envelope,包含pack_version、subject、phase、blocks、data_quality、metadata、created_at。
时间字段约定:
AnalysisContextPack.created_at使用datetime,由model_dump(mode="json")输出 ISO 8601 字符串。AnalysisContextItem.timestamp与AnalysisContextBlock.timestamp使用Optional[str],约定为 ISO 8601 datetime 字符串;P1 schema 在构造时校验该格式,date-only、自然语言时间或斜杠分隔日期会被拒绝;P2 builder 复用现有 artifact 时间戳时不做强制二次转换。
状态语义:
block.status表示整块可用性。item.status表示字段级质量。- P1 不实现
item.status到block.status的自动聚合推导。
P1 Block Catalog:
| block key | P1 语义 | P1 边界 |
|---|---|---|
quote |
实时行情和报价相关输入 | 只定义可表达位置,不抓取或填充数据。 |
daily_bars |
完整日线窗口和最近完整日线日期 | P1 不判断 partial bar。 |
technical |
技术指标、量价结构和形态 | P1 不生成指标。 |
fundamentals |
估值、成长、盈利、财报和股东回报 | P1 不新增基本面 fetcher。 |
news |
新闻、公告、舆情和催化事件输入 | P1 不改变新闻搜索。 |
portfolio |
是否持仓、账户摘要、成本、数量、仓位和 stale 摘要 | P1 不纳入交易流水、现金流水或完整账户隐私数据。 |
chip / capital_flow |
筹码、资金流和主力行为 | 后续扩展键,P1 只允许契约表达。 |
events / market_context |
风险事件、市场宽度、指数、板块和热点环境 | 后续扩展键,不把 market_review / market_light 作为首版单股 pack。 |
phase 字段只接收 #1386 MarketPhaseContext.to_dict() 产物,保持 Dict[str, Any],不重新定义 phase enum 或 phase 子模型。
脱敏边界:
AnalysisContextPack.to_safe_dict()先执行model_dump(mode="json"),再调用redact_sensitive_mapping()。redact_sensitive_mapping()只做 dict/list 的 key-based 递归脱敏,命中api_key、access_token、refresh_token、authorization_header、webhook_url、password、cookie、secret、token、sendkey、license_key等敏感键或短语时把值替换为[REDACTED]。- P1 不扫描普通字符串值,不做 URL 正则脱敏,不把
data_api或裸api/key当作敏感命中,避免把本契约扩展成通用 secrets engine。
P2 Builder 契约
P2 新增 AnalysisContextBuilder,但首版只做 assembler:从普通分析 pipeline 已经拿到的 artifacts 组装内部 AnalysisContextPack。Issue 验收项里的“复用现有数据源”在本 slice 中解释为复用 pipeline 已 fetch 的 realtime_quote、base_context、enhanced_context、trend_result、chip_data、fundamental_context、news_context 等 artifacts;builder 本身 zero-fetch,不调用 DB、fetcher、SearchService、Agent 工具或具体 provider。
P2 输入契约使用 PipelineAnalysisArtifacts:code、stock_name、market、phase、base_context、enhanced_context、realtime_quote、trend_result、chip_data、fundamental_context、news_context、news_result_count、metadata。单股 build() 与批量 build_batch() 复用同一结构,避免 P3 runtime 接入时再次改签名。
P2 block 组装边界:
subject仍只写code、stock_name、market三字段,不扩AnalysisSubject。phase只接收传入的MarketPhaseContext.to_dict()产物,不从enhanced_context反推。quote从realtime_quote组装;缺失为missing;source=fallback或显式fallback_from映射为fallback,但source保留真实成功源;fallback_from只在 artifact/metadata 显式提供时填写,否则只记录稳定 warning code,不伪造 provider 链。quote会透传 #1386 P3 的fetched_at、provider_timestamp、is_stale、stale_seconds、fallback_from。状态优先级固定为STALE > FALLBACK > AVAILABLE:is_stale=True、price_stale、quote_stale、quote_stale_seconds等显式 marker 标为stale;stale_seconds且is_stale=False只是元数据,不单独推断 stale。builder 只映射上游 artifact,不做质量评分。daily_bars只表达完整日线窗口,优先读base_context.today、base_context.yesterday、base_context.date、base_context.data_missing;date-only 放入value或metadata,不写入timestamp。enhanced_context.today上的is_partial_bar、is_estimated、estimated_fields优先进入technical;缺失时仍兼容enhanced_context.today.data_source为realtime:*的旧 heuristic。partial/estimated 只进入technical,daily_bars不承载 partial/estimated,warning 使用intraday_realtime_overlay。technical优先复用trend_result.to_dict();无 trend artifact 时为missing。chip复用chip_data.to_dict();无 chip artifact 默认missing,只有输入 metadata/artifact 明确 not_supported 时才标not_supported。fundamentals只读fundamental_context参数;ok映射为available,not_supported映射为not_supported,partial映射为partial,P5 后failed映射为fetch_failed+ 稳定 reason codefundamental_pipeline_failed;不写入errors[]原文。news非空白字符串为available,空白或缺失为missing;news_result_count写入 pack metadata。
P2 不组装 portfolio、events、market_context,也不把 capital_flow 拆成独立 block;首版只把它保留在 fundamentals 的 coverage/source chain metadata 中。P2 当时也不改变 Prompt、不让普通分析或 Agent runtime 消费 pack、不写入 history/task/report metadata、不暴露完整 pack 到 API/Web/Bot/Desktop/通知;P5 只在现有 builder 上追加低敏评分、fetch_failed 细分和 Prompt 限制,不新增 fetcher。
P3 Runtime Consumption
P3 在 P2 AnalysisContextBuilder 之后接入运行态消费,但消费面限定为低敏 analysis_context_pack_summary。StockAnalysisPipeline 是 summary 的唯一生产者:在普通分析路径和 Agent 路径内完成 PipelineAnalysisArtifacts -> AnalysisContextBuilder.build() -> format_analysis_context_pack_prompt_section(),下游 analyzer、single-agent、multi-agent 只接收 summary 字符串,不自行构造完整 pack,也不读取 AnalysisContextPack.to_safe_dict() 的 block item 原始值。
普通分析 Prompt 的顺序固定为:基础信息 -> #1386 market_phase_context 渲染区块 -> analysis_context_pack_summary -> 技术面、实时行情、新闻等既有区块。analysis_context_pack_summary 只包含 subject、pack_version、block status / source / warnings / missing_reason、metadata.news_result_count、data_quality.warnings 和 P5 低敏数据限制,不得输出 news.content、trend_result、chip、fundamental_context 等原始 payload。
Agent 路径同样只传 summary。AgentExecutor._build_user_message() 在 market phase 段之后、pre-fetched JSON 之前插入 summary;AgentOrchestrator._build_context() 只把 summary 放入 ctx.meta["analysis_context_pack_summary"],禁止写入 ctx.data;BaseAgent._build_messages() 在 market phase user message 之后、_inject_cached_data() 之前插入 summary。Agent 路径会在 _ensure_agent_history() 预取后读取一次 storage.get_analysis_context() 作为 daily_bars 的低敏状态来源,读取失败或无可用上下文时才标记 daily_bars_missing,该读取 fail-open 且不把日线原始 payload 写入 Agent runtime context。Agent 首轮没有复用普通分析新闻检索,news block 为 missing 是当前 P3 的预期状态。
P3 当时不持久化完整 pack,不新增 API/Web/Bot/Desktop 字段,不改变报告 JSON schema,不把 summary 写入 analysis_history.context_snapshot、task status 或 report metadata;history snapshot 和 diagnostic snapshot 会剥离 market_phase_context、analysis_context_pack、analysis_context_pack_summary 等 runtime prompt key。P4 在此基础上新增低敏 overview,可见性只覆盖历史详情、同步分析响应、completed task status 和 Web 报告页;P5 继续复用 summary 消费路径,不改 LLM 输出 JSON schema。Agent 工具级 pack cache 复用仍是后续工作。
#1381 Daily Market Context
#1381 在 AnalysisContextPack 之外新增一个小型每日大盘环境摘要通道,避免把 market_review / market_light 直接 pack 化。DAILY_MARKET_CONTEXT_ENABLED 默认开启;当 MARKET_REVIEW_ENABLED=true 且 DAILY_MARKET_CONTEXT_ENABLED=true 时,StockAnalysisPipeline 会按个股市场(cn / hk / us)加载当日大盘上下文:优先复用 analysis_history(code=MARKET, report_type=market_review) 中同日同市场记录;没有同日记录时才调用 run_market_review(..., return_structured=True, send_notification=False) 生成本次上下文,且通过进程内 cache 避免同一 Pipeline 重复生成,并在 CLI/定时任务并发路径上通过 market review lock 串行化生成。DAILY_MARKET_CONTEXT_ENABLED=false 只关闭个股分析的低敏摘要注入与保守护栏,不关闭大盘复盘本身。
Background:#1381 聚焦单股分析的当日大盘上下文复用与回退控制,不改变现有日内阶段、日报或状态建模架构。该段与 docs/CHANGELOG.md Unreleased 的 #1381 条目保持一致,可作为本轮变更说明的收敛边界。
Scope(本轮实现范围):#1381 仅覆盖后端 runtime 的大盘上下文注入、当日/目标交易日复用控制与保守护栏;不包含独立 API、Web 阶段结果独立展示、四阶段日报结构化持久化或新增日报状态表。涉及主要入口为 main.py(调度与 --no-market-review)、src/core/pipeline.py、src/core/market_review.py、src/services/daily_market_context.py、src/analyzer.py、src/analysis_context_pack_overview.py、src/agent/executor.py、src/agent/orchestrator.py、src/agent/agents/base_agent.py、src/daily_market_context_guardrail.py;Web 侧仅同步 DAILY_MARKET_CONTEXT_ENABLED 设置项文案/帮助,不新增阶段结果展示。
**验收闭环边界:**本 PR 仅对应 #1381 的 runtime 接入与护栏子目标;除非独立 API、Web 阶段展示、四阶段日报持久化和日报状态表已在后续变更中落地并验证,否则不得把 Issue #1381 标记为完整验收通过。
**Acceptance Criteria(验收边界):**本轮仅按 runtime 与配置入口验收,不纳入 PR 过程中的 API/Web UI 独立阶段展示验收。当前验收路径限制为 tests/test_main_schedule_mode.py、tests/test_pipeline_daily_market_context.py、tests/test_daily_market_context.py、tests/test_daily_market_context_guardrail.py、tests/test_agent_executor.py、tests/test_config_env_compat.py、tests/test_config_registry.py 和 apps/dsa-web/tests/system_config_i18n.test.ts。重点覆盖项为:--no-market-review 禁止触发大盘复盘生成、DAILY_MARKET_CONTEXT_ENABLED=false 关闭个股上下文注入但保留大盘复盘、单次 schedule 复用同一 target_date、多市场上下文加载(cn,us)、daily_market_context 只在同一次分析主链路注入一次、普通分析与 Agent 路径应用护栏且不泄漏原始 market_review_payload。
Compatibility/Risk(兼容与风险):#1381 不改变 provider/model/base_url、默认模型或配置清理/回填/迁移语义;不新增数据库或运行时配置表变更。main.py::_bootstrap_environment、src/core/pipeline.py、src/analyzer.py、src/agent/executor.py、src/agent/orchestrator.py、src/agent/agents/base_agent.py、src/services/daily_market_context.py、src/daily_market_context_guardrail.py 只在既有读取链路消费 LLM 与市场复盘上下文,不新增 SystemConfig 保存或回写分支。官方兼容依据沿用 LiteLLM OpenAI-compatible 与 OpenAI Chat Completion(见后文“兼容性证据与核验边界”);回滚方式为常规发布回滚(撤销相关提交),如必要可配合重启并清理 env_file / --env-file / 进程级同名环境覆盖项,恢复用户历史持久化配置。
**兼容性证据与核验边界:**本轮仅复用既有 LLM 配置链路读取配置,不新增 .env 写入分支,不新增配置迁移/清理/回写入口。官方依据沿用:LiteLLM OpenAI-compatible https://docs.litellm.ai/docs/providers/openai_compatible、OpenAI Chat Completion https://platform.openai.com/docs/api-reference/chat/create;版本约束见 requirements.txt(litellm、openai)当前窗口。可回溯代码路径:main.py::_bootstrap_environment、src/analyzer.py::_init_litellm、src/agent/agents/base_agent.py::_get_analyzer_config(仅读取)、src/agent/executor.py、src/agent/orchestrator.py、src/core/pipeline.py、src/services/daily_market_context.py、src/daily_market_context_guardrail.py。回归核验点为 tests/test_config_env_compat.py、tests/test_config_registry.py、tests/test_system_config_service.py、tests/test_system_config_api.py、tests/test_llm_channel_config.py、tests/test_market_review_runtime.py。
普通分析与 Agent 分析只接收低敏字段:daily_market_context(region、trade_date、summary、risk_tags、source、可选 position_cap)和 daily_market_context_summary Prompt 段,不传递完整 market_review_payload、原始新闻、密钥或通知配置。普通分析 Prompt 在市场阶段段落后、技术面数据前插入大盘摘要;Agent 单体与多 Agent 路径在 market phase 后、pre-fetched 数据前插入同一摘要。Agent 自由聊天只在调用方已经提供 daily_market_context / daily_market_context_summary 时注入,不为每次聊天自动触发大盘复盘。
结果后处理新增保守大盘环境护栏:当摘要或标签显示 high_risk、market_cooling、conservative、low_position_cap,且处于保守/高风险语境下时,模型给出 buy 决策(含“立即买入/追高/激进加仓”等买入类建议)会被软化为观望或小仓等待确认,并把高置信度降为中等。该护栏只修改当次 AnalysisResult 与 dashboard 中的低敏限制说明,不新增数据库表或 API 字段。回滚方式为撤销 #1381 相关服务、Prompt 注入和 guardrail 代码,既有大盘复盘历史记录保持兼容。
P4 历史记录、任务状态与 Web 可见性
P4 把 P3 已构建的 AnalysisContextPack 投影为公共低敏 analysis_context_pack_overview。该 overview 由专用 renderer 生成,公共 API 不允许直接返回 AnalysisContextPack.to_safe_dict() 或完整 pack dump。renderer 只输出白名单字段:pack_version、created_at、subject.code / stock_name / market、数据块 key / label / status / source / warnings / missing_reasons、按 block status 计数的 counts、顶层 data_quality.warnings 和 metadata.trigger_source / metadata.news_result_count。P5 在同一 overview 上追加 data_quality 低敏对象,不重复顶层 warnings。
overview 不输出 blocks.*.items、items.value、news.content、trend_result、chip、fundamental_context 原始 payload,也不输出 api_key、token、cookie、webhook_url、password、secret、authorization、sendkey、license_key 等敏感键或值。
P4 持久化面只在 analysis_history.context_snapshot 顶层写入 analysis_context_pack_overview。运行态 prompt 字段仍会从 enhanced_context 和 history snapshot 中剥离:market_phase_context、analysis_context_pack、analysis_context_pack_summary 不进入公开历史详情或任务状态。SAVE_CONTEXT_SNAPSHOT=false 时不持久化整份 analysis_history.context_snapshot,因此也不会落库 overview、market_phase_summary、enhanced_context 或 raw snapshot 字段;旧记录或缺少 overview 的记录继续返回空字段,不影响历史详情读取。
公共 overview block 可追加 timestamp、provider_timestamp、fetched_at:仅透传可解析的 ISO 8601 datetime,保留时区和原始值;不从任意 item 猜测整个 block 的时间,不暴露其他 metadata。timestamp 可能来自抓取时间,不能当作观测时间或新鲜度证明;provider_timestamp 与 fetched_at 分别保留来源观测时间和抓取时间。旧 overview 缺少字段时仍可读取。
公共 API 字段固定为 report.details.analysis_context_pack_overview,Web 端经深度 camelCase 后读取 analysisContextPackOverview。接线面包括:
GET /api/v1/history/{record_id}历史详情。- 同步
POST /api/v1/analysis/analyze返回的AnalysisResultResponse.report.details,但 overview 依赖已持久化的analysis_history.context_snapshot;SAVE_CONTEXT_SNAPSHOT=false时,新记录不保证返回 overview。 - completed
GET /api/v1/analysis/status/{task_id},包括内存队列 enrichment 和 DB completed fallback。
API 返回给 Web 的 details.context_snapshot 会通过 sanitize_context_snapshot_for_api() 剥离顶层 analysis_context_pack_overview,避免 raw snapshot 面板重复展示或被当作完整上下文导出;overview 只从 extract_analysis_context_pack_overview() 单独取出。Agent 路径与普通分析路径写入同一 overview 形状,Agent 无新闻计数时 metadata.news_result_count 可为空。
P4 Web 展示只在报告详情页渲染 AnalysisContextSummary,位置在策略点位和资讯之后、运行诊断之前;该区域默认折叠,折叠头部展示可用数、缺失数、非零的其他状态计数和触发来源,展开后的每个数据块只展示状态、来源、告警和说明。来源为空时显示“未记录输入来源”;说明行把已知缺失原因翻译为原因、对本次分析的影响和简短处理建议,并在括号中保留诊断码,未知原因则按 block 状态提供通用建议。fallback、stale、estimated、partial 等没有缺失原因的降级状态也在同一说明行解释,不新增处理、范围或证据字段。报告页相关资讯由独立接口加载,其有无不能通过 overview 的 metadata.news_result_count 推断;资讯区只说明自身是报告页补充资讯,输入数据块只说明新闻是否进入本次 LLM 分析,避免把两套数据范围混为同一结果集。展开区仍展示状态计数和新闻结果数。P5 后折叠头部还会展示质量分/等级,展开后展示 limitations 和 fetch_failed 状态。无 overview 时不渲染占位。在 #1386 P4b 中,Web 会在同一报告详情页展示 report.meta.market_phase_summary 阶段标签,并继续复用该低敏数据质量摘要;不扩大完整 pack、Prompt summary、raw payload 或 snapshot 内部字段的公开面。P4/P5 不覆盖 pending/processing TaskPanel 的 AnalysisContextPack 数据质量摘要或 SSE 进行中 overview 可见性,不改通知摘要、Bot/Desktop 专属展示或 market_review overview。
P5 数据质量评分与 Prompt 数据限制
P5 在不升级 PACK_VERSION、不新增 fetcher、不新增配置项、不做历史迁移的前提下补齐三件事:内部低敏数据质量评分、跨模型通用的 Prompt 数据限制区块,以及既有 analysis_context_pack_overview 的低敏可见性扩展。#1389 P5 仍不改变 LLM 输出 JSON schema,也不做后处理强制改写;#1386 P5 会消费这里的低敏输入质量,在报告 dashboard.phase_decision 中输出盘中动作字段与质量护栏结果。
状态契约新增 fetch_failed,用于“当前字段或数据块本次抓取明确失败”。首版只在已有 artifact 明确失败时使用,例如 fundamental_context.status == "failed";空新闻、未配置搜索、无实时 quote artifact 或 chip 缺失仍保持既有 missing / not_supported 语义,避免把未启用能力误报成抓取失败。fetch_failed 不代表整次分析失败。
DataQuality 追加以下低敏字段,并保留旧 warnings / metadata:
overall_score: Optional[int]:0-100 总分。level: Optional["good"|"usable"|"limited"|"poor"]:>=85 good、>=70 usable、>=55 limited,否则poor。block_scores: Dict[str, int]:固定六块的状态分。limitations: List[str]:最多 5 条稳定限制说明,使用block: status形式。
评分只计算固定六块,不随辅助块缺失重归一化,未来新增 block 不自动影响总分。权重固定为 quote=25、daily_bars=25、technical=25、news=10、fundamentals=10、chip=5;状态分固定为 available=100、partial=75、estimated=75、not_supported=70、fallback=65、stale=50、missing=35、fetch_failed=25。总分公式为 round(sum(block_score * weight) / 100)。
limitations 优先列出核心块 quote / daily_bars / technical 的 stale、fallback、missing、fetch_failed、partial、estimated;其次列出辅助块 news / fundamentals / chip 的 fetch_failed、fallback、stale。辅助块单纯缺失不进入限制列表,避免把新闻缺失、未配置搜索或不支持能力解释成利好/利空。
Prompt 数据限制只在 format_analysis_context_pack_prompt_section() 内渲染,紧跟 pack summary,因此普通分析、single Agent 和 multi-agent 复用同一消费路径。中文输出 数据限制,英文输出 Data Limitations;只有真实 score 存在时才输出评分行。若 quote、daily_bars 或 technical 为 degraded 状态,Prompt 明确要求最终 JSON 的 confidence_level 不得为 高 / High。Prompt 继续只使用 status/source/warnings/missing_reason/低敏评分,不输出 raw payload、新闻正文、趋势原始值、secret、token 或 webhook。
#1386 P2-full 在 P5 score/limitations 之后、confidence/safety 之前追加最小的 phase × degraded data 交叉约束:当 AnalysisContextPack.phase 来自合法 MarketPhaseContext,且 quote、daily_bars 或 technical 存在 degraded 状态时,Prompt 只补充当前阶段下数据质量如何限制盘中判断、开盘计划或保守分析;它不替代 P5 的 confidence/safety 规则,也不复述 market_phase_context 的 phase-only 文案。pack.phase 缺失、非 dict 或包含非法 phase 时 fail-open,仅保留 P5 通用数据限制。
overview 只扩展现有公开面:analysis_context_pack_overview.data_quality 白名单包含 overall_score、level、block_scores、limitations,不重复公开 warnings。render_analysis_context_pack_overview() 与 extract_analysis_context_pack_overview() / persisted sanitizer 都会清洗该对象;旧 overview 缺少 data_quality 时仍正常读取。details.context_snapshot 继续剥离顶层 analysis_context_pack_overview,不公开完整 pack。
P6 告警、持仓、历史和回测联动
#1386 P6 不新增 pack 版本,也不把完整 pack 暴露到更多公共面。它只复用 P4/P5 已定义的 analysis_context_pack_overview 和 #1386 已定义的 market_phase_summary:
- 告警触发记录仍写入现有
alert_triggers.diagnostics文本字段;当 diagnostics 可 JSON 化时,worker 会合并analysis_visibility.analysis_context_pack_overview,来源只允许 evaluator 已带 overview 或最近 30 天历史 snapshot。旧纯文本 diagnostics 不被覆盖,API 派生字段为空且 source 为legacy_text。 - 持仓手动分析通过 API 构造低敏
portfolio_context并传入 pipeline;builder 会在 pack 中加入可选portfolioblock。该 block 只包含账户 ID/name、symbol、market、currency、quantity、avg cost、total cost、unrealized PnL、price source/provider/date/stale/available 和 cost method,不包含交易流水、现金流水、新闻正文、Prompt、密钥或 webhook。 portfolioblock 是辅助块,metadata={"auxiliary": true, "quality_weighted": false},不改变 P5 固定六块quote、daily_bars、technical、news、fundamentals、chip的权重、总分或 limitations 口径。portfolio_context只在任务执行内部透传;TaskInfo.to_dict()、任务列表、SSEtask_created/task_started/task_completed/task_failed/task_progresspayload 不暴露该对象。- 历史列表、单股历史、StockBar 和回测结果只读取
context_snapshot顶层的公开market_phase_summary;旧记录、SAVE_CONTEXT_SNAPSHOT=false或解析失败返回null/unknown,不失败。 - 回测 phase filter 只基于公开 summary 做 bucket:
premarket保持 premarket,intraday|lunch_break|closing_auction归入 intraday,postmarket保持 postmarket,non_trading|missing|invalid归入 unknown。带 phase 过滤时 repository 先按 SQL 条件批量读取结果和 snapshot,服务层 bucket 后再分页和统计,避免 API 层分页后临时过滤。 - 通知摘要只消费
market_phase_summary与analysis_context_pack_overview.data_quality,输出阶段、trigger source、partial-bar warning、质量等级和前两条 limitations;不输出 raw pack、analysis_context_pack_summaryPrompt 字符串、新闻正文或持仓敏感细节。
P6 文档、迁移与回滚
P6 不改变 P1-P5 的运行时行为,只把已经落地的契约、可见性、配置、迁移和回滚边界写成稳定文档。它不新增 pack enable/disable feature flag,不升级 PACK_VERSION = "1.0",不新增 API 参数,不改变报告 JSON schema,也不做数据库迁移。
四个数据面必须分开理解:
| 数据面 | 位置 | 可见性 | P6 边界 |
|---|---|---|---|
| 内部完整 pack | AnalysisContextPack / AnalysisContextBuilder 产物 |
仅内部运行态使用 | 不作为公共 API,不写入历史,不承诺外部稳定 wire contract。 |
| LLM 低敏摘要 | analysis_context_pack_summary |
普通分析、single Agent、multi-agent Prompt | 只包含 subject、pack version、block status/source/warnings/missing reason、新闻结果数和数据限制;不包含 items.value、新闻正文、趋势/筹码/基本面 raw payload、secret、token 或 webhook。 |
| 公共低敏 overview | report.details.analysis_context_pack_overview |
历史详情、同步分析响应、completed task status、Web 报告页 | 只输出白名单字段和 data_quality 低敏评分;不输出完整 pack、Prompt summary 或 raw payload。 |
| 历史上下文快照 | analysis_history.context_snapshot |
持久化后供历史/API/Web/诊断读取 | details.context_snapshot 经 sanitize_context_snapshot_for_api() 剥离 analysis_context_pack_overview 和 market_phase_summary,避免 raw 面板重复公开稳定摘要。 |
摘要可见性矩阵:
| 消费面 | 暴露内容 | 不暴露内容 |
|---|---|---|
| LLM Prompt | analysis_context_pack_summary 低敏状态摘要和数据限制 |
完整 pack、items.value、新闻正文、趋势/筹码/基本面 raw payload、secret/token/webhook |
GET /api/v1/history/{record_id} |
report.details.analysis_context_pack_overview |
完整 pack、Prompt summary、raw analysis_context_pack_overview duplicate |
同步 POST /api/v1/analysis/analyze |
report.details.analysis_context_pack_overview,前提是本次历史已持久化 analysis_history.context_snapshot |
完整 pack、Prompt summary |
completed GET /api/v1/analysis/status/{task_id} |
status.result.report.details.analysis_context_pack_overview |
完整 pack、Prompt summary |
| Web 报告页 | 默认折叠的 AnalysisContextSummary,展示 block 状态、来源、告警、包含影响/建议/诊断码的说明、质量分和限制 |
完整 pack、raw payload、Prompt summary、报告页独立加载的资讯结果状态 |
raw details.context_snapshot |
剥离后的历史快照 | 顶层 analysis_context_pack_overview、market_phase_summary |
| 通知、Bot、Desktop 专属展示 | P6 不新增专属展示 | 完整 pack、Prompt summary、raw payload |
字段质量状态全集保持为 available、missing、not_supported、fallback、stale、estimated、partial、fetch_failed。这些状态解释输入数据质量,不表示分析任务、告警、回测或通知投递本身成功或失败。
脱敏边界:
- 完整
AnalysisContextPack不进入公共 API、Web、通知、Bot 或 Desktop 专属展示。 AnalysisContextPack.to_safe_dict()只作为内部安全序列化 helper;公共 overview 仍必须通过render_analysis_context_pack_overview()投影。analysis_context_pack_summary与 overview 都不得输出items.value、新闻正文、trend_result、chip、fundamental_context原始 payload、API key、token、cookie、完整 webhook URL、邮箱密码、secret、authorization、sendkey 或 license key。- 已持久化 overview 再读取时必须经过
extract_analysis_context_pack_overview()/ persisted sanitizer;API 透明度面板必须继续通过sanitize_context_snapshot_for_api()剥离顶层稳定摘要。
迁移边界:
- P6 不做 DB migration;旧历史记录缺少
analysis_context_pack_overview或data_quality时返回空字段,报告仍正常读取。 SAVE_CONTEXT_SNAPSHOT=true是默认行为,会继续把analysis_history.context_snapshot作为历史透明度和诊断来源持久化。SAVE_CONTEXT_SNAPSHOT=false或 CLI--no-context-snapshot会停止持久化整份analysis_history.context_snapshot;换言之,新历史不持久化整份analysis_history.context_snapshot,包括enhanced_context、market_phase_summary、analysis_context_pack_overview、diagnostics、realtime_quote_raw和其他 raw snapshot 字段。- 关闭持久化不影响当次
AnalysisContextPack构建、analysis_context_pack_summary注入 Prompt,也不影响内存中的result.diagnostic_context_snapshot。
回滚方式:
| 手段 | 作用 | 不能做什么 |
|---|---|---|
| 发布或代码回滚 P3-P5 相关改动 | 移除 pack prompt summary、overview 和数据质量接入 | - |
SAVE_CONTEXT_SNAPSHOT=false 或 --no-context-snapshot |
停止保存新的历史 context_snapshot,从而不再从新历史公开 overview / phase summary / raw snapshot |
不能关闭当次 pack 构建或 LLM Prompt 中的低敏 summary |
| 运行时 pack 总开关 | 当前不存在 | 不能通过 env 一键关闭 P3-P5 pack 接入;需要代码回滚或后续单独设计 |
字段质量状态
未来 pack 的字段质量状态在 P0 先固定七词;P5 在同一 1.0 umbrella 内追加 fetch_failed。它们描述字段或数据块的质量,不描述业务流程是否成功。
| 状态 | 含义 | 示例边界 |
|---|---|---|
available |
字段存在,来源和时间戳可解释,当前路径可正常使用。 | 实时行情返回价格和来源;历史 K 线窗口满足计算需求。 |
missing |
当前路径需要该字段,但实际未取到或为空。 | DB 无最近日线,普通分析进入 data_missing 结果。 |
not_supported |
当前市场、数据源或路径不支持该字段,不应误报为错误。 | 某些市场无筹码分布或资金流。 |
fallback |
首选来源不可用,使用了备用来源或旧路径。 | 持仓价格从实时行情 fallback 到历史收盘价。 |
stale |
字段存在,但时间新鲜度不足。 | 持仓估值中的 price_stale / fx_stale。 |
estimated |
字段是估算值,不应当作完整事实。 | 盘中用实时价补今日 bar 后生成技术估计。 |
partial |
数据块部分可用、部分缺失。 | 大盘红绿灯 data_quality=partial 或工具返回 partial_cache。 |
fetch_failed |
当前路径确认尝试过抓取,但本次抓取失败。 | fundamental_context.status == "failed" 映射为基本面 block 抓取失败。 |
现有状态映射
当前仓库已有不少状态词。P0 只建立映射或不映射关系,避免后续把业务结果状态混入字段质量枚举。
| 现有词或字段 | 当前位置 | 建议关系 | 说明 |
|---|---|---|---|
data_missing |
普通分析缺历史数据结果 | 可映射到 missing |
这是核心输入缺失,不是业务成功状态。 |
cache_hit / partial_cache |
Agent 历史数据工具 | partial_cache 可映射到 partial |
cache_hit 是来源/缓存元数据,不是质量状态。 |
source / data_source / realtime_source |
数据源、告警、上下文快照 | 不映射 | 这些是来源元数据,应与字段质量状态并列保存。 |
price_source=missing |
持仓快照 | 可映射到 missing |
表示估值价格不可用。 |
price_stale / fx_stale |
持仓快照 | 可映射到 stale |
保留原字段作为业务元数据。 |
triggered / skipped / degraded / failed |
告警评估与记录 | 不映射 | 这是规则评估或记录结果,不是字段级质量状态。 |
insufficient_data / completed / error |
回测服务 | 不映射 | 这是回测执行状态;可在 pack 摘要中解释触发原因。 |
sent / no_channel / partial_failed / all_failed |
通知发送 | 不映射 | 这是通知投递结果,不能反推分析输入质量。 |
data_quality=ok/partial/unavailable |
大盘红绿灯 | partial 可映射,unavailable 视字段场景映射到 missing 或 not_supported |
P0 不把大盘红绿灯纳入首版单股 pack。 |
fetch_failed |
数据质量细分 | P5 映射为 fetch_failed |
只在已有 artifact 明确失败时使用,不代表整次分析失败。 |
七路径盘点
普通分析
普通分析主链路在 src/core/pipeline.py 中组装输入:先读取 storage.get_analysis_context(),再按可用性补充实时行情、筹码、趋势分析、新闻、基本面和报告语言,最后交给 src/analyzer.py 渲染 prompt。当前重复点主要是实时字段同时存在于 enhanced_context.realtime、realtime_quote_raw 和报告 meta;命名上存在 source、data_source、realtime_source 等多种来源字段。
首版 pack 可从普通分析路径抽取单股核心身份、行情、日线、技术、新闻、基本面和数据质量摘要;P0 不改变 _enhance_context()、_build_context_snapshot() 或 analyzer prompt。
Agent
Agent 有三层需要分开记录的数据面。src/core/pipeline.py 的 Agent 路径会构造 initial_context,固定包含 fundamental_context,并在可用时加入 trend_result,最终作为 Agent 路径的 context_snapshot 持久化。AgentExecutor._build_user_message() 只适用于 AGENT_ARCH=single,首轮消息只显式注入 realtime_quote、chip_distribution、news_context 等已取上下文,不显式注入 fundamental_context 或 trend_result。AgentOrchestrator._build_context() 适用于 AGENT_ARCH=multi,可预注入 realtime_quote、daily_history、chip_distribution、trend_result、news_context,这些进入 AgentContext 的字段会作为 pre-fetched data 注入 stage agent 消息;但 orchestrator 不预注入 fundamental_context。trend_result 不是天然存在,取决于 caller 是否传入。
Agent 工具还会独立调用 get_realtime_quote、get_daily_history、get_chip_distribution、get_analysis_context、get_stock_info 等工具,容易与普通分析前置获取产生重复请求。当前 pack 生成只在 Agent 历史预取后复用 storage.get_analysis_context() 的日线可用性状态,不复用或暴露完整工具级 pack cache;P5 再决定是否做更深的数据质量评分与工具缓存复用。
告警
告警链路在 src/services/alert_worker.py 中评估规则、记录触发历史并分发通知,具体字段语义见 实时告警中心。告警状态如 triggered、skipped、degraded、failed 是规则评估或记录状态,不能直接写入字段质量枚举。
首版 pack 不把告警规则评估作为输入数据块;告警后续只消费 pack 的字段质量摘要,例如核心行情是否 fallback、是否 stale、是否 partial。
持仓
持仓快照在 src/services/portfolio_service.py 中聚合账户、仓位、成本、价格、汇率和风险输入,API 输出结构在 api/v1/schemas/portfolio.py。当前已有 price_source、price_provider、price_date、price_stale、price_available、fx_stale 等字段。
首版 pack 可记录“是否持仓、账户摘要、成本、数量、仓位、浮盈浮亏、价格/汇率 stale 摘要”,但不纳入交易流水、现金流水、公司行动或完整账户隐私数据。
回测
回测服务在 src/services/backtest_service.py 和 src/repositories/backtest_repo.py 中消费历史分析记录与日线数据。现有 parse_analysis_date_from_snapshot() 依赖 analysis_history.context_snapshot.enhanced_context.date 解析分析日期。
P0 必须把 enhanced_context.date 标为兼容边界。后续 pack 可以新增更清晰的日期字段,但不能无迁移地删除或改名当前历史快照中的日期位置。
历史
历史详情在 src/services/history_service.py、api/v1/endpoints/history.py、api/v1/schemas/history.py 中返回 raw_result、news_content、context_snapshot 等字段。同步 analysis/status 响应也会在 api/v1/endpoints/analysis.py 中读取 context_snapshot.enhanced_context、realtime_quote_raw 和基本面 fallback。
P0 只记录历史消费面。完整 pack 不应默认公开到历史详情或公共 API;后续 P4 如需展示,应优先暴露摘要、来源和降级说明。
通知
通知链路在 src/notification.py 中消费 AnalysisResult、dashboard、market snapshot、data_sources 等输出,并记录 sent、no_channel、partial_failed、all_failed 等投递状态;渠道配置与边界见 通知能力基线。
通知不是事实数据层,不能把投递失败误写成输入质量失败。后续只应在必要时消费 pack 摘要,例如“实时行情已降级”“基本面缺失”“新闻源不足”。
源码锚点
| 域 | 锚点 |
|---|---|
| 普通分析 | src/core/pipeline.py, src/storage.py, src/analyzer.py |
| Agent | src/agent/orchestrator.py, src/agent/executor.py, src/agent/tools/data_tools.py |
| 告警 | src/services/alert_worker.py, docs/alerts.md |
| 持仓 | src/services/portfolio_service.py, api/v1/schemas/portfolio.py |
| 回测 | src/services/backtest_service.py, src/repositories/backtest_repo.py |
| 历史 | src/services/history_service.py, api/v1/endpoints/history.py, api/v1/endpoints/analysis.py, api/v1/schemas/history.py |
| 通知 | src/notification.py, docs/notifications.md |
兼容与安全边界
analysis_history.context_snapshot.enhanced_context.date是当前回测日期解析兼容点,P1/P2 不能在没有迁移的情况下破坏。- 完整 pack 不默认公开到历史、API、Web 或通知;P4/P5 只公开
analysis_context_pack_overview低敏摘要、来源、fallback、stale、missing reason、block status count 和data_quality低敏评分。 - pack、日志、历史快照和 API 响应不得记录 API key、token、cookie、完整 webhook URL、邮箱密码、私有环境变量或其他密钥。
source、timestamp、fallback、stale、partial等质量元数据只用于解释输入限制,不用于阻断分析;除非现有核心路径本来就是 fail-fast。- #1386 的盘前 / 盘中 phase 感知是后续
phase/data_quality字段的重要背景;P0 只记录关系,不接入 runtime。
Beginner Client Setup (`docs/beginner-client-setup.md`)
小白客户端安装与配置指南
这份文档写给不会代码、只想下载客户端直接用的用户。目标很简单:下载客户端,填一个模型服务密钥(Key),填股票代码,然后生成第一份分析报告。
本项目生成的是辅助分析报告,不构成投资建议。真实交易请自行判断风险。
先准备
- Windows 或 macOS 电脑。
- 一个模型服务密钥(Key),推荐从下面任选一个:
- Anspire Open:支持全球主流模型,一个 Key 可同时用于模型和新闻搜索,第一次配置最省事。
- AIHubMix:支持全球主流模型,适合想在一个平台切换多种模型的用户。
- 想分析的股票代码,例如
600519,hk00700,AAPL。
1. 下载客户端
打开发布页:
https://github.com/ZhuLinsen/daily_stock_analysis/releases/latest
在页面下方 Assets(附件)里下载:
| 电脑 | 下载哪个 |
|---|---|
| Windows | daily-stock-analysis-windows-installer-<版本号>.exe |
| Windows 不想安装 | daily-stock-analysis-windows-noinstall-<版本号>.zip |
| macOS Apple 芯片 | daily-stock-analysis-macos-arm64-<版本号>.dmg |
| macOS Intel 芯片 | daily-stock-analysis-macos-x64-<版本号>.dmg |
不用下载 latest.yml、*.blockmap,它们不是客户端安装包。
不知道 Mac 是哪种芯片:点击左上角苹果图标 -> 关于本机,看到 M1/M2/M3/M4 就选 arm64,看到 Intel 就选 x64。
2. 安装并打开
- Windows 安装包:双击
.exe,按提示安装,安装目录用默认位置即可。 - Windows 免安装包:解压
.zip,双击Daily Stock Analysis.exe。 - macOS:双击
.dmg,把应用拖到Applications。当前 DMG 未经 Apple Developer 签名和公证,Gatekeeper 仍可能阻止启动;仅对 GitHub Releases 官方附件尝试在“隐私与安全性”中允许打开,完整限制与排查方式见docs/desktop-package.md。
macOS 用户升级前建议先在客户端设置里导出一次配置备份。
3. 配置 AI 模型
打开客户端,进入:
系统设置 -> AI 模型
只选下面一个方案即可。
重要:每次改完设置后,都要点击页面上的保存按钮;看到保存成功提示后,再切换页面或回到首页。
方案 A:Anspire Open
- 打开 Anspire Open,注册 / 登录后创建 API Key。
- 回到客户端,在快速添加渠道里选择
Anspire Open。 - 粘贴 API Key。
- 模型名选择控制台里已开通的模型;不确定就先选控制台推荐或轻量模型。
- 点击保存;看到保存成功后,再点击测试连接。
方案 B:AIHubMix
- 打开 AIHubMix,注册 / 登录后创建 API Key。
- 回到客户端,在快速添加渠道里选择
AIHubmix(聚合平台)。 - 粘贴 API Key。
- 模型名选择控制台里已开通的模型;不确定就先选控制台推荐模型。
- 点击保存;看到保存成功后,再点击测试连接。
看到测试成功,就继续下一步。
4. 填写自选股
进入:
系统设置 -> 基础设置
找到 自选股列表,填写:
600519,hk00700,AAPL
多个股票用英文逗号隔开。常见写法:
- A 股:
600519、300750、000001 - 港股:
hk00700、hk09988 - 美股:
AAPL、TSLA、NVDA
填完点击保存,看到保存成功后再回首页。
5. 建议配置新闻源
新闻源不是必填,但建议配置。它会影响近期新闻、公告、事件驱动、热点题材和风险提示。
进入:
系统设置 -> 数据源
按你的模型服务选择:
- 用 Anspire Open:找到
Anspire API Keys,填入同一个 Anspire Key,保存成功后即可。 - 用 AIHubMix:建议再申请 SerpAPI 或 Tavily 的 Key,填到
SerpAPI API Keys或Tavily API Keys,保存成功后即可。
想先试用也可以跳过新闻源,客户端仍然能生成基础分析。
6. 开始分析
回到首页:
- 输入股票代码,例如
600519。 - 点击分析。
- 等任务从排队、分析中变成分析完成。
- 在历史记录里查看报告。
常见问题
下载页面里文件很多,该下哪个?
普通 Windows 用户下载 .exe 安装包。不要下载 latest.yml 或 *.blockmap。
API Key 填了还是不能用?
检查这几项:
- Key 是否复制完整,没有多余空格。
- 平台账号是否有余额或额度。
- 当前模型是否已开通。
- 测试连接里是否提示模型不存在、权限不足或余额不足。
配置乱了怎么办?
在客户端设置里导出配置备份。出问题时可以导入之前的备份,或者只保留这三项重新配置:AI 模型、自选股、新闻源。
Bot Command (`docs/bot-command.md`)
一、整体设计
flowchart TB
subgraph Platforms [外部平台]
FS[飞书]
DT[钉钉]
WC[企业微信(开发中)]
TG[Telegram(开发中)]
More[更多平台...]
end
subgraph BotModule [bot/ 模块]
WH[Webhook Server]
Adapters[平台适配器]
Dispatcher[命令分发器]
Commands[命令处理器]
end
subgraph Core [现有核心模块]
AS[AnalysisService]
MA[MarketAnalyzer]
NS[NotificationService]
end
FS -->|POST /bot/feishu| WH
DT -->|POST /bot/dingtalk| WH
WC -->|POST /bot/wecom| WH
TG -->|POST /bot/telegram| WH
WH --> Adapters
Adapters -->|统一消息格式| Dispatcher
Dispatcher --> Commands
Commands --> AS
Commands --> MA
Commands --> NS二、目录结构
在项目根目录新建 bot/ 目录:
bot/
├── __init__.py # 模块入口,导出主要类
├── models.py # 统一的消息/响应模型
├── dispatcher.py # 命令分发器(核心)
├── commands/ # 命令处理器
│ ├── __init__.py
│ ├── base.py # 命令抽象基类
│ ├── analyze.py # /analyze 股票/指数分析
│ ├── market.py # /market 大盘复盘
│ ├── help.py # /help 帮助信息
│ └── status.py # /status 系统状态
└── platforms/ # 平台适配器
├── __init__.py
├── base.py # 平台抽象基类
├── feishu.py # 飞书机器人
├── dingtalk.py # 钉钉机器人
├── dingtalk_stream.py # 钉钉机器人Stream
├── wecom.py # 企业微信机器人 (开发中)
└── telegram.py # Telegram 机器人 (开发中)三、核心抽象设计
3.1 统一消息模型 (`bot/models.py`)
@dataclass
class BotMessage:
"""统一的机器人消息模型"""
platform: str # 平台标识: feishu/dingtalk/wecom/telegram
user_id: str # 发送者 ID
user_name: str # 发送者名称
chat_id: str # 会话 ID(群聊或私聊)
chat_type: str # 会话类型: group/private
content: str # 消息文本内容
raw_data: Dict # 原始请求数据(平台特定)
timestamp: datetime # 消息时间
mentioned: bool = False # 是否@了机器人
@dataclass
class BotResponse:
"""统一的机器人响应模型"""
text: str # 回复文本
markdown: bool = False # 是否为 Markdown
at_user: bool = True # 是否@发送者3.2 平台适配器基类 (`bot/platforms/base.py`)
class BotPlatform(ABC):
"""平台适配器抽象基类"""
@property
@abstractmethod
def platform_name(self) -> str:
"""平台标识名称"""
pass
@abstractmethod
def verify_request(self, headers: Dict, body: bytes) -> bool:
"""验证请求签名(安全校验)"""
pass
@abstractmethod
def parse_message(self, data: Dict) -> Optional[BotMessage]:
"""解析平台消息为统一格式"""
pass
@abstractmethod
def format_response(self, response: BotResponse) -> Dict:
"""将统一响应转换为平台格式"""
pass3.3 命令基类 (`bot/commands/base.py`)
class BotCommand(ABC):
"""命令处理器抽象基类"""
@property
@abstractmethod
def name(self) -> str:
"""命令名称 (如 'analyze')"""
pass
@property
@abstractmethod
def aliases(self) -> List[str]:
"""命令别名 (如 ['a', '分析'])"""
pass
@property
@abstractmethod
def description(self) -> str:
"""命令描述"""
pass
@property
@abstractmethod
def usage(self) -> str:
"""使用说明"""
pass
@abstractmethod
async def execute(self, message: BotMessage, args: List[str]) -> BotResponse:
"""执行命令"""
pass3.4 命令分发器 (`bot/dispatcher.py`)
class CommandDispatcher:
"""命令分发器 - 单例模式"""
def __init__(self):
self._commands: Dict[str, BotCommand] = {}
self._aliases: Dict[str, str] = {}
def register(self, command: BotCommand) -> None:
"""注册命令"""
self._commands[command.name] = command
for alias in command.aliases:
self._aliases[alias] = command.name
def dispatch(self, message: BotMessage) -> BotResponse:
"""分发消息到对应命令"""
# 1. 解析命令和参数
# 2. 查找命令处理器
# 3. 执行并返回响应四、已支持的命令
| 命令 | 别名 | 说明 | 示例 |
|------|------|------|------|
| /analyze | /a, 分析 | 分析指定股票或已登记指数 | /analyze 600519、/analyze sh000016、/analyze 上证50 |
| /market | /m, 大盘 | 大盘复盘 | /market |
| /batch | /b, 批量 | 批量分析自选股 | /batch |
| /help | /h, 帮助 | 显示帮助信息 | /help |
| /status | /s, 状态 | 系统状态 | /status |
`/analyze` 支持边界
- 股票:A 股 6 位数字(
600519)、港股HK+5 位数字(hk00700)、美股 1-5 个字母(AAPL、usfd→USFD)。股票名称(贵州茅台)由本次 Bot 入口新暴露:复用既有名称解析器(resolve_name_to_code)解析后提交 legacy code,不携带结构化 target。 - 已登记指数:显式代码(
sh000016)、CSI alias(930955.CSI收敛为csi930955)、注册中文名(上证50)均可提交;指数以结构化AnalysisTarget贯穿到分析 Pipeline,sh000016不会被改写为SH000016。 - 未登记 CSI(如
930956.CSI)、旧闸门拒绝的代码形态(如12345、裸00700、600519.SH、未登记sh999999)或无法识别的名称:返回明确错误,不提交任务。 - 注册表内存在等价名称歧义时:要求改用显式代码,不猜测、不进入股票名称兜底。
Bot 指数入口在线 E2E smoke(`scripts/smoke_bot_index_entry.py`)
用于从真实 CommandDispatcher.dispatch_async -> AnalyzeCommand -> TaskService -> StockAnalysisPipeline 在线贯穿单标的分析,覆盖 SH.000016 / 上证50 / 930955.CSI 三条矩阵场景,不经过任何 webhook transport(不 mock 在线依赖、不 dry-run)。
运行(单 target 参数,--timeout 默认 900 秒):
.venv\Scripts\python.exe scripts/smoke_bot_index_entry.py "SH.000016"
.venv\Scripts\python.exe scripts/smoke_bot_index_entry.py "上证50"
.venv\Scripts\python.exe scripts/smoke_bot_index_entry.py "930955.CSI"进程职责:worker 独立子进程提交并轮询(同进程 TaskService),输出单行 E2E_EVENT {json} 事件(phase=submitted|completed|failed|timeout,含 target,按阶段附加 task_id/stock_code/result/error);父进程只负责 deadline、进程树清理和退出码(0=成功 / 1=失败 / 124=超时)。
失败语义:任务失败、completed 但结果不完整(canonical code 或注册名称与矩阵期望不符,或 analysis_summary/operation_advice/trend_prediction 任一为空或仅空白)即输出 failed 事件并不为零退出;超时清理进程树(Windows taskkill /T /F,POSIX 杀进程组),清理成功输出 timeout 事件并退出 124,清理失败则输出含清理错误的 failed 事件并退出 1,不回滚 DB / 报告 / 通知等既有副作用。用户 Ctrl-C 中止时父进程同样先清理进程树:清理成功透传中断,清理失败输出含清理错误的 failed 事件并退出 1,绝不静默吞掉清理失败。worker 的期望 code/name 来自脚本内置的矩阵权威映射(SH.000016/上证50 → sh000016/上证50,930955.CSI → csi930955/红利低波100),不信任响应或任务结果自报身份:提交响应的 stock_code 与期望不符时输出含期望值与实际值的显式 mismatch 错误(failed 事件同时携带结构化 stock_code 实际值);矩阵之外的 target 与非正 --timeout 在提交与子进程 spawn 之前即被拒绝(参数/输入错误,退出码 2)。worker 意外异常(dispatch/轮询/事件序列化)输出 failed 事件并退出 1(异常证据保留在 stderr),KeyboardInterrupt 不按普通失败处理;父进程将 worker 的任意其他退出码归一化为 1,运行时契约只暴露 0 / 1 / 124。父进程以内部 --worker flag 显式拉起子进程,不依赖环境变量。
前置条件:需要网络与数据源/AI 凭据配置齐全(在线链路)。本脚本不属于离线 gate,不进入 CI。
五、`/status` 与模型配置诊断说明
可配置层级与可用性判断依据
/status显示的 LLM 可用性遵循系统统一运行时优先级:LITELLM_CONFIG(LiteLLM YAML)LLM_CHANNELS- legacy provider 键(
GEMINI_API_KEY/OPENAI_API_KEY/ANTHROPIC_API_KEY/DEEPSEEK_API_KEY)
- 当主模型(
LITELLM_MODEL或AGENT_LITELLM_MODEL)在当前激活层无可用来源时,会展示“AI 服务未配置”,并保留用户可见原因行。 - 本仓库
requirements.txt的运行时依赖约束为litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0,该约束内本链路以现有兼容行为为准。 - 该诊断规则与
GET /api/v1/system/config/setup/status的 LLM 检查保持一致:LITELLM_CONFIG/LLM_CHANNELS为高优先级;模式切换时不会做静默迁移,切回旧模式由用户显式恢复历史值或回滚。
回退与迁移边界
LITELLM_CONFIG与LLM_CHANNELS任一生效时,下层 legacy 配置会被该层忽略(不会继续作为本次调用来源)。- 诊断增强不进行 silent migration:不会主动清空/删除
GEMINI_*、OPENAI_*、ANTHROPIC_*、LITELLM_*的历史值,仅在可用性诊断上提示。
官方兼容来源(用于排障核对)
- LiteLLM 官网:https://docs.litellm.ai/
- LiteLLM OpenAI Compatible 说明:https://docs.litellm.ai/docs/providers/openai_compatible
- OpenAI Chat API:https://platform.openai.com/docs/api-reference/chat
- DeepSeek API 文档:https://api-docs.deepseek.com/
- Kimi Moonshot 兼容说明:https://platform.moonshot.ai/docs/guide/compatibility
- Gemini OpenAI 兼容说明:https://ai.google.dev/gemini-api/docs/openai
- Ollama API 文档:https://github.com/ollama/ollama/blob/main/docs/api.md
六、Webhook 路由
在 api/v1/router.py 中注册路由:
# Webhook 路由
/bot/feishu # POST - 飞书事件回调
/bot/dingtalk # POST - 钉钉事件回调
/bot/wecom # POST - 企业微信事件回调 (开发中)
/bot/telegram # POST - Telegram 更新回调 (开发中)配置
在 config.py 中新增机器人配置:
# === 机器人配置 ===
bot_enabled: bool = False # 是否启用机器人
bot_command_prefix: str = "/" # 命令前缀
# 飞书机器人(事件订阅)
feishu_app_id: str # 已有
feishu_app_secret: str # 已有
feishu_verification_token: str # 新增:事件校验 Token
feishu_encrypt_key: str # 新增:加密密钥
# 钉钉机器人(应用)
dingtalk_app_key: str # 新增
dingtalk_app_secret: str # 新增
# 企业微信机器人(开发中)
wecom_token: str # 新增:回调 Token
wecom_encoding_aes_key: str # 新增:EncodingAESKey
# Telegram 机器人(开发中)
telegram_bot_token: str # 已有
telegram_webhook_secret: str # 新增:Webhook 密钥扩展说明
怎样新增一个通知平台
- 在
bot/platforms/创建新文件 - 继承
BotPlatform基类 - 实现
verify_request,parse_message,format_response - 在路由中注册 Webhook 端点
怎样新增新增命令
- 在
bot/commands/创建新文件 - 继承
BotCommand基类 - 实现
execute方法 - 在分发器中注册命令
安全相关配置
- 支持命令频率限制(防刷)
- 敏感操作(如批量分析)可设置权限白名单
在 config.py 中新增机器人安全配置:
bot_rate_limit_requests: int = 10 # 频率限制:窗口内最大请求数
bot_rate_limit_window: int = 60 # 频率限制:窗口时间(秒)
bot_admin_users: List[str] = field(default_factory=list) # 管理员用户 ID 列表,限制敏感操作Bot Command EN (`docs/bot-command_EN.md`)
Bot Integration Guide
This document covers the bot module architecture, supported commands, webhook routes, and how to configure platform integrations.
Glossary: "Enterprise bot" in this context means a chatbot that receives commands via webhook from a messaging platform (Feishu / DingTalk / WeChat Work / Telegram) and calls the analysis pipeline to reply inline.
1. Architecture Overview
flowchart TB
subgraph Platforms [Messaging Platforms]
FS[Feishu]
DT[DingTalk]
WC[WeChat Work]
TG[Telegram]
More[More platforms...]
end
subgraph BotModule [bot/ module]
WH[Webhook Server]
Adapters[Platform Adapters]
Dispatcher[Command Dispatcher]
Commands[Command Handlers]
end
subgraph Core [Core Modules]
AS[AnalysisService]
MA[MarketAnalyzer]
NS[NotificationService]
end
FS -->|POST /bot/feishu| WH
DT -->|POST /bot/dingtalk| WH
WC -->|POST /bot/wecom| WH
TG -->|POST /bot/telegram| WH
WH --> Adapters
Adapters -->|Unified message format| Dispatcher
Dispatcher --> Commands
Commands --> AS
Commands --> MA
Commands --> NS2. Directory Structure
bot/
├── __init__.py # Module entry, exports main classes
├── models.py # Unified message/response models
├── dispatcher.py # Command dispatcher (core)
├── handler.py # Webhook handler functions (one per platform)
├── commands/ # Command handlers
│ ├── __init__.py
│ ├── base.py # Abstract base class for commands
│ ├── analyze.py # /analyze — stock/index analysis
│ ├── ask.py # /ask — single-turn question
│ ├── batch.py # /batch — batch watchlist analysis
│ ├── chat.py # /chat — multi-turn strategy chat
│ ├── market.py # /market — market review
│ ├── help.py # /help — help text
│ └── status.py # /status — system status
└── platforms/ # Platform adapters
├── __init__.py
├── base.py # Abstract base class for platforms
├── dingtalk.py # DingTalk bot
├── dingtalk_stream.py # DingTalk Stream bot
└── feishu_stream.py # Feishu (Lark) Stream bot3. Core Abstractions
3.1 Unified Message Model (`bot/models.py`)
@dataclass
class BotMessage:
platform: str # Platform ID: feishu / dingtalk / wecom / telegram
user_id: str # Sender ID
user_name: str # Sender display name
chat_id: str # Conversation ID (group or DM)
chat_type: str # Conversation type: group / private
content: str # Message text
raw_data: Dict # Raw request data (platform-specific)
timestamp: datetime
mentioned: bool = False # Whether the bot was @-mentioned
@dataclass
class BotResponse:
text: str
markdown: bool = False # Whether the response is Markdown
at_user: bool = True # Whether to @-mention the sender3.2 Platform Adapter Base (`bot/platforms/base.py`)
class BotPlatform(ABC):
@property
@abstractmethod
def platform_name(self) -> str: ...
@abstractmethod
def verify_request(self, headers: Dict, body: bytes) -> bool:
"""Verify request signature (security check)"""
...
@abstractmethod
def parse_message(self, data: Dict) -> Optional[BotMessage]:
"""Parse platform message into unified format"""
...
@abstractmethod
def format_response(self, response: BotResponse, message: BotMessage) -> WebhookResponse:
"""Convert unified response to platform format"""
...3.3 Command Base Class (`bot/commands/base.py`)
class BotCommand(ABC):
@property
@abstractmethod
def name(self) -> str: ... # e.g. 'analyze'
@property
@abstractmethod
def aliases(self) -> List[str]: ... # e.g. ['a', 'analyse']
@property
@abstractmethod
def description(self) -> str: ...
@property
@abstractmethod
def usage(self) -> str: ...
@abstractmethod
def execute(self, message: BotMessage, args: List[str]) -> BotResponse: ...4. Supported Commands
| Command | Description | Example |
|---|---|---|
/analyze |
Analyze a specific stock or a registered index | /analyze AAPL, /analyze 600519, /analyze sh000016, /analyze 上证50 |
/ask |
Single-turn question about a stock or the market | /ask what is RSI for AAPL |
/batch |
Batch-analyze your configured watchlist | /batch |
/chat |
Multi-turn strategy chat (maintains conversation context) | /chat |
/market |
Market review (A-shares / US stocks) | /market |
/help |
Show help text | /help |
/status |
Show system status | /status |
Stock code formats: A-shares use 6-digit codes (e.g.
600519); HK stocks prefixhk(e.g.hk00700); US stocks use ticker symbols (e.g.AAPL,TSLA).
Registered index inputs: explicit codes (
sh000016), CSI aliases (930955.CSIconverges tocsi930955) and registered Chinese names (上证50) are all accepted. Index inputs are submitted as a structuredAnalysisTargetthat flows through to the analysis pipeline, sosh000016is never rewritten toSH000016. Unregistered CSI forms (e.g.930956.CSI), code shapes the legacy gate rejected (12345, bare00700,600519.SH, unregisteredsh999999) and unrecognized names return an explicit error without submitting a task; ambiguous registered names require an explicit code instead of guessing. Stock inputs (A-share 6-digit,HK+5-digit, US 1-5 letters, e.g.usfd→USFD) keep the legacy code path. Stock-name inputs (e.g.贵州茅台) are newly exposed by this Bot entry: they reuse the existing name resolver (resolve_name_to_code) and then submit the legacy code without a structured target.
Bot index entry online E2E smoke (`scripts/smoke_bot_index_entry.py`)
Walks the real CommandDispatcher.dispatch_async -> AnalyzeCommand -> TaskService -> StockAnalysisPipeline online for a single target, covering the SH.000016 / 上证50 / 930955.CSI matrix scenarios without any webhook transport (no mocked online dependencies, no dry-run).
Run with a single target argument (--timeout defaults to 900 seconds):
.venv\Scripts\python.exe scripts/smoke_bot_index_entry.py "SH.000016"
.venv\Scripts\python.exe scripts/smoke_bot_index_entry.py "上证50"
.venv\Scripts\python.exe scripts/smoke_bot_index_entry.py "930955.CSI"Process responsibilities: a worker subprocess submits and polls the in-process TaskService and prints single-line E2E_EVENT {json} events (phase=submitted|completed|failed|timeout, with target, plus task_id/stock_code/result/error per phase); the parent only enforces the deadline, cleans up the process tree and maps exit codes (0=success / 1=failure / 124=timeout).
Failure semantics: a failed task or a completed-but-incomplete result (canonical code or registered name not matching the matrix expectation, or any of analysis_summary/operation_advice/trend_prediction empty or whitespace-only) emits a failed event and exits non-zero; on timeout the process tree is killed (Windows taskkill /T /F, POSIX process group) — a successful cleanup emits a timeout event and exits 124, a failed cleanup emits a failed event carrying the cleanup error and exits 1 — never rolling back DB / reports / notifications side effects. On a user Ctrl-C the parent also cleans up the process tree first: a successful cleanup propagates the interrupt, a failed cleanup emits a failed event carrying the cleanup error and exits 1 — a cleanup failure is never silently swallowed. The worker's expected code/name come from the script's built-in authoritative matrix map (SH.000016/上证50 → sh000016/上证50, 930955.CSI → csi930955/红利低波100) — never from the response or the result's self-reported identity: a submission whose extra.stock_code does not match the expectation fails with an explicit mismatch error carrying the expected and actual values (the failed event also carries the structured actual stock_code), and targets outside the matrix as well as non-positive --timeout are rejected before any submission or subprocess spawn (argument/input error, exit code 2). Unexpected worker exceptions (dispatch / poll / event serialization) emit a failed event and exit 1 with the exception evidence kept on stderr; KeyboardInterrupt is not treated as an ordinary failure; the parent normalizes any other worker exit code to 1 so the runtime contract only exposes 0 / 1 / 124. The parent spawns the worker with an internal --worker flag, not via environment variables.
Prerequisites: network plus configured data-source and AI credentials (online chain). This script is not part of the offline gate and does not run in CI.
5. `/status` and LLM configuration diagnostics
Configuration precedence for readiness in `/status`
- The AI availability displayed by
/statusfollows runtime precedence:LITELLM_CONFIG(LiteLLM YAML)LLM_CHANNELS- legacy provider keys (
GEMINI_API_KEY/OPENAI_API_KEY/ANTHROPIC_API_KEY/DEEPSEEK_API_KEY)
- If the primary model (
LITELLM_MODELorAGENT_LITELLM_MODEL) has no configured source in the active layer,/statusshowsAI 服务未配置and keeps the explicit reason line. - Runtime dependency constraint in this repository is
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0; current status semantics are aligned with this constraint. - This diagnostic follows the same readiness rules as
GET /api/v1/system/config/setup/statusfor LLM checks: channels/yaml are active higher priority than legacy keys, and no silent migration is performed when toggling modes.
Fallback and migration boundary
- When
LITELLM_CONFIGorLLM_CHANNELSis active, lower-priority legacy provider keys are ignored as the active source for that run (no silent downgrade). - This change only improves diagnosis and does not perform automatic migration: legacy configuration values are not deleted or rewritten during startup or status collection.
Official compatibility references (for triage)
- LiteLLM docs: https://docs.litellm.ai/
- LiteLLM OpenAI-compatible provider: https://docs.litellm.ai/docs/providers/openai_compatible
- OpenAI Chat API: https://platform.openai.com/docs/api-reference/chat
- DeepSeek API docs: https://api-docs.deepseek.com/
- Kimi Moonshot compatibility: https://platform.moonshot.ai/docs/guide/compatibility
- Gemini OpenAI compatibility: https://ai.google.dev/gemini-api/docs/openai
- Ollama API docs: https://github.com/ollama/ollama/blob/main/docs/api.md
6. Webhook Routes
Handler functions for each platform live in bot/handler.py.
These routes are not yet wired into the FastAPI application — you must mount them manually.
| Route | Method | Status | Notes |
|---|---|---|---|
/bot/dingtalk |
POST | Ready | DingtalkPlatform is registered in ALL_PLATFORMS |
/bot/feishu |
POST | Stream only | Use feishu_stream.py; no Webhook adapter in ALL_PLATFORMS |
/bot/wecom |
POST | Not implemented | Handler exists but no platform adapter |
/bot/telegram |
POST | Not implemented | Handler exists but no platform adapter |
To mount the DingTalk webhook in your FastAPI app:
from bot.handler import handle_dingtalk_webhook
@app.post("/bot/dingtalk")
async def dingtalk_webhook(request: Request):
headers = dict(request.headers)
body = await request.body()
return handle_dingtalk_webhook(headers, body)7. Configuration
Add the following to your .env. Some of these bot-specific keys are already listed in .env.example (for example the DingTalk and Feishu app credentials), while others are not, so treat this section as a consolidated reference for bot setup:
# --- Bot general ---
BOT_ENABLED=false
BOT_COMMAND_PREFIX=/
# --- Feishu (Lark) bot ---
FEISHU_APP_ID=
FEISHU_APP_SECRET=
FEISHU_DOMAIN=feishu # feishu (China) or lark (international/Lark)
FEISHU_VERIFICATION_TOKEN= # Event verification token
FEISHU_ENCRYPT_KEY= # Encryption key (optional)
# --- DingTalk bot ---
DINGTALK_APP_KEY=
DINGTALK_APP_SECRET=
# --- WeChat Work bot (in development) ---
WECOM_TOKEN=
WECOM_ENCODING_AES_KEY=
# --- Telegram bot ---
TELEGRAM_BOT_TOKEN= # Get from @BotFather
TELEGRAM_WEBHOOK_SECRET= # Webhook secret token7. Extending the Bot
Adding a new platform adapter
- Create a new file in
bot/platforms/. - Subclass
BotPlatformand implementverify_request,parse_message,format_response. - Mount the webhook route directly in your FastAPI app (for example in
api/app.py) instead ofapi/v1/router.py, so the callback path stays/bot/rather than/api/v1/bot/.
Adding a new command
- Create a new file in
bot/commands/. - Subclass
BotCommandand implement theexecutemethod. - Register the command in the dispatcher startup code.
Data Center Diagnostics (`docs/data-center-diagnostics.md`)
数据中心
Web 数据中心位于 /data,用于只读查看数据源能力、数据集质量与运行时优先级。页面只消费 GET /api/v1/data/overview,不读取系统配置原始值,也不自行拼接 screening、provider 或配置探针。
页面内容
- Provider 能力:卡片展示运行状态及
dataset_markets给出的精确数据集/市场支持关系;页面顶部汇总已配置的 Provider 数量,不在卡片中单独展示 enabled/configured 标志。 - 数据集质量:
ok、degraded、partial、unconfigured、unavailable、unknown、stale,以及当前来源、最近成功时间和脱敏诊断。 - 路由优先级:每个运行场景的 provider 顺序及其配置来源。
- Overview warnings:接口认为需要关注、但不应阻断其他块展示的警告代码。
状态边界
- 页面不会根据“已知 provider 名称”把冷启动状态推断为
ok;后端返回unknown时原样展示。 - 页面不会把 Provider 的
markets与datasets计算笛卡尔积;精确支持范围以dataset_markets为准。 - 多市场数据集使用不同 provider 时,顶层
source合法地为空;页面会读取coverage.markets,按市场展示实际来源,而不是显示成--。 - Overview 请求失败时整页显示可重试错误;接口成功但 provider/dataset 均为空时显示 empty 状态。
- 单个 provider 或 dataset 的
partial、unknown、stale、unavailable只影响对应条目,其余条目继续展示。
安全边界
该页面不调用 /api/v1/system/config,不展示 API Key、Token、Cookie 或原始环境变量。需要修改配置时仅跳转到 Settings,由现有配置脱敏和权限边界负责处理。
验证与回滚
前端测试使用与 DataCapabilityOverviewResponse 同形的 payload,覆盖 success、cold-start unknown、warning、empty、error 和 retry。回滚页面时可移除 /data 路由、导航、API/type、页面及文档;后端 capability 契约不受影响。
浏览器验收
cd apps/dsa-web && npx playwright test --config playwright.fixture.config.ts 使用固定 API fixture 渲染真实页面,不依赖后端服务或密钥。CI 的 web-gate 安装 Chromium、执行验收,并将截图上传为 web-ui-evidence-<head SHA> artifact(保留 30 天);截图不提交到仓库。该验收验证页面交互和展示,不代表实时 provider/LLM 可用性。
桌面导航在短窗口中仅滚动链接列表,主题、语言与退出控件保留在可见区域;主题菜单不进入滚动裁剪容器。浏览器验收包括 1280×720、认证与选股均开启时的设置/退出可达性、主题菜单及取消退出。
Data Source Stability (`docs/data-source-stability.md`)
数据源稳定性与故障处理图示
本文面向用户、部署者和维护者,说明 DSA 已接入的数据源如何参与分析、选股和大盘复盘,以及当数据源失败时系统会怎么降级。
核心原则:先用项目已经接入并验证过的数据源,把失败路径讲清楚;新增外部数据源应放在第二阶段,避免先扩大维护面。
基本面报告期与股票范围
AkShare 的业绩预告、快报是按 date=YYYYMMDD 查询的全市场表,机构持股按“年份+季度”查询;适配器依次尝试最近两个已结束季度,并在返回前筛选当前股票。表中没有目标股票或缺少代码列时继续尝试前一期,不会用其他股票的首行,也不会退回接口内置的多年以前默认日期。十大股东查询复用现有沪/深/北代码转换,明确传入带市场前缀的股票和报告期。
机构推荐不代表机构持仓变化,普通股东户数也不代表十大股东持仓变化;这些不同含义的数据不会再作为对应字段的备用来源。业绩报表也不再替代业绩预告。未披露、缺数据或上游解析失败时保留局部数据和错误诊断;partial 本身不代表全部取数失败。此修复不改变超时预算、普通日 K 数据源优先级、API/报告字段或用户配置,也不保证上游东财返回结构始终可用。
业绩快报摘要只从实际返回的营收、净利润、同比、每股收益和净资产收益率生成,保留零值与负值;公告日期不作为快报内容。业绩预告按明确的披露文本字段提取,不把日期或变动幅度数值当作摘要。只有日期或指标全部缺失时不生成有效摘要,避免缓存及 Agent 输出把元数据误当作业绩信息。
一句话答复用户
如果遇到“数据源失败”,通常不是系统只能用一个源,而是免费源被限流、上游接口临时变更、网络抖动或当前市场/标的不支持。DSA 已经内置多数据源 fallback,会按场景自动尝试下一个源;如果你希望更稳定,建议至少配置一个 token 型稳定源:
- A 股个股与选股:优先配置
TUSHARE_TOKEN,并保留 AkShare / Efinance / Tencent / TickFlow / Baostock / YFinance 兜底;普通个股日线按 priority 配置排序。 - 已登记 A 股指数:固定按 Tencent → AkShare → TickFlow → YFinance 降级,不读取普通日 K 的
*_PRIORITY配置。 - A 股大盘复盘:配置
TICKFLOW_API_KEY后,复盘聚合所需的指数和市场宽度会优先尝试 TickFlow,失败后回退现有免费源;这与单标的指数日线的 Tencent-first 固定链是不同入口。 - 港股:配置
FUTU_OPEND_HOST后,Futu 可作为港股实时与基本面主源;FUTU_HK_REALTIME_SOURCE_PRIORITY控制港股实时行情顺序,Longbridge、AkShare、YFinance 保留为 fallback。 - 美股:配置
LONGBRIDGE_*后可优先使用 Longbridge,YFinance 保留实时主链 fallback;Finnhub、AlphaVantage 只在已有主源报价后补充字段或参与日线链,不能在 Longbridge/YFinance 全部失败时独立返回实时报价。 - 热点题材:选股的热点实现参考 AlphaSift,默认走 EastMoney provider,并使用本地 last-good cache 降低实时接口失败影响。
已接入数据源矩阵
| 场景 | 已接入源 | 默认使用方式 | 失败处理 |
|---|---|---|---|
| A 股个股日线 / 技术面 | Efinance、Tencent、AkShare、Tushare、TickFlow、Pytdx、Baostock、YFinance | DataFetcherManager 按 priority 尝试;配置 TUSHARE_TOKEN 后 Tushare 自动进入候选源 |
单源失败后尝试下一个源;连续失败会短期熔断该源 |
| 已登记 A 股指数日线 / 技术面 | Tencent、AkShare、TickFlow、YFinance | 当前 5 个 IndexRegistry 标的固定按 Tencent → AkShare → TickFlow → YFinance 尝试,不读取普通日 K 的 *_PRIORITY 配置 |
未配置、熔断、异常或空结果均继续下一源;全部失败返回空结果并记录汇总告警 |
| A 股实时行情 | Tencent、AkShare Sina、Efinance、AkShare EM、Tushare | REALTIME_SOURCE_PRIORITY 控制顺序,默认偏向 Tencent / Sina 这类轻量源 |
失败源记录 fallback_from,成功源继续返回 |
| A 股大盘复盘 | TickFlow、AkShare、Tushare、Efinance | 配置 TICKFLOW_API_KEY 后,复盘聚合的主指数和市场宽度优先尝试 TickFlow;不等同于单标的指数日线链 |
TickFlow 权限不足或失败时回退 AkShare / Tushare / Efinance 链路 |
| 选股快照 | Tushare、Sina、Efinance、AkShare EM、EastMoney Datacenter | 有 TUSHARE_TOKEN 时自动把 tushare 放入快照优先级;否则使用免费源链路 |
选股引擎维护 source health;状态接口透出 snapshot/daily health |
| 选股日线补特征 | DataFetcherManager |
选股引擎优先复用现有日线与缓存链路 | 现有链路失败后才回到引擎自身的日线源 |
| 选股热点题材 | EastMoney provider、参考 AlphaSift 的 hotspot 实现、last-good cache | 未指定 provider 时默认使用 EastMoney provider | 实时失败时回退热点缓存;无缓存时返回稳定空态和可读错误 |
| 港股 | Futu、Longbridge、YFinance、AkShare、Tushare | 配置 FUTU_OPEND_HOST 后 Futu 作为实时与基本面主源,按 FUTU_HK_REALTIME_SOURCE_PRIORITY 顺序尝试 |
Futu 失败时回退 Longbridge / AkShare / YFinance;Longbridge 冷却或失败时继续回退 YFinance / 其他可用源 |
| 美股 | Longbridge、YFinance、AkShare、Tushare、Finnhub、AlphaVantage、Stooq | 配置 Longbridge 凭证后参与美股日线/实时主链;YFinance 保持实时基础 fallback;Finnhub/AlphaVantage 可补充已有报价字段并参与日线链 | Longbridge 冷却或失败时实时主链回退 YFinance;两者都失败时不把补充源伪装成可独立返回报价的 fallback |
统一数据能力只读契约
Web/API 提供 GET /api/v1/data/overview 和等价别名 GET /api/v1/data/capabilities,用于把数据源能力、数据集质量和场景优先级以同一个只读结构暴露给首页看板、数据中心、个股详情、自选和选股页面。
首版契约只读取配置和 DataFetcherManager 的 fetcher 快照,不触发外部行情请求,也不返回任何原始密钥。响应分三层:
providers:每个 provider 的enabled、configured、status、markets、datasets、精确的dataset_markets和非敏感 warning。markets与datasets只是聚合索引,不能推断为笛卡尔积;例如 YFinance 可参与 A 股行情,但 A 股基本面固定走 AkShare,因此dataset_markets.financial.snapshot不包含cn。Finnhub/AlphaVantage 的美股报价 handler 当前只在 Longbridge/YFinance 已返回主报价后补字段,因此不声明quote.realtime独立路由能力。datasets:quote.realtime、kline.daily、index.daily、market.overview、financial.snapshot、news.events、strategy.screening、alert.monitor、portfolio.account的status/source/fallback_from/stale/warnings;其中quote.realtime、kline.daily、market.overview和financial.snapshot都聚合市场级 coverage,避免把单市场健康度误报成全局可用。A 股实时额外拆分cn股票优先级、cn.index.exchange固定指数链和cn.index.csiEfinance-only 链;美股实时拆分us个股动态优先级与us.index固定 YFinance-only 链。AkShare Tencent/Sina/EM、AkShare HK 双子源、Efinance 股票/指数子源级熔断及日线 breaker 会先于 provider-wide unknown 判定,使 overview 与运行时跳过行为一致。YFinance 的港股实时声明对应其实际.HK执行路径;未配置 OpenD 时 HK priority 与运行时一致地跳过 Futu;美股个股实时也按当前请求可用性跳过处于连接冷却的 Longbridge,使 YFinance 成为实际主源而不是伪 fallback。PyTDX 不声明未接入统一 route 的实时能力,YFinance 指数日线只声明实际可执行的 CN/US route。任何按运行时顺序尝试的数据集在首个优先源尚未探测且没有更具体的 open breaker 证据时保持unknown,不会越过它宣称后续源已被选中。alert.monitor只有在开关启用且当前 API 进程的 scheduler 已实际注册并运行agent_event_monitor时才为ok;开关关闭返回agent_event_monitor_disabled,scheduler 未运行则返回agent_event_monitor_not_running。priorities:cn.realtime、hk.realtime、us.realtime、daily.generic、cn.index.daily、market.overview、screening.snapshot、news.events的当前 source order;daily.generic与运行时一样先排除当前请求不可用或处于连接冷却的 fetcher,不把已跳过的数据源记录成 fallback。index.daily额外区分cn.exchange、cn.csi与uscoverage:沪深交易所指数使用 Tencent、AkShare、TickFlow、YFinance 固定多源链,CSI 指数按运行时契约只认 AkShare,美股指数只声明当前能够接收指数 symbol 的