Dayward AI
Week 1 · D4About 6 hours

Model Integration and System Prompts: a Multi-Provider Abstraction With Fallback, Overriding the Default Persona (dg P03/P04/M04)

Learn to abstract multiple model providers behind one layer with automatic failover, and understand why you must override the framework's default system prompt.

Today's goals 0/3

Sign in to tick these off and save your progress.

今日目标

  1. 能实现一个至少支持 3 个 provider、可按配置切换的模型调用层
  2. 能给 Agent 写一段自定义 system prompt,覆盖框架默认人设
  3. 能说出模型调用失败时 fallback 策略要考虑哪些因素(延迟、成本、可用性)

前三天你的代码里始终只有一家 provider:地址写死在 fetch 里,模型名最好也不过来自一个 MODEL 环境变量(D1 实验第 4 步就是这么干的)——换模型 id 可以,换厂商就得动代码。今天把这层彻底拆开。读完回来把上面三条勾掉。

小白版讲解

只接一家模型,等于把命交给别人的运维

想象你开了一家只有一个供货商的餐厅。菜品、价格、出餐速度全靠他,平时相安无事,直到某天早上他的货车抛锚——你不是"生意差一点",你是今天开不了门

接模型也是同一件事。代码里写死 openai/gpt-4o-mini,等于把服务的可用性完全绑定在某一家厂商的运维水平上。而大模型 API 的可用性比你想象的低:区域性故障、账号触发限流(rate limit)、模型被下线或改名、某次调用莫名卡住 60 秒不返回——这些不是"小概率意外",是每月都会遇上几次的日常。

算笔账你就明白为什么不能赌。假设某家厂商的月可用性是 99.5%,听着很高,但那意味着一个月里有约 3.6 小时你的 Agent 是废的,用户看到的全是转圈和报错。而如果接了三家、且它们的故障互不相关,三家同时挂掉的概率是 0.5% 的三次方,即千万分之一点二五——不可用时间从 3.6 小时(12960 秒)掉到 0.33 秒,一秒都不到。同样的代码量级,可用性差了四个多数量级,约四万倍。

但这个数字有个必须说清的前提:故障互不相关。 本章的示例和今天的实验为了让你一把 key 就跑通,三个 provider 全走 OpenRouter 这一个聚合网关——那是三个模型 id,不是三条独立的路:网关一挂、key 一被封,三家一起完蛋,聚合网关自己成了新的单点。真正的冗余要接不同厂商的直连端点,凭证、机房、计费各自独立,那个四万倍才算数。面试官爱从这里切进来,所以要主动说:数量级是拿独立当前提算的,共用网关不满足这个前提。

问题是,前三天的代码离"接三家"有多远?看一眼你现在写的:

hardcoded.js
// 前三天的写法:地址写死在调用处,模型名最好的情况来自一个环境变量
const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: process.env.MODEL ?? 'openai/gpt-4o-mini',
    messages,
  }),
})
const json = await res.json()
// 这一家挂了怎么办?没有别的路可走——整个服务跟着它一起挂。

这段代码没有一行是"错"的,问题在于它只有一条路MODEL 变量看着像留了灵活性,可它换得了模型 id、换不了厂商:地址、鉴权头、响应字段的形状全焊死在这次调用里。真出事时你只能改变量重启,再祈祷另一家的响应格式恰好一样。

还有一个更现实的理由:价格和能力每个月都在变。今天最划算的模型,两个月后可能被另一家的新版本超越,价格还便宜一半。如果换模型意味着重写调用代码、重测所有 prompt,你就会因为"太麻烦"而一直用着贵的慢的那个。接入成本高,本质上是在剥夺你未来做选择的自由。

所以生产级 Agent 的第一条规矩是:模型是可替换的零件,不是写死的地基。

那问题来了——三家的请求格式、响应结构、报错方式各不相同,接三家岂不是要把调用代码写三遍?答案当然是"加一层抽象",但难的不是那一层,是它背后的三件事:哪几类错误重试就是白烧钱(404 被绝大多数人归错了桶)、fallback 怎么在你毫无察觉时把账单翻三倍、以及一个一年省六万多的分层路由算法。 下面挨个讲清楚。

统一调用层:把三家的怪癖收进一个接口

