Files

1 line
59 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "ahricool/raise", "files": {"docs/LLM_CONFIG_GUIDE.md": {"1fcd5ce40f0567296c9280ba3186d2f21b7a404d": {"t": 1785768985.3570457, "n": 18948, "h": "5c1294352ccf0532", "c": "# LLM (大模型) 配置指南\n\n欢迎!无论你是刚接触 AI 的新手小白,还是精通各种 API 的高玩老手,这份指南都能帮你快速把大模型(LLM)跑起来。\n\n本项目对外提供统一的 AI 模型接入体验,支持主流官方 API、OpenAI 兼容平台以及本地模型。底层由 [LiteLLM](https://docs.litellm.ai/) 驱动,但大多数用户只需要理解“选服务商、填 API Key、选主模型/渠道”这条默认路径。为了照顾不同阶段的用户,我们设计了“三层优先级”配置,按需选择最适合你的方式即可。\n\n如果你正在选择具体服务商、配置 GitHub Actions Secrets / Variables、排查 `details.reason` 错误或准备回滚配置,请优先查看 [LLM 服务商配置指南](./llm-providers.md)。该文档集中维护 provider 预设、Actions 变量对照、运行时能力检测边界和常见错误处理建议。\n\n> 本页的 provider/model/Base URL 说明本次未新增外部兼容语义,仅用于同步现网约定;实际兼容判断仍按当前仓库锁定依赖与运行时实现执行:\n> - 依赖边界:`litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(与 `requirements.txt` 一致)。\n> - 兼容验证入口:`tests/test_system_config_service.py`、`tests/test_system_config_api.py` 以及现有前端模型配置页回归用例。\n> - 回退路径:优先使用 `.env` 配置备份 + `POST /api/v1/system/config/import` 恢复;也可在重启前手动回填旧 `LITELLM_MODEL` / `LLM_*` / `AGENT_LITELLM_MODEL` / `VISION_MODEL` / `LLM_TEMPERATURE`。\n\n> **说明**:本页对 provider/model/base URL 的说明同步沿用当前依赖约束与历史约定,仅做文档补充,不引入新的运行时 provider、模型或 Base URL 行为变更。\n\n---\n\n## 快速导航:你应该看哪一节?\n\n1. **【新手小白】** \"我只想赶紧把系统跑起来,越简单越好!\" -> [指路【方式一:极简单模型配置】](#方式一极简单模型配置适合新手)\n2. **【进阶用户】** \"我有好几个 Key,想配置备用模型,还要改自定义网址(Base URL)。\" -> [指路【方式二:渠道(Channels)模式配置】](#方式二渠道channels模式配置适合进阶多模型)\n3. **【高玩老手】** \"我要做复杂的负载均衡、请求路由、甚至多异构平台高可用!\" -> [指路【方式三:YAML 高级配置】](#方式三yaml高级配置适合老手自定义)\n4. **【本地模型】** \"我想用 Ollama 本地模型!\" -> [指路【示例 4:使用 Ollama 本地模型】](#示例-4使用-ollama-本地模型)\n5. **【视觉模型】** \"我想用图片识别股票代码!\" -> [指路【扩展功能:看图模型(Vision)配置】](#扩展功能看图模型vision配置)\n\n---\n\n## 方式一:极简单模型配置(适合新手)\n\n**目标:** 只要记得填入 API Key 和对应的模型名就能立刻用。不需要折腾复杂概念。\n\n如果你只打算用一种模型,这是最快捷的办法。打开项目根目录下的 `.env` 文件(如果没有,复制一份 `.env.example` 并重命名为 `.env`)。\n\n### Anspire Open 示例:\n\n> 💡 **推荐 [Anspire Open](https://open.anspire.cn/?share_code=QFBC0FYC)**:支持中文优化的联网搜索与 OpenAI-compatible 路径一体化体验,适合只准备一个 Key 的用户。\n> - 以下为配置示例,模型与网关可用性以账号权限和 Anspire 控制台为准;文档示例不替代实际连通性验证。\n> - 建议在 Web 设置页点击“测试连接”进行实际鉴权与模型可用性检查,避免以文档默认值直接当作可用性承诺。\n\n```env\n# Anspire Open API Keys(支持多个,逗号分隔)\n# 获取: https://open.anspire.cn/?share_code=QFBC0FYC\n# 满足默认优先级条件时,系统会复用该 Key 处理搜索与 LLM(仅限示例兜底路径)。\n# 示例模型:Doubao-Seed-2.0-lite;示例网关:https://open-gateway.anspire.cn/v6\nANSPIRE_API_KEYS=sk-xxxxxxxxxxxxxxxx\n# 可选:按控制台可用性切换模型或网关\n# ANSPIRE_LLM_MODEL=Doubao-Seed-2.0-pro\n# ANSPIRE_LLM_BASE_URL=https://open-gateway.anspire.ai/v6\n```\n\n### 示例 1:使用通用第三方平台(兼容 OpenAI 格式,推荐)\n\n现在市面上绝大多数第三方聚合平台(例如硅基流动、AIHubmix、阿里百炼、智谱等)都兼容 OpenAI 的接口格式。只要平台提供了 API Key 和 Base URL,你都可以按照以下格式无脑配置:\n\n```env\n# 填入平台提供给你的 API Key\nOPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx\n# 填入平台的接口地址 (非常重要:结尾通常必须带有 /v1)\nOPENAI_BASE_URL=https://api.siliconflow.cn/v1\n# 填入该平台上具体的模型名称(非常重要:注意前面必须加上 openai/ 前缀帮系统识别)\nLITELLM_MODEL=openai/deepseek-ai/DeepSeek-V3 \n```\n\n### 示例 2:使用 DeepSeek 官方接口\n```env\n# 填入你在 DeepSeek 官方平台申请的 API Key\nDEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx\n```\n*兼容提示:仅填这一行时,系统仍会默认使用 `deepseek/deepseek-chat` 并在日志提示迁移。*\n`deepseek-chat` / `deepseek-reasoner` 仍可用于兼容旧配置,但 DeepSeek 官方已标记为 2026/07/24 后废弃;新配置建议通过 Web 快速渠道或显式 `LITELLM_MODEL=deepseek/deepseek-v4-flash` 迁移到 `deepseek-v4-flash` / `deepseek-v4-pro`。\n\n### 示例 3:使用 Gemini 免费 API\n```env\n# 填入你获取的 Google Gemini Key\nGEMINI_API_KEY=AIzac...\n```\n\n### 示例 4:使用 Ollama 本地模型\n```env\n# Ollama 无需 API Key,本地运行 ollama serve 后即可使用\nOLLAMA_API_BASE=http://localhost:11434\nLITELLM_MODEL=ollama/qwen3:8b\n```\n\n> **重要**: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,<2.0.0`(与 requirements.txt 一致)。\n\n> **恭喜!小白读到这里就可以去运行程序了!**\n> 想测测看通没通?在主目录打开命令行输入:`python scripts/check_env.py --llm`\n\n---\n\n## 方式二:渠道(Channels)模式配置(适合进阶/多模型)\n\n**目标:** 我有多个不同平台的 Key 想要混着用,如果主模型卡了/网络挂了,我希望它能自动切换到备用模型。\n\n**网页端可以直接配:** 你可以启动程序后,在 **Web UI 的“系统设置 -> AI 模型 -> AI 模型接入”** 中非常直观地进行可视化配置!\n\n> **新版编辑体验补充**:对于 DeepSeek、阿里百炼(DashScope)以及其他兼容 OpenAI `/v1/models` 的渠道,设置页现在支持直接点击“获取模型”,从 `{base_url}/models` 拉取可用模型并多选;底层仍会保存为原来的 `LLM_{CHANNEL}_MODELS=model1,model2` 逗号格式。若渠道不支持该接口、鉴权失败或暂时不可达,仍可继续手动填写模型列表,不影响保存。\n\n### 首次启动配置状态\n\n后端提供只读状态接口 `GET /api/v1/system/config/setup/status`,用于判断首次启动闭环中最基础的几类配置是否已经就绪:LLM 主渠道、Agent 渠道、自选股、通知渠道和本地存储。这个接口只读取已保存的 `.env` 与当前进程环境变量,不会重载运行时配置、写入 `.env`、测试真实模型或创建数据库文件;前端向导和后续 smoke run 可以基于该接口逐步接入。\n\n### Web 渠道编辑器的兼容性 / 迁移 / 回退规则\n\n- 预设里的 provider / Base URL / 示例模型只用于**初始化表单**;真正落盘时仍是你当前输入的 `LLM_{CHANNEL}_PROTOCOL`、`LLM_{CHANNEL}_BASE_URL`、`LLM_{CHANNEL}_MODELS`、`LLM_{CHANNEL}_API_KEY(S)`,不会在后台偷偷改成别的 provider 名或 URL。\n- 设置页的“获取模型”只对 `OpenAI Compatible` / `DeepSeek` 渠道调用 `{base_url}/models`;“测试连接”默认只对模型列表首项发起一次最小聊天请求,并在结果中展示后端规范化后的 `resolved_model`。若返回 `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 检测。上述检测返回的 `stage / error_code / details / latency_ms / capability_results` 仅用于结构化诊断提示,**不会写回** `.env`,也不会阻止保存。\n- 若返回 `details.reason=provider_blocked`,表示服务商或中转网关明确拦截了本次请求;它区别于本地网络 / TLS 异常和 `model_access_denied`,应优先检查账号风控、地域或请求来源限制、模型权限、代理商网关策略和内容安全策略。\n- 运行时能力检测会产生真实 LLM 请求,可能带来 token / 图像输入费用、RPM/TPM 限流、余额不足或超时。检测失败可能来自账号权限、模型未开通、endpoint 区域、余额、服务商兼容层或 LiteLLM 转换路径,不等于该 provider 全局不支持对应能力。P3 未对所有真实 provider 做在线 smoke;兼容依据来自当前依赖约束 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 下的 LiteLLM `completion()` / OpenAI I/O format / streaming / exception mapping,以及 OpenAI Chat Completions 的 JSON mode、tool calling、streaming 和 vision input 形状。\n- 相关外部来源: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>。\n- 保存渠道时,只会更新这次提交的 key;不会因为切换渠道模式而静默迁移整个旧配置。唯一会被**同步清理**的是运行时模型引用:如果 `LITELLM_MODEL`、`AGENT_LITELLM_MODEL`、`VISION_MODEL` 或 `LITELLM_FALLBACK_MODELS` 指向了当前已启用渠道里已经不存在的模型,设置页会在保存前把这些失效引用清空/移除,避免运行时继续指向无效模型;即使当前启用渠道没有任何可选模型,也会清理缺少 legacy Key 支撑的托管 provider 旧值。`cohere/*`、`google/*`、`xai/*` 这类直连模型仅用于说明历史 `direct-env` 兼容保留语义,不等于可用性承诺,是否可用请按各厂商官方模型/API 文档再做实际验证。\n- 后端一致性依据:配置校验链路在 `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` 不在该白名单中,因此会保留为直连模型。\n- 回退方式也保持最小:把对应渠道模型列表改回去后重新选择主模型 / fallback,或直接用桌面端导出备份 / 手动 `.env` 还原之前的 `LLM_*`、`LITELLM_MODEL`、`AGENT_LITELLM_MODEL`、`VISION_MODEL`、`LLM_TEMPERATURE` 即可,不需要额外跑迁移脚本。Web 端如需恢复配置,也可在启用管理员鉴权(`ADMIN_AUTH_ENABLED=true`)后通过 `POST /api/v1/system/config/import` 回滚。\n- 当前仓库对此链路的依赖约束是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.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`。\n\n> **外部 provider 示例模型说明**:`cohere/*`、`google/*`、`xai/*` 等 provider 前缀值仅用于说明当前保存清理语义,**不代表该依赖约束内的逐型号可用性保证**。文档或测试中的具体模型名都是配置保留行为样例,不是生产推荐;实际可用性请以对应官方模型文档为准,并结合仓库依赖约束 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 复核。\n\n### 回退与兼容性证据\n\n- 依赖约束与静默清理范围:在 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 下,保存仅清理失效的 runtime 模型引用(`LITELLM_MODEL`、`AGENT_LITELLM_MODEL`、`VISION_MODEL`、`LITELLM_FALLBACK_MODELS`),`cohere/*`、`google/*`、`xai/*` 等非渠道直连模型会被保留。\n- 回退方式:可直接用桌面端导出备份后通过 `POST /api/v1/system/config/import` 恢复;也可手动把 `.env` 中历史 `LITELLM_* / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE` 回填后重启生效。Web 端执行导入前请先开启管理员鉴权(`ADMIN_AUTH_ENABLED=true`)。\n- 回退回归证据:`tests/test_system_config_service.py::test_import_desktop_env_restores_runtime_models_after_cleanup` 覆盖“清理后用桌面导出备份恢复 runtime 引用”。\n- 直连 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 保留语义。\n- 前端回归命令:`cd apps/dsa-web && npm run lint && npm run build && npm run test -- src/components/settings/__tests__/LLMChannelEditor.test.tsx`。\n- 建议回退操作链路(含设置页刷新):先导出桌面备份,`POST /api/v1/system/config/import` 导入后,再通过 `GET /api/v1/system/config` 刷新页面配置,再确认 `LITELLM_MODEL / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE` 与模型列表一致后再继续使用。\n\n### 常用官方文档来源(用于核对预设 provider / Base URL / 模型命名)\n\n- OpenAI Compatible 规范(LiteLLM):<https://docs.litellm.ai/docs/providers/openai_compatible>\n- OpenAI 官方:<https://platform.openai.com/docs/api-reference/chat>\n- DeepSeek 官方:<https://api-docs.deepseek.com/>\n- Anspire Open:<https://open.anspire.cn/?share_code=QFBC0FYC>\n- 阿里百炼 DashScope 兼容模式:<https://help.aliyun.com/zh/model-studio/compatibility-of-openai-with-dashscope>\n- Moonshot / Kimi 官方:<https://platform.moonshot.ai/docs/guide/compatibility>\n- Anthropic 官方:<https://docs.anthropic.com/en/api/messages>\n- Gemini 官方:<https://ai.google.dev/gemini-api/docs/openai>\n- Cohere 官方:<https://docs.cohere.com/>\n- Cohere API 参考:<https://docs.cohere.com/reference/>\n- Cohere LiteLLM Provider:<https://docs.litellm.ai/docs/providers/cohere>\n- Google Gemini API 与模型:<https://ai.google.dev/gemini-api/docs/openai>、<https://ai.google.dev/gemini-api/docs/models>\n- Google LiteLLM Provider:<https://docs.litellm.ai/docs/providers/gemini>\n- xAI 官方:<https://docs.x.ai/docs>\n- xAI LiteLLM Provider:<https://docs.litellm.ai/docs/providers/xai>\n- Ollama 官方:<https://github.com/ollama/ollama/blob/main/docs/api.md>\n\n如果不方便用网页版,在 `.env` 文件中配置也非常丝滑,它能让你同时管理多个第三方平台。规则如下:\n\n1. **先声明你有几个渠道**:`LLM_CHANNELS=渠道名称1,渠道名称2`\n2. **给每个渠道分别填写配置**(注意全大写):`LLM_{渠道名}_XXX`\n\n### 示例:同时配置 DeepSeek 和某中转平台,并设置备用切换\n```env\n# 1. 开启渠道模式,声明这里有两个渠道:deepseek 和 aihubmix\nLLM_CHANNELS=deepseek,aihubmix\n\n# 2. 渠道一:配置 DeepSeek 官方\nLLM_DEEPSEEK_BASE_URL=https://api.deepseek.com\nLLM_DEEPSEEK_API_KEY=sk-1111111111111\nLLM_DEEPSEEK_MODELS=deepseek-v4-flash,deepseek-v4-pro\n\n# 3. 渠道二:配置一个常用的聚合中转 API\nLLM_AIHUBMIX_BASE_URL=https://api.aihubmix.com/v1\nLLM_AIHUBMIX_API_KEY=sk-2222222222222\nLLM_AIHUBMIX_MODELS=gpt-5.5,claude-sonnet-4-6\n\n# 4. 【关键】指定主模型和备用模型列表\n# 平时首选用 deepseek 这款模型:\nLITELLM_MODEL=deepseek/deepseek-v4-flash\n# 可选:Agent 问股单独指定主模型(留空则继承主模型)\nAGENT_LITELLM_MODEL=deepseek/deepseek-v4-pro\n# 主模型崩了立刻挨个尝试下面这俩备用模型:\nLITELLM_FALLBACK_MODELS=openai/gpt-5.4-mini,anthropic/claude-sonnet-4-6\n```\n\n### 示例:Ollama 渠道模式(本地模型,无需 API Key)\n```env\n# 1. 开启渠道模式,声明 ollama 渠道\nLLM_CHANNELS=ollama\n\n# 2. 配置 Ollama 地址(本地默认 11434 端口)\nLLM_OLLAMA_BASE_URL=http://localhost:11434\nLLM_OLLAMA_MODELS=qwen3:8b,llama3.2\n\n# 3. 指定主模型\nLITELLM_MODEL=ollama/qwen3:8b\n```\n\n### MiniMax 渠道模型填写说明\n\n- 如果你通过 OpenAI Compatible 渠道接 MiniMax,请在渠道模型里直接填写 `minimax/<模型名>`,例如 `minimax/MiniMax-M1`。\n- Web 设置页里的主模型、Agent 主模型、Fallback、Vision 下拉会保留这个值原样展示,不会再错误改写成 `openai/minimax/<模型名>`。\n\n### 问股 Agent / LiteLLM 配置兼容说明\n\n- 问股 Agent 运行时沿用与普通分析相同的三层优先级:`LITELLM_CONFIG`(LiteLLM YAML)> `LLM_CHANNELS` > legacy provider keys。只要上层配置有效生效,下层配置就不会再参与本次请求。\n- YAML 模式下,Agent 直接复用 LiteLLM `model_list` / `model_name` 路由语义;渠道模式下,优先读取 `AGENT_LITELLM_MODEL`,留空时继承 `LITELLM_MODEL`,再按 `LITELLM_FALLBACK_MODELS` 继续 fallback。\n- 如果你没有启用 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>`。\n- 该兼容逻辑只增强“失败时保留后端真实错误原因”和“未配置 LLM 时给出更具体诊断”,**不会**静默删除、清空、迁移或改写你现有的 `GEMINI_*` / `OPENAI_*` / `ANTHROPIC_*` / `LITELLM_*` 配置。\n- 如果当前环境没有任何有效 Agent 模型链路,问股页面会继续按失败语义返回,并直接展示后端真实配置诊断;补齐任一有效模型来源后即可恢复,无需额外执行配置迁移脚本。\n- 推荐的新配置方式仍然是显式设置 `LITELLM_MODEL` / `AGENT_LITELLM_MODEL` 或使用 `LLM_CHANNELS`;legacy provider keys 目前保留为兼容回退路径,方便旧 `.env`、本地 macOS 开发环境和历史部署平滑继续运行。\n\n### Kimi K2.6 固定 temperature 兼容说明\n\n- Moonshot 官方说明 Kimi API 兼容 OpenAI 接口,Base URL 使用 `https://api.moonshot.ai/v1`:<https://platform.kimi.ai/docs/guide/kimi-k2-6-quickstart>\n- LiteLLM 官方要求 OpenAI Compatible 渠道模型名使用 `openai/` 前缀:<https://docs.litellm.ai/docs/providers/openai_compatible>\n- Moonshot 官方兼容性文档区分两种固定值:**thinking 模式固定 `1.0`,non-thinking 模式固定 `0.6`**;传其它值会被接口拒绝:<https://platform.moonshot.ai/docs/guide/compatibility#parameters-differences-in-request-body>\n- 当前仓库的运行时依赖约束是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(见 `requirements.txt`);本次兼容逻辑按该约束回归验证了主分析、大盘复盘、Agent 直连 LiteLLM,以及系统设置页的渠道连通性测试。\n- 因此本项目会在请求发出前按**实际请求模式**归一化 `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` 不会被改写。\n- `SystemConfigService` 在 Web 设置保存 / 桌面端 `.env` 导入时只更新你提交的 key,不会因为切到 Kimi 静默清空、迁移或重写已有 `LLM_TEMPERATURE`;渠道测试请求里临时使用的 `1.0/0.6` 也不会回写到配置文件。\n- 非 Kimi 主模型、非 Kimi fallback 以及切回普通模型后的请求,仍继续使用你配置的温度;也就是说旧配置无需迁移,切换模型即可自动恢复原行为。\n- 本仓库兼容性回归覆盖见:`tests/test_llm_channel_config.py`、`tests/test_market_analyzer_generate_text.py`、`tests/test_agent_pipeline.py`、`tests/test_system_config_service.py`。\n- 最小回滚方式:直接回退本次 Kimi 固定温度相关改动,无需单独迁移已有 `LLM_TEMPERATURE` 配置。\n\n### 兼容性与回退复核清单(按 PR 审核口径)\n\n- 运行时依赖约束:`litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(与 `requirements.txt` 一致)。\n- 回归验证入口:\n - 渠道模型发现与连接:`tests/test_llm_channel_config.py`\n - 运行时源清理与恢复(含桌面导出备份链路):`tests/test_system_config_service.py`\n - 接口校验与问题面向字段:`tests/test_system_config_api.py`\n - 设置页交互与保存后提示:`apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx`\n- 旧配置回退路径:`桌面端导出备份 -> /api/v1/system/config/import`,或手动恢复 `LLM_* / LITELLM_* / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE`;Web 导入备份前同样要求 `ADMIN_AUTH_ENABLED=true`,否则会返回 403。\n\n> **致命避坑说明**:如果你启用了 `LLM_CHANNELS`,那么你直接写在外面的 `DEEPSEEK_API_KEY` 或 `OPENAI_API_KEY` 将**全部失效(系统一律无视)**!二者**选其一即可**,千万不要既写了新手模式又写了渠道模式结果产生冲突。\n> **Docker 注意**:如果你在 `docker compose environment:` 或 `docker run -e` 中显式传入 `LITELLM_MODEL`、`LLM_CHANNELS`、`LLM_DEEPSEEK_MODELS` 等变量,容器重启后这些环境变量会覆盖 Web 设置页写入的 `.env`,需要同步修改部署配置。\n\n---\n\n## 方式三:YAML 高级配置(适合老手自定义)\n\n**目标:** 我不在乎学习门槛,我要最高控制权,我要用原生规则做企业级高可用!\n\n这一层会直接映射到底层 LiteLLM 路由能力,支持高并发、自动重试、按 RPM/TPM 负载均衡等操作。\n\n### 本地运行 / Docker 部署模式配置说明\n\n1. 在 `.env` 中只保留一行指向声明:\n ```env\n LITELLM_CONFIG=./litellm_config.yaml\n ```\n2. 在项目根目录创建一个 `litellm_config.yaml`(可以参考自带的 `docs/examples/litellm_config.example.yaml`)。\n\n示例 `litellm_config.yaml`:\n```yaml\nmodel_list:\n - model_name: my-smart-model\n litellm_params:\n model: deepseek/deepseek-v4-flash\n api_base: https://api.deepseek.com\n api_key: \"os.environ/MY_CUSTOM_SECRET_KEY\" # 从环境变量读取 Key,安全防泄漏\n\n # Ollama 本地模型(无需 api_key)\n - model_name: ollama/qwen3:8b\n litellm_params:\n model: ollama/qwen3:8b\n api_base: http://localhost:11434\n```\n\n### GitHub Actions配置说明\n\n1. `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`\n\n2. 按下表配置,只有全部必填配置正确配置,YAML 高级配置模式才可以生效,YAML配置文件的写法,可以参考自带的 `docs/examples/litellm_config.example.yaml`\n\n| Secret 名称 | 说明 | 必填 |\n|------------|------|:----:|\n| `LITELLM_CONFIG` | 高级模型路由配置文件路径,通常配置 `./litellm_config.yaml` | 必填 |\n| `LITELLM_MODEL` | 默认主模型名称或路由别名 | 必填 |\n| `LITELLM_CONFIG_YAML` | 存放 YAML 配置文件内容,可不在仓库中提交实体文件 | 可选 |\n| `LITELLM_API_KEY` | 用于存储API Key,可在配置文件中引用(环境变量引用方式)。由于GitHub Actions必须要指定导入的环境变量,因此你不能像本地运行模式那样自由命名环境变量 | 可选,必须配置到repository secret中 |\n| `ANTHROPIC_API_KEY` | 如果要多个API Key,这个变量名称也能拿来用 | 可选,必须配置到repository secret中 |\n| `OPENAI_API_KEY` | 同上,可以用来存储API Key | 可选,必须配置到repository secret中 |\n\n渠道模式无需上传 YAML 文件。仓库自带 `daily_analysis.yml` 已显式透传以下常用字段:\n\n- 运行时选择:`LLM_CHANNELS`、`LITELLM_MODEL`、`LITELLM_FALLBACK_MODELS`、`AGENT_LITELLM_MODEL`、`VISION_MODEL`、`VISION_PROVIDER_PRIORITY`、`LLM_TEMPERATURE`\n- 多 Key:`GEMINI_API_KEYS`、`ANTHROPIC_API_KEYS`、`OPENAI_API_KEYS`、`DEEPSEEK_API_KEYS`(当前 workflow 仅从 repository secrets 导入,不会读取同名 Variables)\n- 常用渠道名:`primary`、`secondary`、`aihubmix`、`deepseek`、`dashscope`、`zhipu`、`moonshot`、`minimax`、`volcengine`、`siliconflow`、`openrouter`、`gemini`、`anthropic`、`openai`、`ollama`\n\n例如在 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 不受这个限制。\n\n\n> **三层配置互斥准则**:YAML 优先级最高!只要配置了 YAML,**渠道模式** 和 **新手极简模式** 统统被忽略。系统优先级为:`YAML配置 > 渠道模式 > 极简单模型`。\n\n---\n\n## 扩展功能:看图模型 (Vision) 配置\n\n系统中有些特定功能(比如上传股票软件截图,让 AI 提取出截图里的股票代码并放入自选股池)必须用到具备“视觉能力”的模型。你需在 `.env` 单独给它指派一个懂图片的模型。\n\n```env\n# 指定你看图专用的模型名\nVISION_MODEL=openai/gpt-5.5\n# 别忘了填写它对应提供商的 API KEY,如果是 OpenAI 兼容渠道就提供 OPENAI_API_KEY:\n# OPENAI_API_KEY=xxx\n```\n\n**备用看图机制:** 为了防止偶尔罢工,系统内置了切换策略。如果主视觉模型调用失败,它会按照下方的顺位尝试寻找是否有其他看图模型的 Key:\n```env\n# 默认的备用顺序:\nVISION_PROVIDER_PRIORITY=gemini,anthropic,openai\n```\n\n---\n\n## 检测与排错 (Troubleshooting)\n\n配好了之后心惊胆战不知道对不对?在命令行(Terminal)里敲入下面代码帮你挂号问诊:\n\n- `python scripts/check_env.py --config` :纯检测 `.env` 配置文件里的逻辑写得对不对,是不是少写了什么。(秒出结果,不调用网络,纯检查本地文本拼写)\n- `python scripts/check_env.py --llm` :系统会真的发一句问候语给大模型,让你亲眼看到他的回答。这能彻底测出你的**网络通不通、账号有没有欠费**。\n\n### 常见踩坑答疑台\n\n| 遇到了什么诡异报错? | 罪魁祸首可能是啥? | 该怎么收拾它? |\n|----------------------|----------------------|------------------|\n| **界面提示主模型未配置** | 系统不知道你到底想用哪家的哪个模型 | 在 `.env` 中写上一句明白话:`LITELLM_MODEL=provider/你的模型名`。比如 `openai/gpt-5.5` |\n| **我写了好几家的Key,为什么死活只有一个生效?修改还没用?** | 你把 **极简模式** 和 **渠道模式** 混着写了! | 想好一条路走到黑——只要简单就删掉 `LLM_CHANNELS` 开头的;想要丰富备用切换就要全部转投到 `LLM_CHANNELS` 下的编制里。 |\n| **错误码报 400 或 401 或 Invalid API Key** | API Key 填错、少复制了一截、账号充值没到账、或者模型名字敲错(极度常见)。 | 1. 检查复制的 Key 前后是否有误填空格。<br> 2. 检查 Base URL 最后是不是少了一个 `/v1`。<br> 3. 检查模型名是否少写了 `openai/` 之类的前缀! |\n| **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`。 |\n| **转圈转不停,最后报 Timeout / ConnectionRefused 等** | 1. 在国内使用国外原版(像 Google、OpenAI),没开代理被墙了。<br>2. 你买的云服务器压根不能出境。 | 非常推荐使用**国内官方**(如DeepSeek、阿里)或者各种**兼容 OpenAI 的聚合中转接口**。因为中转站把网络问题帮你解决好了。 |\n| **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`) |\n\n*进阶老手的叮嘱:如果你开启了 **Agent (深度思考网络搜索问股) 模式**,这里有个经验之谈,推荐选用如 `deepseek-v4-pro` 这种逻辑推导能力更强的大模型。如果为了省钱用小微模型跑 Agent,它逻辑能力大概率跟不上,不仅达不到预期,还会白跑一堆空流程。*\n"}}, "docs/LLM_CONFIG_GUIDE_EN.md": {"1fcd5ce40f0567296c9280ba3186d2f21b7a404d": {"t": 1785769008.2274368, "n": 28617, "h": "81b72ac84617dd77", "c": "# LLM Configuration Guide\n\nWelcome! 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.\n\nThis 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](https://docs.litellm.ai/), 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.\n\nIf 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](./llm-providers.md). It is the maintained reference for provider presets, Actions variable mapping, runtime capability-check boundaries, and common error handling.\n\n---\n\n## Quick Navigation: Which section should you read?\n\n1. **[Beginners]** \"I just want to get the system running ASAP, keep it as simple as possible!\" -> [Go to Method 1: Simple Model Config](#method-1-simple-model-config-for-beginners)\n2. **[Advanced Users]** \"I have several Keys, want to configure fallback models, and define custom Base URLs.\" -> [Go to Method 2: Channels Mode Config](#method-2-channels-mode-config-advancedmulti-model)\n3. **[Veterans]** \"I want complex load balancing, request routing, and enterprise-level high availability!\" -> [Go to Method 3: Advanced YAML Config](#method-3-advanced-yaml-config-expert-setup)\n4. **[Local Models]** \"I want to use Ollama local models!\" -> [Go to Example 4: Using Ollama Local Models](#example-4-using-ollama-local-models)\n5. **[Vision Models]** \"I want to extract stock codes from images!\" -> [Go to Vision Model Config](#advanced-feature-vision-model-config)\n\n---\n\n## Method 1: Simple Model Config (For Beginners)\n\n**Goal:** Just paste your API Key and the model name to start using it immediately. No need to mess with complex concepts.\n\nIf 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`).\n\n### Anspire Open Example:\n\n> 💡 **[Anspire Open](https://open.anspire.cn/?share_code=QFBC0FYC)**: supports Chinese-optimized search and OpenAI-compatible model access using a shared key.\n> - The following values are configuration examples only; model availability depends on your account and Anspire console.\n> - Documentation examples do not replace connectivity validation; please validate with the Web \"Test connection\" flow before relying on production traffic.\n\n```env\n# Anspire Open API keys (multiple keys supported, separated by commas)\n# Get your key at: https://open.anspire.cn/?share_code=QFBC0FYC\n# When no higher-priority OpenAI-compatible source is set, this key is reused for Anspire search + LLM path (example fallback behavior only).\n# Example model: Doubao-Seed-2.0-lite; example gateway: https://open-gateway.anspire.cn/v6\nANSPIRE_API_KEYS=sk-xxxxxxxxxxxxxxxx\n# Optional: switch example model or gateway according to your Anspire account and official docs.\n# ANSPIRE_LLM_MODEL=Doubao-Seed-2.0-pro\n# ANSPIRE_LLM_BASE_URL=https://open-gateway.anspire.ai/v6\n```\n\n### Example 1: Using a Third-party OpenAI-Compatible Platform (Highly Recommended)\n\nMost 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:\n\n```env\n# Fill in the API Key provided by your platform\nOPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx\n# Fill in the platform's API Base URL (Very Important: Usually must end with /v1)\nOPENAI_BASE_URL=https://api.siliconflow.cn/v1\n# Fill in the specific model name (Very Important: You must add the \"openai/\" prefix so the system recognizes it)\nLITELLM_MODEL=openai/deepseek-ai/DeepSeek-V3 \n```\n\n### Example 2: Using the Official DeepSeek API\n```env\n# Fill in the API Key requested from the official DeepSeek platform\nDEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx\n```\n*Compatibility note: with only this line, the system still defaults to `deepseek/deepseek-chat` and logs a migration warning.*\n`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`.\n\n### Example 3: Using the Free Gemini API\n```env\n# Fill in your Google Gemini Key\nGEMINI_API_KEY=AIzac...\n```\n\n### Example 4: Using Ollama Local Models\n```env\n# Ollama requires no API Key; works after running ollama serve locally\nOLLAMA_API_BASE=http://localhost:11434\nLITELLM_MODEL=ollama/qwen3:8b\n```\n\n> **Important**: Ollama must be configured with `OLLAMA_API_BASE`. **Do not** use `OPENAI_BASE_URL`, or the system will concatenate URLs incorrectly (e.g. 404, `api/generate/api/show`). For remote Ollama, set `OLLAMA_API_BASE` to the actual address (e.g. `http://192.168.1.100:11434`). Current dependency constraint is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (matches requirements.txt).\n\n> **Congratulations! If you're a beginner, you can stop reading here and run the program!**\n> Want to test the connection? Open your terminal in the root directory and run: `python scripts/check_env.py --llm`\n\n---\n\n## Method 2: Channels Mode Config (Advanced/Multi-model)\n\n**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.\n\n**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.\n\n> **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}/models` and let you select multiple entries visually. The underlying storage format is still the existing comma-separated `LLM_{CHANNEL}_MODELS=model1,model2` value. 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.\n\n### First-run Setup Status\n\nThe 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.\n\n### Web channel editor: compatibility, migration, and rollback rules\n\n- The preset provider / Base URL / sample models are **form defaults only**. What gets persisted is still exactly what you submit in `LLM_{CHANNEL}_PROTOCOL`, `LLM_{CHANNEL}_BASE_URL`, `LLM_{CHANNEL}_MODELS`, and `LLM_{CHANNEL}_API_KEY(S)`; the editor does not silently rewrite them to a different provider name or URL.\n- \"Discover models\" only calls `{base_url}/models` for `OpenAI Compatible` / `DeepSeek` channels, and the default \"Test connection\" action sends one minimal chat completion request against the first model in the list and shows the backend-normalized `resolved_model` in the result. If the response includes `details.reason=model_access_denied` (for example, the observed Issue #1208 SiliconFlow / OpenAI Compatible sample returned `Model disabled` through LiteLLM), treat it as a best-effort model availability diagnostic based on provider wording: first confirm that the tested model is enabled for the current account/key, then adjust the model order or remove unavailable models before retrying. Provider messages not covered by this conservative rule, or provider messages with different semantics, continue to use the fallback diagnostic path. Optional runtime capability checks must be explicitly selected by the user and send additional JSON / tools / stream / vision smoke requests; the result only represents a best-effort check for the current account, model, and endpoint at that moment. The returned `stage / error_code / details / latency_ms / capability_results` fields are for structured diagnostics only, are **never persisted** back into `.env`, and do not block saving.\n- 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 and `model_access_denied`; first check account risk controls, region or request-source restrictions, model entitlement, relay gateway policy, and content-safety policy.\n- 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,<2.0.0`, LiteLLM `completion()` / OpenAI I/O format / streaming / exception mapping, and the OpenAI Chat Completions shapes for JSON mode, tool calling, streaming, and vision input.\n- 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>.\n- 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`, or `LITELLM_FALLBACK_MODELS` point 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/*`, and `xai/*` are kept as explicit direct-env compatibility examples for legacy retention behavior only, and are not a runtime availability guarantee.\n- 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`). Only `gemini`, `vertex_ai`, `anthropic`, `openai`, and `deepseek` are treated as managed key-backed providers; `cohere`, `google`, and `xai` are not in that allowlist, so they remain valid direct provider runtime entries.\n- 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`, and `LLM_TEMPERATURE` values from your desktop export / manual `.env` backup. No extra migration script is required.\n- The current dependency constraint for this flow in the repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (see `requirements.txt`). Regression coverage for it lives in `tests/test_system_config_service.py`, `tests/test_system_config_api.py`, and `apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx`.\n\n> **External provider model examples notice**: `cohere/*`, `google/*`, and `xai/*` 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 constraint `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` before production use.\n\n### Rollback & compatibility evidence\n\n- Scope and cleanup behavior under `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`: only runtime references (`LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `LITELLM_FALLBACK_MODELS`) are sanitized during save; non-channel direct providers such as `cohere/*`, `google/*`, and `xai/*` are preserved.\n- Rollback path: export desktop config, then restore the backup through `POST /api/v1/system/config/import`; or manually restore historical `.env` entries (`LITELLM_*`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `LLM_TEMPERATURE`) and restart.\n- Rollback evidence: `tests/test_system_config_service.py::test_import_desktop_env_restores_runtime_models_after_cleanup` covers restore from exported desktop backup after runtime cleanup.\n- 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`, and `test_validate_accepts_xai_model_as_direct_env_provider` cover the preserved direct-provider behavior.\n- Frontend regression commands: `cd apps/dsa-web && npm run lint && npm run build && npm run test -- src/components/settings/__tests__/LLMChannelEditor.test.tsx`.\n- Recommended rollback sequence (including UI reload): export desktop backup, restore via `POST /api/v1/system/config/import`, then call `GET /api/v1/system/config` to refresh the settings page and verify `LITELLM_MODEL` / `AGENT_LITELLM_MODEL` / `VISION_MODEL` / `LLM_TEMPERATURE` before continuing.\n\n### Official references for provider presets / Base URLs / model naming\n\n- OpenAI-compatible routing in LiteLLM: <https://docs.litellm.ai/docs/providers/openai_compatible>\n- OpenAI official API docs: <https://platform.openai.com/docs/api-reference/chat>\n- DeepSeek official API docs: <https://api-docs.deepseek.com/>\n- Anspire Open: <https://open.anspire.cn/?share_code=QFBC0FYC>\n- DashScope OpenAI-compatible mode: <https://help.aliyun.com/zh/model-studio/compatibility-of-openai-with-dashscope>\n- Moonshot / Kimi official compatibility docs: <https://platform.moonshot.ai/docs/guide/compatibility>\n- Anthropic official Messages API: <https://docs.anthropic.com/en/api/messages>\n- Gemini official OpenAI compatibility docs: <https://ai.google.dev/gemini-api/docs/openai>\n- Cohere official: <https://docs.cohere.com/>\n- Cohere API reference: <https://docs.cohere.com/reference/>\n- Cohere LiteLLM provider page: <https://docs.litellm.ai/docs/providers/cohere>\n- Google Gemini API and model list: <https://ai.google.dev/gemini-api/docs/openai>, <https://ai.google.dev/gemini-api/docs/models>\n- Google LiteLLM provider page: <https://docs.litellm.ai/docs/providers/gemini>\n- xAI official: <https://docs.x.ai/docs>\n- xAI LiteLLM provider page: <https://docs.litellm.ai/docs/providers/xai>\n- Ollama API docs: <https://github.com/ollama/ollama/blob/main/docs/api.md>\n\nIf 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:\n\n1. **Declare your channels first**: `LLM_CHANNELS=channel_name_1,channel_name_2`\n2. **Provide configurations for each channel** (Note the uppercase): `LLM_{CHANNEL_NAME}_XXX`\n\n### Example: Configuring DeepSeek and a Third-party Relay with Fallbacks\n```env\n# 1. Enable channel mode, declare two channels here: deepseek and aihubmix\nLLM_CHANNELS=deepseek,aihubmix\n\n# 2. Channel 1: Configure Official DeepSeek\nLLM_DEEPSEEK_BASE_URL=https://api.deepseek.com\nLLM_DEEPSEEK_API_KEY=sk-1111111111111\nLLM_DEEPSEEK_MODELS=deepseek-v4-flash,deepseek-v4-pro\n\n# 3. Channel 2: Configure a common relay/proxy API\nLLM_AIHUBMIX_BASE_URL=https://api.aihubmix.com/v1\nLLM_AIHUBMIX_API_KEY=sk-2222222222222\nLLM_AIHUBMIX_MODELS=gpt-5.5,claude-sonnet-4-6\n\n# 4. [Key Step] Specify the primary model and fallback list\n# Set your primary model:\nLITELLM_MODEL=deepseek/deepseek-v4-flash\n# Optional: set an Agent-only primary model (empty = inherit the primary model)\nAGENT_LITELLM_MODEL=deepseek/deepseek-v4-pro\n# If the primary model crashes, try these fallbacks sequentially:\nLITELLM_FALLBACK_MODELS=openai/gpt-5.4-mini,anthropic/claude-sonnet-4-6\n```\n\n### Example: Ollama Channel Mode (Local Models, No API Key)\n```env\n# 1. Enable channel mode, declare ollama channel\nLLM_CHANNELS=ollama\n\n# 2. Configure Ollama address (default local port 11434)\nLLM_OLLAMA_BASE_URL=http://localhost:11434\nLLM_OLLAMA_MODELS=qwen3:8b,llama3.2\n\n# 3. Specify primary model\nLITELLM_MODEL=ollama/qwen3:8b\n```\n\n### MiniMax Model Naming in Channel Mode\n\n- If you access MiniMax through an OpenAI-compatible channel, enter the model as `minimax/<model-name>` in the channel model list, for example `minimax/MiniMax-M1`.\n- 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>`.\n\n### Ask-Stock Agent / LiteLLM compatibility notes\n\n- The ask-stock Agent 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.\n- In YAML mode, the Agent reuses LiteLLM `model_list` / `model_name` routing semantics directly. In channel mode, it first reads `AGENT_LITELLM_MODEL`; when that is empty it inherits `LITELLM_MODEL`, then continues through `LITELLM_FALLBACK_MODELS`.\n- If you do not use YAML or Channels, leave `AGENT_LITELLM_MODEL` empty, 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>`, and `ANTHROPIC_API_KEY + ANTHROPIC_MODEL` -> `anthropic/<model>`.\n- 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_*`, or `LITELLM_*` settings.\n- 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.\n- The recommended forward path is still to configure `LITELLM_MODEL` / `AGENT_LITELLM_MODEL` explicitly or move to `LLM_CHANNELS`; legacy provider keys remain a compatibility fallback for older `.env` files, local macOS development, and existing deployments.\n\n### Kimi K2.6 Fixed-Temperature Compatibility Notes\n\n- Moonshot officially documents Kimi as an OpenAI-compatible API, with `https://api.moonshot.ai/v1` as the base URL: <https://platform.kimi.ai/docs/guide/kimi-k2-6-quickstart>\n- LiteLLM officially requires the `openai/` prefix for OpenAI-compatible model routing: <https://docs.litellm.ai/docs/providers/openai_compatible>\n- Moonshot's compatibility docs distinguish two fixed values: **thinking mode must use `1.0`, while non-thinking mode must use `0.6`**; other values are rejected by the API: <https://platform.moonshot.ai/docs/guide/compatibility#parameters-differences-in-request-body>\n- The current runtime dependency constraint in this repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (see `requirements.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.\n- This repository therefore normalizes `kimi-k2.6` and `kimi-k2.6-*` right before dispatch based on the **actual request mode**: default / thinking requests use `temperature=1.0`; if your LiteLLM YAML route alias explicitly sets `litellm_params.extra_body.thinking.type: disabled` (or an equivalent non-thinking override), it automatically switches to `temperature=0.6`. Your saved `LLM_TEMPERATURE` value in `.env` or the Web settings is not rewritten.\n- `SystemConfigService` only updates keys that you actually submit when saving from the Web settings page or importing a desktop `.env`; switching to Kimi does not silently clear, migrate, or rewrite an existing `LLM_TEMPERATURE`. The temporary `1.0/0.6` used for Kimi channel tests is request-scoped and is not persisted back into the config file.\n- Non-Kimi primary models, non-Kimi fallbacks, and any request after switching away from Kimi still use your configured temperature. Existing configs do not need migration; changing the model restores the original behavior automatically.\n- Repository-side compatibility coverage lives in `tests/test_llm_channel_config.py`, `tests/test_market_analyzer_generate_text.py`, `tests/test_agent_pipeline.py`, and `tests/test_system_config_service.py`.\n- Minimal rollback: revert only the Kimi fixed-temperature change set; no separate `LLM_TEMPERATURE` migration is required.\n\n> **Critical Warning**: If you enable `LLM_CHANNELS`, any standard `DEEPSEEK_API_KEY` or `OPENAI_API_KEY` declared independently will be **completely ignored**. **Use only one mode** to prevent configuration conflicts.\n> **Docker note**: If `LITELLM_MODEL`, `LLM_CHANNELS`, `LLM_DEEPSEEK_MODELS`, or related variables are explicitly passed through `docker compose environment:` or `docker run -e`, they will override the `.env` written by the Web settings page after a container restart. Update the deployment environment at the same time.\n\n---\n\n## Method 3: Advanced YAML Config (Expert Setup)\n\n**Goal:** I want maximum control and origin-level routing rules for enterprise-grade high availability.\n\nThis layer maps directly to the underlying LiteLLM routing capabilities, including high concurrency, automatic retries, and TPM/RPM-based load balancing.\n\n1. Keep only one declaration line in your `.env`:\n ```env\n LITELLM_CONFIG=./litellm_config.yaml\n ```\n2. Create a `litellm_config.yaml` in the project root directory (you can refer to `docs/examples/litellm_config.example.yaml`).\n\nExample `litellm_config.yaml`:\n```yaml\nmodel_list:\n - model_name: my-smart-model\n litellm_params:\n model: deepseek/deepseek-v4-flash\n api_base: https://api.deepseek.com\n api_key: \"os.environ/MY_CUSTOM_SECRET_KEY\" # Fetch from environment vars for security\n\n # Ollama local model (no api_key needed)\n - model_name: ollama/qwen3:8b\n litellm_params:\n model: ollama/qwen3:8b\n api_base: http://localhost:11434\n```\n\n> **Priority Rule**: YAML is king! If YAML is configured, both **Channels Mode** and **Simple Mode** are entirely ignored. Hierarchy: `YAML > Channels > Simple`.\n\n### GitHub Actions Notes\n\nThe bundled `daily_analysis.yml` explicitly passes the common LLM runtime fields to the job environment:\n\n- Runtime selection: `LLM_CHANNELS`, `LITELLM_MODEL`, `LITELLM_FALLBACK_MODELS`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `VISION_PROVIDER_PRIORITY`, `LLM_TEMPERATURE`\n- 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)\n- Common channel names: `primary`, `secondary`, `aihubmix`, `deepseek`, `dashscope`, `zhipu`, `moonshot`, `minimax`, `volcengine`, `siliconflow`, `openrouter`, `gemini`, `anthropic`, `openai`, `ollama`\n\nFor 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.\n\n---\n\n## Advanced Feature: Vision Model Config\n\nCertain 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`.\n\n```env\n# Specify your dedicated vision model name\nVISION_MODEL=openai/gpt-5.5\n# Make sure to provide its corresponding provider API KEY (e.g., OPENAI_API_KEY):\n# OPENAI_API_KEY=xxx\n```\n\n**Vision 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:\n```env\n# Default fallback sequence:\nVISION_PROVIDER_PRIORITY=gemini,anthropic,openai\n```\n\n---\n\n## Troubleshooting\n\nAfraid you got the config wrong? Type the following commands in your terminal to diagnose:\n\n- `python scripts/check_env.py --config`: Only verifies if the logic in your `.env` is structurally correct. (Provides instant results, no network calls, strictly checks for syntax omissions).\n- `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**.\n\n### Common Pitfalls\n\n| Weird Error You Got? | Likely Culprit | How to Fix It? |\n|----------------------|----------------|----------------|\n| **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`. |\n| **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. |\n| **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.<br> 2. Ensure your Base URL ends with `/v1`.<br> 3. Check if you forgot the `openai/` prefix on the model name! |\n| **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`. |\n| **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. |\n| **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 |\n\n*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.*\n"}}}}