Interview Bank
328 questions total; 35 shown with current filters.
48 more tagsShow fewer tags
From Frontend Engineer to Agent Engineer in 30 Days
D1 LLM API Basics: messages/roles, Tokens, Streaming, Temperature; What an Agent Actually Is
What are tokens and the context window, and how do they shape agent design?什么是 token 和上下文窗口?它们如何影响 Agent 的设计?
Common in ChinaCommon overseasBasic#llm-basics#contextHow to reason about it · think before answering
- First decide whether this asks for definitions or engineering consequences; a definition-only answer reads as inexperienced.
- Follow the causal chain: tokens are the unit of billing and length, the window caps that unit, models are stateless so history is resent every turn, cost grows with turns, hence context engineering.
- The differentiator is why agents suffer more: a loop calls the model repeatedly and appends tool results back into history.
- Close with concrete tactics: sliding window, summarization, externalized long-term memory, and the cost of each.
- Expect the follow-up: why compress before the window is full? Long contexts dilute attention and raise latency and cost.
分析过程 · 先想清楚再作答
- 先判断这题问的是「概念」还是「工程后果」。只答定义会被认为没做过工程,必须落到设计影响上。
- 从一条因果链推:token 是计费与长度的计量单位 → 窗口是这个单位的上限 → 模型无状态、历史每轮重发 → 成本随轮数增长 → 所以必须做上下文工程。
- 关键要点出在「Agent 比聊天更严重」:Agent 在循环里反复调模型,还要把工具返回结果也塞回历史,增长速度快得多。
- 结论给出具体手段:滑动窗口、摘要压缩、长期记忆外置到检索系统,并说明各自代价。
- 可以预期的追问:窗口没满为什么也要压缩?答案是长上下文会稀释注意力、抬高延迟与成本,不是塞满了才处理。
Key points
- A token is the smallest unit the model processes; roughly 1.3 tokens per English word
- The context window caps input + output tokens per request; beyond it you truncate or compress
- Models are stateless, so the full history is re-sent every turn and cost grows with length
- Hence context engineering: sliding windows, summarization, and external long-term memory
答题要点
- token 是模型处理文本的最小单位,大致 1 个汉字 ≈ 1–2 token,1 个英文单词 ≈ 1.3 token
- 上下文窗口是一次请求里输入 + 输出 token 的上限;超出就要截断或压缩
- 模型没有记忆,历史必须每轮重新塞进 messages,所以长对话的成本随轮数线性增长
- Agent 设计因此要做上下文工程:滑动窗口、摘要压缩、把长期记忆外置到检索系统
What do the system / user / assistant roles do, and why does system exist?messages 里的 system / user / assistant 三种角色各起什么作用?为什么要有 system?
Common in ChinaCommon overseasBasic#llm-basics#promptHow to reason about it · think before answering
- The discriminating half is 'why does system exist'; the first half is a warm-up.
- Explain that the three roles are structural markers over one continuous text the model continues.
- Then the why: rules placed in user are just another turn and get diluted over dozens of turns; system keeps stable weight and can be governed centrally.
- Add production nuance: a real system prompt is templated — persona plus tool docs plus memory plus runtime facts.
- Likely follow-up: can system go last? Possible but unwise — models weight earlier instructions more and it breaks prompt-cache prefixes.
分析过程 · 先想清楚再作答
- 题眼在后半句「为什么要有 system」——前半句是送分,后半句才是区分度所在。
- 先说清三者构成一段可被模型续写的完整文本,角色是给这段文本打的结构化标记。
- 再回答「为什么」:如果把规则写进 user,它就只是对话里的一句话,会被后续几十轮对话稀释;放进 system 才能保持稳定权重,且便于产品侧统一管控、单独灰度。
- 补一条生产视角:真实的 system prompt 通常是模板拼出来的——人设 + 工具说明 + 记忆片段 + 当前时间,而不是一个写死的字符串。
- 常见追问:能不能把 system 放在最后?可以但不推荐,多数模型对靠前的指令更敏感,且会破坏缓存前缀。
Key points
- system sets identity, constraints and output format; it sits first and carries more weight
- user is the human turn, assistant is the model's prior replies; they alternate
- Rules live in system so they are not diluted by later turns and can be controlled centrally
- In production the system prompt is templated: persona + tool docs + memory + runtime facts
答题要点
- system 设定身份、边界与输出格式,通常放在最前面,权重高于普通对话
- user 是用户输入,assistant 是模型历史回复,两者交替构成对话记录
- 把规则放 system 而不是 user,是为了让规则不被后续对话冲淡,也便于产品统一管控
- 生产里 system prompt 往往由模板拼接:人设 + 工具说明 + 记忆 + 当前时间等动态信息
What fundamentally separates a chatbot from an agent?聊天机器人和 Agent 的本质区别是什么?
Common in ChinaCommon overseasBasic#agent-basicsHow to reason about it · think before answering
- This one invites marketing language; the test is whether your answer names engineering costs.
- Give the structure first: a chatbot is one call, an agent loops think → act → observe until the goal is met.
- Name the three additions — loop, tools, memory — and stress that tools cause side effects on the world.
- Immediately pair each with its cost: permissions and sandboxing, step and budget caps, observability and retries.
- Close with a concrete example and the infrastructure it implies: queues, state machines, cost metering.
分析过程 · 先想清楚再作答
- 这题最容易答成营销话术。判断标准很简单:你的回答里有没有出现「工程代价」,没有就是背概念。
- 先给结构:聊天是一问一答的单次调用;Agent 是在循环里反复「思考 → 调工具 → 观察」直到目标达成。
- 点出三个新增件——循环、工具、记忆——并强调关键差异是「工具能对外部世界产生副作用」,这是可逆与不可逆的分界线。
- 紧接着说代价:有副作用就要管权限与沙箱,有循环就要管步数与成本预算,有多步就要可观测性和失败重试。这一段才是面试官想听的。
- 用一个具体例子收尾(能查库、发消息、定时提醒的助手),并点出它背后需要队列、状态机、成本计量。
Key points
- A chatbot answers once; an agent loops think → act (tool call) → observe until the goal is met
- Three additions: a loop (multi-step), tools (side effects on the world), memory (across turns/sessions)
- They bring engineering concerns: tool permissions and sandboxing, retries, step/cost budgets, observability
- Example: an assistant that queries a DB, sends messages and schedules reminders needs queues, state machines and cost tracking
答题要点
- 聊天机器人是一问一答;Agent 是模型在一个循环里反复思考、调用工具、观察结果直到完成目标
- 三个新增件:循环(多步)、工具(能对外界产生副作用)、记忆(跨轮次/跨会话)
- 随之而来的工程问题:工具权限与沙箱、失败重试、成本与步数预算、可观测性
- 举例:一个能查库、发消息、定时提醒的助手,背后要有消息队列、状态机和成本计量
What do temperature and top_p control, and when would you use 0 versus 0.7?temperature 和 top_p 分别控制什么?什么场景用 0,什么场景用 0.7?
Common overseasBasic#llm-basics#samplingHow to reason about it · think before answering
- Establish that both act on the same next-token distribution but in different ways — that is the discriminator.
- temperature rescales the whole distribution; top_p truncates it to the smallest set reaching cumulative probability p.
- Hence the practical rule: tune one, not both, or you cannot attribute a regression.
- Choose by reproducibility, not by vibes: tool arguments, classification and structured output must be reproducible, so use 0.
- Add the agent angle: planning and tool-calling steps stay cold; only the final user-facing prose warrants higher values.
分析过程 · 先想清楚再作答
- 先说清两者作用在同一个地方——模型算出的下一个 token 概率分布——但作用方式不同,这是区分度所在。
- temperature 是缩放整个分布:越低越尖锐、越确定;top_p 是截断——只保留累计概率达到 p 的那一小圈候选再采样。
- 由此推出实践建议:一般只调其中一个,两个同时调会互相干扰,出了问题分不清是谁造成的。
- 选值不按「创意程度」凭感觉,按「这一步的输出要不要可复现」来定:工具参数、分类判断、结构化输出必须可复现,用 0。
- 补一句 Agent 视角:Agent 的规划与工具调用环节几乎都用低温,只有最终面向用户的自然语言回复才考虑调高。
Key points
- temperature rescales the next-token distribution: lower is more deterministic, higher more random
- top_p samples only from the smallest set whose cumulative probability reaches p; tune one, not both
- Use ~0 for structured output, tool arguments and classification to keep results reproducible
- Use 0.7–1.0 for creative writing; planning steps in production agents usually stay low
答题要点
- temperature 缩放下一个 token 的概率分布:越低越确定,越高越随机
- top_p 只从累计概率达到 p 的候选里采样,是另一种截断随机性的方式;一般只调其中一个
- 结构化输出、工具参数、分类判断用 0 或接近 0,保证可复现
- 创意写作、头脑风暴用 0.7–1.0;生产 Agent 的规划步骤通常也偏低温
D2 How Tool Calling Works: JSON Schema, the tool_use Loop; Hand-Writing an Agent Loop With No Framework
Walk me through the complete function calling flow.function calling 的完整流程是怎样的?
Common in ChinaCommon overseasBasic#tool-calling#agent-loopHow to reason about it · think before answering
- The word 'complete' is the hinge. Most candidates stop at 'the model returns a tool_call, I run it, I hand back the result' and drop both ends: how the tool definitions get into the request, and what makes the loop continue after the result goes back. They want a closed loop, not a one-way call.
- Walk the lifecycle in five steps: send tools (name, description, JSON Schema parameters) with every request, since they are not remembered; the model replies with tool_calls and a finish reason of tool_calls; you parse arguments — a JSON string, not an object — and execute; you append the assistant message verbatim plus one tool-role message per tool call with matching tool_call_id; you send the now-longer messages again until the finish reason is no longer tool_calls.
- Land on the sentence that draws the security boundary: the model executes nothing. It emits a structured request, and execution, validation, authorization and auditing all live in your code. Since that request ultimately derives from user input, permissions and quotas can never be delegated to the model's good behavior.
- Volunteer the three most common 400s — dropping the assistant message that carried the tool_calls, answering only one of several parallel calls, and treating arguments as an object. Naming them shows you have shipped this.
- Expect the follow-up: do tools cost tokens forever? Yes — the tool list is re-sent every turn, so ten tools is one to two thousand tokens multiplied by the number of steps. Trim the tool set per scenario instead of registering everything.
- Second follow-up: what if the model calls a tool that does not exist? Do not throw. Return 'no such tool, pick one from the list' as an ordinary tool message and the model usually corrects itself on the next turn.
分析过程 · 先想清楚再作答
- 题眼在「完整」两个字。大多数人答到「模型返回一个 tool_call、我执行、把结果给它」就停了,漏掉了两头——工具定义是怎么进到请求里的,以及结果回填之后循环凭什么继续。判据是你能不能把它讲成一个闭环,而不是一次单向调用。
- 顺着一次请求的生命周期走五步:第一步把 tools(name、description、JSON Schema 参数)一起放进请求,注意它每一轮都要重发;第二步模型返回 tool_calls,同时停止原因是 tool_calls;第三步你解析 arguments 并执行——arguments 是一段 JSON 文本而不是对象,要再解析一次;第四步把模型那条 assistant 消息原样追加回历史,再为每一个 tool_call 追加一条 role 为 tool 的消息,tool_call_id 逐个对上;第五步带着变长的 messages 再发一次,直到停止原因不再是 tool_calls。
- 结论要落到一句能划安全边界的话:模型不执行任何东西,它只输出一个结构化的「请求」,真正执行、校验、鉴权、审计的全是你的代码。而这个请求的内容归根结底来自用户输入,所以权限和额度绝不能指望模型自觉。
- 主动说三个最高频的 400,能立刻证明你真写过:漏掉模型那条带 tool_calls 的 assistant 消息、并行调用只回了一条 tool 消息、把 arguments 当对象直接取字段。
- 可以预期的追问:工具会不会一直占 token?会——tools 每一轮都要重发,十个工具一两千 token 再乘以循环步数,所以工具集要按场景动态裁剪,不是接得越多越好。
- 第二个追问:模型请求了一个不存在的工具怎么办?不要抛异常,把「没有这个工具,请从工具列表里重新选」当成一条正常的 tool 消息回传,模型通常下一轮就自己纠正了。
Key points
- Send the tool definitions (name, description, JSON Schema parameters) on every request — they are not remembered
- The model returns tool_calls with a finish reason of tool_calls; arguments is a JSON string that needs a second parse
- Append the assistant message verbatim, then one tool-role message per call with a matching tool_call_id
- Send the longer message list again until the finish reason changes — that loop is what makes it an agent
- The model only requests; execution, validation, authorization and auditing stay in your code
答题要点
- 请求里带上 tools 定义(name、description、JSON Schema 参数),每一轮都要重发
- 模型返回 tool_calls,停止原因为 tool_calls;arguments 是 JSON 字符串,需要再解析一次
- 先把模型那条 assistant 消息原样追加回 messages,再为每个 tool_call 追加一条 role 为 tool 的消息,tool_call_id 一一对应
- 带着变长的 messages 继续下一轮,直到停止原因不再是 tool_calls,这才构成闭环
- 模型只发出请求,执行、校验、鉴权、审计全在你的代码里
D3 Getting Started With the Pi SDK: the Three-Layer Architecture, Comparing It to the Agent Loop (dg P01/P02/M02/M03)
What problems does an agent framework's built-in agent loop have to solve?Agent 框架内部的 Agent Loop 一般要解决哪些问题?
Common in ChinaCommon overseasBasic#agent-loop#framework-designHow to reason about it · think before answering
- This looks like a checklist question, but the real signal is whether you have written such a loop yourself. 'Call the model repeatedly until it stops' reads as documentation-only knowledge.
- The safest structure is to walk down your own hand-written loop line by line, because every line is one problem the kernel must own: issue the model request, maintain message history, decide from the stop reason whether to continue, dispatch by tool name, validate arguments against the schema, fold the tool result back in as a message, and cap the number of turns.
- Naming the stop reason explicitly scores well: the loop exits not when 'the model finished talking' but when the turn's stop reason is not a tool-use one. Most candidates blur past this, and it is the switch that drives the whole loop.
- Then add the three things a hand-rolled version usually skips but a framework cannot: running a batch of tool calls concurrently, emitting the whole run as an event stream so callers are not staring at a black box, and compaction plus session persistence once the context outgrows the window.
- Close on tool errors, which is where production experience shows: a failing tool should raise, and the kernel should turn that into a tool result flagged as an error so the model can fix its arguments and retry. Swallowing the exception and returning 'operation failed' as a normal result makes the model believe the tool succeeded.
- Expect the follow-up: how do you stop runaway loops? A max-turn cap is only a backstop; per-run token and wall-clock budgets plus a pre-execution hook that can block a call and hand the reason back to the model are what actually work.
分析过程 · 先想清楚再作答
- 这题看着像背清单,区分度其实在「你有没有自己写过一遍」。只答「循环调用模型直到结束」会被认为读过文档但没写过代码。
- 最稳的拆法是把手写版的代码从上往下念一遍,每一行都是内核必须解决的一件事:发模型请求、维护消息历史、判断停止原因决定继不继续、按工具名分派、按 schema 校验参数、把工具结果回填成一条消息、控制最大轮数。这条链路念完,答案自然是完整的。
- 点名停止原因这一环最能加分:循环的出口条件不是「模型说完了」,而是这一轮的停止原因是不是「要调工具」。很多人把它含糊过去,而它恰恰是整个循环的开关。
- 然后补上手写版通常没做、但框架必须做的三件:并发执行同一批工具调用、把每一步以事件形式播报出去(否则外部完全是黑箱)、以及上下文超限时的压缩与会话持久化。
- 最后落到工具报错这一条,它是最能体现工程经验的:工具异常不应该被吞掉,要转成一条带错误标记的工具结果回给模型,让模型自己改参数重试;吞掉异常返回一句「操作失败」,模型会以为工具成功了。
- 可以预期的追问:怎么防死循环?答最大轮数只是兜底,更实际的是给单次运行设 token 与耗时预算,并在工具调用前留一个可以拦截的钩子,触发条件时把拦截原因回传给模型让它改道。
Key points
- The skeleton: call the model, maintain history, branch on the stop reason, dispatch tools, validate arguments, fold results back in
- The stop reason is the loop's exit condition — a tool-use reason means one more turn, anything else means done
- Tool execution details: batch calls can run concurrently, hooks belong before and after, and exceptions become error-flagged tool results the model can react to
- An event stream is mandatory, otherwise callers see a black box and observability is impossible
- Safety valves: max turns, token and latency budgets, context compaction, and session persistence for resume
答题要点
- 循环骨架:调模型、维护消息历史、按停止原因判断继不继续、分派工具、校验参数、回填工具结果
- 停止原因是循环的出口条件,工具分支意味着还要再来一轮,其他取值意味着结束
- 工具执行的工程细节:同一批调用可以并发、执行前后要留钩子、异常要转成带错误标记的工具结果回给模型
- 对外要有事件流,否则调用方看不到 Agent 在做什么,也没法做可观测性
- 安全阀:最大轮数、token 与耗时预算、上下文超限时的压缩,以及会话的持久化与恢复
What are the responsibilities of Pi SDK's three layers, and what does that layering buy you?Pi SDK 的三层架构分别对应什么职责?这样分层解决了什么问题?
Common in ChinaCommon overseasBasic#framework-design#architectureHow to reason about it · think before answering
- The first half is recall; the second half carries the signal. Reciting three package names without explaining the cut suggests you only skimmed the docs.
- State the layers precisely: the bottom is a unified model layer that normalizes each provider's request format, auth and streaming into one interface while tracking tokens and cost; the middle is the agent kernel built on top of it, owning the agent loop, tool execution, state and the event stream; the top is the application layer, owning session storage, extension and resource loading, built-in tools, and the interactive, print, RPC and embedded-SDK run modes. Dependencies point strictly downward.
- Then answer what it buys: layering lets you take only half. Want just a unified model layer and your own loop? Stop at the bottom. Want the full loop but none of the terminal UX? Stop in the middle. That test generalizes to any framework and is worth far more than the package names.
- Add the practical payoff: when something breaks, first place it in a layer. A stack trace through the model layer points at auth, a wrong model id or a malformed request; one through the kernel points at the loop or tool execution. The two investigations look nothing alike.
- Expect the follow-up: how does this map onto the loop you wrote by hand? All three layers were collapsed into one file — the fetch calls were the model layer, the while loop and tool dispatch were the kernel, and the CLI was the application layer. Making that mapping live is more convincing than any recitation.
分析过程 · 先想清楚再作答
- 前半句是记忆题,后半句才有区分度。只背出三个包名而说不出「为什么这么切」,面试官会判断你只是照着文档看了一遍。
- 先把三层说准:最底层是统一的模型调用层,负责把各家 provider 的请求格式、鉴权、流式分包收敛成一套接口,还统计 token 与成本;中间是 Agent 内核层,构建在模型层之上,负责 Agent 循环、工具执行、状态管理和事件流;最上层是应用层,负责会话存取、扩展与资源装载、内置工具,以及交互式、打印、进程间调用、嵌入式 SDK 这几种运行模式。依赖方向严格单向向下。
- 然后回答「解决了什么」:分层的价值是让你能「只要一半」——只想要统一的模型调用层就停在最底层,想要完整循环但不要终端交互就停在中间层。这条判据可以用来评估任何框架,比复述包名有用得多。
- 补一个很实际的收益:排障时先判断问题落在哪一层。报错栈里出现模型层,多半是鉴权、模型 id 或请求格式;出现内核层,那是循环或工具执行;两者的排查方向完全不同。
- 可以预期的追问:这套分层跟你手写的版本怎么对应?答手写版把三层揉在了一个文件里——fetch 那几行是模型层,while 循环和工具分派是内核层,命令行交互是应用层。能当场做这个映射,比任何背诵都有说服力。
Key points
- Model layer: normalizes provider request formats, auth and streaming, tracks tokens and cost, and only ships tool-calling models
- Kernel layer: the agent loop, tool execution and result folding, state management and the event stream, built on the model layer
- Application layer: session storage, extension and resource loading, built-in tools, and the interactive, print, RPC and embedded-SDK run modes
- Dependencies point one way, so each layer is replaceable and testable on its own and you can adopt only part of the stack
- For debugging, place the failure in a layer first — model-layer and kernel-layer investigations diverge immediately
答题要点
- 模型层:统一各家 provider 的请求格式、鉴权与流式,附带 token 与成本统计,只收录支持工具调用的模型
- 内核层:Agent 循环、工具执行与结果回填、状态管理、事件流,构建在模型层之上
- 应用层:会话存取、扩展与资源装载、内置工具,以及交互式、打印、进程间调用、嵌入式 SDK 几种运行模式
- 依赖单向向下,好处是每层可单独替换、单独测试,也能「只要一半」
- 排障时先定位问题落在哪一层,模型层和内核层的排查方向完全不同
D4 Model Integration and System Prompts: a Multi-Provider Abstraction With Fallback, Overriding the Default Persona (dg P03/P04/M04)
Why do production agents usually integrate more than one model provider?为什么生产级 Agent 通常要接入多个模型 provider?
Common in ChinaCommon overseasBasic#model-routing#reliabilityHow to reason about it · think before answering
- First decide whether this is an availability question or an architecture question; answering only 'so it doesn't go down' reads as inexperienced.
- 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.
- 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.
- 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.
- 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.
- 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.
分析过程 · 先想清楚再作答
- 先判断这题问的是「可用性」还是「架构」。只答「防止挂掉」拿不到分,因为面试官想看的是你有没有真的算过账、踩过坑。
- 从一条因果链推:模型 API 是外部依赖 → 外部依赖必然有故障率 → 你的可用性上限被它锁死 → 所以要么接受这个上限,要么加冗余。
- 把可用性说成数字才有说服力:单家 99.5% 意味着每月约 3.6 小时不可用;三家独立故障时理论不可用时间降到秒级。数量级差异比形容词有力得多。
- 第二个理由往往被忽略,但更能体现工程视角:模型的价格和能力每月都在变,接入成本高会让你因为「改起来麻烦」而一直用贵的慢的那个——高耦合真正的代价是剥夺未来的选择权。
- 这里有个必须自己先说破的前提:那个数量级是拿「三家故障互不相关」算出来的。如果三家其实都走同一个聚合网关、共用同一把 key(很多人的第一版就是这样),网关一挂三家一起挂,冗余是假的,聚合网关反而成了新的单点。真正的独立要落到不同厂商的直连端点、各自的凭证和计费上。主动点破这一条,比背出 0.005 的三次方更能体现你真的部署过。
- 可以预期的追问:多接几家不是更贵吗?答案是不会——正常路径只调一家,多的只是配置和一层抽象;真正贵的是 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 小时不可用;多家冗余能把理论不可用时间降低几个数量级
- 价格与能力每月都在变,统一抽象层让换模型变成改配置,保住了未来做选择的自由
- 正常路径只调一家,冗余的成本是一层抽象而不是多倍账单
D5 The Tool System and Event-Driven Design: Parameter Validation, Feeding Errors Back for Self-Correction, Event Subscription (dg P05/P06/M05/M07)
What principles do you follow when designing tools for an agent — how do you write the name, the description and the parameter schema?设计 Agent 的工具时你会遵循哪些原则?名字、描述、参数分别该怎么写?
Common in ChinaCommon overseasBasic#tool-design#prompt-engineeringHow to reason about it · think before answering
- The discriminator is what you think a tool description is. People who treat it as a docstring answer 'describe what it does'; people who treat it as part of the prompt get it right — the description goes verbatim into the model's context and drives both tool selection and argument filling. Its reader is the model, not your teammate.
- Split it into three: names read like commands (query_order, not handler2) because the name is the model's first filter; the most valuable sentence in a description is not what the tool does but when NOT to use it, which removes most misrouting; and every parameter needs its own description plus a concrete example for format-shaped fields — a model has no notion of 'order id', but SO20260901 makes it far more likely to get it right.
- Then raise the cost point most candidates miss: tool definitions are resent in full every turn. A well-written tool runs 100 to 150 tokens, so twenty of them is a fixed two- to three-thousand-token tax per turn. More tools is not more capable — only mount what the current scenario needs.
- Add a transferable engineering judgment: renaming a tool or silently widening its semantics is a breaking change. Tuned prompts stop working, and old sessions still carry the old name in messages, so resuming one makes the model call a tool that no longer exists. Version and roll out tool changes the way you would a public API.
- Expect the follow-up: what about dozens or hundreds of tools? Retrieve tools with a cheap model first and mount only the top few, rather than shipping the whole catalog every turn.
分析过程 · 先想清楚再作答
- 这题的区分度在于你把工具描述当成什么。当成函数注释的人会答「写清楚做什么」,当成提示词的人才会答到点子上——描述会原样进入模型的上下文,参与「该不该调、参数填什么」的判断,它的读者是模型不是同事。
- 拆成三件事分别说:名字要动词加宾语(query_order 而不是 handler2),因为名字是模型的第一道筛选;描述最有价值的一句不是「做什么」而是「什么时候不该用它」,把边界写进去能砍掉一大半误用;参数里每个字段都要有自己的 description,格式类字段还要给一个合法示例——模型对「订单号」没有概念,看到 SO20260901 这个样例,填对的概率会陡增。
- 接着给出一条几乎没人主动说的成本判断:工具定义每一轮都会被完整重发,一个写得扎实的工具约 100 到 150 token,挂 20 个就是每轮两三千 token 的固定开销。所以「工具越多越强」是错的,只挂当前场景用得上的那几个。
- 再补一条可迁移的工程判断:工具改名或改语义是破坏性变更,等价于换了个工具——调好的提示词会失效,历史会话的 messages 里还留着旧名字,恢复旧会话时模型会去调一个不存在的工具。所以改工具要像改公开 API 一样走版本与灰度。
- 可以预期的追问:几十上百个工具怎么办?答案是先用一轮便宜模型做工具检索,只把最相关的几个塞进正式请求,而不是一股脑全挂上。
Key points
- A tool definition is part of the prompt; the model only sees name, description and parameter schema
- Name it verb plus object; the most valuable line in a description is when not to use it; every parameter needs a description, and format fields need a concrete example
- Definitions are resent every turn, so twenty tools is a fixed two- to three-thousand-token tax — mount only what the scenario needs
- Renaming or redefining a tool is a breaking change that invalidates tuned prompts and breaks resumed sessions
- At scale, retrieve the relevant tools with a cheap model before mounting them
答题要点
- 工具定义是提示词的一部分,读者是模型:它只能看到名字、描述、参数 schema,看不到你的实现
- 名字用动词加宾语;描述里最值钱的是「什么时候不该用它」;每个参数都要有 description,格式类字段给一个合法示例
- 工具定义每轮完整重发,20 个工具就是每轮固定两千多 token,只挂当前场景用得上的
- 改名或改语义等于换工具,会让调好的提示词失效、让旧会话调到不存在的工具,要走版本与灰度
- 工具规模上去之后,先用便宜模型做工具检索再挂载最相关的几个
D6 Messages, Context Engineering and Compression, Session Storage/Recovery/Forking (dg M06/M08/M09/M10)
Where do you draw the line between short-term context and long-term memory, and how do you decide where a given fact belongs?短期上下文和长期记忆的边界怎么划?一条信息该往哪放,你的判断依据是什么?
Common in ChinaCommon overseasBasic#memory#context-engineeringHow to reason about it · think before answering
- This looks conceptual but is really asking for an operational test. Reciting 'short-term lives in messages, long-term lives in a vector store' just describes the status quo and gives no signal.
- Lay out the engineering properties and the boundary draws itself: short-term context dies with the session, ships in full on every request, is billed per token and capped by the window; long-term memory spans sessions, is retrieved and injected rather than always sent, is stored per item and capped by retrieval quality.
- Give a reusable test — this is the core of the answer. Ask three questions: is it still needed after this session ends, does it expire with time, can retrieval find it again? Three yeses means long-term; a no on the first means it stays short-term. Illustrate: 'the user lives in Shanghai' is long-term, 'the user just asked me to shorten that paragraph to three sentences' is not.
- Name the common failure: stuffing all long-term memory into the prompt. Two hundred preferences accumulated over six months will both blow the window and drown the model in irrelevance. The value of long-term memory is retrieving the three or four relevant items, not the volume stored.
- Expect the follow-up on updates and expiry: memories need timestamps and provenance, and a changed preference must overwrite rather than coexist with a contradictory one. Add the deletion angle — long-term memory is the part you must be able to locate and erase when a user asks for their data to be deleted.
分析过程 · 先想清楚再作答
- 这题看着像概念题,其实考的是你有没有一条可执行的判据。背出「短期在 messages 里、长期在向量库里」只是描述现状,答不出「为什么这条该进长期」就没有区分度。
- 先把两者的工程属性摆出来,边界自然就清楚了:短期上下文随会话结束作废、全量进请求、按 token 计费、受窗口约束;长期记忆跨会话存在、不进请求而是检索后注入、按条存储、受检索质量约束。
- 给一条可复用的判据,这是本题的核心:问三句话——跨会话之后还需要吗、会随时间失效吗、能通过检索捞回来吗。三个都是「是」就进长期记忆,第一个是「否」就留在短期。举例说明:用户住上海进长期,用户刚才让我把段落改成三句话留短期。
- 点出最常见的误用:把长期记忆当上下文一次性全塞进去。用了半年攒两百条偏好,全塞进请求既撑爆窗口,又因为大量不相关记忆干扰模型判断——长期记忆的价值在于按需检索出最相关的三五条,不在于存了多少。
- 可以预期的追问:长期记忆怎么更新和失效?答要点是记忆要带时间戳和来源,用户改了主意要能覆盖旧记忆而不是并存两条矛盾的;再补一句删除权——用户要求删数据时,长期记忆是必须能定位并整体删掉的那一部分。
Key points
- Short-term context dies with the session, ships in full, and is billed per token under the window cap; long-term memory spans sessions, is retrieved on demand, and is capped by retrieval quality
- The three-question test: is it needed after this session, does it expire, can retrieval find it — three yeses means long-term
- The common failure is injecting the whole memory store, which blows the window and drowns the model in irrelevance; retrieve the three or four relevant items instead
- Long-term memories need timestamps and provenance so a changed preference overwrites the old one instead of contradicting it
- Long-term memory is the part that must be locatable and deletable per user for compliance, while short-term context simply dies with the session
答题要点
- 短期上下文随会话作废、全量进请求、按 token 计费受窗口约束;长期记忆跨会话、按需检索后注入、按条存储受检索质量约束
- 判据三问:跨会话还需要吗、会随时间失效吗、能被检索捞回来吗——三个都是就进长期记忆
- 常见误用是把长期记忆整包塞进上下文,既撑爆窗口又用不相关的记忆干扰模型,正确做法是检索最相关的三五条
- 长期记忆要带时间戳和来源,用户改主意时覆盖旧记忆,避免两条矛盾记忆并存
- 长期记忆是合规上必须能按用户定位并整体删除的那一部分,短期上下文随会话删除即可
D7 Packaging It as a Service: Fastify + SSE + Docker (dg P07); Week One Retrospective
For streaming LLM responses, would you pick SSE or WebSocket, and why?流式返回大模型回复,你会选 SSE 还是 WebSocket?为什么?
Common in ChinaCommon overseasBasic#sse#streaming#api-designHow to reason about it · think before answering
- The hinge is 'how would you pick', not 'what is the difference'. Reciting 'SSE is one-way, WebSocket is two-way' scores nothing — that is the first paragraph of any doc.
- Ask one question that nearly decides it: does the client need frequent upstream messages on this connection? Chat completion is one request followed by a long push, which is exactly SSE's shape. Collaborative editing, realtime games and voice are what WebSocket is for.
- Give three practical wins for SSE: it is ordinary HTTP, so auth headers, cookies, rate limiting, logging, CDNs and reverse proxies all keep working; the server just writes bytes into a response, with no separate connection lifecycle to manage; and the wire format is plain text, so curl is your debugger. WebSocket runs an upgraded protocol where most of that tooling has to be rebuilt.
- Volunteer SSE's two real limits before they are raised. First, the browser's native EventSource can only issue GET, while model endpoints require POST, so real frontends hand-roll the parser with fetch and the spec's Last-Event-ID auto-reconnect never applies. Second, HTTP/1.1 caps concurrent connections per origin, so several tabs each holding a stream compete; HTTP/2 largely removes this.
- Land on a decision rule: one-way push means SSE, high-frequency bidirectional means WebSocket, and when unsure start with SSE — its escape hatch is adding one upstream endpoint, while WebSocket's escape hatch is rebuilding your infrastructure.
- Expect the follow-up: what about the 'stop generating' button? It does not need the same connection — send a plain POST carrying the run id, have the server abort upstream, and the SSE stream ends on its own. This one separates people who shipped it from people who read about it.
分析过程 · 先想清楚再作答
- 这题的题眼是「怎么选」,不是「有什么区别」。只背出「SSE 单向、WebSocket 双向」拿不到分,因为那是文档第一段。
- 先问自己一个问题,它几乎决定了答案:这条连接上客户端需不需要频繁上行?聊天补全是「一次请求、一路往回推」,上行只有最开始那一次,完全落在 SSE 的形状里;协同编辑、实时游戏、语音这种双向高频才轮到 WebSocket。
- 然后给 SSE 的三条实际好处:它就是普通 HTTP,鉴权头、Cookie、限流、日志、CDN、反向代理这一整套现成设施全部照用;服务端只是往响应里写字节,不需要额外的连接管理;协议是纯文本,出问题 curl 一下就能看。WebSocket 走的是升级后的独立协议,前面那套东西大多要重做一遍。
- 接着说 SSE 的两个真实限制,主动说破比被问出来强:一是浏览器原生的 EventSource 只能发 GET,而大模型接口必须 POST,所以真实前端都是 fetch 手写解析,规范里那套 Last-Event-ID 自动重连一行都用不上;二是 HTTP/1.1 下同域并发连接数有限制,多个标签页各开一条长连接会互相挤占,HTTP/2 之后这条基本消失。
- 结论要落到一句可判断的话:单向推送选 SSE,双向高频选 WebSocket;拿不准就先用 SSE,因为它的退路是加一个上行接口,而 WebSocket 的退路是重做整套基础设施。
- 可以预期的追问:那大模型产品里的「停止生成」按钮怎么办?答案是它根本不需要走同一条连接——另发一个普通的 POST 请求带上这次生成的 id,服务端收到就中止上游,SSE 那条连接自然结束。这个追问很能区分有没有真做过。
Key points
- Decide by upstream frequency: one request plus a long push (chat completion) fits SSE; high-frequency bidirectional traffic needs WebSocket
- SSE is plain HTTP, so auth, rate limiting, logging, proxies and CDNs all still apply, and curl is enough to debug it
- Name SSE's limits yourself: EventSource is GET-only while model endpoints need POST, so spec auto-reconnect does not apply; HTTP/1.1 also caps per-origin connections
- When unsure start with SSE — adding one upstream endpoint is cheaper than rebuilding infrastructure around WebSocket
- A stop button does not need the same connection: POST the run id and abort upstream, and the stream ends by itself
答题要点
- 先判断上行频率:一次请求、一路往回推的场景(聊天补全)用 SSE,双向高频(协同编辑、语音)用 WebSocket
- SSE 就是普通 HTTP,鉴权、限流、日志、代理、CDN 这套设施全部照用,排查时 curl 就够
- SSE 的限制要主动说:EventSource 只能 GET,而模型接口必须 POST,所以自动重连用不上;HTTP/1.1 下同域连接数有限
- 拿不准先选 SSE:加一个上行接口就能补足,而换 WebSocket 要重做整套基础设施
- 「停止生成」不用走同一条连接,另发一个 POST 带 run id 让服务端中止上游即可
In a two-minute self-introduction, how do you convey the value of an agent project?自我介绍时,怎么在两分钟里讲清楚一个 Agent 项目的价值?
Common in ChinaCommon overseasBasic#interview-prep#communicationHow to reason about it · think before answering
- There is no model answer, but there is a clear failure mode: opening with a tool list. Interviewers do not remember stacks; they remember problems and numbers.
- Use a fixed structure that fits two minutes: one line on who you are and where you are heading, one line on the business problem (who suffers, in what situation), three or four lines on your key technical decisions and what each bought you, and one closing line with a verifiable result.
- Choose decisions that involved a trade-off, not decisions that merely involved implementation. 'We stream over SSE rather than WebSocket because upstream traffic is a single request, which lets us keep existing auth, rate limiting and logging' shows you knew the alternative and priced it — far stronger than naming ten tools.
- Attach numbers wherever you can, even self-measured ones: time-to-first-token dropping from seconds to a few hundred milliseconds, tiered routing cutting daily spend by more than half, multi-provider fallback removing a single vendor from your availability ceiling. If the numbers are from a test environment, say so; inventing them collapses after two follow-ups.
- A common mistake is presenting a learning project as production. Position it yourself: a complete system built to understand production agent architecture, at self-test scale, where every decision was made against real constraints. Interviewers forgive honest scoping far more readily than inflated claims.
- Expect: what was the hardest part? Prepare one concrete story with a process — for example, discovering that a streaming endpoint cannot report errors by status code once it has started pushing, and redesigning around an in-stream error event plus front-loaded validation.
分析过程 · 先想清楚再作答
- 这题没有标准答案,但有明确的失败模式:从技术栈开始报菜名(我用了 Fastify、SSE、Docker、向量库……)。面试官记不住工具清单,他记得住的是问题和数字。
- 用一条固定结构去组织,两分钟正好够:一句话说你是谁和转型方向,一句话说项目解决的业务问题(谁在什么场景下受什么苦),三到四句说你的关键技术决定和它换来了什么,最后一句给可验证的结果。
- 关键技术决定要挑「有取舍的」讲,不要讲「有实现的」。比如「流式用 SSE 而不是 WebSocket,因为上行只有一次,这样鉴权限流日志这套现成设施全部照用」——这种句子同时展示了你知道有别的选项、也知道选它的代价,比列出十个工具有效得多。
- 结果要尽量带数字,哪怕是自测数据:首字延迟从几秒降到几百毫秒、分层路由把日成本从 300 元降到 125 元、多 provider 冗余让可用性不再取决于单家厂商。没有生产数据就诚实说明是自测环境,编数字是最危险的做法,追问两句就穿帮。
- 常见误区是把学习项目说成生产项目。正确姿势是主动定位:这是我为了搞懂生产级 Agent 架构而完整实现的一套系统,规模是自测级,但每个决定都对着真实约束做过取舍——面试官对诚实的自评远比对夸大的描述宽容。
- 可以预期的追问:这个项目最难的地方是什么?提前准备一个具体的、有过程的答案(比如流式接口推流之后没法用状态码报错,最后改成流内 error 事件加上把校验全部前置),比任何形容词都有说服力。
Key points
- Keep a fixed structure: positioning, the business problem, three or four traded-off decisions, and one verifiable result
- Do not recite a stack — interviewers retain problems, trade-offs and numbers, not tool lists
- Frame decisions as trade-offs, naming the alternative and why it lost
- Attach numbers even from self-testing, but label their source and never invent them
- Scope the project honestly as a complete build at self-test scale; honest framing survives follow-ups better than inflation
答题要点
- 结构固定:定位一句、业务问题一句、三到四个有取舍的技术决定、一句可验证的结果
- 不要报菜名:面试官记不住工具清单,记得住问题、取舍和数字
- 技术决定要讲取舍而不是讲实现,说清楚备选方案是什么、为什么没选它
- 结果尽量带数字,自测数据也可以,但必须标明来源,绝不编造
- 主动定位项目规模:为搞懂生产架构而完整实现、自测级规模,诚实自评比夸大更容易通过
D8 Why Split Gateway and Worker; Postgres Table Design (sessions/runs/messages) + Drizzle
Why do production agent services usually split a gateway from workers, and when should you not split?为什么生产级 Agent 服务通常要把 Gateway 和 Worker 拆开?什么情况下不该拆?
Common in ChinaCommon overseasBasic#architecture#scalabilityHow to reason about it · think before answering
- The hinge is the second half. Answering only 'decoupling and scalability' sounds copied from a textbook; the interviewer wants to know which concrete symptom forced you to split, and what splitting costs.
- Offer a reusable chain: one agent run is long and unpredictable (model latency plus several tool calls, seconds to tens of seconds), while the ingress path carries all traffic and must stay in the millisecond range. Put workloads three orders of magnitude apart in the same process and the slow one starves the fast one.
- Make the symptom concrete: a single process running a dozen long executions saturates connections and memory, health checks start timing out, the orchestrator declares the instance dead and restarts it, and every in-flight run dies with it. That story lands harder than any abstract argument.
- Then state the rule: anything a worker can do should not live in the gateway, which keeps only auth, rate limiting, persistence and dispatch — four steps with bounded latency. After the split the stateless gateway scales with traffic while worker concurrency is tuned against model quota; the two curves were never the same.
- Volunteer the cost, which is where candidates separate: the contract becomes 202 instead of 200 so clients need a second subscribe round trip, you now operate a bus and a runs table, tracing spans more hops, and local development needs more processes. So do not split when a run takes a few hundred milliseconds, uses no tools, and serves modest traffic.
- Expect the follow-up: could a thread pool or child processes do instead? They ease starvation but fix neither 'restart loses in-flight work' nor 'two instances cannot see each other's state', because the root cause is state living inside the process, not the concurrency model.
分析过程 · 先想清楚再作答
- 题眼在后半句。只答「解耦、可扩展」是从架构书上抄来的,面试官想知道你有没有被某个具体现象逼着拆过——所以答案里必须出现「什么现象」和「不拆的代价」。
- 先给一条可复用的推导链:Agent 的一次执行是长耗时且时长不可预测的(模型响应加上多轮工具调用,几秒到几十秒),而接入层要承载全部流量、必须是毫秒级的短请求;把两种时长量级差三个数量级的工作放进同一个进程,慢的那一类必然会挤占快的那一类的资源。
- 把现象说具体:单进程时一台机器同时跑十几次长执行,连接与内存被占满,新来的健康检查开始超时,编排系统判定实例已死并重启它——正在跑的执行全部陪葬。这个「健康检查被自己的业务拖挂」的故事比任何抽象论证都有说服力。
- 然后给判据:能在 Worker 做的不放 Gateway,接入层只留鉴权、限流、落库、投递这四件耗时确定的事。拆开之后 Gateway 无状态可以任意扩缩,Worker 的并发度可以按模型配额单独调,两者的扩容曲线本来就不一样。
- 主动说代价,这是区分度所在:接口语义从 200 变成 202,客户端要多一次订阅往返;系统里多了一条总线和一张 runs 表,可观测性和排障链路都变长;本地开发要起更多进程。所以单次执行只有几百毫秒、没有工具调用、日活很小的场景不该拆——那时候拆分带来的复杂度远大于收益。
- 可以预期的追问:不拆但用线程池或者子进程行不行?答案是能缓解「挤占」但解决不了「重启即丢失」和「多实例状态不共享」,因为那两件事的根因是状态在进程里,不是并发模型不对。
Key points
- A run takes seconds to tens of seconds while ingress requests are millisecond-scale; in one process the long work starves the short work
- Three concrete failure modes: restarts lose in-flight runs, multiple instances hold separate state, and long runs stall health checks so the orchestrator kills a healthy instance
- The rule is that anything a worker can do stays out of the gateway, which keeps only auth, rate limiting, persistence and dispatch
- After splitting, gateways scale on traffic and workers scale on model quota — two independent curves
- Costs: a 202 contract plus a subscribe round trip, an extra bus and table to operate, longer traces; skip the split for sub-second runs with no tool calls
答题要点
- 一次 Agent 执行是几秒到几十秒的长任务,接入层是毫秒级短请求,两者同进程时长任务必然挤占短请求的资源
- 单进程的三个具体死法:重启丢掉在途执行、多实例状态各存各的、长执行把健康检查拖超时导致实例被误杀
- 判据是「能在 Worker 做的不放 Gateway」,接入层只留鉴权、限流、落库、投递
- 拆开后 Gateway 无状态按流量扩容、Worker 按模型配额扩容,两条曲线可以独立调
- 代价是接口从 200 变 202、多一次订阅往返、排障链路变长;单次执行仅几百毫秒且无工具调用的场景不该拆
D9 A Redis Streams Message Bus: XADD/XREADGROUP/XACK/XAUTOCLAIM, Consumer Groups, Poison Messages
How does a Redis Streams consumer group work, and why can it serve both as a work queue and as pub/sub?Redis Streams 的 consumer group 是怎么工作的?为什么它既能做工作队列又能做发布订阅?
Common in ChinaCommon overseasBasic#message-bus#redis-streamsHow to reason about it · think before answering
- This is a concept question; the discriminator is whether you separate the group layer from the consumer layer. Saying only 'several consumers read together' invites 'so is a message processed twice?' — and that is exactly what the two layers settle.
- Give the structure: the stream is append-only; a group sits on the stream and owns a read cursor plus a pending list; a consumer is just a name inside a group. Consumers in one group share the messages (each message goes to exactly one of them), while separate groups each see the full stream — one data structure, both a work queue and pub/sub.
- Then name the three things the pending entries list records: which consumer owns the message, how many times it has been delivered, and when it was last delivered. Those map to 'who is working on it', 'is it poison yet' and 'can someone else take over' — knowing them signals you read the docs, not just a snippet.
- Land on the dispatch rule: a group hands a message to whoever asks first, with no affinity at all. So a consumer group does not keep multiple messages from the same user in order on the same worker — say this yourself and you steer into ground you have prepared.
- Expect: how do you name consumers? Random names orphan the unacked messages of the previous name after a restart, recoverable only via XAUTOCLAIM. Either use stable ordinals from a stateful deployment, or rely on XAUTOCLAIM and periodically prune dead names with XGROUP DELCONSUMER.
- Expect: how do you preserve per-user order? Shard above the bus — hash the user id onto a fixed number of shards and let one consumer own a shard at a time. The consumer group cannot do this for you.
分析过程 · 先想清楚再作答
- 这题是概念题,区分度在于你有没有把「组」和「消费者」两层分清。只答「多个消费者一起消费」会被追着问「那同一条消息会不会被消费两次」,而这正是两层的区别所在。
- 先给两层结构:流本身只增不减,组挂在流上、维护一个读游标和一份 pending 清单,消费者挂在组上、只是组内的一个名字。同一个组内的消费者分摊消息(一条只进一个人),不同的组各自都能读到全量——工作队列和发布订阅就是这一个数据结构的两种用法。
- 接着点出 pending 清单(PEL)记了哪三件事:这条消息归哪个消费者、被投递过几次、最后一次投递在什么时刻。这三列分别对应「谁在处理」「要不要判成毒消息」「能不能被别人接手」,答出来就说明你真的读过文档而不只是抄过示例。
- 结论要落到分配规则上:组把消息分给谁,完全取决于谁先来问,没有任何亲和性。所以 consumer group 天然不保证「同一个用户的多条消息按顺序被同一个人处理」——这一句是把话题引向自己准备好的深水区。
- 可以预期的追问一:消费者的名字该怎么取?答:随机名会让进程重启后老名字下的未确认消息变成孤儿,只能靠 XAUTOCLAIM 捡回来,所以要么用有状态部署给的稳定序号,要么就必须依赖 XAUTOCLAIM 兜底,并定期用 XGROUP DELCONSUMER 清理不会再回来的名字。
- 可以预期的追问二:怎么保住同一个用户的顺序?答:在总线之上做分片——把用户 id 哈希到固定数量的分片,每个分片同一时刻只由一个消费者持有,顺序就回来了。消费组本身解决不了这件事。
Key points
- The stream is append-only; a group holds a read cursor and a pending list; a consumer is a name within a group
- Within a group messages are split (one message, one consumer); separate groups each get everything, so one structure covers both work queue and pub/sub
- The pending list records owner, delivery count and last-delivery time — used for takeover, poison detection and timeouts
- Dispatch has no affinity, so per-user ordering is not guaranteed and needs sharding above the bus
- Random consumer names orphan unacked messages after a restart; use stable names or rely on XAUTOCLAIM plus XGROUP DELCONSUMER cleanup
答题要点
- 流只增不减;组挂在流上,维护读游标和 pending 清单;消费者是组内的一个名字
- 同组内消息被分摊(一条只进一个消费者),不同组各自拿到全量,所以同一个结构同时支持工作队列和发布订阅
- pending 清单记三件事:归属的消费者、投递次数、最后一次投递时刻,分别用于接手、毒消息判定和超时检测
- 分配没有亲和性,谁先来问给谁,所以不保证同一个用户的多条消息顺序,要在总线之上做分片
- 消费者名字随机会在重启后留下孤儿消息,要么名字稳定,要么依赖 XAUTOCLAIM 并清理死名字
D10 Sharding and Leases: Hashing userId → shard, SET NX + TTL + Lua Renewal, Per-User Ordering, Handoff
Why hash user ids into shards instead of letting the consumer group dispatch freely, and how do you pick the shard count?为什么要对 userId 做哈希分片,而不是让消费组随机派发?分片数应该怎么选?
Common in ChinaCommon overseasBasic#sharding#consistent-hashing#scalabilityHow to reason about it · think before answering
- The hinge is 'why not dispatch freely'. Answering 'for load balancing' misses it — a consumer group already balances load, and free dispatch balances better than hashing. Sharding buys something else: affinity.
- The chain: a consumer group's unit of assignment is one message, while the business requires one user as the smallest serial unit. When those units disagree, two messages from the same user get processed concurrently by two workers.
- Second step: why insert a shard layer instead of taking userId modulo the worker count? Because the worker count changes on scale-up, restart, crash and rolling deploy. Change the divisor and almost every user is remapped, so in-flight sessions migrate wholesale. A fixed shard count pins user-to-shard and lets only shard-to-worker float.
- For the count, give criteria rather than a number: it caps parallelism (256 shards means at most 256 useful workers), and changing it is a data migration (every user is remapped, requiring downtime or a dual-write transition). So oversize it up front — 256 across 3 workers is 85/85/86 and costs a few hundred keys of memory, while picking 8 walls you in at the ninth worker. Use a power of two so the modulo degrades to a bit mask and future splits stay clean.
- Volunteer the limit of uniformity: it means uniform user counts, not uniform message volume. One enterprise account sending a thousand messages a day can share a shard with a thousand one-message users. The fix is an exception table before the hash that gives that account its own shard, not a larger shard count — that would be the migration above.
- Expect the follow-up: why not consistent hashing? It optimizes remap volume, which pays off when shards carry state that is expensive to move. Our workers are stateless executors with state in Postgres and Redis, so nothing needs moving, and shard ownership is already decided dynamically by leases. Fixed sharding optimizes predictability, which is simpler and more reliable here.
分析过程 · 先想清楚再作答
- 题眼在「为什么不随机派发」。只答「为了负载均衡」就掉进坑里了——消费组本来就是负载均衡,随机派发在均衡上比哈希分片更好。分片解决的是另一件事:亲和性。
- 推导链是这样的:消费组的分配单位是「一条消息」,而业务要求的最小串行单位是「一个用户」;单位对不上,同一个用户连发的两句话就会被两个进程同时处理。所以要把分配单位从消息抬到用户。
- 第二步是「为什么中间要垫一层 shard,而不是 userId 直接取模 worker 数」。因为 worker 数会变——扩容、重启、崩溃、滚动发布;除数一变,几乎所有用户的归属都会变,正在处理的会话被整体搬家。固定的 shard 数把「用户到 shard」钉死,只让「shard 到 worker」随伸缩浮动。
- 分片数怎么选,要给出可执行的判据而不是一个数字:它是并行度的上限(256 个 shard 最多让 256 个 worker 有活干),而且改它等于一次数据迁移(所有用户归属重算,必须停机或双写过渡)。所以宁可一开始定得偏大——256 摊在 3 个 worker 上是 85、85、86,多出来的成本只是几百个 key 的内存;定成 8 个的话扩到第 9 个 worker 就撞墙了。要用 2 的幂,取模能退化成位运算,也方便将来对半拆分。
- 主动说出哈希均匀的边界:均匀说的是「用户数均匀」,不是「消息量均匀」。一个日发千条的大客户可能和一千个散户落在同一个 shard 上。缓解是给大客户在哈希前加一张小的例外表、单独占一个 shard,而不是把总分片数调大(那就是上面说的数据迁移)。
- 可预期的追问:为什么不用一致性哈希?答案是它优化的是「节点变化时的迁移量」,前提是分片承载状态、搬迁很贵。我们的 worker 是无状态执行体,状态在数据库和 Redis 里,没有数据要搬;而且 shard 到 worker 的归属本来就由租约动态决定。固定分片优化的是可预测性,在这个场景里更简单,也更可靠。
Key points
- Sharding is about affinity, not balancing: it lifts the unit of assignment from one message to one user so a user always lands on the same worker
- The fixed shard layer keeps user-to-shard stable across scaling; only shard-to-worker ownership moves
- The shard count caps parallelism and changing it is a migration, so oversize it and use a power of two (256 in this course)
- Uniform hashing means uniform user counts, not uniform traffic; hot accounts need an exception table before the hash
- Consistent hashing optimizes remap volume and only pays off for stateful shards; stateless workers do better with fixed shards
答题要点
- 分片解决的是亲和性不是负载均衡:把分配单位从「一条消息」抬到「一个用户」,同一个用户永远落到同一个 worker
- 中间垫一层固定 shard,是为了让 worker 伸缩时用户到 shard 的映射保持不变,只有 shard 到 worker 的归属浮动
- 分片数是并行度上限,改它等于一次数据迁移,所以一开始就定偏大、用 2 的幂(本课 256)
- 哈希均匀保的是用户数均匀,不是消息量均匀;大客户热点要靠哈希前的例外表单独拆 shard
- 一致性哈希优化迁移量,只在分片带状态时划算;无状态 worker 用固定分片更简单
D11 The Run State Machine, Streaming Output Back, Ordering by runId, SSE Waiters, Merging Interruptions Within 30 Seconds
How would you design the state machine for one agent run, and which failure states must it cover?怎么设计一次 Agent 执行(run)的状态机?需要覆盖哪些异常状态?
Common in ChinaCommon overseasBasic#state-machine#distributed-systemsHow to reason about it · think before answering
- The discriminator is not listing states, it is explaining why a single-process service does not need them at all. Without that, you have only memorized a diagram.
- Start from motivation: in one process the call stack *is* the state. Once you split gateway and worker, three parties must answer the same question independently — the gateway decides whether to keep an SSE connection open, the worker decides whether someone already claimed the message, and a reopened browser tab asks whether the previous question is still generating. Different processes, so the answer has to live in a table.
- Then the states: pending to running to streaming to done on the happy path, with failed (retries exhausted) and cancelled (superseded by a merge, or user-cancelled) as exits available from anywhere. Volunteer why running and streaming are separate: running means claimed but no token yet, streaming means the first token is out. That boundary is your time-to-first-token probe and the frontend's cue to switch from spinner to typewriter.
- Land on the real purpose: the machine exists to reject writes. Terminal states having no outgoing edges is the most valuable row in the table. Under at-least-once delivery, a done run receiving one more chunk is routine, and without the table that chunk lands silently — the user sees half a sentence appended and the logs show nothing wrong.
- Add the discipline that separates shipped from read-about: every status write goes through one transition function. One raw UPDATE that bypasses it and the state machine is just a comment.
- Expect the follow-up on storage and concurrency: the database row is the single source of truth, and transitions are conditional updates that include the expected current status in the WHERE clause. Zero rows affected means someone moved first — re-read and decide, never blindly overwrite.
分析过程 · 先想清楚再作答
- 这题的区分度不在「能不能列出几个状态」,而在你有没有说出「为什么单进程时代不需要它」。答不出这一点,说明你只是抄过一张状态图。
- 先给动机:单进程里「执行到哪一步了」就是那个函数栈,状态存在于进程内存里,不需要名字。拆成 Gateway 与 Worker 之后,至少三方要同时回答同一个问题——接入层要判断还挂不挂 SSE,执行层要判断这条消息是否已被人领走,前端重开页面要判断上次的问题还在不在生成。三方不同进程,只能靠一张表对齐。
- 再给状态:pending 到 running 到 streaming 到 done 是正常路径,failed(重试耗尽)与 cancelled(被打断合并或用户取消)是两个随时可以走的异常出口。主动说明为什么 running 和 streaming 要分开:前者是「有人领走了但还没有一个字」,后者是「第一个字已出来」,这条线就是首字延迟的观测点,也是前端决定转圈还是打字机的依据。
- 结论要落到「状态机是用来挡写入的」:终态没有出边这一条最值钱。至少一次投递下「已经 done 的 run 又收到一个片段」是常态,没有转换表,那一笔会安静地写进库,用户看到回复末尾多出半句话,而日志里查不出是谁写的。
- 补一条纪律,这是有没有落地过的分水岭:所有写状态的地方都必须过同一个转换函数。绕过它直接执行一条更新语句,状态机就退化成注释了。
- 可以预期的追问:状态存哪、并发怎么办?答数据库那一行是唯一真相,转换用带条件的更新(更新时把当前状态写进 where 子句),失败说明有人抢先改过,这时候重读再决定,而不是覆盖。
Key points
- In one process the call stack is the state; after splitting gateway and worker, three parties need the same answer, so it has to be a table
- Happy path pending, running, streaming, done; exits are failed (retries exhausted) and cancelled (merged or user-cancelled)
- Separating running from streaming gives you a time-to-first-token probe and tells the UI when to switch from spinner to typewriter
- Terminal states with no outgoing edges reject the late chunks that at-least-once delivery guarantees you will get
- Every status write goes through one transition function, implemented as a conditional update on the expected current status
答题要点
- 单进程里状态就是函数栈;拆成 Gateway 与 Worker 后有三方要独立回答「这次执行到哪了」,必须落成一张表
- 正常路径 pending 到 running 到 streaming 到 done;异常出口 failed(重试耗尽)与 cancelled(打断合并或用户取消)
- running 与 streaming 分开,是为了观测首字延迟,也让前端知道该转圈还是该开始打字机效果
- 终态没有出边是核心:至少一次投递下的迟到片段会被当场挡住,而不是安静写进库
- 纪律:所有状态写入都过同一个转换函数,并用带当前状态条件的更新来处理并发
D12 Long-Term Memory: pgvector, Embeddings, Chunking, the memory_search Tool
Why does an agent need a separate long-term memory instead of stuffing all history into the context window?为什么 Agent 需要额外的长期记忆,而不是把历史全部塞进上下文?
Common in ChinaCommon overseasBasic#long-term-memory#rag#costHow to reason about it · think before answering
- The tempting answer is 'the window is too small'. That is half right and it is the cheap half — windows keep growing, and the interviewer will ask what you would do at a million tokens.
- Separate the two problems first: context compression solves 'this turn does not fit in one session', long-term memory solves 'I cannot recall what was said last month'. One subtracts at request-assembly time, the other adds. Naming that distinction unprompted is where the signal is.
- Then quantify: 200 memories at roughly 400 tokens each is 80k tokens; at 0.15 USD per million input tokens that is 0.012 USD every single turn, about 0.24 USD per user per day at 20 turns. Retrieving the top 5 is 2k tokens, 0.0003 USD per turn — a 40x gap, and it repeats every turn.
- Give the reason that beats cost: irrelevant context lowers accuracy. If one of 200 memories is relevant, the other 199 are noise that pull the model toward answering something nobody asked. So even with an infinite free window, you would still retrieve rather than dump.
- Land on practice: distil cross-session user facts and preferences into standalone statements, store them as vectors, and inject the three to five most relevant per turn — the minimal form of RAG.
- Expect the follow-up: what belongs in long-term memory? Three tests — is it still needed across sessions, does it expire, can retrieval find it again. 'Lives in Shanghai' passes all three; 'shorten that paragraph' passes none.
分析过程 · 先想清楚再作答
- 这题最容易答成「因为窗口装不下」。那只答对了一半,而且是不值钱的那一半——窗口一年比一年大,光靠这条理由,面试官会追问「等窗口到一百万 token 呢」,你就没词了。
- 先把两个问题拆开:上下文压缩解决的是「同一次会话里这一轮塞不下」,长期记忆解决的是「上个月说过的事想不起来」。前者在组装请求时做减法,后者做加法,触发时机、数据去向、失败后果都不同。能主动区分这两件事,是这题最大的区分度。
- 然后给成本账:200 条记忆、每条约 400 token 就是 8 万 token,按输入价 0.15 美元每百万 token 算,每一轮多付 0.012 美元;一天 20 轮就是 0.24 美元一个用户。只检索最相关的 5 条是 2000 token、每轮 0.0003 美元,差 40 倍。而且这笔钱是每轮重复付的,不是一次性的。
- 再给比钱更硬的理由:无关信息会降低命中率。200 条里跟这一轮相关的可能只有 1 条,剩下 199 条是噪声,模型会被带偏去回答一个用户没问的问题。**所以哪怕窗口无限大、token 免费,也该检索而不是全塞。** 这一句是这题的最优解。
- 落到做法上:把跨会话的用户事实与偏好抽成陈述句存进向量库,每轮按语义检索最相关的三五条注入请求——这就是 RAG 最小的一环。
- 可以预期的追问:什么信息该进长期记忆?答三问——跨会话之后还需要吗、会不会随时间失效、能不能靠检索捞回来。「用户住上海」三条都满足,「把刚才那段改成三句话」一条都不满足。
Key points
- Compression handles 'this turn does not fit'; long-term memory handles 'what did they say last month' — different problems, different machinery
- Dumping everything costs on every turn: 200 memories is about 80k tokens and 0.012 USD per turn versus 0.0003 USD for five retrieved ones
- The stronger reason is accuracy — irrelevant memories are noise, so you would retrieve even with an infinite window
- Practice: distil cross-session facts into statements, embed them, inject the top three to five per turn
- Admission test for a memory: still needed across sessions, does not expire, and is findable by retrieval
答题要点
- 压缩管「这一轮塞不下」,长期记忆管「上个月说过的事想不起来」,是两个问题、两套机制
- 全塞的成本是每轮重复付的:200 条约 8 万 token,每轮多 0.012 美元;检索 5 条只要 0.0003 美元
- 更硬的理由是准确率:无关记忆是噪声,会把模型带偏,所以窗口再大也该检索而不是全塞
- 做法是把跨会话的事实抽成陈述句、向量化存储,每轮按语义检索最相关的三五条注入
- 判断一条信息该不该进长期记忆:跨会话还需要吗、会不会失效、能不能被检索到
D13 Cron Scheduling (Central Scheduler → Stream Delivery) + Cost Metering (Token → USD Ledger, Usage Report)
When a service runs multiple replicas, why not let each replica start its own cron? What would you do instead?服务部署了多个实例,定时任务为什么不能让每个实例各自起一个 cron?你会怎么做?
Common in ChinaCommon overseasBasic#scheduling#distributed-systems#costHow to reason about it · think before answering
- The hinge is the phrase multiple replicas. Saying it would run twice is only the symptom; the interviewer wants the business and dollar consequence.
- Make the cost concrete: three replicas each running cron means the job fires three times, users get three identical pushes, and you pay for three model calls. The multiplier tracks replica count, so scaling to ten makes both the bill and the spam tenfold, with no alert firing, because from each process's own point of view it ran exactly once.
- Give the right shape: move the decision of who runs when into one central scheduler whose only job, on a cron match, is to publish a task message onto the bus; the execution side keeps using a consumer group so one message reaches exactly one consumer. The key insight is that a scheduled task is not a new execution path, it just swaps the user for a clock as the thing pressing the button, so the worker code stays untouched.
- Volunteer the obvious follow-up: doesn't the scheduler become a single point of failure? Two layers. It is stateless, so a crash costs you a few minutes of task delay; if you truly need HA, run two instances and dedupe on the idempotency key at publish time rather than bolting a distributed lock onto the scheduler.
- Close with sizing: a central scheduler plus a bus is enough at modest volume. At high volume, or when tasks have dependencies, teams move to a dedicated workflow scheduler with dependency graphs, retry policy and backfill, but the underlying central-decision-plus-queue shape is identical.
- Expect: the scheduler was down for 90 seconds and skipped a minute — now what? Replay the last N minutes on startup, one minute at a time. The idempotency key makes redundant publishes harmless, which is exactly what makes at-least-once plus idempotency the easy combination.
分析过程 · 先想清楚再作答
- 题眼在「多个实例」四个字。只答「会重复执行」拿不到分,因为那是现象;面试官想看你能不能把现象换算成业务后果和钱。
- 先把重复的代价说具体:3 个副本各起 cron,同一个任务被执行 3 次,用户收到 3 份一样的推送,你付 3 份模型调用的钱。而且这个倍数会跟着副本数走——扩容到 10 个副本,账单和骚扰量一起变成十倍,却不会触发任何告警,因为从每个进程自己的视角看它只是老实地执行了一次。
- 然后给出正确的形状:把「谁该在什么时候被执行」收进一个中心调度器,它命中 cron 之后只做一件事——往消息总线投递一条任务消息;执行侧照旧靠消费组分摊,一条消息只会被一个消费者拿到。关键认知是「定时任务不是一种新的执行方式,只是把按按钮的人从用户换成了钟表」,所以执行侧一行代码都不用改。
- 接着主动补上「那调度器自己不就成单点了吗」——这是必被追问的一句。答案分两层:调度器无状态、崩了拉起来就行,短暂不可用的代价只是几分钟内的任务延迟;真要高可用就起两个实例,靠投递时的幂等键去重,而不是靠给调度器加分布式锁。
- 最后点一句选型:任务量不大时中心调度器加消息总线足够;量大或者任务本身有依赖关系时,业界会换成专门的调度框架(带任务依赖、重试策略、补数),但底层的「中心决定 + 队列分发」结构是一样的。
- 可以预期的追问:调度器崩溃 90 秒,中间跨过的那一分钟怎么办?答启动时回看最近 N 分钟逐分钟重放,因为有幂等键兜底,重复投递无害——这正是 at-least-once 加幂等这组搭配能成立的地方。
Key points
- Per-replica cron means the job runs N times: N duplicate pushes, N times the model spend, scaling linearly with replica count and silently
- The right shape is a central scheduler that publishes one message to the bus on a cron match, with a consumer group ensuring exactly one worker picks it up
- A scheduled task is not a new execution path — only the trigger changed from a user to a clock, so worker code is unchanged
- The scheduler is stateless: restart on crash, and if you need HA run two and dedupe on the idempotency key rather than adding a distributed lock
- Missed minutes are recovered by replaying the last N minutes at startup, which is safe because the idempotency key absorbs duplicates
答题要点
- 每个实例各自起 cron 等于同一个任务被执行 N 次:用户收到 N 份重复推送,模型调用花 N 倍的钱,倍数随副本数线性增长且不会触发告警
- 正确形状是中心调度器命中 cron 后只往消息总线投递一条消息,执行侧靠消费组保证一条消息只被一个 Worker 拿到
- 定时任务不是新的执行路径,只是把触发者从用户换成了钟表,所以 Worker 侧不需要任何改动
- 调度器是无状态的,崩了拉起来即可;需要高可用就起两个实例靠投递时的幂等键去重,不要给它加分布式锁
- 崩溃期间跨过的时间点靠启动时回看最近 N 分钟重放补上,幂等键保证重复投递无害
D14 Deployment and Operations: Multi-Worker Compose, Heartbeats, Health Checks, Graceful Shutdown, Dev/Prod Isolation; Week Two Retrospective
How do you isolate dev from prod so local development cannot touch production data?怎么设计 dev 与 prod 的隔离,防止本地开发影响线上数据?
Common in ChinaCommon overseasBasic#operations#security#configurationHow to reason about it · think before answering
- This looks basic, but it screens for whether you have been burned. People who have start with the failure shape; people who have not start with use different config files.
- Describe the failure: same codebase, often the same Redis, and you start a worker locally to debug — except it is connected to the production stream and it claims and executes a real user's message. There is no error anywhere and both sides log business as usual, because from the code's point of view it did dutifully process one message. Precisely because nothing errors, this can run for a long time before anyone notices.
- Then give layered options by cost: namespacing (shared infrastructure, prefixed keys), separate instances (its own Redis and database), and separate environments (network, credentials, accounts all split). Production eventually wants the third layer, but the first is the cheapest and the easiest to get wrong, so that is where the focus belongs.
- The implementation detail in layer one is where the points are: the prefix may only be assembled in one function. Scatter string concatenation around the codebase, miss one key out of twenty, and you have no isolation at all — and the one you missed is usually the newest, least tested feature. This point signals real experience more than add a prefix does.
- Add three companions. Split credentials, so the local key can only reach the dev database and a misconfiguration cannot reach production. Make destructive operations environment-aware: scripts that truncate tables, replay dead letters or rebuild indexes read the environment variable on their first line and demand explicit confirmation in production. And forbid fallback implementations in production: if a config slip makes production take the in-memory path, processes come up quietly, each working in its own memory, with every health check green — that kind of fault hides for hours, so failing fast at startup is far cheaper than diagnosing it later.
- Expect: why not just use separate instances and skip prefixes? Because separate instances solve connected to the wrong address while prefixes solve connected to the right address but the wrong namespace — the two fail differently. Prefixes are also nearly free, and they incidentally isolate each developer's data in a shared test environment. Defence should be layered, and there is no reason to skip the cheapest layer.
分析过程 · 先想清楚再作答
- 这题看着基础,但它筛的是「有没有踩过」。踩过的人第一句会说事故形态,没踩过的人第一句说「用不同的配置文件」。
- 先说事故形态:同一套代码、经常还是同一个 Redis,你在本机起一个 Worker 调试,它连的却是线上那条流,把真实用户的消息捞走执行了。**这类事故没有任何报错,两边日志都显示一切正常**——从代码角度看它确实老老实实处理了一条消息。正因为没有报错,它可能持续很久才被发现。
- 然后按成本分层给方案:命名空间(同一套基础设施,键名带前缀)、独立实例(各自的 Redis 与数据库)、独立环境(网络、凭证、账号全分开)。生产系统最终要走到第三层,但第一层成本最低也最容易漏,所以是重点。
- 第一层的关键实现细节是拿分点:前缀只能在一个函数里拼。散落到各处去拼字符串,二十个键名里漏掉一个就等于没隔离,而漏掉的那个通常是最新加、最没被测过的功能。这一点比「要加前缀」本身更能体现工程经验。
- 再补三件必须一起做的事:凭证分开(本机那把 key 只能连开发库,配置写错也波及不到线上);破坏性操作要认环境(清库、重放死信、重算索引这类脚本第一行先读环境变量,生产上要求显式确认);生产禁止降级实现(离线用的内存实现在生产上一旦因配置疏漏被走到,进程会安静起来、各自在自己内存里干活,健康检查还全是绿的,这类故障能藏好几个小时——启动时直接报错退出比事后排查便宜得多)。
- 可以预期的追问:为什么不干脆只用独立实例,省掉前缀这一层?答:独立实例解决的是「连错了地址」,前缀解决的是「连对了地址但走错了命名空间」——两者失效的方式不同。而且前缀几乎零成本,在共享测试环境、多人并行开发时还能顺带隔离每个人的数据。防御要分层,最便宜那层没理由不做。
Key points
- Lead with the failure shape: a local worker attached to the production stream claims and runs a real user's message, with normal logs on both sides and no error, so it hides for a long time
- Three layers by cost: namespacing (key prefixes), separate instances (own Redis and DB), separate environments (network, credentials, accounts)
- The prefix must be assembled in exactly one function — scattered concatenation misses one key and voids the isolation, usually the newest and least tested feature
- Split credentials so the local key only reaches dev; destructive scripts read the environment first and require explicit confirmation in production
- Forbid the in-memory fallback in production: on a config slip processes come up quietly with green health checks and the fault hides for hours — fail fast at startup instead
- Separate instances prevent wrong address, prefixes prevent right address wrong namespace — different failure modes, and the cheapest layer is free
答题要点
- 先说事故形态:本机 Worker 连上线上流,把真实用户消息捞走执行,且两边日志都显示正常、没有任何报错,所以能藏很久
- 按成本分三层:命名空间(键名前缀)、独立实例(各自 Redis 与库)、独立环境(网络凭证账号全分开)
- 前缀只能在一个函数里拼——散落各处漏掉一个键就等于没隔离,而漏掉的通常是最新加、最没测过的功能
- 凭证分开,本机 key 只能连开发库;破坏性脚本第一行读环境变量并在生产要求显式确认
- 生产禁止降级到内存实现:配置疏漏时进程会安静起来、健康检查全绿,故障能藏几小时,应在启动时直接报错退出
- 独立实例防「连错地址」、前缀防「地址对了但命名空间错了」,失效方式不同,最便宜那层没理由不做
D15 A Tour of Multi-Agent Patterns (Router/Supervisor, Planner-Executor, Critic, Swarm, Blackboard) and When Not to Use Them; Getting Started With LangGraph
What are the common multi-agent collaboration patterns, and what shape of task suits each?常见的多 Agent 协作模式有哪些?分别适合什么形状的任务?
Common in ChinaCommon overseasBasic#multi-agent#orchestration#architectureHow to reason about it · think before answering
- This looks like a giveaway but it separates people who memorised names from people who have split a system. Listing five names is a bare pass; the interviewer wants the axis you use to tell them apart, because an axis means you can classify an architecture you have never seen.
- Offer a reusable axis: the difference is not the name, it is the shape of the graph. Four questions suffice — is there a branch (pick one at runtime), a fan-out (hand it to several at once), a join (merge several outputs), a back edge (send it back for rework).
- Then place each one: Router/Supervisor is branch only, one specialist per turn, the hard part is deciding who; Planner-Executor is fan-out plus join, for work that splits into independent pieces; Critic is branch plus back edge, for output with a clear pass/fail test where redoing is cheaper than shipping; Swarm is also branch plus back edge, but the next hop is chosen by whoever holds the baton; Blackboard is fan-out plus join plus back edge, participants unaware of each other, reacting only to shared state.
- Point out yourself that Critic and Swarm score identically on all four, and that the real difference is who decides the back edge — a fixed reviewer node versus the current agent. Volunteering where your own criterion breaks down scores better than reciting one more pattern name, because it proves you have used the axis rather than invented it on the spot.
- Attach a cost to each: Router adds one routing call of latency; Planner-Executor's parallelism creates write conflicts so fields need merge rules; Critic loops need a hard retry cap or nothing ever ships; Swarm has no upfront bound on steps so cost and latency are hard to cap; Blackboard has the hardest termination condition and tends to either stall or re-trigger.
- Expect: which do you use most in production? Say Router/Supervisor, because its failure mode is the easiest to read — check the recorded routing reason — and because it is the one pattern that can save money, by routing simple intents to a cheaper model.
分析过程 · 先想清楚再作答
- 这题看似送分,其实在筛「背过名词」和「拆过系统」。只报五个名字最多拿及格分,面试官真正想听的是你用什么维度把它们区分开——有维度说明你能给没见过的架构归类,没维度说明你只是读过一篇综述。
- 给一个可复用的维度:模式的差别不在名字,在图的形状。盯四件事就够——有没有分叉(运行时三选一)、有没有扇出(同时交给多个人)、有没有汇合(多份产出合到一起)、有没有回边(可以打回重做)。
- 然后逐个落位:Router/Supervisor 只有分叉,一次只找一个专家,难点在判断该找谁;Planner-Executor 是扇出加汇合,适合一件事拆成几件、几件之间没有先后;Critic 是分叉加回边,适合对错有明确判据、且重做比发出去便宜的产出;Swarm 也是分叉加回边,但下一棒交给谁由当前这位自己决定;Blackboard 是扇出加汇合加回边,参与者互相不知道对方存在,只认公共状态。
- 主动指出 Critic 和 Swarm 的四个特征一模一样,区别落在「回边由谁决定」——Critic 是固定的评审节点在判,Swarm 是当前这位自己判。**主动承认自己的判据在哪里失效,比多背一个模式名更能加分**,因为它证明你真的用过这套维度而不是刚编出来。
- 每种模式还要配一句代价,这是区分度所在:Router 多一次路由调用的延迟;Planner-Executor 的并行会带来状态写冲突,字段必须配合并规则;Critic 的回路必须有次数上限,否则永远出不了稿;Swarm 事先不知道会走多少步,成本和延迟都难封顶;Blackboard 的终止条件最难写,容易谁都不接活或者反复触发。
- 可以预期的追问:生产上你最常用哪个?答 Router/Supervisor,理由是它的失败模式最好理解——路由判错了看一眼路由理由就知道,而且它是唯一一个能顺便省钱的模式,简单意图可以路由到便宜的小模型。
Key points
- Give the axis before the names: branch, fan-out, join and back edge separate all five patterns
- Router/Supervisor is branch only — one specialist per turn, the hard part is choosing who
- Planner-Executor is fan-out plus join — split into independent subtasks, then merge into one deliverable
- Critic is branch plus back edge — for output with a clear pass/fail test, and it needs a hard retry cap
- Swarm scores the same as Critic; the difference is who decides the back edge. Blackboard decouples via shared state and has the hardest termination condition
- Pair each with a cost: extra call latency, parallel write conflicts, infinite review loops, unbounded step count, fuzzy termination
答题要点
- 先给维度再给名字:分叉、扇出、汇合、回边四个特征就能把五种模式分开
- Router/Supervisor 只有分叉,一次只找一个专家,难点是判断该找谁
- Planner-Executor 是扇出加汇合,适合拆成几件互不依赖的小任务再合成一份交付
- Critic 是分叉加回边,适合对错有明确判据、重做比发出去便宜的产出,必须配打回次数上限
- Swarm 与 Critic 的四个特征相同,区别在回边由谁决定;Blackboard 靠公共状态解耦,终止条件最难写
- 每种模式配一句代价:多一次调用的延迟、并行的写冲突、回路的死循环、步数不封顶、终止条件难定
D16 Dynamic Routing With a Supervisor: Structured-Output Routing, Override, routingReason
How is the routing decision usually implemented in a supervisor pattern? What should that node do, and what should it not do?Supervisor 模式里的路由决策一般怎么实现?请说说这个节点该做什么、不该做什么。
Common in ChinaCommon overseasBasic#multi-agent#routing#langgraphHow to reason about it · think before answering
- This is a warm-up question, and warm-ups are where people lose points by restating the prompt: an agent decides who goes next. The discriminator is the second half — can you state the node's responsibility boundary?
- Start with the mechanics: the supervisor is an ordinary node. It reads state, makes one model call, and writes exactly two fields — the route and the reason for it. The actual branching happens on the conditional edge after it, whose selector function maps the route to the next node name.
- Then draw the boundary, which is where the points are: the supervisor never answers the user, never calls business tools, and produces no side effects. It only takes a multiple-choice test, so it can run on a cheaper small model with a short input.
- One boundary people miss: do not call the model inside the selector function. The judgement was already made and stored in state; the selector only translates. Calling a model there makes the same state jump to different nodes across runs, which destroys reproducibility and breaks checkpoint replay and evaluation later.
- Close with one-at-a-time: a supervisor answers who takes this, not how to split a task and who reviews the output. That second problem belongs to planner-executor-critic. Drawing that line yourself signals you have seen a real system.
- Expect: where does the list of sub-agents live, and how many places change when you add one? Answer that the list should be a single source of truth — the enum, the schema, and the edge mapping all derive from it, so adding an agent is one edit and everything else fails at compile time.
分析过程 · 先想清楚再作答
- 这是一道送分题,但送分题最容易答成「让一个 Agent 决定下一步找谁」这种复述题面的话。区分度在后半句:你能不能说清这个节点的职责边界。
- 先给机械原理:Supervisor 是图里的一个普通节点,它读状态、调一次模型、只写两个字段——交给谁(route)和为什么这么判(routingReason);真正的分叉发生在它后面那条条件边上,边上挂一个选择函数,把 route 翻译成下一个节点名。
- 再划边界,这是拿分的地方:Supervisor 不回答用户的问题、不调业务工具、不产生副作用。它只做选择题,所以可以配一个更便宜的小模型,输入通常只有系统提示词加最后一两句话。
- 还有一条边界更容易被忽略:**选择函数里不要再调模型**。判断已经在 Supervisor 节点里做完并落进状态了,选择函数只做翻译。把模型调用塞进选择函数,同一份状态每次可能跳到不同的节点,图就不可复现,后面做检查点重放和评估都会失真。
- 最后补一句「一次只派一个人」:Supervisor 解决的是「交给谁」,不解决「一件事要拆成几件、还得有人验收」。后者是 Planner-Executor-Critic 的活。能主动划出这条线,面试官会认为你见过真实系统的边界。
- 可以预期的追问:那三个子 Agent 的名单从哪来、加一个新的要改几处?答案是名单应该是单一真相来源——枚举定义、schema、条件边的映射表都从它生成,加一个子 Agent 只改一处,其余地方编译期报错提醒你。
Key points
- The supervisor is an ordinary node: read state, one model call, write only the route and the routing reason
- Branching lives on the conditional edge after it — a selector maps the route to a node name, and the mapping table must be exhaustive
- Boundary: it never answers the user, calls no business tools, has no side effects, so it can run on a cheaper small model
- Never call a model inside the selector, or the same state jumps to different nodes across runs and replay and evaluation both break
- A supervisor dispatches one agent at a time and only answers who takes this; splitting and reviewing belong to planner-executor-critic
答题要点
- Supervisor 是图里的一个普通节点:读状态、调一次模型、只写 route 与 routingReason 两个字段
- 真正的分叉在它后面的条件边上:选择函数把 route 翻译成下一个节点名,映射表要写全
- 职责边界:不回答用户、不调业务工具、不产生副作用,因此可以单独配一个更便宜的小模型
- 选择函数里不能调模型,否则同一份状态每次跳的节点不同,图不可复现,检查点重放与评估都会失真
- Supervisor 一次只派一个人,只解决「交给谁」;拆任务与验收是 Planner-Executor-Critic 的职责
D17 Planner-Executor-Critic Plus a Shared Workspace: Workspace State, toolBudget, Parallel Fan-Out, a Review Loop
What problem does the Planner-Executor-Critic structure solve, and how is it different from Supervisor routing?Planner-Executor-Critic 这种结构解决了什么问题?它和 Supervisor 路由的区别在哪?
Common in ChinaCommon overseasBasic#multi-agent#orchestration#architectureHow to reason about it · think before answering
- The hinge is the second half. Reciting plan, execute, review is naming shapes from memory; the interviewer wants to see you separate the two patterns by graph shape.
- Separate by shape: a Supervisor is a fork — at runtime it picks one of several paths and hands the work to exactly one agent, so the graph only branches. Planner-Executor-Critic fans out, joins, and adds a back edge. Branching answers who takes this, fan-out answers this must be split into several pieces, the back edge answers who signs it off.
- Then give the criteria: use a Supervisor when only one specialist is needed per request and the hard part is picking them; only fan out when a request genuinely splits into independent pieces with no ordering between them; only add a Critic when correctness has an explicit rubric and redoing is cheaper than shipping something wrong. If none of these hold, do not build this.
- Land on cost, which is where shipped-it separates from read-the-docs: three subtasks turn one model call into seven (one plan, three executions, three reviews) and nine after a single rejection round; latency is set by the slowest branch rather than the average, and parallelism buys latency, never money.
- Expect: does the Critic have to be its own node? Not necessarily — if the rubric is checkable in code (schema validation, required fields), check it in code: faster, cheaper, and more reliable. A Critic earns a model call only when the rubric requires understanding meaning.
分析过程 · 先想清楚再作答
- 题眼在后半句。只答「拆解、执行、评审」是在背名词,面试官想确认的是你能不能用图的形状把两种模式分开,而不是靠记忆背模式表。
- 先用形状拆:Supervisor 是一个岔路口,运行时在几条路里选一条走,一次只交给一个人,图上只有分叉;Planner-Executor-Critic 是先扇出、再汇合、中间还有一条回边。分叉解决「交给谁」,扇出解决「一件事要拆成几件」,回边解决「谁来验收」。
- 再给适用判据:一次只需要一个专家、难点在判断该找谁,用 Supervisor;一件事必须拆成几件且几件之间没有先后依赖,才值得扇出;产出的对错有明确判据、且错了重做比错了发出去便宜,才值得加 Critic。三条判据都不命中就别上这套结构。
- 结论要落到代价,这是区分「读过文档」和「上线过」的地方:拆出三件事意味着模型调用次数从一次变成七次起步(拆解一次、三次执行、三次评审),有一轮打回就是九次;延迟被最慢的那件事决定而不是平均值,而且并行只省延迟不省钱。
- 可以预期的追问:Critic 一定要单独一个节点吗?答案是不一定——如果验收判据是可以用代码判的(比如 JSON schema 校验、必填字段检查),就别花一次模型调用,代码判更快更准也更便宜。只有判据本身需要理解语义时,Critic 才值得是一次模型调用。
Key points
- A Supervisor branches (one agent per request); Planner-Executor-Critic fans out, joins, and loops back (split, run in parallel, then sign off)
- Three criteria: route when one specialist suffices; fan out only for genuinely independent pieces; add review only when the rubric is explicit and redoing beats shipping wrong
- The cost is seven to nine model calls instead of one, with latency set by the slowest branch — parallelism buys latency, not money
- If the rubric is checkable in code, check it in code; a Critic deserves a model call only when semantics must be understood
答题要点
- Supervisor 是分叉(一次派一个人),Planner-Executor-Critic 是扇出加汇合加回边(拆成几件并行做,做完有人验收)
- 三条适用判据:一次只需一个专家用路由;能拆成互不依赖的几件才扇出;对错有明确判据且重做便宜才加评审
- 拆解的代价是模型调用从一次涨到七到九次、延迟由最慢的分支决定,而并行只省延迟不省成本
- 评审判据能用代码判就别用模型判,Critic 只在需要理解语义时才值一次模型调用
D18 History Fidelity and Summarization, Multimodal Placeholders, Checkpointer Persistence
How should agent memory be layered? What belongs in short-term context, in summaries, and in long-term memory — and what happens when each is lost?记忆应该怎么分层?短期上下文、摘要、长期记忆分别放什么、丢了会怎么样?
Common in ChinaCommon overseasBasic#memory#context-management#multi-agentHow to reason about it · think before answering
- The discriminator here is not listing three layers, it is saying what breaks when each one is lost. An answer that only names the layers tells the interviewer you have never operated one.
- Offer a reusable split first: sort any memory scheme by who reads it, how long it lives, and whether it can be rebuilt after loss. Those three questions cut through every design.
- Short-term context is the message array sent to the model this turn. It dies with the request and is billed in full every turn. Losing it only costs coherence for that turn, because the raw transcript still lives in your own store and can be replayed.
- A summary is derived from short-term context, produced to shrink early turns before the window fills. It can be regenerated after loss — but only if the raw transcript was stored separately. That is the practical reason a summary must never overwrite the original.
- Long-term memory holds cross-session user facts and preferences. It never enters the message array; it lives in a retrieval layer and a few hits get injected on demand. Losing it means the system forgot the user — single requests still work, but the product gets noticeably worse.
- Multi-agent adds a fourth layer people usually miss: graph execution state — messages, shared workspace, review rounds, degraded flags. It is the only copy that gets checkpointed and replayed on resume, and losing it is the most expensive failure: a run that already burned nine model calls starts over while the user watches a spinner.
- Expect the follow-up: should the summary live inside the message array or in its own field? Say its own field — keeping raw and derived data apart is what lets you regenerate with a different strategy later; merged together you can no longer tell what actually happened from what was written after the fact.
分析过程 · 先想清楚再作答
- 这题的区分度不在能不能列出三层,而在能不能说出**每一层丢了会怎样**。只报名词的答案,面试官听不出你有没有真的运维过。
- 先给一条可复用的拆法:按「谁在读它、活多久、丢了能不能补」三个问题去分,任何一个记忆方案都能被这三问切开。
- 短期上下文是这一次请求要发给模型的那个消息数组,随请求结束作废,全量进 token 账单;它丢了只影响这一轮的连贯性,原文还在你自己的会话记录里,可以重放。
- 摘要是短期上下文的派生数据,用来在窗口顶到之前把早期内容压短;它丢了可以重新生成——**前提是原文另存了一份**。所以摘要绝不能覆盖原文,这是「压缩不可逆」那条纪律的实际落点。
- 长期记忆是跨会话的用户事实与偏好,不进消息数组,存在外部检索层里按需捞几条注入;它丢了的表现是「这个用户被系统忘光了」,不影响单次可用,但产品价值直接掉一层。
- 多 Agent 还要补第四层,也是最容易被忽略的一层:**图的执行状态**。它包含消息、共享工作区、评审轮次、降级标记,是唯一一份会被检查点持久化并在恢复时重放的数据。它丢了的后果最重——一次已经花掉九次模型调用的执行必须从头再来,而且用户界面还停在转圈。
- 可以预期的追问:摘要该放在消息数组里还是单独一个字段?答单独字段,理由是原文与派生数据要分开存,才可能换一种策略重新生成;混在一起之后你分不清哪条是真发生过的、哪条是事后编的。
Key points
- Layer by who reads it, how long it lives, and whether it can be rebuilt — that beats reciting names
- Short-term context: this turn's message array, discarded after the request, billed in full, replayable from your own transcript
- Summary: derived from short-term context and regenerable, but only if the raw transcript is stored separately — so it must never overwrite the original
- Long-term memory: cross-session user facts in a retrieval layer, injected on demand; losing it means the system forgot the user
- Multi-agent adds graph execution state — messages, workspace, review rounds, degraded flags — checkpointed and replayed on resume, and the most expensive to lose
- Keep the summary in its own field rather than back in the message array, so raw and derived data stay separable
答题要点
- 按「谁在读、活多久、丢了能不能补」三问分层,比背名词有用
- 短期上下文:本轮请求的消息数组,随请求作废,全量计费,丢了可从原始记录重放
- 摘要:短期上下文的派生数据,可重新生成,前提是原文另存——所以摘要不能覆盖原文
- 长期记忆:跨会话的用户事实,存在检索层按需注入,丢了是「系统忘了这个人」
- 多 Agent 多一层图执行状态:消息 + 工作区 + 评审轮次 + 降级标记,会被检查点持久化并在恢复时重放,丢了最贵
- 摘要放独立字段而不是塞回消息数组,原文与派生数据分开存才可能换策略重生成
D19 Cross-Service Agent Integration: Minting a User-Level JWT, JWKS Signature Verification, the inject/memory/usage Interfaces, Idempotent externalId
How does JWKS-based verification work, and why does it fit cross-service scenarios better than a shared secret?JWKS 验签是怎么工作的?为什么跨服务场景下它比共享密钥更合适?
Common in ChinaCommon overseasBasic#auth#jwt#securityHow to reason about it · think before answering
- This is the giveaway question of the chapter, but it still separates people: can you turn key rotation into a concrete operational sequence rather than saying it is easier to manage?
- Describe the mechanism in three sentences. The issuer holds the private key and signs; the public key set is published at a fixed address (/.well-known/jwks.json here); the token header carries a kid, and the verifier picks the matching public key from the set. Verification needs only public material, so the endpoint is public by design.
- Then give three reasons, each as an operational action: rotation needs no synchronized deploy on both sides (publish the new public key, let both coexist, drop the old one after old tokens expire); the verifier holds verification power, not signing power, so compromising it does not let anyone forge tokens; and adding a caller does not scatter another copy of a secret.
- Volunteer the part people forget: verifying the signature is not the whole check. A valid signature only proves the issuer signed it. You still validate iss, aud and exp — and missing aud is the most common cross-service incident, because a token the issuer signed for a different downstream is equally well signed, so skipping audience means holding the door open for someone else's API.
- Two engineering details worth adding: cache the key set but refetch on an unknown kid, or rotation day becomes a mass failure; and allow a small clock skew on exp, but not so large that it cancels out the point of short lifetimes.
- Expect: so is HS256 unusable? Answer that it is fine when one service signs and verifies its own tokens, and it is faster. The criterion is whether signer and verifier sit in the same trust domain; across domains, asymmetric is mandatory. Framing it as a trade-off shows judgment rather than memorization.
分析过程 · 先想清楚再作答
- 这是本章的送分题,但送分题也有区分度:能不能把「密钥轮换」这件事讲成一个具体的运维动作,而不是一句「更方便管理」。
- 先讲机制,三句话:签发方持私钥签名,公钥集合挂在一个固定地址上(本课用 /.well-known/jwks.json);令牌头部带一个 kid,验签方按 kid 从集合里挑对应的公钥;验签只用公钥,所以这个地址是公开的,谁都能拉。
- 再讲为什么比共享密钥好,三条都要落到运维动作上:轮换不用两边同时发版(新旧两把公钥并存一段时间,等老令牌自然过期再摘旧的);验签方拿到的只是验签能力而不是签名能力,被入侵也伪造不出令牌;多一个调用方不用多散一份密钥出去。
- 然后主动补上最容易被忽略的一段:验签不等于验完。签名合法只说明「这确实是那个签发方签的」,还必须校验 iss、aud、exp——**漏掉 aud 是跨服务集成里最常见的事故**,因为签发方给别的下游服务签的令牌,签名一样合法,不校验受众就等于替别人的接口开门。
- 工程细节可以再加两条:公钥集合要缓存,但遇到没见过的 kid 要能主动重拉,否则轮换那一刻会集体失败;以及时钟偏移,exp 校验要留一点容忍度,但容忍度不能大到把短有效期的意义抵消掉。
- 可以预期的追问:那 HS256 是不是就不能用了?答「同一个服务自己签自己验时它没问题,而且更快」——判据是签名方和验签方是不是同一个信任域,跨了域就必须非对称。这么答显得你在做权衡而不是背结论。
Key points
- Mechanism: private key signs, public key set sits at a fixed URL, the token header carries a kid, the verifier selects by kid
- Rotation needs no synchronized deploy: publish the new key, let both coexist, retire the old one after old tokens expire
- The verifier gets verification power only, never signing power, so compromising it cannot forge tokens
- Adding callers does not scatter more secrets; the public key being public is the design intent
- Beyond the signature you must check iss, aud and exp — skipping aud opens your API to tokens signed for someone else
- Cache the key set but refetch on an unknown kid; HS256 is still reasonable when one service signs and verifies its own tokens
答题要点
- 机制:私钥签名、公钥集合挂在固定地址、令牌头部带 kid、验签方按 kid 取公钥
- 轮换不用两边同时发版:新旧公钥并存,等老令牌自然过期再摘旧的
- 验签方只拿到验签能力而不是签名能力,被入侵也伪造不出令牌
- 调用方增加不需要多散一份密钥,公钥公开本来就是设计意图
- 验签之外必须校验 iss、aud、exp,漏掉 aud 等于替别的下游服务开门
- 缓存公钥集合但要能按未知 kid 主动重拉;HS256 在同一信任域内自签自验仍然是合理选择
D20 Scheduled Jobs and Proactive Outreach: Time Zones, Quiet Hours, Daily Caps, a Notification Provider Abstraction
How does a system-initiated message differ from a user-triggered one, from a system design point of view?系统主动发给用户的消息,和用户自己触发的消息,在系统设计上有什么不同?
Common in ChinaCommon overseasBasic#proactive-messaging#system-design#product-engineeringHow to reason about it · think before answering
- This looks like a definition question but it is really a filter. Answering both send a message, only the trigger differs stays at the shallowest layer — the interviewer wants to know what extra code the difference forces you to write.
- Give three structured differences: who is waiting (a user-triggered reply has someone staring at the screen, a proactive message has nobody waiting); how failure is handled (user-triggered failures must surface as errors, proactive failures should usually be silently deferred or dropped); and what justifies sending (the user asked, versus you having to justify it yourself).
- The third is the hinge, so make it explicit: the default answer for a proactive message is do not send. Every one must answer why now, why this user, and why this content is worth interrupting them. Fail any of the three and it should not go out.
- Then land the difference in the system: the proactive path needs an admission layer the reactive path does not — compute the user's local time from their timezone, defer if it falls inside quiet hours, drop if the daily cap is used up.
- Quantify the cost, which is what separates having read about this from having shipped it: tolerance for proactive messages is very low. After a few irrelevant pushes the user will not argue about the content, they will revoke the notification permission — and once revoked, the genuinely important message cannot reach them either. You are spending a budget that never refills.
- Expect: so is a cron job the same thing as a proactive message? No. The scheduler solves firing on time (central scheduling, an idempotency key anchored to the scheduled minute); proactive care solves whether to send at all. One is mechanism, the other is admission, and they belong in separate layers.
分析过程 · 先想清楚再作答
- 这题看着像概念题,其实是筛人题。答「都是发消息,只是触发方不同」就落进了最浅的一层——面试官想听的是这个差别会逼你多写哪些代码。
- 先给三条结构化的差别:谁在等(用户触发时他正盯着屏幕,主动消息没有人在等);失败怎么处理(用户触发的失败必须报错给他看,主动消息的失败多数时候应该安静地推迟或放弃);凭什么发(用户触发是他开了口,主动消息你得自己说出理由)。
- 第三条是题眼,要说透:主动消息的默认答案是不发。每一条都要能回答为什么是现在、为什么是这个用户、为什么这条内容值得打断他,三个问题答不上任何一个就不该发。
- 然后给出这个差别在系统里的落点:主动消息这一侧必须多出一层准入判断,本课叫三道闸——按用户时区算本地时间、安静时段命中就推迟、每日上限满了就拦下。用户触发那一侧完全不需要这层。
- 代价也要算清楚,这是区分「读过文章」和「做过系统」的地方:用户对主动消息的容忍度极低,连着几条无关紧要的推送之后他不会争论内容对不对,直接关掉通知权限——而权限一关,你连真正重要的那条也送不出去了。你消耗的是一个用完就拿不回来的额度。
- 可以预期的追问:那定时任务和主动消息是不是一回事?答不是。定时任务解决的是「能按时触发」(中心调度、幂等键锚在计划触发的那一分钟),主动消息解决的是「该不该发」,前者是机制、后者是准入,两层要分开做。
Key points
- Three differences: who is waiting, how failure is handled, and what justifies sending — the third is the crux
- The default answer for a proactive message is no; each one must justify why now, why this user, why worth interrupting
- In the system this becomes an admission layer — timezone, quiet hours, daily cap — that the reactive path does not need
- The cost is a non-renewable budget: annoy the user and they revoke notifications, taking the important messages down with them
- Scheduling (fire on time) and proactive care (should we send) are two separate layers
答题要点
- 三条差别:谁在等、失败怎么处理、凭什么发;第三条是关键
- 主动消息的默认答案是不发,每条要能回答为什么是现在、为什么是这个用户、为什么值得打断他
- 落到系统上就是多一层准入判断:时区换算、安静时段、每日上限,用户触发那一侧不需要
- 代价是一个不可再生的额度:推送惹烦了用户,他关掉权限之后重要消息也送不出去
- 定时机制(能按时触发)和主动关怀(该不该发)是两层,不要混在一起做
D22 Security: Prompt Injection, Least Privilege for Tools, Sandboxing Approaches, Secret Management
What is prompt injection? How do direct and indirect injection differ, and why can't it be fixed the way SQL injection was?什么是 prompt injection?直接注入和间接注入有什么区别,为什么它不像 SQL 注入那样能被彻底修复?
Common in ChinaCommon overseasBasic#prompt-injection#security#agent-designHow to reason about it · think before answering
- It looks like a definition question, but the whole spread is in the second half. 'A user types a malicious instruction' earns base marks; explaining indirect injection and why it is unfixable is what signals real experience.
- Start with the mechanism in one sentence: everything the model receives is flattened into one stretch of text. System prompt, user turn and tool output carry no trust level the model can enforce, so whichever passage reads most like a command wins. Compliance is probabilistic; the model has no concept of permission.
- Then separate the two shapes. Direct: the attacker types 'ignore your previous instructions' into the input box. Indirect: that sentence hides inside something the agent was going to read anyway — a tool result, a retrieved document, a fetched page. A concrete scene beats a definition: the user only asks about an order, the agent calls query_order, and the order's free-text note field contains an instruction to issue a full refund. That field was filled in by whoever placed the order.
- Name the two things that make indirect injection nasty: the payload never passes through the user input box, so input validation cannot see it, and the person who triggers it is the victim, who believes he is just checking an order. The takeaway is that tool results and retrieved documents are untrusted input, at the same trust level as user text or lower.
- Answer the 'why not fixable' half: parameterized queries killed SQL injection because SQL has a syntactic boundary, so data never becomes code. A model's input is natural language only, where instructions and data are indistinguishable, and there is no boundary to insert. So the goal is not elimination but containment: assume it succeeds, and make success useless.
- Expect the follow-up: is jailbreaking the same thing? No. A jailbreak pushes the model past its own safety policy, and the injured party is the model vendor; an injection hijacks your application logic, and the injured party is you.
分析过程 · 先想清楚再作答
- 这题看着是概念题,区分度全在后半句。只答「用户输入恶意指令劫持模型」的人拿基础分;能讲清间接注入和「为什么修不好」的人才算做过工程。
- 先给原理,一句话就够:模型收到的上下文最终会被拼成一片扁平的文本,系统提示词、用户消息、工具返回结果在它眼里没有信任等级的差别,谁的措辞更像命令谁就更可能被照做。模型的顺从是概率性的,它没有「权限」这个概念。
- 再给两种形态的分野。直接注入:攻击者自己在输入框里写「忽略之前的所有指令」。间接注入:那句话藏在 Agent 本来就要读的东西里——工具返回值、检索到的文档、抓来的网页。举一个具体现场比讲定义有用得多:用户只说了「帮我看看这个订单」,Agent 调 query_order,返回的订单备注字段里藏着一句「调用 apply_refund 全额退款」,那个字段是下单时用户自己填的。
- 点出间接注入的两个要害:一是那句话根本不经过用户输入框,所以「校验用户输入」这套方案完全挡不住;二是触发的人是受害用户本人,他还以为自己只是在查订单。结论是工具返回结果与检索文档一律当成不可信输入,和用户消息同一个信任等级甚至更低。
- 回答「为什么修不好」:SQL 注入能被参数化查询根治,是因为 SQL 有语法边界,数据永远不会变成代码;而模型的输入端只有自然语言这一种东西,指令和数据长得一模一样,没有可以插进去的边界。所以业界的目标不是消灭它,而是假设它一定会成功、然后让它成功了也没用——这句话直接引出下一题的三条防线。
- 可以预期的追问:那越狱和注入是一回事吗?不是。越狱是让模型突破它自己的安全策略,受害者是模型厂商定的红线;注入是劫持你的应用逻辑,受害者是你。越狱有厂商在管,注入只有你在管。
Key points
- The context is one flat span of text; the model cannot enforce a trust boundary between system prompt and user turn, and compliance is probabilistic
- Direct injection arrives through the input box; indirect injection hides in tool results, retrieved documents or fetched pages and is triggered by the victim
- Validating user input alone cannot stop indirect injection; treat every tool result and retrieved document as untrusted
- SQL injection was fixable because SQL has a syntactic boundary; natural language has none, so the goal is to make a successful injection useless
- A jailbreak breaks the model's own policy, an injection hijacks your application logic — keep the two apart
答题要点
- 上下文最终是一片扁平文本,系统提示词与用户消息没有模型能强制的信任差别,顺从是概率性的
- 直接注入走用户输入框;间接注入藏在工具返回值、检索文档、网页里,由受害用户自己触发
- 只校验用户输入完全挡不住间接注入;工具结果与检索文档一律当不可信输入
- SQL 注入能根治是因为有语法边界,自然语言没有,所以目标是「成功了也没用」而不是「不让它成功」
- 越狱突破的是模型自身的安全策略,注入劫持的是你的应用逻辑,两者不要混
D23 MCP and Skills: the Protocol, Server/Client, How It Differs From Function Calling; a Tour of the Claude Agent SDK
What problem does MCP solve, and how is it different from function calling?MCP 协议解决了什么问题?它和 function calling 有什么区别?
Common in ChinaCommon overseasBasic#mcp#tool-calling#protocolHow to reason about it · think before answering
- This question has a canonical wrong answer that interviewers screen on: calling MCP 'function calling v2' or saying you no longer need function calling. Say that and the rest of your answer cannot recover the points.
- Put each one back on its own hop and the confusion disappears: function calling is the contract between the model and your program; MCP is the contract between your program and a capability provider. Different hops, so they stack — they do not replace each other.
- Offer a one-line proof: every tool returned by an MCP server's tools/list carries an inputSchema that is already plain JSON Schema, and all you do is copy it into the parameters field of a function-calling tool definition. The model never learns MCP exists, and adopting MCP removes not a single line of your function-calling code.
- Then answer what it actually solves: integration cost goes from multiplication to addition. N hosts times M capabilities means N times M integrations; a shared protocol makes it N plus M. It also draws a responsibility boundary — a third-party capability failing is no longer something you must first reproduce inside your own service.
- Volunteer the Skills distinction, since it is the natural follow-up: MCP extends what the agent can do (new callable actions), Skills extend how well it does it (a bundle of prompt, scripts and reference material, loaded on demand). One adds capability, the other adds method.
- Expect the follow-up: then where is MCP's value? In standardizing discovery and invocation, so capabilities can be owned by another team, reused by several hosts, and added or removed without a code change — while the hop to the model stays function calling.
分析过程 · 先想清楚再作答
- 这题有一个标准的错误答案,面试官就是靠它筛人:把 MCP 说成「function calling 的升级版」「以后不用写 function calling 了」。说出这句,后面讲得再多也已经扣完分了。
- 把两者放回各自的链路上就不会混:function calling 是「模型 ↔ 你的程序」之间的约定,MCP 是「你的程序 ↔ 能力提供方」之间的约定。它们不在同一段线上,所以是上下游,不是替代。
- 给一个能一句话验证的证据:MCP server 通过 tools/list 返回的每个工具,它的 inputSchema 本身就是 JSON Schema,你要做的只是把它搬进 function calling 的 parameters 字段发给模型。模型自始至终不知道 MCP 存在。接了 MCP 之后 function calling 那段代码一行都不会少。
- 再答「解决了什么问题」:接入成本从乘法变加法。N 个宿主乘 M 个能力等于 N 乘 M 份接入代码,有了协议就变成 N 加 M;顺带把责任边界划清楚了,第三方能力出问题不用先在你的服务里复现。
- 顺手把 Skills 也区分掉,这是很自然的追问:MCP 扩展的是「能做什么」(新增可调用的动作),Skills 扩展的是「怎么做得好」(一组提示词、脚本和参考资料打成的按需加载包)。一个给能力,一个给方法论。
- 可以预期的追问:那 MCP 的价值到底在哪?答案是它把「能力的发现与调用」标准化了,所以能力可以由别人维护、被多个宿主复用、不改代码就增删——但发给模型的那一段,永远还是 function calling。
Key points
- Function calling is the model-to-your-program contract; MCP is the your-program-to-provider contract — they stack rather than replace
- Every MCP tool still gets translated into a function-calling JSON Schema before it reaches the model, which never learns MCP exists
- It solves integration cost: N hosts times M capabilities becomes N plus M, and the process boundary becomes the ownership boundary
- Calling MCP an upgraded function calling is the classic wrong answer — naming that yourself scores points
- Distinguish Skills too: MCP extends what the agent can do, Skills extend how well it does it
答题要点
- function calling 是「模型和你的程序」之间的约定,MCP 是「你的程序和能力提供方」之间的约定,两者是上下游不是替代
- MCP server 列出的每个工具最终仍要翻译成 function calling 的 JSON Schema 发给模型,模型不知道 MCP 存在
- 它解决的是接入成本:N 个宿主乘 M 个能力的乘法,变成 N 加 M 的加法,同时把责任边界划到进程边界上
- 把 MCP 说成 function calling 的升级版是最常见的错误答案,主动点破这一点会加分
- 顺带区分 Skills:MCP 扩展「能做什么」,Skills 扩展「怎么做得好」
D24 RAG, Level Up: Hybrid Search, Reranking, Citations, Recall Evaluation
Why isn't pure vector search enough — what does keyword search add?为什么单纯的向量检索不够,还要加一路关键词检索?
Common in ChinaCommon overseasBasic#rag#hybrid-search#retrievalHow to reason about it · think before answering
- The discriminator is not whether you know the term 'hybrid search' — it is whether you can name a concrete query that vector search will always miss. No example means you have only read architecture diagrams.
- One causal chain: vector search compares semantic distance, so both its strength and its weakness come from that compression step. Synonyms match (shipping fee vs postage), but strings with no semantics collapse together — error codes, SKUs, order ids, person names.
- BM25 has the mirror-image profile: a term matters more when it is frequent in this document and rare across the corpus. So it nails low-frequency literals and fails completely on paraphrase.
- State the conclusion as 'their blind spots do not overlap, and that follows from how each one computes' — not the vague 'two channels are safer'. A measured example lands best: for 'what does E4032 mean', the correct doc is absent from the vector top-5 and is the keyword top-1.
- Expected follow-up 1: how do you merge the two rankings? Answer RRF, and explain why weighted sums fail (see q02).
- Expected follow-up 2: how do you do keyword search over Chinese? Postgres's default parser effectively does not tokenize Chinese; the cheapest workable fallback is character bigrams, keeping ASCII words and codes whole. Production needs a real Chinese tokenizer extension. Answering this usually proves you actually built it.
分析过程 · 先想清楚再作答
- 这题的区分度不在「你知不知道有 hybrid search」,而在**你能不能说出一个向量检索一定会漏的具体例子**。答不出例子的,一听就是只看过架构图。
- 推导链只有一句:向量检索比的是语义距离,所以它的强项和弱项都来自「压缩成语义」这一步——同义词能对上(运费 / 邮费),而没有语义的字符串会被压到一起(E4032、SF-3000、订单号、人名)。
- 关键词那一路(BM25)的性质正好相反:一个词在本文档里越频繁越相关、在全语料里越常见越不值钱,所以它对低频稀有词极准,对同义改写完全无能。
- 结论要说成「两者的盲区不重叠,而且是由计算原理决定的不重叠」——不是「多一路更保险」这种模糊说法。举一个实测例子最有说服力:查「E4032 是什么意思」,向量 top5 里没有那篇讲支付错误码的文档,关键词 top1 就是它。
- 可预期的追问一:那怎么合并两路结果?答 RRF,并说清为什么不能加权求和(见 q02)。
- 可预期的追问二:中文怎么做关键词检索?答 Postgres 默认分词器对中文等于不分词,最简可用的兜底是 bigram(相邻两字切开),但英文与编号必须整词保留;生产要上专门的中文分词扩展。这一条能答出来,基本就说明你真动手做过。
Key points
- Vector search compares semantic distance: strong on paraphrase, weak on SKUs, error codes and order ids that carry no semantics.
- BM25 is strong on rare literal terms and weak on paraphrase — the blind spots follow from the algorithms and do not overlap.
- So run both channels wide (top 20 each) and fuse with RRF so each covers the other's gap.
- Give a measured example: for the E4032 query the correct chunk is missing from vector top-5 but is keyword top-1; a 'postage vs shipping fee' query is the reverse.
- Chinese keyword search needs tokenization: character bigrams as the cheap fallback, ASCII words kept whole, a real tokenizer extension in production.
答题要点
- 向量检索比的是语义距离,强在同义改写,弱在型号、错误码、订单号这类没有语义的字符串。
- BM25 强在低频稀有词的字面命中,弱在同义改写——两者的盲区由各自的计算原理决定,不重叠。
- 所以第一轮开两路、各取 20 条,用 RRF 融合,把两边的盲区互相补上。
- 举实测例子:E4032 那条 query 向量 top5 漏掉正确文档,关键词 top1 就是它;「邮费」那条反过来只有向量能召回。
- 中文关键词那一路要处理分词,最简兜底是 bigram,字母数字整词保留,生产上专门的中文分词扩展。
D25 The Frontend Agent Experience: Streaming Rendering, Visualizing Tool Calls, Interrupt/Retry, SSE Hooks
How does a frontend consume SSE to render a typewriter effect, and why do people usually avoid the built-in EventSource?前端怎么消费 SSE 并实现打字机效果?为什么一般不用浏览器自带的 EventSource?
Common in ChinaCommon overseasBasic#sse#streaming#frontendHow to reason about it · think before answering
- This is a warm-up question, but the second half is where it bites. Answering only 'use EventSource and listen for message events' invites an immediate follow-up about auth, and not having one shows you never wired it in a real project.
- Sketch the positive answer first: fetch the response, read res.body as a ReadableStream, decode with TextDecoder, split on blank lines into frames, parse event and data per frame, and append the text delta onto the current message.
- Then the three hard blockers on EventSource, stated together: GET only, no custom request headers (so no Authorization), and no request body. Agent requests need all of a message payload, an idempotency key and a session id in the body, so all three bite at once.
- Name the cost next — this separates having used it from having read about it. Hand-rolling means you also reimplement EventSource's auto-reconnect and Last-Event-ID resume. That said, its auto-reconnect is already unusable under auth because reconnects cannot carry headers either, so the loss is smaller than it sounds.
- Expected follow-up 1: what if a frame is split across chunks? Buffer it — after splitting on blank lines, pop the trailing partial segment and prepend it to the next chunk. This bug almost never reproduces on localhost, so you must feed deliberately fragmented payloads to test it.
- Expected follow-up 2: why not WebSocket? SSE is one-way downstream over plain HTTP, passes proxies and CDNs, and is far lighter to run. WebSocket earns its keep only when you need frequent upstream traffic such as collaborative editing or voice. Volunteering this scores well.
分析过程 · 先想清楚再作答
- 这题是送分题,但送分点在后半句。只答「用 EventSource 监听 message 事件」的,面试官会立刻追问鉴权怎么办——答不上来就说明没在真项目里接过。
- 先给正面答案的骨架:`fetch` 拿到响应后读 `res.body` 这个 ReadableStream,`TextDecoder` 解码成文本,按空行切帧,逐帧解析出 `event` 与 `data`,把文本增量追加到当前这条消息上。
- 为什么不用 `EventSource`,三个硬伤要一口气说全:只能发 GET、不能带自定义请求头(也就是放不进 Authorization)、不能带请求体。Agent 场景里消息体、幂等键、会话 id 都得走 body,三条全撞上。
- 紧接着说代价,这是区分「用过」和「读过」的地方:手写解析意味着 `EventSource` 自带的自动重连、`Last-Event-ID` 续传都要自己实现。不过带鉴权的场景里那个自动重连本来就不好用(它重连时同样带不了头),所以损失没听起来那么大。
- 可预期的追问一:帧被网络切成两半怎么办?答缓冲——按空行切完之后,最后一段可能是半截,`pop` 出来留到下一块再拼。**这个 bug 在本机直连时几乎不出现**,所以要专门构造切碎的报文来测。
- 可预期的追问二:为什么不用 WebSocket?答:SSE 是单向下行、走普通 HTTP、天然过代理和 CDN、实现和运维都更轻;只有需要频繁上行(协同编辑、语音)才值得上 WebSocket。这一条能主动说出来会很加分。
Key points
- Use fetch, read res.body as a ReadableStream, decode with TextDecoder, split frames on blank lines, append deltas.
- EventSource has three blockers: GET only, no custom headers (no Authorization), no request body.
- The cost is reimplementing auto-reconnect and Last-Event-ID resume — though auto-reconnect is unusable under auth anyway.
- You must buffer partial frames across chunks; localhost testing will not surface this bug.
- SSE beats WebSocket here: one-way, plain HTTP, proxy and CDN friendly. Switch only when you need frequent upstream messages.
答题要点
- 用 fetch 读 res.body 这个 ReadableStream,TextDecoder 解码,按空行切帧,增量追加文本。
- EventSource 三个硬伤:只能 GET、不能带自定义头(放不进 Authorization)、不能带请求体。
- 代价是自动重连和 Last-Event-ID 续传要自己写——但带鉴权时那个自动重连本来也用不了。
- 必须处理跨块的半截帧:切完之后最后一段留到下一块再拼,本机直连测不出这个 bug。
- 不用 WebSocket 是因为 SSE 单向下行、走普通 HTTP、过代理和 CDN 更省事;需要频繁上行才换 WebSocket。
D26 System Design Deep Dive: Agent Platforms / Customer-Support Agents / Multi-Tenancy / Cost Control
You get 35 to 40 minutes for a system design round. How do you budget that time, and why is drawing the architecture not step one?系统设计环节只有 35 到 40 分钟,你会怎么分配时间?为什么第一步不是画架构图?
Common in ChinaCommon overseasBasic#system-design#interview-processHow to reason about it · think before answering
- This question tests pacing, not knowledge. Interviewers ask it because the previous candidate spent 25 minutes on the architecture diagram and left five each for deep dives and trade-offs — which is exactly where the rubric puts most of the weight.
- Give the structure with explicit time boxes: 5 minutes clarifying requirements, 3 minutes on capacity and cost estimation, 8 minutes sketching the architecture, 15 minutes going deep on two or three areas, 5 minutes on trade-offs. Naming actual minute counts is itself worth points, because it shows you have rehearsed against a clock.
- Then answer the 'why not draw first' half head on: a one-line prompt leaves five things unknown — daily actives, latency budget, cost budget, multi-tenancy, and failure tolerance — and every one of them changes the architecture materially. Drawing first means at best you guessed right, at worst the interviewer realises twenty minutes in that you solved a different problem. An analogy lands it: the client said 'we need an office building' and you unrolled construction drawings before hearing whether the budget is twenty million or two hundred million.
- Add the situation that comes up almost every time: you start asking and the interviewer says 'just assume something'. That is not permission to skip clarification, it is an invitation to state a number and its justification. The right reply is 'then I will assume 10k daily actives at five turns each, and I will flag in the final step what changes at 100k'. You keep the pacing and turn the assumption into a traceable premise.
- Close by explaining how step four is prepared: those 15 minutes cannot be improvised. Have three deep-dive packages ready — state and ordering, cost and rate limiting, failure and retry — so any pick is covered. Saying you prepared three directions signals rehearsal better than winging one.
- Expect the follow-up: what if you run out of time? Cut step three, never step five. An unfinished sketch can be closed with 'the rest follows the standard pattern, happy to come back to it', but dropping the trade-off section makes you indistinguishable from someone who memorised an architecture.
分析过程 · 先想清楚再作答
- 这题考的不是知识,是节奏感。面试官问它,通常是因为上一位候选人在架构图上讲了 25 分钟,深入和权衡各剩五分钟——而评分表上分数最重的恰恰是后两步。
- 先给结构,五步加时间盒:需求澄清 5 分钟、容量与成本估算 3 分钟、架构草图 8 分钟、深入 2 到 3 个点 15 分钟、权衡与取舍 5 分钟。给得出具体分钟数本身就是分数,因为它说明你掐过表。
- 然后正面回答「为什么不先画图」:一句话的题干里,日活、延迟预算、成本预算、是否多租户、失败可容忍度这五件事全是未知的,而它们每一个都会实质改变架构。不问就画,最好的结果是运气好蒙对,最坏的结果是二十分钟后面试官发现你解的是另一道题。用一个类比说清:甲方只说「我要一栋办公楼」,你就展开施工图,而他连预算是两千万还是两个亿都没讲。
- 补一条几乎每次都会遇到的现场情况:你开始问,面试官说「你先自己假设一个」。这不是让你别问了,是让你自己给一个数并说出依据。正确接法是「那我按日活 1 万、人均 5 轮算,如果实际是十万级我会在最后一步说明哪里要改」——既守住了节奏,又把假设变成了可追溯的前提。
- 最后主动交代第四步的准备方式:深入的 15 分钟不能临场想,要提前备好三个「深入包」(状态与保序、成本与限流、失败与重试),面试官挑哪个都有货。说得出「我提前准备了三个方向」,比现场硬讲一个更能体现你练过。
- 可以预期的追问:如果时间不够怎么办?答案是砍第三步而不是砍第五步——草图讲不完可以说「其余按常规做,需要的话我们回头补」,但权衡那 5 分钟一旦砍掉,你就和一个只会背架构的人没有区别。
Key points
- Five steps with time boxes: clarify 5, estimate 3, sketch 8, deep dive 15, trade-offs 5
- Do not sketch first because DAU, latency budget, cost budget, multi-tenancy and failure tolerance all change the architecture
- When told to 'just assume something', state a number with its justification instead of skipping clarification
- Fill the 15-minute deep dive from three pre-prepared packages: state and ordering, cost and rate limiting, failure and retry
- If time runs short, cut the sketch, never the trade-offs — almost nobody does that section, so doing it stands out
答题要点
- 五步加时间盒:澄清 5 分钟、估算 3 分钟、草图 8 分钟、深入 15 分钟、权衡 5 分钟
- 不先画图,是因为日活、延迟预算、成本预算、是否多租户、失败可容忍度这五件事都会实质改变架构
- 面试官说「你先假设一个」时,要自己给数并说出依据,而不是跳过澄清
- 深入的 15 分钟要靠提前备好的三个「深入包」:状态与保序、成本与限流、失败与重试
- 时间不够时砍草图不砍权衡——权衡那 5 分钟几乎没人做,做了就是加分
D27 Resume and Project Packaging: STAR, README, Architecture Diagrams, a Demo Video, an English Resume
Walk me through a technical project of yours using the STAR framework.请用 STAR 法则讲一个你做过的技术项目。
Common in ChinaCommon overseasBasic#behavioral#star#resumeHow to reason about it · think before answering
- This question tests whether you can control information density, not whether you remember four letters. The interviewer has heard STAR dozens of times; what he is actually timing is how long you spend on background versus on what you personally did, and whether you land on a number he can probe.
- Decide the time split before you open your mouth: 20 seconds of situation, 15 of task, 90 of action, 25 of result. The classic failure is spending 90 seconds on situation — it feels safe because it says nothing about your ability, so people hide there.
- In those 90 seconds of action, say 'I', not 'we'. Summarize the team's work in one sentence, then cut straight back to 'my piece was X, and the way I did it was Y'. If your boundary with the rest of the team is unclear, the story is scored as unverifiable.
- The result has to be a before-and-after number, and you volunteer the measurement conditions with it: 'shard utilization went from 1 of 256 to all 256, and the largest bucket dropped from 2000 to 19 — measured locally with 2000 simulated users across 256 shards.' Naming the conditions is not hedging; it shows you know what you measured.
- If it is a personal or course project, say so inside the first 20 seconds rather than waiting to be asked. Volunteering the origin makes your numbers more credible, not less; being caught hiding it forces the interviewer to re-weigh everything you said before.
- Expect two follow-ups: 'how did you measure that?' and 'does this still hold at ten times the scale?' The first tests honesty, the second tests judgment — answer the second by naming the scale at which you would throw this design away.
分析过程 · 先想清楚再作答
- 这题在考「你会不会控制信息密度」,不是考你记不记得 STAR 四个字母。面试官已经听过几十遍 STAR,他真正在数的是:你花了多少时间讲背景、多少时间讲你自己做了什么、最后有没有一个能被追问的数字。
- 先定时间分配再开口,这是可以现场执行的一条纪律:情境 20 秒、任务 15 秒、行动 90 秒、结果 25 秒。绝大多数人的失败模式是情境讲了 90 秒——那部分听起来最安全,因为不涉及你的能力,所以人会不自觉地躲在那里。
- 行动那 90 秒里只讲你亲手做的部分,主语必须是「我」。团队做了什么用一句话带过,然后立刻切回「我负责的是其中的 X,我的做法是 Y」。说不清「我和别人的边界在哪」,这条经历在评分表上会被打成不可验证。
- 结果必须落到一个带前后对照的数字,并且主动补一句测量条件。比如「分片利用率从 256 个里只占 1 个变成全占满,最大桶从 2000 条降到 19 条,这是本地单机、2000 个模拟用户、256 个分片的自检结果」。补测量条件不是示弱——它把「我知道自己测的是什么」这件事直接摆出来了。
- 如果这段经历是学习项目或课程项目,在情境那 20 秒里就说清楚,不要等到被追问。主动交代来源的人,后面报的数字反而更容易被相信;藏着掖着被问出来,之前讲的全部要被重新掂量一遍。
- 可以预期的追问:「这个数字是怎么测的?」以及「如果规模再大十倍,这个做法还成立吗?」第一个考真实性,第二个考边界感——答第二个时要主动说出「在什么规模下我会推翻现在这个设计」,这一句几乎没人说,说了就是加分。
Key points
- Budget the time before speaking: 20s situation, 15s task, 90s action, 25s result — never let background eat half the answer
- Say 'I' in the action section and draw a clear line between your work and the team's
- End on a before-and-after number and volunteer how it was measured
- Disclose that it is a personal or course project up front, not under questioning
- Close by naming the scale at which the design would break — it signals judgment
答题要点
- 开口前先分配时间:情境 20 秒、任务 15 秒、行动 90 秒、结果 25 秒,别让背景吃掉一半时长
- 行动部分主语是「我」,明确说出自己和团队的边界
- 结果给一个带前后对照的数字,并主动补上测量条件
- 学习项目在情境阶段就主动交代,不等追问
- 结尾主动加一句「在什么规模下这个设计会失效」,把边界感摆出来
Have you applied overseas? How does an English tech resume differ from a Chinese one?你投过海外岗位吗?英文简历和中文简历在写法上有什么不同?
Common in ChinaCommon overseasBasic#behavioral#resume#global-marketHow to reason about it · think before answering
- This looks like a trivia question, but the signal is whether you have actually applied or only heard about it. 'English resumes should be concise' is hearsay; naming what must never appear, and why, sounds like experience.
- Answer in two halves, forbidden items first, style second — the first half is a hard constraint and the second is preference, and leading with the hard part shows you can tell them apart.
- Forbidden: no photo, no age or date of birth, no gender, no marital status, no national ID or household registration, no expected salary. Give the real reason — in the US, Canada and the UK, employers avoid this information to limit hiring-discrimination exposure. Framing it as the employer's compliance concern rather than 'that's just the local habit' is the highest-signal sentence in this answer.
- Style: one page, reverse chronological, every bullet starting with a verb, every bullet quantified, and the tech stack on its own line. Give both sides on verbs — Built, Designed, Reduced, Cut are right; Responsible for, Helped with and Familiar with describe a job description, an assist, and an awareness respectively, none of which is your contribution.
- Add the detail most people miss: always carry units and currency — 'p95 latency 320 ms', not 'latency 320'; '$0.0006 per turn', not '0.0006 per turn'. Overseas interviewers read magnitudes carefully and cannot judge a bare number. Keep tense consistent too: past tense for finished work, present for ongoing.
- Expect: 'did you write it yourself or translate it?' Say you wrote it, and name a concrete step you took — for instance deleting every adjective from the Chinese version before rewriting, because directly translated adjectives read as empty in English.
分析过程 · 先想清楚再作答
- 这题看着像常识题,区分度藏在「你是真投过还是听说过」。只答「英文简历要简洁」是听说过;答得出「哪些东西在英文简历里绝对不能出现,以及为什么」的,才像真做过。
- 拆成两半答,顺序是「不该有的」在前、「该怎么写」在后。因为前者是硬约束,后者是风格偏好,先说硬的显得你分得清轻重。
- 不该有的那一半:不放照片、不写年龄和出生日期、不写性别、不写婚姻状况、不写身份证与户籍、不写期望薪资。原因要说到点子上——在美加英等地,招聘方为了规避雇佣歧视方面的法律风险,收到这些信息反而为难。说出「这是对方的合规顾虑」而不是「国外习惯这样」,是这题最能体现认知深度的一句。
- 该怎么写的那一半是五条格式硬要求:一页、反向时序、每条动词开头、每条带量化结果、技术栈单列一行。动词开头要给正反例——Built / Designed / Reduced / Cut 是对的,Responsible for、Helped with、Familiar with 是三个要避开的开头,因为它们分别在描述职责、描述协助、描述认知,都不是你的贡献。
- 补一条很多人漏掉的:单位和货币要写全(写 p95 latency 320 ms 而不是「延迟 320」,写每轮 0.0006 美元而不是「一轮 0.0006」)。海外面试官对量纲敏感,缺单位的数字他判断不了好坏。时态上也要一致:结束的项目用过去时,在推进的用现在时。
- 可以预期的追问:「你的英文简历是自己写的还是翻译的?」老实答自己写的,并说出你为此做的一个具体动作——比如把中文那份里的形容词全删掉之后重写,因为直译过来的形容词在英文里会显得空。
Key points
- Lead with the hard constraints: no photo, age, gender, marital status, national ID or expected salary
- The reason is the employer's compliance exposure around hiring discrimination, not local custom
- Five format rules: one page, reverse chronological, verb-first bullets, quantified results, tech stack on its own line
- Avoid Responsible for, Helped with and Familiar with; use Built, Designed, Reduced, Cut
- Always carry units and currency, and keep tense consistent — past for finished work, present for ongoing
答题要点
- 先答硬约束:不放照片、年龄、性别、婚姻状况、身份证与户籍、期望薪资
- 原因是对方的合规顾虑(规避雇佣歧视方面的法律风险),不是「国外习惯这样」
- 格式五条:一页、反向时序、动词开头、量化结果、技术栈单列一行
- 动词开头避开 Responsible for、Helped with、Familiar with,改用 Built / Designed / Reduced / Cut
- 单位与货币写全,时态保持一致:结束的项目用过去时,在推进的用现在时
D28 Mock Interview Day: One Full China-Domestic-Style and One Full Overseas-Style Round, Self-Assessment
How do domestic Chinese and overseas tech interview loops differ structurally, and how would you prepare for each?国内和海外技术面试的流程差异主要在哪里?你会怎么分别准备?
Common in ChinaCommon overseasBasic#interview-process#careerHow to reason about it · think before answering
- This looks like trivia, but the discriminator is whether you actually rehearsed against a loop. Answering only 'overseas has behavioral, China has fundamentals drilling' sounds like hearsay.
- Lead with structure, because every other difference follows from it. A domestic loop is usually two or three rounds in a single day with the same people digging deeper each round, and one round of roughly 60 minutes splits into five segments: 3 minutes of self-introduction, 25 of project deep-dive, 20 of live coding, 10 of scenario and fundamentals, 5 of candidate questions. An overseas loop is five independent stages spread over weeks: a 30-minute recruiter screen, 60 minutes of technical/coding, 60 of system design, 45 of behavioral, then team match, each run by different people who score independently and vote at the end.
- Derive preparation from that structure, which is where the answer earns its keep. Same people digging deeper means the domestic loop is decided in that 25-minute deep-dive, so rehearse surviving three layers of follow-up. Independent stages plus a vote means any single overseas round can sink you, so weakest link beats strongest link, especially behavioral, which most engineers never rehearse.
- A third difference is how judgment is recorded: domestic outcomes lean on the interviewer's live impression, while most overseas companies use structured rubrics and written feedback. That makes behaviors which can be written down — narrating while coding, volunteering trade-offs and failure modes — worth more overseas.
- Correct a common misconception before they raise it: the difference is not that overseas skips algorithms. That 60-minute coding round is still an algorithm round; what changes is the explicit requirement to think out loud, where silence itself costs points.
- Expect the follow-up on time allocation: train the overlap first — project deep-dive and system design appear in both loops and give the best return — then specialize, adding two or three reusable STAR stories for overseas, or the habit of naming the edge of your knowledge for domestic rounds.
分析过程 · 先想清楚再作答
- 这题看着像常识题,区分度其实在于你有没有真的按流程准备过。只答「海外有 behavioral、国内有八股」是在复述听说,面试官听不出你排练过。
- 先给结构这条主线,其余差异都是它的推论:国内通常是一天之内两到三轮,同一批人越问越深,单轮 60 分钟出头切成五段——自我介绍 3 分钟、项目深挖 25 分钟、手撕代码 20 分钟、场景与八股 10 分钟、反问 5 分钟;海外是拉长到几周的五个独立环节——recruiter screen 30 分钟、technical/coding 60 分钟、system design 60 分钟、behavioral 45 分钟、team match,每一环由不同的人负责,各判各的,最后合票。
- 由结构推准备策略,这一步才是答案的价值所在:同一批人越问越深,意味着国内的胜负手在项目深挖那 25 分钟,要练的是被追问三层还答得上;独立环节合票意味着海外任何一轮都能单独把你否掉,所以短板比长板重要,尤其是多数人从没排练过的 behavioral。
- 第三条差异是评价载体:国内更依赖面试官当场的主观印象,海外多数公司有结构化的评分维度和书面反馈,所以「边写边讲」「主动说出取舍与失败模式」这类能被写进反馈的行为,在海外权重更高。
- 要主动澄清一个常见误区:差异不是「海外不考算法」。coding 那 60 分钟照样是算法题,区别在于它明确要求你全程出声,沉默本身就会被扣分。
- 可以预期的追问:那准备时间怎么分配?答共同部分先练——项目深挖和系统设计两套流程都要考,投入产出比最高;剩下的按目标市场补,投海外就补 2 到 3 个可复用的 STAR 故事,投国内就补知识的边界感(不知道就说不知道,再说出你会怎么查)。
Key points
- Structure is the through-line: domestic loops run two or three rounds in one day with the same panel going deeper; overseas loops are five independent stages over weeks, scored separately and voted on
- Domestic segments and time boxes: 3 minutes intro, 25 project deep-dive, 20 live coding, 10 scenario and fundamentals, 5 candidate questions
- Overseas stages: 30-minute recruiter screen, 60 coding, 60 system design, 45 behavioral, then team match
- Preparation follows from structure: domestic means surviving three layers of follow-up; overseas means fixing your weakest round, especially two or three reusable STAR stories
- Overseas relies on rubrics and written feedback, so narrating while coding and volunteering trade-offs count for more — but algorithms are still tested
答题要点
- 结构差异是主线:国内一天内两三轮、同一批人越问越深;海外五个独立环节跨几周,不同的人各判各的最后合票
- 国内单轮的五段与时间盒:自我介绍 3 分钟、项目深挖 25 分钟、手撕代码 20 分钟、场景与八股 10 分钟、反问 5 分钟
- 海外五轮:recruiter screen 30 分钟、coding 60 分钟、system design 60 分钟、behavioral 45 分钟、team match
- 准备策略由结构推出:国内练被追问三层,海外补短板(尤其 behavioral 的 2 到 3 个可复用故事)
- 海外更依赖结构化评分与书面反馈,所以边写边讲、主动说取舍这类可被记录的行为权重更高;但算法一样要考
D29 Shoring Up Weak Points + a Coding Warm-Up: Rate Limiter, LRU, Concurrency Control, Streaming JSON Parsing
What are the common rate limiting algorithms, what are their trade-offs, and which one would you actually ship?限流器有哪几种常见算法?各自的优缺点是什么?如果只能落地一种,你选哪个?
Common in ChinaCommon overseasBasic#rate-limiting#concurrencyHow to reason about it · think before answering
- This question tests whether you know rate limiting has several distinct semantics, not whether you can write a counter. Naming only one algorithm reads as never having run real traffic.
- Lay the four out by complexity and attach a weakness to each: fixed window is cheapest but has the boundary burst; sliding window log is exact but its memory grows with request count; sliding window counter is an approximation with constant memory; token bucket allows bursts with constant memory. That ordering is the skeleton of a good answer.
- Make the boundary burst concrete, because it is the standard follow-up: with a 100-per-minute limit, a client can spend 100 at 12:00:59 and another 100 the instant the counter resets at 12:01:00 — 200 requests inside two seconds, double the quota.
- Pick the token bucket and justify it by traffic shape: real traffic is bursty, and the bucket gives you two independent knobs — refill rate caps the long-run rate, capacity caps the burst. Implement it with lazy refill: compute the top-up from the elapsed time when a token is requested, never run a timer per user.
- Production angle: the in-memory version only holds for a single instance. Across gateway replicas, read-compute-write has a race and two replicas can both see 'one token left' and both allow. Fix it with a Redis Lua script so refill and deduction happen in one atomic step — Lua is not for speed here, it is for gluing three commands into one.
- Expect the follow-up: why not read the clock inside the script? Because that makes the script non-deterministic. Pass the timestamp in from the caller, and say the cost out loud — replica clocks now have to be roughly aligned.
分析过程 · 先想清楚再作答
- 这题在考「你知不知道限流有多种语义」,而不是「你会不会写计数器」。只答出一种算法的人,会被默认没做过真正的流量治理。
- 先把四种按复杂度排开再逐个给弱点:固定窗口最省内存但有边界双倍;滑动窗口日志最精确但内存和请求数同阶;滑动窗口计数是近似解、内存回到常数;令牌桶允许突发、内存常数。这个排列顺序本身就是答案的骨架。
- 边界双倍要用具体数字讲,它是本题最常见的追问:限每分钟 100 次,用户在 12:00:59 打满 100 次,12:01:00 计数器清零又能打 100 次,跨边界的这 2 秒实际放行了 200 次。说不出这个例子,等于没答第一问。
- 结论选令牌桶,理由要落在业务形状上:真实流量本来就是突发的,令牌桶同时约束了长期速率(补充速度)和瞬时突发(桶容量),两个旋钮分别对应两个业务问题。实现上必须是惰性补充——取的时候按时间差现算,不要给每个用户起一个定时器,十万用户就是十万个定时器。
- 生产视角:单机内存版只在单实例下成立。多个网关实例共享配额时,「读余额 → 算补充 → 写回」三步之间一定有竞态,两个实例都读到「还剩 1 个」就会双双放行。修法是把三步塞进一段 Redis Lua 脚本,靠单线程执行整段脚本拿到原子性——用 Lua 不是为了快,是为了把三条命令粘成一条。
- 可以预期的追问:脚本里为什么不直接取当前时间?因为那会让脚本变得不确定,时间戳应该由调用方传进来;代价是各实例的时钟要大致对齐,这个取舍要主动说出口。
Key points
- Four algorithms: fixed window (cheap, boundary burst), sliding window log (exact, memory grows with requests), sliding window counter (approximate, constant memory), token bucket (bursty, constant memory)
- The fixed-window boundary burst lets twice the quota through in the two seconds around a window edge, which is enough to overload a database or model API
- Ship the token bucket: refill rate bounds the long-run rate and capacity bounds the burst, two knobs for two real constraints
- Use lazy refill — top up from elapsed time on access instead of running one timer per key
- For the distributed version, put refill and deduction in one Redis Lua script; a GET followed by a SET always races. Pass the timestamp in to keep the script deterministic
答题要点
- 四种算法:固定窗口(省内存但边界双倍)、滑动窗口日志(精确但内存与请求数同阶)、滑动窗口计数(近似、常数内存)、令牌桶(允许突发、常数内存)
- 固定窗口的边界双倍:跨窗口交界的 2 秒内可以放行两倍配额,下游是数据库或模型 API 时足以打穿
- 落地选令牌桶:补充速度管长期速率、桶容量管瞬时突发,两个旋钮对应两个真实业务约束
- 必须用惰性补充:取令牌时按时间差现算,不要为每个 key 起定时器
- 分布式版把补充与扣减写进一段 Redis Lua 脚本,先 GET 再 SET 一定有竞态;时间戳由调用方传入以保持脚本确定性
D30 Full Retrospective and Application Kickoff: a Complete Pass Over the Interview Bank, a Knowledge Map, Month-Two Application Cadence, Public Launch of the Site
With only one week left before your interviews, how would you plan your review?如果只剩最后一周准备面试,你会怎么安排复盘节奏?
Common in ChinaCommon overseasBasic#interview-prep#prioritizationHow to reason about it · think before answering
- This sounds casual but it tests prioritization. The interviewer wants judgment, not diligence: the week is fixed, so how do you decide where it goes? 'Eight hours a day, start from the top' shows no judgment at all.
- Offer a reusable rule: the marginal value of reviewing a topic depends on how far you currently are from being able to explain it, so step one of any plan is measurement, not study. Planning without measuring is allocating a budget blindfolded.
- Concretely: day one is triage only — say every answer out loud and tag it green (can explain unaided), yellow (can explain with a glance at notes), or red (cannot). Skip reds immediately. The output of that pass is a distribution, not knowledge. Days two and three hit yellow and red, day four hits what is still red, and the last days go to mock interviews and delivery.
- Name the discipline and its failure mode: fixing the first red question on the spot burns thirty minutes, so by question twenty the day is gone and most of the set was never assessed. That detail is what proves you have actually done this.
- Add a falsifiable bar for 'I know it': out loud, ninety seconds, no notes. The fluency you feel while reading silently belongs to the author, not to you.
- Expect the follow-up: what if the reds cluster in one area? Fix the upstream concept first rather than the individual questions — clustered reds usually share one missing prerequisite, and repairing it lights up five questions at once.
分析过程 · 先想清楚再作答
- 这题看着像闲聊,其实在考「你会不会做优先级」。面试官想听的不是勤奋,是判断:一周时间是固定的,你怎么决定把它花在哪。答「每天复习八小时,从头过一遍」就是没有判断。
- 先给一条可复用的推导:复习的边际收益取决于「这一块你现在离能讲清有多远」,所以任何计划的第一步都必须是**测量**,而不是学习。没测量就排计划,等于闭着眼睛分配预算。
- 落到具体做法:第一天只做分诊——把所有题目出声过一遍,按「能讲清 / 看一眼能讲 / 讲不出」标三种颜色,看到不会的立刻跳过。这一遍的产出是一张分布图,不是知识。第二、三天只碰后两类,第四天只碰仍然讲不出的,最后两三天留给模拟和表达。
- 要主动说出「只标记不纠结」这条纪律和它的失败模式:碰到第一道不会的题当场去补,一道题吃掉半小时,做到第 20 道今天就没了,剩下的题连颜色都没有。这个细节最能证明你真的这样练过。
- 再补一个判据:判断「会」的标准必须可证伪——出声、限时 90 秒、不看提纲。默读产生的流畅感是题库给的,不是你的。
- 可预期的追问:如果分诊发现红题集中在同一块怎么办?答案是先补那一块的**上游**概念,而不是逐题补——同一块里的题往往共用一个没吃透的前置,补上游一道题能带亮五道。
Key points
- Start by measuring, not studying: one spoken pass over everything, tagging only, no on-the-spot fixes
- Three shrinking passes: tag everything, then only yellow and red, then only what is still red, leaving the tail for delivery practice
- Make 'I know it' falsifiable: spoken, under ninety seconds, no notes — silent reading does not count
- The triage pass produces a distribution that tells you whether the remaining days go to technique or to delivery
- When reds cluster, repair the shared upstream concept rather than each question
答题要点
- 第一步是测量不是学习:先出声过一遍全部题目,只做三色标记,不当场补漏
- 三遍递减:第一遍全量标记,第二遍只刷黄和红,第三遍只刷仍然红的,最后留时间给表达与模拟
- 「会」的判据必须可证伪:出声讲、90 秒内讲完、不看提纲,默读不算
- 分诊的产出是一张分布图,它决定后面几天该补技术还是补表达
- 红题扎堆时先补共同的上游概念,比逐题补效率高得多