答案是加一层。这层的职责只有一句话:对上暴露一个统一的函数,对下把各家的差异全部吃掉。

用生活里的例子类比,它就是电源转换头。笔记本只认一种插头,世界上却有十几种插座标准;转换头的价值不在于多聪明,而在于它把"世界的混乱"挡在了你的设备之外。统一调用层一模一样:不管底下是 OpenAI 的格式、Anthropic 的格式,还是某个开源模型的兼容接口,上层拿到的永远是同一个形状。

先定义"同一个形状"是什么。输入:一段 messages、一个可选的温度、一个可选的超时。输出:回复文本、实际用了哪个 provider、消耗了多少 token。注意输出里那个"实际用了哪个 provider"——它不是调试信息,是这层的核心产出,后面的 fallback 和成本核算全靠它。

model-router.js
const PROVIDERS = [
  { name: 'fast', model: 'openai/gpt-4o-mini', timeoutMs: 8000 },
  { name: 'strong', model: 'anthropic/claude-3.5-sonnet', timeoutMs: 15000 },
  { name: 'backup', model: 'google/gemini-2.0-flash-001', timeoutMs: 8000 },
]
 
// 带结构化 status 字段的错误:让上层读字段,而不是去解析错误消息字符串
class ProviderError extends Error {
  constructor(message, status) {
    super(message)
    this.name = 'ProviderError'
    this.status = status
  }
}
 
async function callOne(provider, messages) {
  const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ model: provider.model, messages }),
    signal: AbortSignal.timeout(provider.timeoutMs),
  })
  if (!res.ok) throw new ProviderError(`${provider.name} 返回 HTTP ${res.status}`, res.status)
  const json = await res.json()
  return {
    text: json.choices[0].message.content,
    provider: provider.name,
    tokens: json.usage.total_tokens,
  }
}

这里有个容易被忽略的细节:超时写在配置里,而且三家不一样。"强模型"本来就慢,给它 8 秒会把大量本可成功的请求掐死;"快模型"如果 8 秒还没吐第一个字,基本可以判定这次废了。超时值必须按 provider 分别设,一刀切的全局超时是新手最常见的配置错误。

顺带说一个四种语言都躲不掉的细节:各家返回体的字段命名并不统一,OpenAI 系用 snake_case 的 total_tokens,而 Swift、Java 的模型对象习惯写 camelCase。解码时要么显式写映射(Swift 用 CodingKeys,Java 用 @JsonProperty),要么给解码器开全局的 snake_case 转换策略。上面 Swift 那份用的是前者——别指望字段名自动对上,这是接第二家 provider 时最高频的低级 bug。

最后划一条边界:统一层只负责"把请求送出去、把结果收回来",不要往里塞业务逻辑。 见过有人图省事把"客服场景自动追加一段提示词"写进这层,结果换条业务线就得改公共代码,改一次三个调用方一起回归。这层越薄,能服务的调用方越多。

fallback:什么时候该换,什么时候不该换

你去 ATM 取钱,机器吐出一张纸条。上面写"本机现金不足"——你换一台就行;写"您的卡已挂失"——你把全城的 ATM 跑一遍也没用。同样是"换一台机器",值不值得做完全取决于失败的原因

模型调用一模一样。难点不在那个 for 循环,在于判断"这个错误值不值得换一家再试"。 直觉陷阱是:既然都失败了那就统统重试呗。错——分类必须发生在重试之前,而分类的依据不是状态码本身,是一句话:换一家有没有可能变好。

  • 换了也没用:400 请求体不合法、403 被安全策略拦截。请求本身有问题,重试只是把同一个 bug 再犯两遍。
  • 换了大概率就好:408 超时、429 限流、5xx 服务端故障,典型的"对方的问题"。
  • 最容易被归错桶的是 404 和 402。 404 通常意味着你写的模型 id 被下线或改名——本章开头列举的日常故障之一,它长得像"你的问题",换一家却完全可能成功;402 是余额不足,换个有钱的账号当然能救。把 4xx 一刀切归成"不该重试"是这里最常见的错误。
  • 401 看你怎么管凭证:三家共用一把网关 key 时换谁都一样;各有各的 key 时,A 的被吊销、切到 B 完全能救。

