ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

知识库容灾问答,TaoToken 的 Base URL 做主备切换

知识库容灾问答,TaoToken 的 Base URL 做主备切换 1. 知识库问答的生成链路为什么需要容灾给腾讯开源的知识库问答项目接 LLM 时很多人把注意力放在文档切片、Embedding 和向量检索上直到问答接口开始报401 invalid api key、429 rate limit exceeded、504 upstream timeout才发现生成链路只有一个 API Key 和一个 API 地址。这个单点不在知识库内部而在 LLM 供应商入口。更稳的做法是先去 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_intro拿到 KeyBase URL 固定为https://taotoken.net/api再把知识库问答服务的 LLM 调用层改成可健康检查、可主备切换的配置。本文从高可用工程师视角给出一套能直接复现的方案主备切换脚本、健康检查配置、问答恢复对照。这类知识库问答系统通常分成两段第一段是离线索引把 Markdown、PDF、Word、HTML 等文档解析、切块、向量化第二段是在线问答用户提问后先召回相关片段再把「问题 上下文」送给大模型生成答案。离线索引失败可以重跑在线问答失败会直接影响用户。如果 LLM API Key 写死在代码里、Base URL 写死在配置文件里一旦供应商侧限流、网络抖动、Key 被误删或模型临时不可用整条问答链路就会从「检索正常」变成「生成不可用」。高可用工程师要解决的不是「有没有 Key」而是「Key 和地址失效时服务还能不能回答」。目标可以拆成三个可验证产出主备切换脚本主通道异常时自动切到备用通道主通道恢复后按策略切回。健康检查配置定时探测 TaoToken Base URL 和最小模型请求提前发现不可用。问答恢复对照发生 401、429、超时、模型不存在时能按表判断是切 Key、切模型、切 Base URL还是降级返回检索片段。下面先不急着改源码先把配置入口标准化。知识库问答服务无论用什么语言最终都要落到三个变量base_url、api_key、model。只要这三个变量可替换主备切换就有落脚点。2. 接入前准备在 TaoToken 官网拿到 Key 与 Base URL知识库问答服务要调用大模型需要先有一个可用的 API Key。注册、申请 Key、查看控制台这些步骤不要在项目 issue 里找也不要用来源不明的共享 Key。直接去 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_prepare完成注册和 Key 创建。创建完成后你会拿到类似YOUR_API_KEY的占位符真实 Key 只放在环境变量或密钥管理服务里不要提交到 Git。基础配置如下export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYYOUR_API_KEY export PRIMARY_MODEL你的主模型名称 export SECONDARY_MODEL你的备用模型名称如果你用 OpenAI 兼容 SDK初始化方式如下from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_keyYOUR_API_KEY, timeout15.0, max_retries0, ) resp client.chat.completions.create( model你的主模型名称, messages[ {role: system, content: 你是知识库问答助手只根据给定上下文回答。}, {role: user, content: 问题如何配置主备切换\n上下文...}, ], temperature0.2, ) print(resp.choices[0].message.content)注意两个细节。第一base_url写成https://taotoken.net/api不要在后面拼接来源不明的代理地址。知识库问答服务里的「API 地址」「OpenAI Base URL」「LLM Endpoint」如果是同一个字段就统一读环境变量。第二Key 不要写在docker-compose.yml、config.yaml、前端.env或 Notebook 输出里。生产环境建议用 Kubernetes Secret、Docker Secret、Vault 或云厂商密钥管理。本地开发可以用.env.local并加入.gitignore。准备阶段还要确认模型名称。知识库问答对模型能力有要求要能遵循「只基于上下文回答」要能处理长上下文要能稳定输出引用片段。主模型可以选质量更高的备用模型可以选延迟更低或额度更充裕的。主备模型不要求完全一样但要在提示词和输出格式上做兼容。3. 把 Base URL 做成可切换变量知识库问答服务改造点腾讯开源的知识库问答项目通常会在服务端配置中读取 OpenAI 兼容参数。你要改的不是业务问答逻辑而是 LLM 客户端创建逻辑。理想结构是配置层从环境变量或配置中心读取LLM_BASE_URL、LLM_API_KEY、LLM_MODEL。路由层根据健康状态选择主通道或备用通道。调用层只负责发请求、超时控制、错误分类。观测层记录 provider、model、耗时、失败原因、是否降级。一个最小配置示例# knowledge_qa_llm.yaml llm: primary: base_url: https://taotoken.net/api api_key_env: TAOTOKEN_API_KEY model: 你的主模型名称 timeout: 15 secondary: base_url: https://taotoken.net/api api_key_env: TAOTOKEN_API_KEY_SECONDARY model: 你的备用模型名称 timeout: 20 failover: failure_threshold: 3 recovery_threshold: 5 cooldown_seconds: 60这里主备可以都走 TaoToken 的 Base URL但使用不同 Key、不同模型或不同通道。这样做的原因是知识库问答服务不需要感知底层供应商细节它只认「主配置」和「备配置」。当主配置连续失败达到阈值路由层切到备配置当主配置连续健康检查成功达到恢复阈值再切回。Python 配置读取示例import os from dataclasses import dataclass dataclass class LLMProvider: name: str base_url: str api_key: str model: str timeout: float 15.0 def load_provider(prefix: str) - LLMProvider: return LLMProvider( nameprefix, base_urlos.getenv(f{prefix}_BASE_URL, https://taotoken.net/api), api_keyos.getenv(f{prefix}_API_KEY, YOUR_API_KEY), modelos.getenv(f{prefix}_MODEL, 你的主模型名称), timeoutfloat(os.getenv(f{prefix}_TIMEOUT, 15)), ) primary load_provider(PRIMARY) secondary load_provider(SECONDARY)改造时注意不要在代码里写if 知识库 in question: use_model_a这种业务耦合逻辑。主备切换是基础设施能力和文档内容无关。问答服务只需要在生成阶段拿一个可用的 LLM 客户端。错误分类也很重要。不是所有错误都应该切换401、403Key 无效或无权限立即切备并告警。429限流可短重试后切备。500、502、503、504上游异常可重试后切备。timeout网络或上游慢达到超时阈值切备。model_not_found模型名错误先修正配置不要盲目切备。上下文超长应先截断或重新召回不要切备。把这些分类写进路由层后续排查会快很多。4. 主备切换脚本可复制的 Python 实现下面是一个可运行的主备切换脚本。它不依赖特定知识库框架只依赖 OpenAI 兼容 SDK。你可以把它放在llm_router.py在知识库问答服务的生成阶段调用router.ask(messages)。# llm_router.py import os import time import logging from dataclasses import dataclass from openai import OpenAI logging.basicConfig( levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, ) dataclass class Provider: name: str base_url: str api_key: str model: str timeout: float 15.0 def build_provider(prefix: str) - Provider: return Provider( nameprefix, base_urlos.getenv(f{prefix}_BASE_URL, https://taotoken.net/api), api_keyos.getenv(f{prefix}_API_KEY, YOUR_API_KEY), modelos.getenv(f{prefix}_MODEL, 你的主模型名称), timeoutfloat(os.getenv(f{prefix}_TIMEOUT, 15)), ) class FailoverRouter: def __init__(self, primary: Provider, secondary: Provider): self.primary primary self.secondary secondary self.failure_count 0 self.success_count 0 self.failure_threshold int(os.getenv(FAILURE_THRESHOLD, 3)) self.recovery_threshold int(os.getenv(RECOVERY_THRESHOLD, 5)) self.cooldown_seconds int(os.getenv(COOLDOWN_SECONDS, 60)) self.active primary self.last_switch_at 0.0 def _client(self, provider: Provider) - OpenAI: return OpenAI( base_urlprovider.base_url, api_keyprovider.api_key, timeoutprovider.timeout, max_retries0, ) def _chat(self, provider: Provider, messages: list) - str: client self._client(provider) resp client.chat.completions.create( modelprovider.model, messagesmessages, temperature0.2, ) return resp.choices[0].message.content or def _mark_success(self): self.success_count 1 self.failure_count 0 now time.time() if ( self.active secondary and self.success_count self.recovery_threshold and now - self.last_switch_at self.cooldown_seconds ): self.active primary self.last_switch_at now logging.info(切回主通道 primary%s, self.primary.model) def _mark_failure(self, reason: str): self.failure_count 1 self.success_count 0 if ( self.active primary and self.failure_count self.failure_threshold ): self.active secondary self.last_switch_at time.time() logging.warning( 主通道失败达到阈值切换到备用通道 reason%s secondary%s, reason, self.secondary.model, ) def ask(self, messages: list) - str: providers ( [self.primary, self.secondary] if self.active primary else [self.secondary, self.primary] ) last_error None for provider in providers: try: start time.time() content self._chat(provider, messages) cost time.time() - start logging.info( LLM 调用成功 provider%s model%s cost%.2fs, provider.name, provider.model, cost, ) self._mark_success() return content except Exception as exc: last_error exc logging.error( LLM 调用失败 provider%s model%s error%s, provider.name, provider.model, repr(exc), ) self._mark_failure(repr(exc)) raise RuntimeError(f主备通道均失败: {last_error!r})使用方法primary build_provider(PRIMARY) secondary build_provider(SECONDARY) router FailoverRouter(primary, secondary) answer router.ask([ {role: system, content: 你是知识库问答助手只根据上下文回答。}, {role: user, content: 问题主备切换的阈值是多少\n上下文...}, ]) print(answer)这个脚本的重点不是代码多复杂而是把「失败计数、切换阈值、冷却时间、切回条件」显式化。生产环境可以把状态存到 Redis避免多副本各自判断也可以加入 Prometheus 指标例如llm_failover_total、llm_provider_latency_seconds、llm_active_provider。但即使先从单实例内存状态开始也比写死一个 Key 要稳。如果主备都走https://taotoken.net/api建议至少使用不同的 Key 或不同的模型。这样当一个 Key 被限流或误删时另一个 Key 仍能工作当一个模型临时不可用时另一个模型可以兜底。切换脚本不关心底层是谁只关心base_url api_key model这组配置是否可用。5. 健康检查配置提前发现问答不可用主备切换脚本解决「调用时失败」健康检查解决「调用前发现」。知识库问答服务不应该等用户提问失败后才切备。可以用定时任务每分钟探测一次主备通道探测成功只发一个极短请求避免消耗大量 token。健康检查脚本# healthcheck.py import os import sys import time from openai import OpenAI def check(prefix: str) - bool: base_url os.getenv(f{prefix}_BASE_URL, https://taotoken.net/api) api_key os.getenv(f{prefix}_API_KEY, YOUR_API_KEY) model os.getenv(f{prefix}_MODEL, 你的主模型名称) client OpenAI(base_urlbase_url, api_keyapi_key, timeout10.0, max_retries0) start time.time() try: resp client.chat.completions.create( modelmodel, messages[{role: user, content: ping}], max_tokens1, temperature0, ) cost time.time() - start ok bool(resp.choices) print(f{prefix} ok{ok} cost{cost:.2f}s base_url{base_url}) return ok except Exception as exc: print(f{prefix} failed base_url{base_url} error{repr(exc)}) return False if __name__ __main__: primary_ok check(PRIMARY) secondary_ok check(SECONDARY) if not primary_ok and not secondary_ok: sys.exit(2) if not primary_ok: sys.exit(1) sys.exit(0)本地运行export PRIMARY_BASE_URLhttps://taotoken.net/api export PRIMARY_API_KEYYOUR_API_KEY export PRIMARY_MODEL你的主模型名称 export SECONDARY_BASE_URLhttps://taotoken.net/api export SECONDARY_API_KEYYOUR_API_KEY_SECONDARY export SECONDARY_MODEL你的备用模型名称 python3 healthcheck.py如果放在 Kubernetes 中可以用 CronJob 定时执行apiVersion: batch/v1 kind: CronJob metadata: name: llm-healthcheck spec: schedule: */1 * * * * jobTemplate: spec: template: spec: restartPolicy: Never containers: - name: healthcheck image: your-registry/kb-qa:latest command: [python3, healthcheck.py] envFrom: - secretRef: name: taotoken-llm-secret健康检查结果要接入告警。推荐策略主通道连续 3 次失败告警并触发切换。备通道失败告警但暂不切换因为主通道可能仍可用。主备均失败严重告警知识库问答服务进入降级模式只返回检索片段和原文引用。主通道恢复连续 5 次成功延迟一个冷却窗口再切回避免抖动。如果你用 systemd也可以写成 timer# /etc/systemd/system/llm-healthcheck.service [Unit] DescriptionLLM healthcheck [Service] Typeoneshot EnvironmentFile/etc/kb-qa/llm.env ExecStart/usr/bin/python3 /opt/kb-qa/healthcheck.py# /etc/systemd/system/llm-healthcheck.timer [Unit] DescriptionRun LLM healthcheck every minute [Timer] OnCalendar*:*:00 Persistenttrue [Install] WantedBytimers.target健康检查不要只检查域名能不能解析也不要只curl首页。要发一个真实的最小模型请求因为 401、429、模型不存在、额度耗尽这些问题只有在真实调用时才会暴露。请求设置max_tokens1成本可控。健康检查日志里保留base_url、model、耗时和错误码方便后续和问答恢复对照表结合排查。6. Claude Code、Codex 与 CC Switch 的供应商配置知识库问答服务本身可能是一个 Web 服务但排障和文档维护过程中你可能会用 Claude Code、Codex 等工具。它们的配置也要和知识库问答服务保持一致Base URL 用https://taotoken.net/apiKey 用YOUR_API_KEY。注意不要把 Claude Code 的ANTHROPIC_*变量套到 Codex 上两者配置格式不同。Claude Code 可以用settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: 你的主模型名称, ANTHROPIC_SMALL_FAST_MODEL: 你的备用模型名称 } }也可以在 shell 中临时导出export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODEL你的主模型名称Codex 用config.toml不要写ANTHROPIC_*model 你的主模型名称 model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY对应的环境变量export TAOTOKEN_API_KEYYOUR_API_KEY如果你用 CC Switch 管理多个供应商记住三件套Provider 名称例如TaoToken-Primary、TaoToken-Secondary。Base URLhttps://taotoken.net/api。API KeyYOUR_API_KEY备用配置用另一个 Key。切换时不要只改模型名要同时确认 Base URL 和 Key 属于同一组配置。常见错误是主配置改了 Base URL备用配置还留着旧地址导致切换后仍然失败。CC Switch 里可以为知识库问答单独建一组配置主用高能力模型备用低延迟模型出现 429 或超时时先切到备用配置验证问答恢复再回头排查主配置。如果你还没有 Key可以到 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_console创建和管理。创建后不要直接粘贴到聊天窗口或 issue 中只放在本机环境变量或密钥管理里。7. 问答恢复对照从报错到动作主备切换和健康检查都配好后还需要一张恢复对照表。它的作用是值班时看到日志里的错误码不用重新推理直接按动作执行。故障场景典型现象健康检查结果切换动作恢复验证主 Key 被删除或失效401 invalid api key主通道失败备通道正常切到备用 Key禁用主 Key 配置用备用配置发一次问答确认返回答案主 Key 被限流429 rate limit主通道间歇失败短重试后切备降低主通道并发观察 5 分钟确认 429 消失上游超时504、timeout主通道耗时超过 10 秒切到备通道检查网络和 DNS问答延迟恢复到阈值内模型名错误model_not_found主备均可能失败不切换修正模型名配置重启服务后健康检查通过上下文超长context_length_exceeded健康检查正常不切换调整召回条数或截断策略用长文档问题验证不再报错主备均不可用连续失败主备均失败进入降级模式返回检索片段问答接口返回引用和原文不返回模型生成主通道恢复健康检查连续成功主通道成功 5 次冷却后切回主通道观察无抖动再关闭告警问答恢复对照不仅要看「接口是否返回 200」还要看「答案是否基于知识库」。有些备用模型虽然能返回内容但可能忽略上下文开始自由发挥。建议在切换后做三项验证引用验证答案中是否包含召回文档的片段或来源。拒答验证问一个知识库中没有的问题模型是否明确说不知道。延迟验证P95 延迟是否在可接受范围内。可以写一个简单的验证脚本# verify_qa.py import os from openai import OpenAI client OpenAI( base_urlos.getenv(PRIMARY_BASE_URL, https://taotoken.net/api), api_keyos.getenv(PRIMARY_API_KEY, YOUR_API_KEY), timeout20.0, ) cases [ { name: 有答案, question: 主备切换的失败阈值默认是多少, context: 失败阈值 failure_threshold 默认 3连续失败达到 3 次触发切换。, }, { name: 无答案, question: 知识库里有没有提到火星移民预算, context: 知识库只包含运维手册和产品文档。, }, ] for case in cases: resp client.chat.completions.create( modelos.getenv(PRIMARY_MODEL, 你的主模型名称), messages[ {role: system, content: 只根据上下文回答不知道就说不知道。}, {role: user, content: f问题{case[question]}\n上下文{case[context]}}, ], temperature0, ) print(case[name], resp.choices[0].message.content)切换后跑一遍这个脚本比只看日志更可靠。知识库问答的核心不是「模型能说话」而是「模型按知识库说话」。另外日志字段建议至少包含request_id一次用户提问的追踪 ID。providerprimary 或 secondary。base_urlhttps://taotoken.net/api。model实际调用的模型名。latency_ms调用耗时。fallback_reason切换原因例如429、timeout、401。retrieval_hit召回片段数量。这样出现问题时你可以快速回答三个问题是不是知识库召回失败是不是 LLM 通道失败是不是切换后模型不遵循上下文8. 上线检查清单与 CTA最后给出一份上线检查清单。知识库问答服务接入 TaoToken 后按这个清单逐项确认可以避免大部分「文档入库成功但问答失败」的问题。[ ] 注册和 Key 创建已到 TaoToken 官网完成Key 未提交到 Git。[ ]base_url使用https://taotoken.net/api没有硬编码在业务代码中。[ ] 主备配置分别有独立的base_url、api_key、model。[ ] 主备切换脚本已加入失败阈值、冷却时间、恢复阈值。[ ] 健康检查每分钟执行主备均检查失败有告警。[ ] 问答恢复对照表已放到值班手册。[ ] Claude Code 使用settings.json或ANTHROPIC_*Codex 使用config.toml没有混用。[ ] CC Switch 三件套配置正确Provider、Base URL、API Key。[ ] 降级模式已实现主备均失败时返回检索片段和来源。[ ] 切换后验证脚本已跑通确认答案遵循上下文。如果你还没有开始配置可以按下面路径快速进入先看模型对话能力https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_chat需要长期编码和问答额度查看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_coding_plan创建或管理 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_api_keys需要配置 Claude Code参考文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentkb_failover_claude_doc把 Base URL、API Key、模型名做成可切换配置后知识库问答服务就从「依赖单个入口」变成「主备可切换、健康可探测、故障可恢复」。文档变成知识库只是第一步让问答链路在异常时仍然可用才是高可用工程师真正要交付的部分。
返回列表