还有一件必须做对:判断要读结构化字段,别解析错误消息字符串。 err.message.startsWith('HTTP ') 这种写法,在你改一次日志文案的那天就会静默失效。所以下面每份代码都先定义一个带 status 的错误类型。

fallback.js
// 402 余额不足、404 模型被下线:也属于"换一家有可能变好",别漏
const RETRIABLE_STATUS = new Set([402, 404, 408, 429])
 
function shouldTryNext(err) {
  if (err.name === 'TimeoutError') return true
  if (err instanceof ProviderError) {
    return RETRIABLE_STATUS.has(err.status) || err.status >= 500
  }
  // 401 在这里落进 false,前提是本课三家共用同一把 OpenRouter key;
  // 各家凭证独立时要把 401 单独判成「该换下一家」
  return false // 400/403:你自己的问题,换谁都一样
}
 
async function callModel(messages) {
  const errors = []
  for (const provider of PROVIDERS) {
    try {
      return await callOne(provider, messages)
    } catch (err) {
      errors.push(`${provider.name}: ${err.message}`)
      if (!shouldTryNext(err)) throw err
    }
  }
  throw new Error(`所有 provider 都失败了:${errors.join('; ')}`)
}

这段循环只有十几行,代价却是三笔账。

第一笔是钱。 切到下一家意味着同一段 prompt 你付了两次费,三家链路最坏是三倍成本。平时看不出来,因为正常路径只调一家;一旦主 provider 抖动,账单会在你毫无察觉时涨上去,所以"fallback 触发次数"必须是被监控的指标。

第二笔是延迟。 串行 fallback 的总耗时是各家超时值的累加:三家各给 15 秒,最坏用户要等 45 秒——不如在 20 秒时干脆失败。所以除了每家的超时,还要有一个总预算

第三笔最贵,叫雪崩。 主 provider 限流的那一刻,全部流量会在同一秒压到备用上;而备用的配额是按"平时分一点流量"申请的,扛不住突然翻倍,于是它也开始返回 429,流量继续压向第三家——三家依次倒下,比只接一家还惨。解法是熔断:给每个 provider 记连续失败计数,超阈值就暂时跳过,冷却后放少量流量试探,成功才恢复。它和微服务网关里的熔断器同源,面试问得非常频繁。

系统提示词:框架默认人设是个定时炸弹

招一个新客服上班,你会先给他一份岗位说明:代表哪家公司、什么能答什么不能答、话术多长、查订单走哪个系统。要是什么都不交代就把他推到工位上,他照样会接待客户——用他从别处学来的那一套。你没写的部分从来不是空白,是别人替你填的默认值。

第三天你上手 Pi SDK 时可能没注意:你没写 system prompt,但 Agent 依然表现得像个助手。 那段人设就是框架替你填的默认值。

这在 demo 阶段很贴心,上线之后就是隐患。默认人设通常写着"你是一个乐于助人的 AI 助手"这类通用描述,它带来三个具体的麻烦:

第一,它不知道你的业务边界。用户问"帮我写一封辞职信",通用助手会热情地写;但你的产品是电商客服,这个回答就是彻底跑题,还浪费 token。

第二,它的输出格式不受你控制。默认人设不会约束"回答不超过三句"或"不要用 Markdown 标题",于是聊天气泡里冷不丁冒出一个二级标题,前端样式全乱。

第三,也是最要命的——它会随框架升级而改变。你测好的所有行为,都建立在一段你没写、看不见、还可能在下次 pnpm update 后悄悄变掉的文本上。这类 bug 极难排查,因为代码一行没动。

所以规矩是:永远显式写 system prompt,哪怕只有一句话。 生产环境里它通常不是一个写死的字符串,而是拼出来的模板:

TextText
你是「某电商平台」的售后助手。
 
【能力边界】
- 只回答订单、退换货、物流相关问题
- 被问到无关问题时,礼貌说明你只负责售后,不要尝试回答
 
【输出要求】
- 每次回复不超过 3 句话,不使用 Markdown 标题和列表
- 涉及金额和时效时必须调用工具查询,禁止凭记忆回答
 
【当前上下文】
- 现在时间:2026-09-04 14:30
- 用户等级:金卡
- 可用工具:query_order、apply_refund

看这个结构:人设 + 能力边界 + 输出要求 + 动态上下文。前三块是静态模板,最后一块每次请求现拼。把"现在时间"塞进去是个高频考点——模型没有时钟,你不告诉它今天几号,它算不出"三天前下的单"是哪天。

路由:让便宜模型干粗活

有了统一层和 fallback,最后一步是主动选模型,而不是被动等失败。

核心事实是:不同模型的价格能差 50 倍以上,但你的任务里有很大一部分根本不需要最强的模型。判断"用户这句话是不是在问订单",一个最便宜的小模型就能做到 99% 准确;而"根据这三份合同起草一段争议条款",你必须上最强的。用旗舰模型做意图识别,就像开跑车去楼下取快递。

常见的路由维度有三个:

维度怎么判断典型做法
任务类型这一步是分类、抽取,还是长文本推理分类抽取走便宜模型,推理生成走强模型
延迟要求用户在等着看,还是后台批处理前台走低延迟模型,后台任务可以用慢但便宜的
输入长度prompt 有多少 token超长上下文只有部分模型支持,且价格陡增

举个能立刻感受到的数字。假设客服 Agent 每天 1 万轮对话,每轮平均 2000 token。全走旗舰模型按每百万 token 15 元算,一天约 300 元;而至少六成轮次只是意图识别和信息抽取这类粗活,把它们改走每百万 token 0.3 元的小模型,一天成本掉到 125 元左右——一年省六万多,用户完全感知不到差别。所以面试官问"你们怎么控成本"时,想听到的第一个答案往往不是"压缩上下文",而是"分层路由"。

实现上不复杂——给 callModel 加一个 tier 参数,让调用方声明这次要"快"还是要"强",路由层据此挑起始 provider,fallback 逻辑完全复用。先按任务类型静态分档,别一上来就做"让模型自己判断该用哪个模型"的动态路由,那方案本身又要多一次模型调用,延迟和成本都可能得不偿失。

源码导读

动手实验

🧪 D4 实验:3 provider 可切换模型层 + 自定义 system prompt

Code location: labs/agent-30days/day-04-multi-provider-router

验收标准:

  1. MOCK=1 pnpm start 打印出完整的降级路径,形如 [降级路径] fast 超时(模拟) → strong 被限流(模拟) → backup 成功
  2. fastmockFailure'timeout' 改成 'bad-request'(模拟层会抛一个 400),重跑后确认程序打印 [不重试] … 并直接抛出,没有再去试 strongbackup
  3. 同一条 MOCK=1 pnpm start 帮我写首诗,用默认人设跑会给你写诗,写完能力边界后改口拒答——从跑题变成拒答才是提示词生效的证据。
  4. 每轮结束打印 [usage] provider=… tokens=…,换几个问题连问五轮,能看出命中分布。
  5. pnpm typecheck 通过,没有 any

starter/ 里挖了四个练习点,MOCK=1 下完全离线跑通:模拟层按 mockFailure 制造超时、限流和 400,不用真把账号打到限流就能验证 fallback。里面的假模型还会读你拼出来的 system prompt——没声明能力边界就有问必答,声明了才拒答。buildSystemPrompt 的默认返回值正是本章批判的那句敷衍人设,先原样跑一次「帮我写首诗」,你会亲眼看到它跑题。

  1. 把三个 provider 的配置抽成数组,每个带独立的超时值,写出统一的 callOne
  2. 实现 shouldTryNext:按"换一家有没有可能变好"分类——400 和 403 直接抛出,408、429、5xx 以及最容易被漏掉的 402、404 继续下一家。判断要读 ProviderError 上的 status 字段,不许解析错误消息字符串。
  3. 先用默认人设问一个业务外的问题,记住它跑题的样子;再写一段自定义 system prompt(人设 + 能力边界 + 输出要求 + 当前时间),跑同一条命令看它改口拒答。
  4. MOCK=1 让第一个 provider 超时、第二个返回 429,验证最终由第三个成功返回,并且日志里能看到完整的降级路径。
  5. 把每次调用的 provider 名和 token 数打印出来,连问五轮,观察成本分布。

面试题

今天 4 道题在下方题库区,覆盖多 provider 的动机、fallback 权衡、system prompt 的作用与成本延迟取舍。展开后先看"分析过程"再看要点——第 2 题的追问(熔断和雪崩)是这一章最容易被问到的地方,别跳过。

检查清单与明日预告

  • 能实现一个至少支持 3 个 provider、可按配置切换的模型调用层
  • 能给 Agent 写一段自定义 system prompt,覆盖框架默认人设
  • 能说出模型调用失败时 fallback 策略要考虑哪些因素(延迟、成本、可用性)
  • 能说清哪些错误该 fallback、哪些不该,并解释为什么 400 重试是浪费、404 反而应该换一家
  • 实验的降级路径日志能看到"超时 → 429 → 成功"的完整链路
  • 4 道面试题不看要点也能答出至少 3 道

明天(D5)我们把注意力从"怎么调模型"转到"怎么让模型调你"。你会给 Agent 装上真正的工具系统:用 schema 校验参数,在工具报错时把错误信息原样回传给模型让它自己纠错,再用事件订阅把 Agent 内部每一步都暴露出来。今天的 fallback 处理的是"外部服务不可靠",明天的错误回传处理的是"模型自己会犯错"——这两件事叠在一起,才是生产级 Agent 稳定性的真正来源。

Interview questions

  • Why do production agents usually integrate more than one model provider?为什么生产级 Agent 通常要接入多个模型 provider?
    Common in ChinaCommon overseasBasic#model-routing#reliability

    How to reason about it · think before answering

    1. First decide whether this is an availability question or an architecture question; answering only 'so it doesn't go down' reads as inexperienced.
    2. Follow the causal chain: the model API is an external dependency, dependencies have failure rates, your ceiling is capped by theirs, so you either accept the cap or add redundancy.
    3. Quantify it: 99.5% monthly availability is about 3.6 hours of downtime; three independently failing providers push that to seconds. Orders of magnitude beat adjectives.
    4. The second reason shows engineering maturity: model pricing and capability shift monthly, and high switching cost means you stay on the expensive slow one out of inertia — coupling really costs you future optionality.
    5. Say the premise out loud before they ask: that order of magnitude assumes the three providers fail independently. If all three are model ids behind one aggregator gateway on a single key — which is what most first versions look like — the gateway going down takes all three with it, the redundancy is fake, and the aggregator has become the new single point of failure. Real independence means direct endpoints at different vendors, with separate credentials and billing. Naming this yourself signals operational experience far more than reciting 0.005 cubed.
    6. Expect the follow-up: isn't this more expensive? No — the happy path calls one provider; what costs money is fallback firing often, which is a signal to investigate the primary, not to remove redundancy.

    分析过程 · 先想清楚再作答

    1. 先判断这题问的是「可用性」还是「架构」。只答「防止挂掉」拿不到分,因为面试官想看的是你有没有真的算过账、踩过坑。
    2. 从一条因果链推:模型 API 是外部依赖 → 外部依赖必然有故障率 → 你的可用性上限被它锁死 → 所以要么接受这个上限,要么加冗余。
    3. 把可用性说成数字才有说服力:单家 99.5% 意味着每月约 3.6 小时不可用;三家独立故障时理论不可用时间降到秒级。数量级差异比形容词有力得多。
    4. 第二个理由往往被忽略,但更能体现工程视角:模型的价格和能力每月都在变,接入成本高会让你因为「改起来麻烦」而一直用贵的慢的那个——高耦合真正的代价是剥夺未来的选择权。
    5. 这里有个必须自己先说破的前提:那个数量级是拿「三家故障互不相关」算出来的。如果三家其实都走同一个聚合网关、共用同一把 key(很多人的第一版就是这样),网关一挂三家一起挂,冗余是假的,聚合网关反而成了新的单点。真正的独立要落到不同厂商的直连端点、各自的凭证和计费上。主动点破这一条,比背出 0.005 的三次方更能体现你真的部署过。
    6. 可以预期的追问:多接几家不是更贵吗?答案是不会——正常路径只调一家,多的只是配置和一层抽象;真正贵的是 fallback 被频繁触发,那说明你该查主 provider 而不是砍掉冗余。

    Key points

    • The model API is an external dependency; outages, rate limits and model deprecations are monthly realities
    • 99.5% monthly availability is roughly 3.6 hours down; multi-provider redundancy cuts that by orders of magnitude
    • Pricing and capability shift constantly, so an abstraction layer turns model swaps into config changes
    • The happy path still calls one provider — redundancy costs an abstraction, not a multiplied bill

    答题要点

    • 模型 API 是外部依赖,厂商故障、限流、模型下线都是每月都会遇到的日常,不是小概率事件
    • 单家 99.5% 可用性等于每月约 3.6 小时不可用;多家冗余能把理论不可用时间降低几个数量级
    • 价格与能力每月都在变,统一抽象层让换模型变成改配置,保住了未来做选择的自由
    • 正常路径只调一家,冗余的成本是一层抽象而不是多倍账单
  • What trade-offs shape a model fallback strategy?设计模型 fallback 策略时要权衡哪些因素?
    Common in ChinaCommon overseasIntermediate#model-routing#reliability#cost

    How to reason about it · think before answering

    1. The word 'trade-offs' is the hinge: they are not asking for a for-loop, they want to know you understand fallback has costs.
    2. First key judgment: not every error deserves a fallback, and the test is not the leading digit of the status code but whether another provider could plausibly succeed. A 400 (malformed body) or 403 (blocked by safety policy) fails everywhere, so retrying repeats your own bug at double the cost and latency; a 408, 429 or 5xx is theirs and usually succeeds elsewhere. The two that people get wrong are 402 (out of credit) and 404 (model retired or renamed): both are 4xx, both look like your fault, and both are fixed by switching. A 401 depends on how credentials are managed — one shared gateway key fails everywhere, but per-provider keys mean a revoked key on A is survivable on B. Classification comes before retry.
    3. Cost: switching means paying for the same prompt twice, up to 3x across a three-provider chain. Volunteering this separates people who shipped from people who only read about it.
    4. Latency: serial fallback accumulates timeouts. Three providers at 15s each means a 45s wait — worse than failing fast. Timeouts must be per-provider with an overall budget.
    5. Thundering herd is the most common follow-up: when the primary rate-limits, shifting all traffic at once can take down the backup too. Hence circuit breaking — drop a provider after N consecutive failures, then probe with a trickle.
    6. Expect: how long do you drop it for? Exponential backoff with a half-open probe — the same pattern as database connection pool breakers.

    分析过程 · 先想清楚再作答

    1. 题眼在「权衡」两个字——面试官不要你背一个 for 循环,他要看你知不知道 fallback 是有代价的。
    2. 先拆出第一个关键判断:不是所有错误都该 fallback,而分类的依据不是状态码的首位数字,是「换一家有没有可能变好」。400 请求体不合法、403 被安全策略拦截,换谁都一样,重试只是把同一个 bug 再犯一遍、白花两倍的钱和时间;408 超时、429 限流、5xx 服务端故障是对方的问题,换一家大概率能成。最容易答错的是 402 余额不足和 404 模型被下线或改名——它们同属 4xx、长得像「你的问题」,其实换一家完全可能成功;401 则要看凭证怎么管,三家共用一把网关 key 时换了也没用,各有各的 key 时 A 被吊销切到 B 完全能救。分类是 fallback 的第一步,不是重试。
    3. 再说成本:切换意味着同一段 prompt 你付了两次钱,三家链路最坏是三倍成本。这条一定要主动说出来,它区分了「写过」和「上过线」。
    4. 然后是延迟:串行 fallback 的总耗时是各家超时值的累加。如果每家给 15 秒、三家串下来用户要等 45 秒,那还不如早点失败。所以超时值必须按 provider 分别设,且要设总预算上限。
    5. 最后是雪崩,这是最容易被追问的点:主 provider 限流时你把全部流量瞬间压到备用上,很可能把备用也压垮。所以要加熔断——连续失败 N 次就暂时摘掉该 provider,过一段时间放少量流量试探。
    6. 可以预期的追问:怎么知道该摘多久?答案是指数退避 + 半开状态试探,和数据库连接池的熔断是同一套思路。

    Key points

    • Classify before retrying, judging by whether another provider could plausibly succeed rather than the leading digit: 400/403 must not fail over; 408/429/5xx should; so should 402 (out of credit) and 404 (model retired); 401 depends on whether the providers share one key
    • Cost: every fallback re-pays for the same prompt, so worst-case cost scales with chain length
    • Latency: serial fallback sums the timeouts, so set per-provider timeouts plus an overall budget
    • Thundering herd: shifting full traffic to the backup can topple it too — use circuit breaking with exponential backoff and half-open probes

    答题要点

    • 先分类再重试,判据是「换一家有没有可能变好」而不是状态码首位:400/403 不该切,408/429/5xx 该切,402 余额不足和 404 模型下线同样该切,401 取决于三家是否共用同一把凭证
    • 成本:每次 fallback 都要重付一遍 prompt 的钱,链路越长最坏成本越高
    • 延迟:串行 fallback 的耗时是各超时值累加,必须按 provider 分设超时并设总预算
    • 雪崩防护:主 provider 故障时全量流量压向备用会把备用也压垮,需要熔断 + 指数退避 + 半开试探
  • What does the system prompt do in an agent, and why not rely on the framework default?系统提示词(system prompt)在 Agent 里起什么作用?为什么不能用框架默认的?
    Common in ChinaCommon overseasIntermediate#prompt-engineering#system-prompt

    How to reason about it · think before answering

    1. The first half is a warm-up; the discriminating half is why the default is dangerous.
    2. State the role: it is the one instruction block whose weight stays stable across dozens of turns, setting identity, capability boundaries and output format.
    3. Then give three concrete consequences rather than 'not customized enough': it does not know your business boundary so it happily answers off-topic questions; it does not constrain output format so stray Markdown headings break your UI; and worst, it changes when the framework updates — your tested behavior rests on invisible text, and the bug appears with zero code changes.
    4. Land on practice: a production system prompt is assembled from a template — persona, capability boundary, output requirements, dynamic context — with the last part rebuilt per request.
    5. Expect: what gets forgotten in dynamic context? The current time. Models have no clock; without today's date they cannot resolve 'the order I placed three days ago'.
    6. Second follow-up: how do you test a prompt? Treat it as configuration, not code — store it, version it, roll it out to a percentage, because you cannot unit-test 'the tone got friendlier'.

    分析过程 · 先想清楚再作答

    1. 前半句是送分题,后半句才是区分度所在——很多人答得出 system prompt 是干什么的,答不出「默认值有什么坑」。
    2. 先说作用:它是唯一一段在整段对话里权重稳定、不会被后续几十轮稀释的指令,用来设定身份、能力边界和输出格式。
    3. 再答「为什么不能用默认的」,要给出三条具体后果而不是泛泛说「不够定制」:一是它不知道你的业务边界,用户问业务外的问题它会热情地答;二是它不约束输出格式,前端样式会被冷不丁冒出的 Markdown 标题打乱;三是最要命的——它会随框架升级而变化,你测好的所有行为建立在一段看不见的文本上,出 bug 时你的代码一行没动,极难排查。
    4. 结论落到工程做法:生产环境的 system prompt 是拼出来的模板,结构是「人设 + 能力边界 + 输出要求 + 动态上下文」,最后一块每次请求现拼。
    5. 可以预期的追问:动态上下文里最容易漏什么?答「当前时间」——模型没有时钟,不告诉它今天几号,它算不出「三天前下的单」是哪天。这个细节很能体现有没有真做过。
    6. 第二个追问:prompt 怎么测试?答案是把它当配置而不是代码——存库、加版本号、支持按比例灰度,因为你没法写单元测试断言「模型语气变友好了」。

    Key points

    • The system prompt sets identity, capability boundaries and output format, and keeps stable weight across turns
    • A default persona does not know your business boundary and will cheerfully answer off-topic questions
    • It does not constrain formatting, so stray Markdown can break your UI
    • Most dangerous: defaults change on framework upgrades, producing behavior regressions with no code change
    • Production practice: assemble it explicitly, treat it as versioned configuration, and roll changes out gradually

    答题要点

    • system prompt 设定身份、能力边界与输出格式,是对话里权重最稳定、不被后续轮次稀释的一段指令
    • 框架默认人设不知道你的业务边界,会热情回答业务外的问题,浪费 token 且跑题
    • 默认人设不约束输出格式,模型可能吐出 Markdown 标题打乱前端样式
    • 最危险的是默认值会随框架升级而变化,代码一行没动却出现行为回归,极难排查
    • 生产做法:显式拼模板(人设 + 能力边界 + 输出要求 + 动态上下文),当作配置存储、加版本号、可灰度
  • How do you pick the right model per task, balancing cost against latency?如何在成本和延迟之间给不同任务选择合适的模型?
    Common in ChinaCommon overseasIntermediate#model-routing#cost#latency

    How to reason about it · think before answering

    1. This question tests whether you have ever spent your own money. 'Use the best model' is the worst answer; 'it depends' is too vague — give actionable routing dimensions.
    2. Establish the core fact: model pricing spans 50x or more, and much of your workload does not need the strongest model. Using a flagship for intent detection is driving a sports car to fetch a parcel downstairs.
    3. Give three routing dimensions: task type (classification and extraction go cheap, long-form reasoning goes strong), latency requirement (foreground users need low latency, background batches can be slow and cheap), and input length (only some models handle very long context, and pricing rises steeply).
    4. Quantify it: 10k conversations a day at 2000 tokens each costs roughly 300 CNY/day on a flagship; routing the 60% of grunt work to a small model drops it to about 125 CNY/day, saving 60k+ CNY a year with no perceptible quality change.
    5. Volunteer the implementation trade-off: start with static tiers by task type. Dynamic routing that asks a model which model to use adds another model call, and the latency and cost may not pay for themselves — optimize once you have real data.
    6. Expect: how do you verify the cheaper tier did not hurt quality? A golden set — run both tiers over the same inputs and compare with human or LLM-as-judge scoring, so the decision rests on data rather than vibes.

    分析过程 · 先想清楚再作答

    1. 这题考的是「你有没有真的在花自己的钱」。答「用最好的模型」是最差的答案,答「按需选择」太空,要给出可执行的分档维度。
    2. 先建立核心事实:不同模型的价格能差 50 倍以上,而你的任务里很大一部分根本不需要最强的模型。用旗舰模型做意图识别,等于开跑车去楼下取快递。
    3. 然后给出三个可操作的路由维度:任务类型(分类抽取走便宜模型,长文推理走强模型)、延迟要求(前台用户在等就走低延迟,后台批处理可以慢而便宜)、输入长度(超长上下文只有部分模型支持且价格陡增)。
    4. 结论要落到数字上才有说服力:1 万轮对话每轮 2000 token,全走旗舰约 300 元一天;把六成粗活改走小模型后降到 125 元左右,一年省六万多,用户感知不到差别。
    5. 还要主动说出实现上的取舍:先按任务类型静态分档,不要一上来就做「让模型判断该用哪个模型」的动态路由——那个方案本身又要多一次模型调用,延迟和成本可能得不偿失,等有真实数据再优化。
    6. 可以预期的追问:怎么验证降档没有损失质量?答案是准备 golden set,对同一批输入跑两档模型,用人工或 LLM-as-judge 比对准确率,把降档决策建立在数据上而不是感觉上。

    Key points

    • Model pricing spans 50x or more, so a flagship doing intent detection is obvious waste
    • Three routing dimensions: task type, latency requirement, and input length
    • Add a tier parameter to the call layer, pick the starting provider statically, and reuse the fallback chain
    • Prefer static tiers first — dynamic model-picks-model routing adds a call and may not pay off
    • Validate downgrades against a golden set rather than intuition

    答题要点

    • 不同模型价格能差 50 倍以上,用旗舰模型做意图识别是明显的浪费
    • 三个路由维度:任务类型(分类抽取 vs 推理生成)、延迟要求(前台 vs 后台)、输入长度(是否需要超长上下文)
    • 实现上给调用层加 tier 参数,按任务静态分档挑起始 provider,fallback 逻辑完全复用
    • 先静态分档再考虑动态路由,让模型判断该用哪个模型本身要多一次调用,可能得不偿失
    • 用 golden set 对比两档模型的准确率,把降档决策建立在数据上

Comments

Sign in to join the discussion

No comments yet — be the first.