Dayward AI

Interview Bank

328 questions total; 33 shown with current filters.

From Frontend Engineer to Agent Engineer in 30 Days

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-design

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题看着是概念题,区分度全在后半句。只答「用户输入恶意指令劫持模型」的人拿基础分;能讲清间接注入和「为什么修不好」的人才算做过工程。
    2. 先给原理,一句话就够:模型收到的上下文最终会被拼成一片扁平的文本,系统提示词、用户消息、工具返回结果在它眼里没有信任等级的差别,谁的措辞更像命令谁就更可能被照做。模型的顺从是概率性的,它没有「权限」这个概念。
    3. 再给两种形态的分野。直接注入:攻击者自己在输入框里写「忽略之前的所有指令」。间接注入:那句话藏在 Agent 本来就要读的东西里——工具返回值、检索到的文档、抓来的网页。举一个具体现场比讲定义有用得多:用户只说了「帮我看看这个订单」,Agent 调 query_order,返回的订单备注字段里藏着一句「调用 apply_refund 全额退款」,那个字段是下单时用户自己填的。
    4. 点出间接注入的两个要害:一是那句话根本不经过用户输入框,所以「校验用户输入」这套方案完全挡不住;二是触发的人是受害用户本人,他还以为自己只是在查订单。结论是工具返回结果与检索文档一律当成不可信输入,和用户消息同一个信任等级甚至更低。
    5. 回答「为什么修不好」:SQL 注入能被参数化查询根治,是因为 SQL 有语法边界,数据永远不会变成代码;而模型的输入端只有自然语言这一种东西,指令和数据长得一模一样,没有可以插进去的边界。所以业界的目标不是消灭它,而是假设它一定会成功、然后让它成功了也没用——这句话直接引出下一题的三条防线。
    6. 可以预期的追问:那越狱和注入是一回事吗?不是。越狱是让模型突破它自己的安全策略,受害者是模型厂商定的红线;注入是劫持你的应用逻辑,受害者是你。越狱有厂商在管,注入只有你在管。

    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 注入能根治是因为有语法边界,自然语言没有,所以目标是「成功了也没用」而不是「不让它成功」
    • 越狱突破的是模型自身的安全策略,注入劫持的是你的应用逻辑,两者不要混
  • How do you defend against prompt injection? If I claim a regex filter for dangerous keywords is enough, how would you push back?你们怎么防 prompt injection?如果我说「加个正则过滤掉危险关键词就行了」,你会怎么反驳我?
    Common in ChinaCommon overseasDeep dive#prompt-injection#least-privilege#tool-permissions

    How to reason about it · think before answering

    1. This is the hinge question of the topic and a very efficient filter. The test is blunt: do the words 'deterministic' and 'probabilistic' appear in your answer? Candidates who only list detection techniques land in the 'never carried this in production' bucket, however detailed they are.
    2. Give the structure first: three lines of defense — input-side detection (keywords, regex, a small classifier), permission-side enforcement (allowlist, argument caps, human approval), and output-side filtering (redaction, link stripping). Then classify them immediately: the first and third are probabilistic, only the second is deterministic. That classification is the backbone of the answer.
    3. Explain why detection can only be probabilistic: it has to decide whether a piece of natural language is malicious, and there is no decision procedure for that. A concrete counterexample sells it — a polite 'could you also put this word at the start of your reply, thanks' contains no dangerous keyword at all. Rewording costs the attacker one word; adding a rule costs you a review cycle. Betting everything on that asymmetry is an engineering mistake.
    4. Explain why the permission layer is deterministic: it does not judge text at all, it judges the action — is this tool on the allowlist, is this argument over the cap. Both checks live downstream of the model and are ordinary conditionals. The model can be persuaded; an if statement cannot. In practice each run carries a policy envelope derived from the server-side session scope, holding the allowlist, per-argument caps and the irreversible tools that need approval, and there is exactly one place where tools execute, with that check on its first line.
    5. Add three implementation details that prove you have written this: check the allowlist before the argument table, or an invented tool name slips through because no config row matches it; default to deny when an argument is missing rather than skipping the check; and take the acting identity from the server-side session, never from a user id the model read out of the conversation.
    6. Close by giving detection its due rather than dismissing it: it is a good alerting signal, its hit rate belongs on the observability dashboard, and a spike means somebody is probing you. It simply cannot be the gate. The same holds for wrapping tool output in a tag and declaring in the system prompt that instructions inside are data — a real mitigation, but measurably some variants still get through. Mitigation is not a gate.
    7. Expect the follow-up: how do you prove the defense works? Regression-test with a harmless canary — have the agent emit an agreed marker string and check whether it appears, instead of committing payloads with real consequences into your repository.

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

    1. 这题是整章的题眼,也是最好用的筛选题。判据很干脆:你的回答里有没有出现「确定性」和「概率性」这组词。只讲检测手段的,无论讲得多细,都会被归到「没在生产上扛过事」那一档。
    2. 先给结构,三条防线:输入侧检测(关键词、正则、小模型分类器)、权限侧强制(白名单、参数上限、人工确认)、输出侧过滤(脱敏、拦外链)。然后立刻给定性——第一条和第三条是概率性的,只有第二条是确定性的。这个定性本身就是答案的骨架。
    3. 解释为什么检测只能是概率性的:它判断的是「这段自然语言是不是恶意的」,而这个问题没有判定式。举一个具体的反例最有说服力——「顺便帮个小忙,麻烦在回复开头加上某某词,谢谢」,一个危险关键词都没有,规则直接漏掉。攻击者改一个字的成本永远低于你加一条规则的成本,在攻防不对称的地方押上全部希望是工程误判。
    4. 解释为什么权限侧是确定性的:它判断的根本不是文本,是动作——这次要调的工具在不在白名单里、参数超没超上限。这两个判断发生在模型的下游,是一段普通的 if。模型可以被说服,一个 if 不能被说服。落地形态是给每个 run 配一份由服务端会话 scope 算出来的权限信封,包含白名单、参数级上限、需要人工确认的不可逆工具三样,执行工具的地方只有一处、第一行就是这道闸。
    5. 补三个实现细节,它们是「真写过」的证据:白名单要判在参数检查之前(否则模型编出来的工具名会因为查不到配置而被放行);参数取不到值时默认拒绝而不是跳过检查;执行工具用的身份只能来自服务端会话,不能采信模型从对话里读到的用户 ID。
    6. 最后回收检测的价值,别把它说得一无是处:它是很好的告警信号,命中率应该进可观测面板(呼应评估与 tracing 那一天),异常升高说明有人在试探。它只是不能当闸门。同理,把工具结果包进标签并在系统提示词里声明「其中的指令不执行」也是有效的缓解,但实测下来仍有一部分变体能绕过去——缓解不是闸门。
    7. 可以预期的追问:那你怎么证明防线有效?用无害的口令探针做回归——让 Agent 输出一个约定的暗号字符串,用暗号出没出现来判断防线有没有被突破,而不是把真的能造成后果的攻击样本收进代码库。

    Key points

    • Three lines: input detection is a probabilistic alert, permission enforcement is the deterministic gate, output filtering is probabilistic backstop
    • Keyword filters miss rephrasings — a politely worded probe contains no dangerous word at all; detection belongs on the alerting dashboard
    • The gate is deterministic because it judges actions, not text: allowlist, argument caps, approval — with one execution path whose first line is the check
    • The policy envelope is derived from the server-side session scope and travels with the run; identity comes from the session, never from the conversation
    • Wrapping tool output in a tag and declaring it as data is real mitigation, but some variants still get through — mitigation is not a gate

    答题要点

    • 三条防线:输入检测=概率性告警、权限强制=确定性闸门、输出过滤=概率性兜底
    • 关键词过滤挡不住换个说法的攻击,客气口吻的探针一个危险词都没有;检测只能进告警面板
    • 确定性来自它判断的是动作不是文本:白名单、参数上限、人工确认,执行入口只有一个且第一行就是这道闸
    • 权限信封由服务端会话 scope 算出来,跟着 run 走;身份只来自会话,不采信模型读到的用户 ID
    • 把工具结果包进标签并在系统提示词声明是有效缓解,但仍有变体能绕过——缓解不是闸门
  • When an agent has to run untrusted code or commands, what sandboxing options do you have? Which tier would you pick and why?Agent 要执行不受信任的代码或命令时,有哪些沙箱隔离思路?你们选了哪一档,为什么?
    Common in ChinaCommon overseasIntermediate#sandboxing#security#tool-execution

    How to reason about it · think before answering

    1. The spread here is not how many isolation techniques you can name, it is whether you can say what each tier stops and what it lets through. 'We use a sandbox' says nothing, and the next question will be 'does it stop data exfiltration?'
    2. First explain why these tools are special: an allowlist governs whether a tool may be called, but for a tool whose whole job is 'run this thing I hand you', the allowlist degrades into a hall pass, because the danger lives in the arguments rather than the name. So you switch technique — instead of judging whether the code is bad, you shrink what it can reach. Same idea as permission enforcement, applied to a process instead of a tool.
    3. Then give three tiers by cost. Process level: a separate child process, a hard timeout, an environment-variable allowlist, a read-only working directory; stops crash propagation, hung loops and secret theft; does not stop network exfiltration or reads elsewhere on the host. Container level: no network, read-only rootfs, non-root user, CPU/memory/pid limits, disposable per run; adds exfiltration and out-of-bounds access; does not stop a kernel escape. MicroVM: a lightweight VM with its own kernel, stops most escapes, at the price of cold start and cost.
    4. Give the selection rule, which is what the interviewer actually wants: if you wrote the code and only the arguments are untrusted, process level is enough; if the code itself comes from the model or a user, container level is the floor; if you run arbitrary third-party code as a service, go to microVM.
    5. Call out the classic implementation bug: people spawn a child process and assume they are isolated, then hand it the parent's entire environment. The process is separate but the secrets went with it, and one line reading an environment variable prints your API key. The child's environment must be a fresh object copied from an allowlist, never inherited.
    6. Expect the follow-up: what happens on timeout? Use a signal that actually kills the process, and report 'killed by timeout' as its own failure class rather than folding it into generic errors — it usually means somebody is probing for resource exhaustion, not that the code has a bug.

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

    1. 这题的区分度不在于能背出几种隔离手段,而在于你说不说得出每一档挡住了什么、放过了什么。只说「我们用了沙箱」等于没说,面试官下一句一定是「那它挡得住外发数据吗」。
    2. 先说清这类工具为什么特殊:白名单管的是「能不能调」,但「执行一段你给的东西」这类工具一旦进了工具表,白名单就退化成一张通行证,因为危险面在参数里不在工具名里。所以要换一种手段——不判断这段代码坏不坏,而是收窄它能触碰的东西。这和权限侧强制是同一个思路,只是对象从工具换成了进程。
    3. 然后按代价从低到高给三档。进程级:独立子进程、超时必杀、环境变量白名单、只读工作目录;挡住崩溃传染、死循环挂住主进程、密钥被读走;挡不住网络外发和读系统里的其他文件。容器级:无网络、只读 rootfs、非 root、CPU 与内存限额、进程数限额、用完即弃;把外发和越界读写也挡掉;挡不住内核漏洞逃逸。microVM:独立内核的轻量虚拟机,挡住多数逃逸,代价是冷启动和成本。
    4. 给选型判据,这是面试官真正想听的:代码是你写的、只是参数不可信,进程级够用;代码本身来自模型或用户,最低容器级;要跑第三方任意代码还对外提供服务,上 microVM。
    5. 点一个高频实现坑:很多人起了子进程就以为隔离了,却把父进程的环境变量整个传过去——进程是独立了,密钥跟着过去了,子进程一句读环境变量就把 API key 打印出来。子进程的环境必须是白名单拷出来的新对象,而不是继承。
    6. 可以预期的追问:超时之后怎么办?要用能真正杀死进程的信号,并且把「被超时杀掉」当成一个独立的失败类型上报,而不是混进普通报错——它通常意味着有人在试资源耗尽,而不是代码写错了。

    Key points

    • For execute-style tools the danger is in the arguments, so an allowlist cannot help; isolate instead — shrink what the code can reach rather than judging it
    • Process level: child process, hard timeout, environment allowlist, read-only workdir; stops crashes, hangs and secret theft, not exfiltration
    • Container level: no network, read-only rootfs, non-root, CPU/memory/pid limits, disposable; stops exfiltration and out-of-bounds access, not kernel escapes
    • MicroVM: own kernel, stops most escapes, costs cold start and money; choose by who wrote the code and whether you serve it publicly
    • The classic bug is handing the child process the whole parent environment — isolated process, leaked secrets

    答题要点

    • 执行类工具的危险面在参数里,白名单管不住,要靠隔离:不判断代码坏不坏,而是收窄它能触碰的东西
    • 进程级:子进程 + 超时必杀 + 环境变量白名单 + 只读工作目录;挡崩溃、死循环、密钥泄漏,挡不住外发
    • 容器级:无网络、只读 rootfs、非 root、CPU 内存与进程数限额、用完即弃;挡外发与越界读写,挡不住内核逃逸
    • microVM:独立内核,挡多数逃逸,代价是冷启动与成本;判据是代码来自谁、要不要对外提供服务
    • 最常见的实现坑是把 process.env 整个传给子进程——进程隔离了,密钥跟着过去了
  • How should secrets be managed in an agent system? Where must they never appear, and how do you rotate them without downtime?Agent 系统里的密钥应该怎么管理?它绝对不能出现在哪些地方,轮换要怎么做才能不停机?
    Common in ChinaCommon overseasIntermediate#secrets-management#security#observability

    How to reason about it · think before answering

    1. It reads like a giveaway, but there is one answer point specific to agents, and missing it makes you sound like a generic backend engineer: secrets must never enter the LLM context. The interviewer asked about an agent system, and that is the line he is waiting for.
    2. Give the four 'nevers', one line each. Never in code — hardcoding hands the secret to everyone with read access, and deleting the line does not remove it from git history. Never in logs — the highest-frequency leak channel; nobody prints a secret on purpose, but 'log the whole request header so we can debug' is universal. Never in the LLM context. Never in error messages — responses to the frontend and exceptions thrown upstream are both outbound channels.
    3. Expand the third one, since it is what differentiates the answer: once a secret is in the context it will be sent to the model vendor, stored in conversation history, written into traces, and eventually read out loud by some prompt injection. What the agent needs is the capability to call an API, not the key itself — the key stays inside the tool implementation, and the model only ever sees the tool name and its arguments.
    4. Then the mechanics: redact at a single logging exit rather than trusting callers. Relying on everyone to mask by hand guarantees a miss. Do it in the one place logs leave the process, with two passes — replace known secret values from the environment, then catch the rest with generic shape patterns. Route the exception path through the same exit, because stack traces routinely carry connection strings with credentials.
    5. Storage and rotation: dotenv plus gitignore locally; in production a secret manager the process reads at startup under its own workload identity, never values baked into an image or a deployment manifest. Rotate dual-key: accept old and new simultaneously, shift traffic to the new one, confirm the old one has no remaining callers, then revoke. A single-shot swap always leaves a failure window on some replica.
    6. Expect the follow-up: how often do you rotate? The interval is secondary — what you should actually rehearse is whether you can revoke and replace a suspected-leaked key within five minutes. Saying that shows you are thinking about incident response rather than a compliance checkbox.

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

    1. 这题看着是送分题,但有一个专属于 Agent 的答案点,答不出来就只是通用后端水平:密钥不能进 LLM 上下文。面试官问的是 Agent 系统,这一条就是他在等的。
    2. 先给四不入,一条一句:不入代码(写死在源码里等于给了所有有仓库读权限的人,而且删掉那一行 git 历史里还在);不入日志(最高频的泄漏渠道,没人故意打印密钥,但「把请求头整个打出来方便排查」每个团队都干过);不入 LLM 上下文;不入错误信息(返回给前端的报错和抛给上游的异常都是对外出口)。
    3. 把第三条展开,这是本题的差异点:密钥一旦进了上下文,就意味着它会被送到模型厂商、被存进会话历史、被写进 trace,然后在某一次提示词注入里被完整地念出来。正确的形态是 Agent 需要的是「能调用某个 API」这个能力,而不是那把钥匙本身——密钥留在工具的实现里,模型只看得到工具名和参数。
    4. 再给落地手段:日志出口统一脱敏,不靠调用方自觉。靠每个人写日志时记得手动打码,一定会漏。做法是在唯一的日志出口做替换,两条路一起用——进程里已知的密钥值整段替换,再用通用形状兜底那些不是从环境变量来的密钥。异常处理那一支也要走同一个出口,堆栈里经常夹着带密钥的连接串。
    5. 存储与轮换:本地开发用 .env 加 gitignore;线上走密钥管理服务,进程启动时按自己的身份去取,不要把值烤进镜像或写进部署清单。轮换要双活——同时允许新旧两把 key,流量切到新 key、观察到没有旧 key 的调用了再吊销,一次性替换必然在某个副本上留下失败窗口。
    6. 可以预期的追问:轮换周期定多久?周期是次要的,真正要演练的是「能不能在 5 分钟内换掉一把疑似泄漏的 key」。答得出这一句,说明你想的是事故响应而不是合规打卡。

    Key points

    • Four nevers: never in code, never in logs, never in the LLM context, never in error messages
    • The agent-specific one is the context — anything there reaches the vendor, the history and the traces, and can be read out by an injection
    • The agent needs the capability to call an API, not the key; the key stays inside the tool implementation
    • Redact at one logging exit instead of trusting callers, and route the exception path through it too
    • Use a secret manager with workload identity in production, and rotate dual-key: accept both, shift traffic, verify no old callers, then revoke

    答题要点

    • 四不入:不入代码、不入日志、不入 LLM 上下文、不入错误信息
    • Agent 特有的一条是不入上下文——进了上下文就会被送到厂商、存进历史、写进 trace,并可能被注入念出来
    • Agent 需要的是「能调用某个 API」的能力而不是钥匙本身,密钥留在工具实现里
    • 日志出口统一 redact,不靠调用方自觉;异常路径走同一个出口,堆栈里常夹着连接串
    • 线上走密钥管理服务按身份拉取;轮换用双活,新旧同时有效、切流量、确认无旧调用再吊销

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#protocol

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题有一个标准的错误答案,面试官就是靠它筛人:把 MCP 说成「function calling 的升级版」「以后不用写 function calling 了」。说出这句,后面讲得再多也已经扣完分了。
    2. 把两者放回各自的链路上就不会混:function calling 是「模型 ↔ 你的程序」之间的约定,MCP 是「你的程序 ↔ 能力提供方」之间的约定。它们不在同一段线上,所以是上下游,不是替代。
    3. 给一个能一句话验证的证据:MCP server 通过 tools/list 返回的每个工具,它的 inputSchema 本身就是 JSON Schema,你要做的只是把它搬进 function calling 的 parameters 字段发给模型。模型自始至终不知道 MCP 存在。接了 MCP 之后 function calling 那段代码一行都不会少。
    4. 再答「解决了什么问题」:接入成本从乘法变加法。N 个宿主乘 M 个能力等于 N 乘 M 份接入代码,有了协议就变成 N 加 M;顺带把责任边界划清楚了,第三方能力出问题不用先在你的服务里复现。
    5. 顺手把 Skills 也区分掉,这是很自然的追问:MCP 扩展的是「能做什么」(新增可调用的动作),Skills 扩展的是「怎么做得好」(一组提示词、脚本和参考资料打成的按需加载包)。一个给能力,一个给方法论。
    6. 可以预期的追问:那 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 扩展「怎么做得好」
  • What roles do the MCP server and client play, what can a server expose, and which transports exist?MCP 里 server 和 client 分别承担什么角色?server 能暴露哪几类东西,传输方式有哪些?
    Common in ChinaCommon overseasIntermediate#mcp#protocol#transport

    How to reason about it · think before answering

    1. This looks like recall, but it discriminates on two small things: whether you separate host from client, and whether you know there are primitives beyond tools. 'Server provides tools, client calls them' is below the bar.
    2. Lay out three roles: the server is the capability provider and its own process; the client is the piece inside the host that talks to exactly one server; the host is your agent application, holding several clients at once. People who conflate host and client fall apart the moment you ask how they would connect to three servers.
    3. Cover all three server-side primitives and say who chooses each: tools are executable actions chosen by the model; resources are read-only data addressed by URI; prompts are reusable templates — the latter two are normally chosen by the user or host. That 'who chooses' framing shows you actually read the spec: modelling a large document as a resource rather than a tool moves the decision to spend those tokens from the model back to a human.
    4. The client side declares capabilities too, letting the server call back into the host: sampling asks the host to run a model completion, roots tells the server which directories are visible, elicitation asks the host to collect user input. Naming them without elaborating is the right level of detail.
    5. Two transports: stdio for a local subprocess, Streamable HTTP for remote. The dated detail worth knowing is that the older two-endpoint HTTP+SSE transport is now legacy, kept only for backwards compatibility — presenting it as current signals you read last year's blog posts.
    6. Expect the follow-up: anything special about stdio servers? Stdout is reserved for JSON-RPC, so every log line must go to stderr or the client receives unparseable messages; and the host owns the subprocess lifecycle, so it must reap the child on exit or leave orphans behind.

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

    1. 这题看着是背概念,实际区分度在两个小地方:一是能不能把宿主和 client 分开说,二是知不知道 tools 之外还有别的原语。只答「server 提供工具、client 调用工具」是及格线以下。
    2. 先把三个角色摆清楚:server 是能力提供方,一个独立进程;client 是宿主里负责跟某一个 server 说话的那一小块,一个 client 只连一个 server;宿主是你的 Agent 应用,它同时持有多个 client。很多人把宿主和 client 当成一个东西,一问「连三个 server 怎么办」就露馅。
    3. server 侧三种原语要一起说,并且要说清谁来选:tools 是可执行的动作,由模型来挑;resources 是按 URI 读的只读数据;prompts 是可复用的提示词模板,后两者通常由用户或宿主来挑。这句「谁来选」比原语名字本身更能体现你真读过协议——把一份大文档做成 resource 而不是 tool,等于把花不花这笔 token 的决定权从模型手里收回给人。
    4. client 侧也能声明能力让 server 反过来请求宿主:sampling 是让宿主跑一次模型补全,roots 是告诉 server 哪些目录可见,elicitation 是请宿主向用户要一条输入。知道有这三样、不展开,分寸刚好。
    5. 传输两种:stdio 用于本地子进程,Streamable HTTP 用于远程。这里有个时间戳式的加分点——旧的 HTTP 加 SSE 双端点传输已经被标为 legacy,只为兼容老客户端保留;把它当现行方案讲,等于告诉对方你看的是去年的文章。
    6. 可以预期的追问:stdio server 有什么特别要注意的?答 stdout 被 JSON-RPC 独占,所有日志必须走 stderr,否则 client 会收到解析不了的消息;另外子进程的生命周期归宿主管,退出时要杀掉,不然留一堆孤儿进程。

    Key points

    • The server is the capability provider in its own process; a client connects to exactly one server; the host holds many clients
    • Three server-side primitives: tools chosen by the model, resources as URI-addressed read-only data, prompts as reusable templates — the latter two usually chosen by a human
    • Clients can declare sampling, roots and elicitation so the server can call back into the host
    • Two transports: stdio for local subprocesses and Streamable HTTP for remote; the old HTTP+SSE transport is legacy
    • On stdio, stdout belongs to JSON-RPC so logs must go to stderr, and the host must reap the child process

    答题要点

    • server 是能力提供方(独立进程),client 是宿主里连接单个 server 的那一块,宿主可以同时持有多个 client
    • server 侧三种原语:tools 由模型挑,resources 是按 URI 读的只读数据,prompts 是可复用模板,后两者通常由人来挑
    • client 侧还能声明 sampling、roots、elicitation,让 server 反过来请求宿主做事
    • 传输两种:stdio(本地子进程)与 Streamable HTTP(远程);旧的 HTTP 加 SSE 已是 legacy,不要当现行方案讲
    • stdio server 的 stdout 被 JSON-RPC 独占,日志必须走 stderr;子进程生命周期由宿主负责回收
  • When should you reach for MCP instead of plain function calling, and what does it cost when you shouldn't?什么场景下应该考虑用 MCP,而不是直接写 function calling?不该用的时候硬上会付出什么代价?
    Common in ChinaCommon overseasIntermediate#mcp#architecture#trade-offs

    How to reason about it · think before answering

    1. The hinge is the second half. Answering only 'MCP is more standard and decoupled' is like saying 'microservices are more decoupled' — true-sounding but with no criterion, and the interviewer will immediately ask whether you turned every tool into an MCP server.
    2. Give three actionable criteria: the capability must be reused by more than one host, owned by another team or a third party, or added and removed without changing host code. Any one of them justifies MCP; none of them means write a local function. Making 'no' the default answer shows more engineering judgment than the criteria themselves.
    3. Attach a reason to each: multi-host reuse turns N times M into N plus M; external ownership makes the process boundary the responsibility boundary, so their change is not your release; hot-swapping demotes adding an internal tool from a deployment to a config change.
    4. Then state the costs honestly, which is where shipped experience shows: another process to keep alive, another handshake with its own timeouts and reconnects, and a debugging path that went from one hop to three — a tool that never got called might mean the model did not pick it, the schema lost fields in translation, or the server never started. On stdio you also own reaping the child process.
    5. One more point that is easy to miss and scores well: MCP does not change your cost structure. Tool descriptions still enter the context every turn, and more tools still degrade tool selection. The rule that you should consolidate tools past a certain count survives MCP unchanged — arguably it matters more, because now other people can add entries to your tool list.
    6. Expect the follow-up: so internal tools never go through MCP? Not quite. If you want the same capability available to an IDE assistant and an ops bot as well, the first criterion is met even though you own the code.

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

    1. 题眼在后半句。只会说「MCP 更标准更解耦」的人,等于说「微服务更解耦」——听起来对,但没有判据,面试官会立刻追问「那你们所有工具都做成 MCP server 了吗」。
    2. 先给判据,而且要是可执行的三条:能力要被多个宿主复用、能力由另一个团队或第三方维护、需要不改宿主代码就能增删能力。命中任意一条才考虑,**一条都不命中就直接写本地函数**——把默认答案摆成「不上」,这条比三条判据本身更能体现工程判断。
    3. 每条判据配一句为什么:多宿主复用把 N 乘 M 变成 N 加 M;别人维护时进程边界就是责任边界,他们改他们的、你不用发版;热插拔让加一个内部工具从一次发布降级成一次配置变更。
    4. 然后老实说代价,这是区分「用过」和「读过」的地方:多一个进程要保活、多一次握手要处理超时与重连、排障链路从一段变三段——工具没被调用,现在可能是模型没选、可能是 schema 翻译时丢了字段、也可能是 server 压根没起来。stdio 的子进程还要你自己回收,否则留孤儿进程。
    5. 还有一条容易被忽略但很加分:MCP 不改变你的成本结构。工具描述照样每轮都进上下文,工具多了照样会让模型选错——D5 那条「工具超过一定数量就该合并描述」在接了 MCP 之后一字不变,甚至更需要,因为现在别人可以往你的工具列表里塞东西。
    6. 可以预期的追问:那内部工具一律不上 MCP 吗?不是。有一类值得例外——你希望它能被 IDE 里的助手和运维机器人一起用,那第一条判据就命中了,即使它是你自己维护的。

    Key points

    • Three criteria, any one justifies MCP: reuse across hosts, ownership by another team, or add/remove without touching host code
    • The default is no — if none of the three apply, a local function is the better engineering decision
    • Costs: another process to supervise, another handshake with timeouts, and a debug path that grows from one hop to three
    • MCP does not change your cost structure: descriptions still enter context every turn and too many tools still hurt selection
    • Once third-party capabilities are attached, your tool list is no longer fully under your control, which is itself a design problem

    答题要点

    • 三条判据,命中任意一条才考虑 MCP:多宿主复用、由他人维护、需要不改代码增删能力
    • 默认答案是不上:三条都不命中就直接写本地函数,这是更好的工程决策
    • 代价是多一个进程要保活、多一次握手要处理超时、排障从一段链路变成三段
    • MCP 不改变成本结构:工具描述照样每轮进上下文,工具过多照样会让模型选错,该合并还是要合并
    • 第三方能力接进来之后,工具列表不再完全由你掌控,这本身就是需要设计的一件事
  • You are about to attach a third-party MCP server in production. What worries you, and what do you check?你要把一个第三方维护的 MCP server 接进生产环境,会担心什么、做哪些检查?
    Common in ChinaCommon overseasDeep dive#mcp#security#operations

    How to reason about it · think before answering

    1. This stacks yesterday's security topic onto today's openness topic, and it discriminates hard: every benefit of MCP rests on the capability being maintained by someone else, and that is also its biggest risk.
    2. First name the new trust assumptions: you put someone else's code into your own process tree, you feed its returned text straight into the model, and you let it add entries to your tool list. Each maps to a class of risk.
    3. Then go through the checks. Execution: the server is a process that runs, so constrain which files it can read, whether it has network access, its timeout and the identity it runs as — the least-privilege and sandbox story from yesterday. Data: treat everything it returns as untrusted input, which is exactly the indirect-injection scenario where instructions hide in a field of a tool result. Tool output is never instructions, and the permission gate must live in your process and fire before the call.
    4. Third, governance, the part most people miss: the tool list can change at runtime — one listChanged notification and a new tool appears. So pin your allowlist by tool name, keep newly appearing tools out of the model's list until a human approves, and pin the server version instead of tracking upstream latest.
    5. Fourth, availability and cost: this is a new external dependency. If it is down your agent silently loses a set of capabilities, so you need timeouts, graceful degradation (tell the model the capability is temporarily unavailable rather than failing the whole turn), and its calls on your observability dashboard.
    6. Expect the follow-up: how do you decide it is worth attaching at all? Back to the three criteria — if only one host uses it and you could implement it yourself, you are taking third-party risk with no matching benefit.

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

    1. 这题是把昨天的安全和今天的开放性叠在一起考,区分度极高:接 MCP 的全部好处,都建立在「能力由别人维护」这一点上,而这一点同时就是它最大的风险。
    2. 第一层想清楚新增了什么信任假设:你把一段别人写的代码放进了自己的进程树,把它返回的文本直接喂给了模型,还允许它往你的工具列表里加条目。这三件事各自对应一类风险。
    3. 第二层逐条给检查项。执行侧:server 是一个会跑起来的进程,要限制它能读哪些文件、能不能联网、超时多久、以什么身份运行,也就是昨天讲的最小权限和沙箱那一套。数据侧:**它的返回结果一律当不可信输入**,这正是昨天间接注入的固定现场——工具返回的备注字段里可以藏指令;所以工具结果不能当指令执行,权限闸门必须在你自己的进程里、在调用之前判。
    4. 第三层是治理,最容易被漏掉:工具列表可以在运行中变化,server 发一条 listChanged 通知就能加一个新工具。所以你的白名单要按工具名固定,新出现的工具默认不进模型的工具列表,要有人点头;server 的版本要锁定,不能跟着上游 latest 漂。
    5. 第四层是可用性与成本:这是一个新的外部依赖,它挂了你的 Agent 就少一批能力,所以要有超时、要有降级(工具不可用时告诉模型「这个能力暂时不可用」而不是整轮失败),要把它的调用计入你的可观测面板。这三条正好复用前面几周讲过的东西。
    6. 可以预期的追问:怎么判断它值不值得接?答案回到那三条判据——如果这个能力只有你一个宿主用,而且你完全可以自己实现,那接一个第三方 server 承担的风险没有对应的收益。

    Key points

    • Three new trust assumptions: their code in your process tree, their text in your model context, their entries in your tool list
    • Execution: least privilege — restrict filesystem and network, set timeouts, run as a low-privilege identity, sandbox where warranted
    • Data: treat every result as untrusted input; tool output is never instructions, and the permission gate must fire in your process before the call
    • Governance: allowlist by tool name so newly appearing tools stay out until approved, and pin the server version rather than tracking latest
    • Availability: treat it as an external dependency with timeouts, graceful degradation and dashboard coverage

    答题要点

    • 三个新增信任假设:别人的代码进了你的进程树、它的返回文本进了模型上下文、它能往你的工具列表里加条目
    • 执行侧按最小权限收紧:限制文件访问与网络、设超时、以低权限身份运行,必要时进沙箱
    • 数据侧一律当不可信输入:工具返回结果不能当指令执行,权限闸门必须在自己的进程里、在调用之前判
    • 治理侧锁死变化面:按工具名做白名单,新出现的工具默认不进模型的工具列表;锁定 server 版本,不跟 latest
    • 可用性侧当外部依赖对待:超时、降级、把它的调用与失败计入可观测面板

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#retrieval

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. Expected follow-up 1: how do you merge the two rankings? Answer RRF, and explain why weighted sums fail (see q02).
    6. 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.

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

    1. 这题的区分度不在「你知不知道有 hybrid search」,而在**你能不能说出一个向量检索一定会漏的具体例子**。答不出例子的,一听就是只看过架构图。
    2. 推导链只有一句:向量检索比的是语义距离,所以它的强项和弱项都来自「压缩成语义」这一步——同义词能对上(运费 / 邮费),而没有语义的字符串会被压到一起(E4032、SF-3000、订单号、人名)。
    3. 关键词那一路(BM25)的性质正好相反:一个词在本文档里越频繁越相关、在全语料里越常见越不值钱,所以它对低频稀有词极准,对同义改写完全无能。
    4. 结论要说成「两者的盲区不重叠,而且是由计算原理决定的不重叠」——不是「多一路更保险」这种模糊说法。举一个实测例子最有说服力:查「E4032 是什么意思」,向量 top5 里没有那篇讲支付错误码的文档,关键词 top1 就是它。
    5. 可预期的追问一:那怎么合并两路结果?答 RRF,并说清为什么不能加权求和(见 q02)。
    6. 可预期的追问二:中文怎么做关键词检索?答 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,字母数字整词保留,生产上专门的中文分词扩展。
  • How do you merge two retrieval rankings, and why not just take a weighted sum of the scores?两路检索结果怎么合并?为什么不能直接加权求和?
    Common in ChinaCommon overseasIntermediate#rag#rrf#ranking

    How to reason about it · think before answering

    1. The second half is the real question. Anyone can say 'RRF'; explaining why weighted sums fail is what separates people who have looked at the score distributions.
    2. Decompose it: are the two scores even the same unit? Cosine similarity is bounded in 0 to 1 and tightly clustered — candidates often differ by 0.02. BM25 is unbounded and a few rare-term hits reach 12. Adding them lets the larger-magnitude channel decide everything; the weight only tunes how much it dominates.
    3. Worse, it is unstable. Weights tuned on one corpus drift on the next, so you re-tune forever.
    4. Conclusion: fuse ranks, not scores. RRF maps each rank to 1/(k + rank) and sums, with k = 60. Ranks are unitless and need no calibration. k flattens the head of the list so that 'top-ranked in both channels' beats 'first in one channel' — consensus over single-source confidence.
    5. A hand-checkable example helps: rankings [a,b,c] and [c,d,a] give a = 1/61 + 1/63 ≈ 0.0323, while a raw score sum promotes c on the strength of its BM25 12.
    6. Expected follow-up: what about ties? You must break them explicitly, e.g. by id. Otherwise ordering depends on hash-map iteration order and differs across languages and runs, which makes your evaluation numbers irreproducible. Mentioning this signals you actually ran it more than once.

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

    1. 题眼在后半句。前半句答「RRF」谁都会,后半句「为什么不能加权求和」才是筛人的地方——它考的是你有没有真的看过两路分数的分布。
    2. 怎么拆:先问自己两个分数是不是同一个量纲。余弦相似度有界(0 到 1)且分布密集,同一批候选常常只差 0.02;BM25 无上界,命中几个稀有词就能到 12 分。**不同量纲的数相加,等于让量纲大的那一路单方面决定结果**,权重只是在调「它说了算的程度」。
    3. 更麻烦的是它不稳定:权重在这批语料上调好了,换一批语料分布就变了,得重调。这是一个永远还不完的技术债。
    4. 结论:改用名次。RRF 把每一路的名次折算成 `1/(k + rank)` 再相加,k 取 60。名次是无量纲的,不需要任何标定。k 的作用是压平头部差距,让「两路都进前列」压过「一路排第一」——共识优先于单点自信。
    5. 一个能当场手算的例子很加分:两路排名 [a,b,c] 与 [c,d,a],a 得 1/61 + 1/63 ≈ 0.0323;而分数直接相加的版本会把 BM25 里 12 分的 c 顶到第一。
    6. 可预期的追问:同分了怎么办?必须显式定序(比如按 id),否则结果取决于哈希表遍历顺序,同一份输入在不同语言、不同运行里给出不同排序——评估集量出来的数字也就不可复现了。这一条答出来会非常加分,因为它说明你真的跑过多次。

    Key points

    • Use RRF: map each channel's rank to 1/(k + rank) and sum, with k = 60.
    • Weighted sums fail because the scores are different units — bounded, tightly clustered cosine versus unbounded BM25, so BM25 decides the outcome.
    • Weights also do not transfer: tuned on one corpus, they drift on the next.
    • Ranks are unitless and need no calibration; k flattens the head so cross-channel consensus outweighs single-channel confidence.
    • Break ties explicitly (by id) or ordering depends on hash iteration order and your evaluation numbers stop being reproducible.

    答题要点

    • 用 RRF:每一路的名次折算成 1/(k + rank) 再相加,k 取 60。
    • 不能加权求和是因为两个分数量纲不同——余弦有界密集、BM25 无上界,相加等于让 BM25 单方面决定结果。
    • 而且权重不可迁移:这批语料调好,换一批就得重调,是还不完的债。
    • 名次是无量纲的,不需要标定;k 压平头部差距,让两路共识压过单路自信。
    • 同分必须显式定序(按 id),否则结果依赖哈希表遍历顺序,评估数字不可复现。
  • How is reranking usually implemented, what problem does it solve, and what does it cost?重排(rerank)一般怎么实现?它解决了初步检索的什么问题,代价是什么?
    Common in ChinaCommon overseasIntermediate#rag#rerank#latency

    How to reason about it · think before answering

    1. The lazy answer is 'sort again, more accurately'. What the interviewer wants is why the first pass cannot rank well, and why reranking cannot run over the whole corpus.
    2. Decompose: the first pass ranks by retrieval signals — cosine distance or term statistics — which are designed to scan millions of items fast, and coarseness is the price. Reranking changes the algorithm: query and candidate go into one model together (a cross-encoder), which is far more accurate but costs one forward pass per candidate. Hence it must sit behind a wide recall stage.
    3. Distinguish two implementations. For teaching or prototypes, batch-score with an LLM (0-10 for 40 candidates in one call). Production uses a trained cross-encoder reranker. Name the cost: an extra 100-300 ms hop plus an inference box — it is not a per-token API, it consumes capacity.
    4. Framing it as a funnel is clearest: recall sets the ceiling, reranking decides whether what is under the ceiling reaches the top five. Measured: adding the keyword channel lifts recall@20 from 83% to 95%; adding reranking moves recall@20 only to 98%, but recall@5 jumps from 80% to 91% and MRR from 0.732 to 0.908.
    5. Expected follow-up 1: does reranking improve recall? No. It introduces no new candidates, so recall@20 is the wrong metric to judge it by.
    6. Expected follow-up 2: why not ship LLM scoring to production? Unpredictable latency, per-token cost, scores that drift with prompt wording, and no clean path to offline distillation.

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

    1. 这题最容易答成「再排一次序,更准」。面试官想听的是**为什么第一轮不能直接排准**,以及**为什么重排不能对全库做**。
    2. 怎么拆:第一轮的排序依据是「检索信号」——余弦距离或词频统计,它们是为了能在百万条里快速筛选而设计的,代价就是粗。重排换了一种算法:把 query 和候选**拼在一起**送进同一个模型算相关度(cross-encoder),精度高得多,但复杂度是每条候选一次前向,没法对全库做。所以它必须跟在一个宽召回后面。
    3. 结论要区分两种实现:教学 / 原型可以用 LLM 批量打分(一次调用给 40 条打 0 到 10 分),生产用专门训练的 cross-encoder 重排模型。**代价说清楚:多一次 100 到 300 毫秒的调用,外加一台推理机器**——它不是按 token 计费的 API,是要占资源的。
    4. 把它放进漏斗里说最清楚:召回决定天花板,重排决定天花板上的东西能不能排到前五。实测的样子是——加了关键词那一路,recall@20 从 83% 涨到 95%(天花板抬高);再加重排,recall@20 只到 98%,但 recall@5 从 80% 跳到 91%、MRR 从 0.732 到 0.908。
    5. 可预期的追问一:重排能不能提高召回?不能。它不引入新候选,只重排已有的那批——所以看 recall@20 判断重排效果是错的指标。
    6. 可预期的追问二:为什么不用 LLM 打分上生产?延迟不可控、成本按 token 走、分数会随提示词措辞漂移,而且没法做批量离线蒸馏。

    Key points

    • The first pass ranks by retrieval signals so it can scan a large index fast; coarseness is the trade.
    • Reranking feeds query and candidate through one model together (cross-encoder): much sharper, but one forward pass per candidate, so only tens of items.
    • Batch LLM scoring works for teaching; production uses a dedicated reranker, costing an extra 100-300 ms hop plus an inference box.
    • Reranking does not raise recall — it raises recall@5 and MRR (measured 80% to 91%, 0.732 to 0.908) while recall@20 barely moves from 95% to 98%.
    • So judge a reranker by small-k metrics, never by recall@20.

    答题要点

    • 第一轮按检索信号粗排(余弦、词频),为的是能在大库里快速筛,代价是粗。
    • 重排把 query 和候选拼在一起过同一个模型(cross-encoder),精度高但每条一次前向,只能对几十条做。
    • 教学版可用 LLM 批量打 0 到 10 分;生产用专用重排模型,代价是多一次 100 到 300 毫秒的调用加一台推理机器。
    • 重排不提高召回,它提高的是 recall@5 与 MRR——实测 80% → 91%、0.732 → 0.908,而 recall@20 只从 95% 到 98%。
    • 所以判断重排效果要看前 k 小的指标,不要看 recall@20。
  • How do you evaluate retrieval quality in a RAG system, and how should the evaluation set be built?怎么评估一个 RAG 系统的检索效果?评估集应该怎么构造?
    Common in ChinaCommon overseasDeep dive#rag#evaluation#recall

    How to reason about it · think before answering

    1. This is a very common question in the Chinese market and the fastest way to expose someone who has assembled RAG but never tuned it. The test: does your answer contain concrete metric names and an annotation granularity?
    2. First separate what is being evaluated — the step people most often conflate. Retrieval evaluation asks 'was it found'; generation evaluation asks 'was the answer right'. Keep two separate sets. Merge them and, when the score drops, you cannot tell whether retrieval missed or the model fumbled — and those have completely different fixes.
    3. Shape of the set: about 20 queries, each annotated with 1-3 chunk ids that must be retrieved. Annotate at chunk level, not document level — chunks are the retrieval unit, and document-level labels inflate the numbers. Cover the real query mix, especially the types you know break: codes, paraphrase, cross-document.
    4. Three metrics, three questions. recall@5 is what actually reaches the model, so it is the number you care about. recall@20 is the ceiling — if it does not move, the problem is on the recall side and no reranker will save you. MRR is sensitive to ordering and breaks ties when recall is equal.
    5. Production view: freeze the set once agreed, because changing samples destroys comparability — the same reason a factory keeps fixed reference samples. Pair it with online counterparts (empty-citation rate, hallucinated-citation rate, escalation rate), since passing offline does not mean passing in production.
    6. Expected follow-up: is 20 enough given the labelling cost? Not for statistical significance, but enough for regression — its job is to stop retrieval silently getting worse. Scale up before you settle an A/B, and grow it from failure cases rather than random additions.

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

    1. 这题是国内面试的极高频题,也是最容易暴露「只搭过没调过」的一题。判据很简单:你的回答里有没有出现**具体的指标名和标注粒度**,没有就是没做过。
    2. 先把评估对象分清楚——这是最容易混的一步:**检索评估问「找得到找不到」,生成评估问「答得对不对」**。两套评估集要分开维护。混成一套的后果是分数掉了你分不清是检索漏了还是模型答砸了,而这两件事的修法完全不同。
    3. 评估集的形状:20 条左右的 query,每条**人工标注 1 到 3 个必须召回的 chunkId**。注意标注粒度是**块**不是文档——检索的单位就是块,标到文档级会让指标虚高。query 要覆盖真实分布,尤其要包含那些你知道会翻车的类型(编号、同义改写、跨文档)。
    4. 三个指标各回答一个问题:recall@5 是「进上下文的那几条覆盖了多少」,也就是你真正关心的数;recall@20 是天花板,它上不去说明问题在召回侧、重排再强也没用;MRR 对排序质量敏感,recall 打平时用它分高下。
    5. 生产视角:评估集一旦定下来就要冻结,换了样本分数就没有可比性——这和产线质检必须用固定的标准样品是同一个道理。同时线上要有对照指标(引用为空率、幻觉引用率、转人工率),因为离线过了不等于线上没事。
    6. 可预期的追问:标注成本这么高,20 条够吗?答:20 条不够做统计显著性,但足够做**回归**——它的作用是「改了检索之后别悄悄变差」。要做 A/B 定论再上规模,而且优先扩充失败案例,不是随机加样本。

    Key points

    • Retrieval and generation evaluation are two separate sets: 'was it found' versus 'was the answer right'.
    • Around 20 queries, each labelled with 1-3 chunk ids that must be retrieved — chunk level, not document level.
    • recall@5 is what the model actually sees, recall@20 is the ceiling, MRR measures ordering quality.
    • Freeze the set once agreed or scores stop being comparable; pair it with online empty-citation and hallucinated-citation rates.
    • Twenty cases is a regression guard, not a significance test; grow it from failure cases, not random samples.

    答题要点

    • 检索评估和生成评估是两套:前者问「找得到找不到」,后者问「答得对不对」,分开维护。
    • 评估集是 20 条左右的 query,每条人工标 1 到 3 个必须召回的 chunkId——标到块级,不是文档级。
    • recall@5 是真正关心的数(模型只看得到这几条),recall@20 是天花板,MRR 衡量排序质量。
    • 评估集一旦定下来就冻结,否则分数没有可比性;线上再配引用为空率、幻觉引用率做对照。
    • 20 条不够做显著性但够做回归;扩充时优先补失败案例,不是随机加样本。

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#frontend

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题是送分题,但送分点在后半句。只答「用 EventSource 监听 message 事件」的,面试官会立刻追问鉴权怎么办——答不上来就说明没在真项目里接过。
    2. 先给正面答案的骨架:`fetch` 拿到响应后读 `res.body` 这个 ReadableStream,`TextDecoder` 解码成文本,按空行切帧,逐帧解析出 `event` 与 `data`,把文本增量追加到当前这条消息上。
    3. 为什么不用 `EventSource`,三个硬伤要一口气说全:只能发 GET、不能带自定义请求头(也就是放不进 Authorization)、不能带请求体。Agent 场景里消息体、幂等键、会话 id 都得走 body,三条全撞上。
    4. 紧接着说代价,这是区分「用过」和「读过」的地方:手写解析意味着 `EventSource` 自带的自动重连、`Last-Event-ID` 续传都要自己实现。不过带鉴权的场景里那个自动重连本来就不好用(它重连时同样带不了头),所以损失没听起来那么大。
    5. 可预期的追问一:帧被网络切成两半怎么办?答缓冲——按空行切完之后,最后一段可能是半截,`pop` 出来留到下一块再拼。**这个 bug 在本机直连时几乎不出现**,所以要专门构造切碎的报文来测。
    6. 可预期的追问二:为什么不用 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。
  • The user hits Stop and the frontend calls AbortController.abort(). What is the backend doing at that moment?用户点了「停止生成」,前端调用 AbortController.abort() 之后,后端在做什么?
    Common in ChinaCommon overseasDeep dive#streaming#cancellation#cost

    How to reason about it · think before answering

    1. This is the core question of the chapter and a deliberate trap: the prompt states the abort as a given and waits for you to say 'so it stopped'. Saying that ends the conversation.
    2. The correct answer in one line: the backend knows nothing and is still running — still calling the model, still writing messages, still billing tokens. abort only stops your end from reading; at most it drops the TCP connection, and whether the backend notices, or acts on noticing, is a separate matter.
    3. Decompose by drawing who knows what: the user knows, the frontend knows, the chain breaks, the backend does not know. That broken link must be closed with an explicit request: POST /runs/:id/cancel. So stopping is two steps, not one.
    4. A quantified contrast lands best: on the same 70-character reply interrupted at character 5, the two-step version stops the backend at 5/70 while abort-only runs to 70/70. That is 14x the tokens, and those 65 characters also land in conversation history and get resent as context next turn, billing you twice.
    5. Production addendum: on cancel, do not hard-kill. Move the run to a cancelled state and let the current step finish, or you leave half-written messages and gaps in the sequence numbers. Also make cancel idempotent, because you will retry it when the network flakes.
    6. Expected follow-up: can the backend just detect the dropped connection and stop by itself? It can and should, as a safety net, but not as the only mechanism. Proxies and load balancers often hold connections open, so detection can lag by tens of seconds, and if the client auto-reconnects the connection never drops at all. The net is a net; the explicit cancel is the main path.

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

    1. 这题是本章题眼,也是一道**陷阱题**:题干里已经把「前端 abort 了」当成既成事实,等你顺着说「那就停了」。答「停了」的直接出局。
    2. 正确答案一句话:**后端什么都不知道,它还在跑。** 还在调模型、还在往库里写消息、还在按 token 计费。`abort` 只是让你这一端不再读了,它顶多让 TCP 连接断开,而后端是否感知得到连接断开、感知到之后做不做事,是另一回事。
    3. 怎么拆:把「谁知道这件事」画出来。用户知道 → 前端知道 → **中间断了** → 后端不知道。断掉的这一环必须用一个显式的请求补上:`POST /runs/:id/cancel`。所以打断是两步,不是一步。
    4. 给一个量化的对照最有说服力:同一段 70 个字的回复,在第 5 个字打断——两步打断的后端停在 5/70,只 abort 的后端照跑到 70/70。差 14 倍的 token,而且那 65 个字还会落进会话历史,下一轮当上下文重新发一遍,付第二遍钱。
    5. 生产视角的补充:cancel 收到之后**不要硬杀**,把 run 迁到 cancelled 状态、让当前这一步跑完再退出——硬杀会留下半写的消息和对不上的序号。而且 cancel 本身必须幂等,因为网络抖动时你会重试它。
    6. 可预期的追问:那能不能靠后端检测连接断开来自动停?可以做,而且应该做(作为兜底),但不能只靠它——反向代理和负载均衡常常会把连接维持一段时间,后端感知到断开可能已经是十几秒之后;而且用户点停止之后如果自动重连,连接根本没断。**兜底归兜底,显式 cancel 才是主路径。**

    Key points

    • The backend has no idea: still calling the model, still writing, still billing. abort only stops your side reading.
    • Stopping is two steps: abort for instant UI response, plus POST /runs/:id/cancel to actually halt the run.
    • Quantified: interrupting the same 70-character reply at character 5 gives 5/70 with both steps versus 70/70 with abort alone.
    • On cancel, transition the run to cancelled and let the current step finish rather than hard-killing; make cancel idempotent.
    • Backend disconnect detection is only a safety net — proxies hold connections open and auto-reconnect means no disconnect at all.

    答题要点

    • 后端完全不知情:还在调模型、还在写库、还在计费。abort 只让前端这一端停止读取。
    • 打断必须两步:abort(界面立刻响应)+ POST /runs/:id/cancel(后端真的停)。
    • 量化差别:同一段 70 字的回复在第 5 个字打断,两步是 5/70,只 abort 是 70/70。
    • 后端收到 cancel 不要硬杀,迁到 cancelled 状态让当前步跑完;cancel 必须幂等。
    • 靠后端检测连接断开只能当兜底:代理会维持连接、自动重连时连接根本没断。
  • What is different about frontend state management under streaming, and why not call setState on every token?流式场景下前端的状态管理要注意什么?为什么不能每个 token 都 setState?
    Common in ChinaCommon overseasIntermediate#react#streaming#performance

    How to reason about it · think before answering

    1. This question probes whether you have watched a long reply drop frames. 'Keep messages in useState and setState on each delta' is functionally correct but reveals you only tried short replies.
    2. Do the arithmetic first: streaming delivers tens of tokens per second, so one setState per token means tens of full render passes per second. The message list keeps growing, so each pass gets more expensive as the conversation goes — the jank peaks late in long replies and long sessions, exactly when it hurts most.
    3. The fix is batching: append tokens into a ref without rendering, and flush the accumulated text on a 30 ms timer. Thirty milliseconds is roughly 33 fps, still a smooth typewriter, while render count drops by one to two orders of magnitude — measured, 200 tokens produced 8 commits.
    4. Three details that must ship with it: force a final flush when the stream ends, or the last sub-batch stays in the buffer and the user sees a truncated reply; flush on interrupt too, so the user sees exactly where it stopped; and keep the buffer in a ref, not state, or the code you wrote to avoid renders is itself causing them.
    5. One level up is layering: streaming logic should live outside React. Parsing, event reduction and batching are pure functions; a store holds state and exposes subscribe and getSnapshot; React only calls useSyncExternalStore. The concrete payoff is that this logic can be unit tested with no browser instead of being click-tested.
    6. Expected follow-up: why not just use a state library? Libraries solve cross-component sharing and update granularity, while the hard parts here are lifecycle (connect, cancel, cleanup on unmount) and flush cadence — no library does those for you. The interviewer wants your reasoning, not your library list.

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

    1. 这题考的是「你有没有在长回复下真的看过掉帧」。答「用 useState 存消息数组,收到 delta 就 setState」在功能上没错,但它暴露的是只在短回复上试过。
    2. 先算一笔账:流式一秒来几十个 token,每个 token 一次 setState 就是一秒几十轮完整渲染。而消息列表是越来越长的,每一轮的代价随对话轮数增长——所以卡顿在回复后半段和长会话里最明显,正好是最不该卡的时候。
    3. 做法是攒批:token 先追加进 ref(不触发渲染),一个定时器每 30 毫秒把攒下的一次性提交。30 毫秒约等于 33 帧每秒,肉眼仍是连续的打字机,渲染次数掉一到两个数量级——实测 200 个 token 只提交 8 次。
    4. 三个必须配套的细节:流结束时强制 flush 一次(否则最后不足一个批次的内容永远留在缓冲里,用户看到回复少半句);打断时也要 flush(让用户看到停在哪个字);缓冲状态必须放 ref 不放 state,否则你为了省渲染写的代码本身在触发渲染。
    5. 再往上一层是分层:**流式逻辑应该活在 React 外面。** 解析、事件归并、攒批都是纯函数,store 持有状态并暴露 subscribe 和 getSnapshot,React 侧只用 useSyncExternalStore 订阅。这样做的直接好处是**这套逻辑可以在没有浏览器的环境里跑单元测试**,而不是只能靠手点。
    6. 可预期的追问:为什么不直接用某个状态库?答:状态库解决的是跨组件共享和更新粒度,而流式的难点在生命周期(连接、取消、卸载清理)和批处理频率——这两件事没有哪个库替你做。面试官问这题想听的是你怎么想,不是你会用哪个库。

    Key points

    • One setState per token means tens of full renders per second, and each render costs more as the list grows — long replies jank at the end.
    • Batch instead: accumulate tokens in a ref and flush every 30 ms; measured, 200 tokens produced only 8 commits.
    • Ship the details with it: force a flush on stream end and on interrupt, and keep the buffer in a ref rather than state.
    • Keep parsing, event reduction and batching as pure functions outside React; subscribe via useSyncExternalStore.
    • The payoff of that split is unit-testable streaming logic with no browser in the loop.

    答题要点

    • 每个 token 一次 setState 等于一秒几十轮全量渲染,而消息列表越长每轮越贵,长回复后半段必然掉帧。
    • 做法是攒批:token 进 ref 不触发渲染,30 毫秒定时 flush 一次,实测 200 个 token 只提交 8 次。
    • 必须配套:流结束和打断时强制 flush;缓冲放 ref 不放 state。
    • 流式逻辑(解析、归并、攒批)应该是 React 之外的纯函数,React 只用 useSyncExternalStore 订阅。
    • 这样分层的直接好处是能脱离浏览器做单元测试,而不是只能手点验证。
  • How do you design retry so it does not duplicate side effects, and should the tool-call process be visible to the user?失败重试怎么设计才不会产生重复副作用?工具调用过程要不要暴露给用户?
    Common in ChinaCommon overseasIntermediate#idempotency#retry#ux

    How to reason about it · think before answering

    1. The question bundles two topics, and the test is whether you see what they share: both turn invisible intermediate state into something the user can act on. Answering them separately is fine, but naming the link reads as senior.
    2. Chain for retry: retrying means the same message may execute twice, costing double tokens and possibly duplicating irreversible tool calls such as issuing a refund twice. Hence idempotency. The key must be generated by the client on the first attempt and resent unchanged on retry, and the backend enforces it with a unique constraint, reattaching to the existing run instead of creating a new one.
    3. State the decision rule clearly: when do you mint a new key? The rule is whether the content being sent changed, not which button the user pressed. Same message retried keeps the key; edited content is a new message and needs a new key.
    4. Mentioning how far this pattern reaches scores well: write deduplication, cron ticks consumed exactly once, cross-service delivery, and frontend retry — the same shape at four layers, with the database's unique constraint always the final arbiter rather than an application-level check-then-write.
    5. For tool visibility: expose the process, for three reasons. The user can decide whether to interrupt instead of waiting blind; waiting becomes tolerable, since a spinner for fifteen seconds invites a page refresh that wastes the whole turn; and when something breaks the user can say 'it hung on looking up my order', which saves everyone time.
    6. Expected follow-up: does exposing everything leak internals? It can, so filter. Show human-readable tool names rather than function names, hide user identifiers, internal ids and secrets from the arguments, and show classified error reasons rather than raw stack traces. You are surfacing the process, not the internal structure.

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

    1. 这题把两件事绑在一起问,考的是你能不能看出它们的共同点:**都是「把不可见的中间状态变成可控的」**。分开答也行,但点出这层关系会显得成熟。
    2. 重试这一半的推导链:重试意味着同一句话可能被执行两遍 → 两倍 token,还可能两次不可逆的工具调用(比如退款打两次钱)→ 所以要幂等 → 幂等键必须由**客户端在第一次发送时生成**并在重试时原样带上 → 后端拿它做唯一约束,命中就把已有 run 的流接回来,而不是新建。
    3. 关键判据要说清:**什么时候该换新键?** 判据是「要发送的内容变没变」,不是「用户点了哪个按钮」。同一句话重试用同一个键;用户改了内容重新发,那是新的一句话,必须换新键。
    4. 顺带提一句这一招的复用面会很加分:落库去重、定时任务防止一个 tick 被消费两次、跨服务调用防重复投递、前端重试——同一个形状用在四个层面,最终裁判永远是数据库的唯一约束,不是应用层的先查后写。
    5. 工具可视化这一半:中间过程要暴露,理由有三条——用户能判断要不要打断(不然他只能盲等);等待变得可以忍受(十几秒的转圈会让人刷新页面,而刷新意味着这一轮的钱白花);出问题时用户能说清「卡在查订单那一步」,客服和你都省事。
    6. 可预期的追问:全都暴露会不会泄露内部实现?会,所以要过滤——工具名用人话不用函数名,参数里的用户标识、内部 id、密钥一律不显示,错误显示归类后的原因而不是原始堆栈。**可视化的是过程,不是内部结构。**

    Key points

    • Retry carries the idempotency key minted on the first attempt; the backend hits a unique constraint and reattaches to the existing run.
    • The rule for minting a new key is whether the content changed — same message keeps the key, edited content gets a new one.
    • The same pattern recurs in write dedup, cron ticks, cross-service delivery and frontend retry, always arbitrated by a database unique constraint.
    • Make tool calls visible so users can decide whether to interrupt, tolerate the wait, and describe where it hung.
    • But filter: human-readable tool names, no internal ids or secrets in the arguments, classified error reasons instead of raw stack traces.

    答题要点

    • 重试要带客户端首次生成的幂等键,后端用唯一约束命中后把已有 run 的流接回来,不新建。
    • 换不换键的判据是「内容变没变」:同一句话重试用同一个键,改了内容才换新键。
    • 同一招在落库、定时任务、跨服务调用、前端重试四处复用,最终裁判永远是数据库的唯一约束。
    • 工具调用要可视化:用户才能判断要不要打断、等待变得可忍受、出问题时说得清卡在哪一步。
    • 但要过滤:工具名用人话、参数里的内部 id 与密钥不显示、错误显示归类原因而不是原始堆栈。

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-process

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题考的不是知识,是节奏感。面试官问它,通常是因为上一位候选人在架构图上讲了 25 分钟,深入和权衡各剩五分钟——而评分表上分数最重的恰恰是后两步。
    2. 先给结构,五步加时间盒:需求澄清 5 分钟、容量与成本估算 3 分钟、架构草图 8 分钟、深入 2 到 3 个点 15 分钟、权衡与取舍 5 分钟。给得出具体分钟数本身就是分数,因为它说明你掐过表。
    3. 然后正面回答「为什么不先画图」:一句话的题干里,日活、延迟预算、成本预算、是否多租户、失败可容忍度这五件事全是未知的,而它们每一个都会实质改变架构。不问就画,最好的结果是运气好蒙对,最坏的结果是二十分钟后面试官发现你解的是另一道题。用一个类比说清:甲方只说「我要一栋办公楼」,你就展开施工图,而他连预算是两千万还是两个亿都没讲。
    4. 补一条几乎每次都会遇到的现场情况:你开始问,面试官说「你先自己假设一个」。这不是让你别问了,是让你自己给一个数并说出依据。正确接法是「那我按日活 1 万、人均 5 轮算,如果实际是十万级我会在最后一步说明哪里要改」——既守住了节奏,又把假设变成了可追溯的前提。
    5. 最后主动交代第四步的准备方式:深入的 15 分钟不能临场想,要提前备好三个「深入包」(状态与保序、成本与限流、失败与重试),面试官挑哪个都有货。说得出「我提前准备了三个方向」,比现场硬讲一个更能体现你练过。
    6. 可以预期的追问:如果时间不够怎么办?答案是砍第三步而不是砍第五步——草图讲不完可以说「其余按常规做,需要的话我们回头补」,但权衡那 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 分钟几乎没人做,做了就是加分
  • System design: design an e-commerce customer support agent. It looks up orders and shipments, drafts refunds by policy, answers product and policy questions, and escalates to a human when it cannot resolve the issue.系统设计:请设计一个电商客服 Agent。它要能查订单和物流、按规则拟退款方案、回答商品与政策问题,并在搞不定时转人工。
    Common in ChinaCommon overseasDeep dive#system-design#customer-support#escalation

    How to reason about it · think before answering

    1. Start by separating this from 'design an agent platform', or you will answer an infrastructure question. The platform question is about running execution reliably; this one is about not trapping users inside a bot. The rubric lives in the business exits, not the message bus. So pin the thesis in your first sentence: every conversation must end in exactly one of three exits — self-served, handed to a human, or filed as a ticket.
    2. Clarify for 5 minutes, asking four things: daily actives and concurrent sessions (does execution need to be split out), whether human agents work nights (does exit three exist), whether the agent executes refunds or only drafts them (do you need an approval tier), and how large the knowledge base is and how often it changes (is retrieval the centre of this problem). The third question matters most: it decides whether this system has irreversible side effects.
    3. Estimate for 3 minutes, out loud: 2000 input plus 500 output per turn, input is 2000 over a million times $0.15 which is $0.0003, output is 500 over a million times $0.60 which is also $0.0003, so about $0.0006 per turn. 10k daily actives at five turns is 50k turns, roughly $30 a day and $900 a month in model spend, machines excluded. For concurrency, a peak factor of 3 and 6 seconds per turn gives about 11 in-flight executions at peak, which is 3 worker replicas at 4 concurrent each. State the arithmetic before the result — the interviewer's next line is always 'where did that number come from'.
    4. Sketch for 8 minutes, four blocks: ingress does auth, rate limiting, persistence and publish, then returns immediately; execution pulls from the bus, runs the agent loop, and streams sequenced fragments back; storage is sessions, runs and messages plus a chunk table for the knowledge base; observability is tracing plus a cost ledger. Then mark on the diagram which node decides between the three exits — that single annotation tells the interviewer you are answering the support question rather than the generic platform one.
    5. Go deep for 15 minutes, starting with escalation because that is the crux. The criteria must be quantified, any one of four triggering a handoff: two consecutive unresolved turns, an explicit user request, an amount above the auto-execution ceiling (50 CNY in our setup), or a sentiment keyword hit. Then describe the handoff payload: not forty turns of raw transcript, but a structured summary — the user's ask in one line, verified facts, actions already taken, and the failure reason, with a link to the full transcript. Cover the knowledge base in one line (hybrid search, rerank, inline citations) and spend the weight on 'when the citation set comes back empty, take exit two or three rather than letting the model invent an answer' — that is the sentence they will push on. Cover multi-turn in one line too: compress once history passes 70% of budget, cut on a turn boundary, and merge a change of mind within 30 seconds into the same execution.
    6. Trade-offs for 5 minutes, three points: bias the escalation threshold toward escalating, because a false handoff costs one human conversation while trapping a user costs a churned customer and a bad review — different orders of magnitude. Drafting refunds instead of executing them trades one human approval for an entire class of irreversible incidents. And name what breaks the design: once the agent team is large enough to need skill-based routing and queueing, escalation stops being a boolean and becomes its own scheduling system.
    7. Expect, in rough order: does 'I want to file a complaint' count as a sentiment hit (yes, and track that class separately — it is a product signal); should the agent keep listening after handoff (yes, to summarise and prompt the human, but not to speak); how do you stop users being bounced repeatedly (allow one handoff per conversation, then file a ticket); and what happens to old answers when the knowledge base changes (cite chunk ids and versions so you can trace which revision was wrong).

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

    1. 先说这题和「设计一个 Agent 平台」的区别,否则你会把它答成一道基础设施题。平台题考的是怎么把执行跑稳,这题考的是**怎么保证不把用户困在机器人里**——面试官心里的评分点在业务出口上,不在消息总线上。所以主线要一开口就钉死:任何一通会话最后只能落到三条出口之一,自助解决、转人工、留工单。
    2. 第一步澄清 5 分钟,问四件事:日活与并发会话数(决定要不要拆执行层)、人工坐席有没有夜班(决定出口三存不存在)、退款是 Agent 直接执行还是只拟方案(决定要不要人工确认档)、知识库有多大且多久更新一次(决定检索是不是本题的重点)。第三个问题尤其关键,它直接决定这道题是不是带副作用。
    3. 第二步估算 3 分钟,现场算:单轮 2000 输入加 500 输出,输入 2000 除以一百万乘 0.15 等于 0.0003 美元,输出 500 除以一百万乘 0.60 也等于 0.0003 美元,一轮约 0.0006 美元;日活 1 万、人均 5 轮就是 5 万轮,一天约 30 美元、一个月约 900 美元,模型费不含机器。并发按峰谷比 3、单轮 6 秒算,峰值在途约 11 次执行,每个 worker 并发 4 就是 3 个副本。报数字之前先报算式,面试官插的那句一定是「这个数怎么来的」。
    4. 第三步草图 8 分钟,四块:接入层只做鉴权、限流、落库、投递并立刻返回;执行层从消息总线取活跑 Agent 循环、片段带序号回传;存储是会话、执行、消息三张表加一张知识库切块表;可观测是 tracing 加成本台账。在这张图上额外标出三条出口的分叉点在哪一个节点上——这是本题独有的一笔,画上去面试官立刻知道你答的是客服而不是通用平台。
    5. 第四步深入 15 分钟,优先讲转人工这一支,因为它是本题的题眼。判据必须量化,四条任一命中就转:连续 2 轮未解决、用户明确要求、涉及金额超过自动执行上限(本课口径 50 元)、情绪词命中。接着讲交接形状——不是把 40 轮原文丢给客服,而是一段结构化摘要:用户诉求一句、已核实事实几条、Agent 已做过的动作、失败原因,附原始对话链接。知识库那一支一句话带过混合检索加重排加引用,重点落在「引用为空时走出口二或三,而不是让模型编一个答案」,这是最容易被追的一句。多轮那一支同样一句话:历史超七成预算触发压缩且切口对齐到一轮开头,用户中途改口则 30 秒内合并进同一次执行。
    6. 第五步权衡 5 分钟,说三件事:转人工的判据宁可偏松,因为误转的代价是一次人工会话,把用户困住的代价是一个流失客户加一条差评,两者不在一个量级;退款只拟方案不直接执行,是拿一次人工点头换掉一整类不可逆事故;以及什么规模会推翻这个设计——坐席团队大到需要技能路由和排队策略时,转人工就不再是一个布尔判断,而是另一套调度系统。
    7. 可以预期的追问,按频率排:用户说「我要投诉」算不算情绪词命中(算,且这一类要单独统计,它是产品问题的信号);转人工之后 Agent 还要不要继续在旁边听(要,用来生成小结和给坐席提示,但不允许再发言);怎么防止用户被反复转来转去(同一通会话只允许转一次,第二次直接留工单);以及知识库更新后旧答案怎么办(回答里带引用编号和版本,出问题能倒查是哪一版说错的)。

    Key points

    • Thesis: every conversation ends in exactly one of three exits — self-served, escalated to a human, or filed as a ticket
    • Clarify four things: concurrent sessions, whether humans cover nights, whether refunds are executed or only drafted, and knowledge base size and churn
    • Estimate with arithmetic: about $0.0006 per turn, so 10k DAU at five turns is roughly $30/day and $900/month; peak concurrency about 11, meaning 3 worker replicas
    • Quantify escalation: two consecutive unresolved turns, an explicit request, an amount over the auto-execution ceiling, or a sentiment keyword
    • Hand over a structured summary — ask, verified facts, actions taken, failure reason — plus a transcript link, not forty raw turns
    • When retrieval returns no citations, take exit two or three instead of letting the model improvise; answers carry citation ids
    • Reuse compression and 30-second merge for multi-turn; draft refunds rather than executing them, trading one approval for a class of irreversible incidents
    • Trade-off: bias toward escalating, because a false handoff and a trapped user cost different orders of magnitude

    答题要点

    • 主线一句话:任何一通会话只能落到三条出口之一——自助解决、转人工、留工单
    • 澄清必问四件事:并发会话数、人工有没有夜班、退款是执行还是只拟方案、知识库规模与更新频率
    • 估算带算式:单轮约 0.0006 美元,日活 1 万人均 5 轮约 30 美元一天、900 美元一月;峰值并发约 11、3 个 worker 副本
    • 转人工判据必须量化,四条任一命中:连续 2 轮未解决、用户明确要求、金额超自动执行上限、情绪词命中
    • 交接给人工的是结构化摘要(诉求、已核实事实、已做动作、失败原因)加原始对话链接,不是 40 轮原文
    • 知识库检索不到时走出口二或三,绝不让模型自由发挥编答案;回答带引用编号
    • 多轮沿用压缩与 30 秒打断合并,退款只拟方案不直接执行,用一次人工点头换掉一类不可逆事故
    • 权衡:判据宁可偏松,因为误转和困住用户的代价不在一个量级
  • For a multi-tenant agent service, how do you design data isolation and billing isolation, and when do you move from a shared table to a dedicated database per tenant?一个多租户的 Agent 服务,数据隔离和计费隔离要怎么设计?什么时候该从共享表升级到独立库?
    Common in ChinaCommon overseasIntermediate#system-design#multi-tenancy#isolation

    How to reason about it · think before answering

    1. The hinge is that 'isolation' is plural. Plenty of candidates answer only data isolation, but the layer that actually breaks in production is resources: one tenant's spike starves everyone else, no rows leak, and users still complain. Open with all three — data, resources, billing — and note that each missing layer maps to its own class of incident.
    2. On data, one sentence separates people who shipped this from people who read about it: 'every query carries tenant_id' versus 'row-level security is the backstop'. The first eventually misses a query, and the one it misses is always the newest, least-tested feature. The correct framing is that RLS is the gate and the application-level where clause is just an optimisation — the same reasoning as idempotency being adjudicated by a database unique constraint. Whatever the data layer can enforce should not depend on everyone remembering.
    3. On resources, give two concrete things: a rate-limit bucket per tenant, and workers sharded by a hash of the tenant id. That is the same sharding mechanism used to preserve per-user ordering, with a different hash input and a different purpose — containing spikes rather than serialising. Noisy neighbours hurt more in agent workloads because a single execution can run thirty seconds, so a thousand queued items from one tenant leaves everyone else waiting.
    4. Billing is the simplest and the most often forgotten: add a tenant column to the token usage ledger and tag every write. Invoicing, quotas and over-budget degradation all hang off it. Bring the cost figures too — roughly $0.0006 per turn at 2000 in and 500 out, about $900 a month at 10k daily actives and five turns each — because quoting per-tenant economics shows you actually ran the numbers.
    5. Then the escalation criteria, the second discriminator. Three tiers: shared table with a tenant column, schema per tenant, database per tenant. The trigger is not tenant count, it is whether a single tenant can starve the rest and whether there is a hard compliance requirement. 'Split the database past a hundred tenants' is guesswork: a hundred small tenants share a table happily, while one regulated enterprise customer may require physical separation on its own. State the cost too — a database per tenant looks clean, but migrations, backups, monitoring and connection pools all multiply by tenant count, so operational cost jumps rather than scaling linearly.
    6. Expect the sharpest follow-up: does the idempotency key change under multi-tenancy? The algorithm does not, but its scope must include the tenant id. Without it, two tenants whose clients independently produce the same string — both using order id order-1024 — collide, and the later request is rejected by the unique constraint as a duplicate. One tenant's write is swallowed by another tenant's history, both logs look perfectly normal, and it is the hardest class of multi-tenant bug to find.

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

    1. 这题的题眼在「隔离」是复数。只答数据隔离的候选人非常多,而多租户翻车最多的其实是资源那一层——一个租户的洪峰打穿别人的处理能力,数据一条都没串,用户照样投诉。所以第一句先把三层摆出来:数据、资源、计费,缺哪一层对应一类事故。
    2. 数据这一层,判断一个人有没有真做过就看一句话:他说「每条查询都带 tenant_id」还是「靠数据库的行级安全兜底」。前者迟早会漏一处,而漏掉的那处通常是最新加、最没被测过的功能。正确说法是行级安全是闸门,应用层那句 where 只是优化——这和幂等的最终裁判必须是数据库唯一约束,是同一种思路:能在数据层强制的,不要指望每个人写代码时都记得。
    3. 资源这一层给两件具体的东西:每个租户一个独立限流桶,以及 worker 按租户标识哈希分片。分片这一招和「按用户哈希保住同一用户顺序」是同一套机制,只是哈希的输入换了,目的从保序变成隔离洪峰。Agent 场景里噪声邻居格外突出,因为单次执行可能跑三十秒,一个租户灌一千条进来,别人就得排队。
    4. 计费这一层最简单也最容易漏:token 用量台账加一列租户标识,写入时打标。账单、配额、超支降级三件事全靠它。顺带说一句成本口径——单轮 2000 输入加 500 输出约 0.0006 美元,日活 1 万人均 5 轮约每月 900 美元,能报出这个量级说明你真的算过每租户成本。
    5. 然后回答升级判据,这是本题的第二个区分点。三档是共享表加租户列、schema 级、库级;**判据不是租户数量,是「有没有单个租户能把别人拖垮」和「有没有合规硬要求」**。答「超过一百个租户就该分库」是典型的凭感觉,因为一百个小租户共享一张表毫无问题,而一个受监管的大客户哪怕只有一个也可能必须物理隔离。代价要一起说:库级隔离看着干净,但迁移脚本、备份、监控、连接池全部乘以租户数,运维成本是陡增不是线性。
    6. 可以预期的追问,也是最见功力的一问:幂等键在多租户下要不要变?答案是键的算法不用变,但**作用域必须带上租户标识**。不带的话两个租户的客户端各自生成了同一个字符串(都用订单号 order-1024),后来那个会被唯一约束当成重复请求挡掉——一个租户的写入被另一个租户的历史请求吞掉,两边日志都完全正常,是多租户里最难查的一类 bug。

    Key points

    • Three parallel layers, each missing one causing its own class of incident: data, resources, billing
    • Data isolation is backstopped by row-level security; the application where clause is only an optimisation
    • Resource isolation is a per-tenant rate-limit bucket plus sharding workers by tenant hash, aimed at noisy neighbours
    • Billing isolation is a tenant column on the usage ledger, powering invoices, quotas and degradation
    • Escalate to schema or database isolation based on starvation risk and compliance mandates, not tenant count
    • Per-tenant databases multiply migrations, backups, monitoring and connection pools — operational cost jumps
    • The idempotency key algorithm stays, but its scope must include the tenant id or identical keys across tenants collide

    答题要点

    • 三层隔离并列,缺一层对应一类事故:数据、资源、计费
    • 数据靠行级安全兜底,应用层的 where 只是优化——能在数据层强制的不要靠人记得
    • 资源是每租户独立限流桶加按租户标识哈希分片,防的是噪声邻居而不是数据串
    • 计费是台账加一列租户标识,账单、配额、超支降级全靠它
    • 升级到 schema 级或库级的判据是「单租户能否拖垮别人」与「有没有合规硬要求」,不是租户数量
    • 库级隔离的代价是迁移、备份、监控、连接池全部乘以租户数,运维成本陡增
    • 幂等键算法不变,但作用域必须带租户标识,否则两个租户的同名键会互相挡掉请求
  • An agent system's model spend is out of control. Which levers do you pull, in what order, and roughly how much does each save?一个 Agent 系统的模型成本失控了,你会从哪几个层面着手控制?每一层大概能省多少?
    Common in ChinaCommon overseasIntermediate#system-design#cost#capacity-planning

    How to reason about it · think before answering

    1. The reflex answer is 'switch to a cheaper model', and it is also the easiest one to get killed on: the follow-up is 'how do you know quality did not drop', and without an offline eval set and a comparison run you are exposed. The right opening is 'look at the ledger first' — slice by user, by day and by model to find which dimension is growing. Locate before you act.
    2. Second, put the baseline on the table, because cost talk without a baseline is noise. At 2000 input and 500 output tokens per turn, input is 2000 over a million times $0.15 which is $0.0003, output is 500 over a million times $0.60 which is also $0.0003, so about $0.0006 per turn. 10k daily actives at five turns is 50k turns, roughly $30 a day and $900 a month.
    3. Then give five layers ordered by the cost you pay, not by the savings: caching and prompt caching, tiered model routing, context compression, step and tool budget caps, rate limiting and degradation. The ordering is part of the answer, because it also communicates your rollout sequence.
    4. Attach a number derived from the baseline to each layer. Caching at a 15% hit rate takes $900 to roughly $765. For tiered routing, state the precondition honestly: it is the only layer that can change the order of magnitude, but only if your baseline runs a flagship model — if you already run the cheapest tier there is nothing left to squeeze. Saying that out loud is far more credible than inventing a savings percentage. Compression takes input from 2000 to 1200 tokens, so $0.00048 per turn, about $720 a month, a 20% cut.
    5. Layer four is usually mis-sold as savings; what it actually buys is predictability. With a cap of five tool calls per subtask, per-turn cost finally has a ceiling: five calls each feeding back 800 tokens pushes input to 6000, so $0.0012 per turn, exactly double the baseline — and with no cap there is no ceiling at all. The right phrasing is 'this does not save money, it makes the bill predictable'.
    6. Layer five is rate limiting and degradation, last because it costs the most: $900 across 10k daily actives is about $0.09 per user per month, so a $1 monthly hard cap is invisible to real users and only stops scripted abuse. The nuance is degrade before refusing — this is the only layer users can feel.
    7. Expect the follow-up: which layer first? Say layers one and three, because they only touch your own code, change no product promise, and need no quality re-validation, whereas tiered routing needs an eval set and rate limiting needs product buy-in.

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

    1. 这题最容易脱口而出的答案是「换个便宜模型」,也是最容易被追死的答案——面试官紧跟着就问「你怎么知道换了质量不掉」,答不出离线评估集和对比实验就露馅了。正确的第一句是「先看台账」:按用户、按天、按模型各切一刀,找出是哪一维在涨。先定位再动手,这是工程习惯。
    2. 第二步是把基准摆到桌上,没有基准的成本讨论全是废话。单轮 2000 输入加 500 输出,输入 2000 除以一百万乘 0.15 等于 0.0003 美元,输出 500 除以一百万乘 0.60 也等于 0.0003 美元,一轮约 0.0006 美元;日活 1 万、人均 5 轮就是 5 万轮,一天约 30 美元、一个月约 900 美元。
    3. 然后给五层,排序的依据是**你要付出的代价从小到大**,不是省钱多少:缓存与 prompt cache、模型分级路由、上下文压缩、步数与工具预算上限、限流与降级。这个顺序本身就是答案的一部分,因为它同时说明了你的落地顺序。
    4. 每层配一个从基准推出来的数字。缓存按一成半命中估,900 降到 765 左右。分级路由要诚实说清前提:它是唯一能改数量级的一层,但前提是你的基准用的是旗舰模型;基准已经是最便宜那档时这一层榨不出东西——主动说破这一条,比硬编一个省钱比例可信得多。上下文压缩把输入从 2000 压到 1200,单轮变成 0.00048 美元,一个月 720 美元,降两成。
    5. 第四层最容易被讲成「省钱」,其实它买的是**可预测**:给每个子任务设 5 次工具调用上限之后,单轮成本才有上界——调满 5 次、每次结果回灌 800 token,输入涨到 6000,单轮 0.0012 美元,正好是基准的两倍;没有上限时这个数字没有上界。这一层的正确说法是「我不是靠它省钱,我是靠它让账单可以被预测」。
    6. 第五层是限流与降级,代价最大所以放最后:900 美元摊到 1 万日活是每人每月 0.09 美元,给单用户设 1 美元硬顶,正常用户碰不到,挡的是脚本刷接口那种极端户。要点是超预算先降档再拒绝,而不是直接拒绝——它是五层里唯一用户能感觉到的一层。
    7. 可以预期的追问:这五层里哪一层最先做?答「第一层和第三层」,因为它们只改自己的代码、不动产品承诺、也不需要重新验证质量;而分级路由要配离线评估集,限流要配产品沟通,都不是当天能上的。

    Key points

    • Open with 'look at the ledger', not 'use a cheaper model': slice by user, by day and by model to locate the growth
    • Set a baseline: about $0.0006 per turn, roughly $30/day and $900/month at 10k DAU and five turns
    • Five layers ordered by cost to you: caching and prompt cache, tiered routing, context compression, step and tool budget caps, rate limiting and degradation
    • Tiered routing is the only order-of-magnitude lever, but only if the baseline is a flagship model — say so when it is not
    • Compression from 2000 to 1200 input tokens gives $0.00048 per turn, about $720/month, a 20% cut
    • Tool budget caps buy predictability: with a cap the per-turn ceiling is $0.0012, without one there is no ceiling
    • Rate limiting comes last because users feel it; degrade before refusing

    答题要点

    • 第一句不是「换便宜模型」,是「先看台账」:按用户、按天、按模型各切一刀定位是哪一维在涨
    • 先立基准:单轮约 0.0006 美元,日活 1 万人均 5 轮约每天 30 美元、每月 900 美元
    • 五层按代价从小到大:缓存与 prompt cache、模型分级路由、上下文压缩、步数与工具预算上限、限流与降级
    • 分级路由是唯一能改数量级的一层,但前提是基准用的是旗舰模型;基准已经最便宜时要诚实说没得省
    • 上下文压缩把输入从 2000 压到 1200,单轮 0.00048 美元、每月 720 美元,降两成
    • 工具预算上限买的是可预测:有上限时单轮上界是 0.0012 美元,没上限时没有上界
    • 限流降级放最后,因为它是唯一用户能感觉到的一层;超预算先降档再拒绝

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#resume

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题在考「你会不会控制信息密度」,不是考你记不记得 STAR 四个字母。面试官已经听过几十遍 STAR,他真正在数的是:你花了多少时间讲背景、多少时间讲你自己做了什么、最后有没有一个能被追问的数字。
    2. 先定时间分配再开口,这是可以现场执行的一条纪律:情境 20 秒、任务 15 秒、行动 90 秒、结果 25 秒。绝大多数人的失败模式是情境讲了 90 秒——那部分听起来最安全,因为不涉及你的能力,所以人会不自觉地躲在那里。
    3. 行动那 90 秒里只讲你亲手做的部分,主语必须是「我」。团队做了什么用一句话带过,然后立刻切回「我负责的是其中的 X,我的做法是 Y」。说不清「我和别人的边界在哪」,这条经历在评分表上会被打成不可验证。
    4. 结果必须落到一个带前后对照的数字,并且主动补一句测量条件。比如「分片利用率从 256 个里只占 1 个变成全占满,最大桶从 2000 条降到 19 条,这是本地单机、2000 个模拟用户、256 个分片的自检结果」。补测量条件不是示弱——它把「我知道自己测的是什么」这件事直接摆出来了。
    5. 如果这段经历是学习项目或课程项目,在情境那 20 秒里就说清楚,不要等到被追问。主动交代来源的人,后面报的数字反而更容易被相信;藏着掖着被问出来,之前讲的全部要被重新掂量一遍。
    6. 可以预期的追问:「这个数字是怎么测的?」以及「如果规模再大十倍,这个做法还成立吗?」第一个考真实性,第二个考边界感——答第二个时要主动说出「在什么规模下我会推翻现在这个设计」,这一句几乎没人说,说了就是加分。

    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 秒,别让背景吃掉一半时长
    • 行动部分主语是「我」,明确说出自己和团队的边界
    • 结果给一个带前后对照的数字,并主动补上测量条件
    • 学习项目在情境阶段就主动交代,不等追问
    • 结尾主动加一句「在什么规模下这个设计会失效」,把边界感摆出来
  • Tell me about the most challenging project you have worked on. What made it hard?讲讲你做过的最有挑战的一个项目,难在哪里?
    Common in ChinaCommon overseasDeep dive#behavioral#project-storytelling

    How to reason about it · think before answering

    1. The hinge is the word 'challenging', and almost everyone falls into the same trap: treating 'a lot of work' as a challenge. Three months of overtime proves stamina, not judgment. The interviewer wants to see how you decide under incomplete information.
    2. Pick the story first, because it caps everything after it: choose the project where you can name what you gave up. Hard test — if your answer is only 'I did A and it worked', with no 'I chose A over B and paid C for it', pick a different project.
    3. Order the answer as difficulty, decision, cost, outcome — not chronologically. Chronology drags the listener through a diary; leading with the difficulty pins their attention on the first sentence. 'With three replicas consuming in parallel, one user's messages arrived out of order' beats 'the project started in March'.
    4. When describing the difficulty, explain why it could not be solved by reading the docs. Real challenges carry conflicting constraints: parallel replicas for throughput versus strict per-user ordering. Surfacing that conflict is what makes the difficulty credible.
    5. Do not end on pure success. Volunteer one sentence on what you would change today — this is the highest-signal line available on this question, because it shows you kept thinking after shipping.
    6. Expect: 'did you consider alternatives?' It is nearly guaranteed, so prepare the option you rejected and an engineering reason for rejecting it — latency, cost, operational load — never 'it just felt wrong'.

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

    1. 这题的题眼在「挑战」这两个字上,而它有一个几乎所有人都会踩的陷阱:把「工作量大」当成挑战。加了三个月班、写了两万行代码,这些证明的是耐力,不是判断力。面试官想看的是你在信息不足的情况下怎么做决定。
    2. 先做选题,这一步决定了后面的天花板:选那个**你能说出「我放弃了什么」**的项目。判据很硬——如果你的答案里只有「我做了 A,效果很好」,没有「我在 A 和 B 之间选了 A,代价是 C」,那这个项目就不适合回答这道题,换一个。
    3. 组织顺序建议用「困难 → 我的判断 → 代价 → 结果」,而不是时间顺序。时间顺序会把听众拖进流水账;从困难切入,第一句话就把对方的注意力钉住了。比如「三个副本同时消费的时候,同一个用户的消息顺序会乱」,比「这个项目是三月份开始的」有效得多。
    4. 描述困难时要给出「为什么这不是查一下文档就能解决的」。真正的挑战都带着约束冲突:既要多副本并行提高吞吐,又要同一用户严格保序——这两个诉求天然打架,所以才需要设计而不是查资料。把这层冲突讲出来,难度就立住了。
    5. 结果那部分不要只报成功。**主动说一句「现在回头看,我会改哪里」**,这是这道题上区分度最大的一句话。它表明你在项目结束之后还继续想过这件事,而不是交付完就翻篇了。
    6. 可以预期的追问:「当时有没有考虑过别的方案?」这几乎是必问。所以准备答案时要备好那个被你放弃的方案,以及放弃它的具体理由——理由要是工程性的(延迟、成本、运维复杂度),不能是「感觉那样不好」。

    Key points

    • Challenge means judgment, not volume — overtime and lines of code are not difficulty
    • Pick a story where you can say 'I chose A over B and paid C for it'
    • Structure it as difficulty, decision, cost, outcome — never as a chronological diary
    • Name the conflicting constraints (parallel replicas versus strict per-user ordering) to make the difficulty real
    • Close with what you would change today, and have the rejected alternative plus an engineering reason ready

    答题要点

    • 挑战 = 判断力,不是工作量;别拿加班和代码行数当难度
    • 选题判据:这个项目你能说出「我在 A 和 B 之间选了 A,代价是 C」
    • 按「困难 → 判断 → 代价 → 结果」组织,不要按时间顺序讲流水账
    • 把约束冲突讲出来(比如既要多副本并行、又要同一用户保序),难度才立得住
    • 结尾主动说「现在回头看我会改哪里」,并备好那个被放弃的方案和工程性的理由
  • Suppose I open one of your GitHub projects — what should a good README show me?我们点开了你 GitHub 上的项目,你觉得一份好的 README 应该让我看到什么?
    Common in ChinaCommon overseasIntermediate#behavioral#documentation#portfolio

    How to reason about it · think before answering

    1. On the surface this is about documentation conventions; underneath it tests reader awareness. Reciting a list of headings sounds like a template. They want to hear that you know who the reader is, how much time he has, and what he is looking for.
    2. Define the reader before listing sections: someone skimming a README has about three minutes and has no intention of cloning the repo. So the first screen must answer 'what is this' and 'can it run'; everything deep goes below.
    3. Then give the structure, naming the reader each part serves: one-line positioning (the resume screener), architecture diagram (anyone building a mental model), quick start (anyone verifying it runs), key design decisions (the interviewer), known limitations (the interviewer), directory guide and license (people who will actually read the code).
    4. Put the weight on two sections. Quick start has a hard bar — running in three commands or fewer; more than that means hidden setup. Verify it on a machine that has never run the project, not on your own. Key design decisions must each state what you gave up, because that is the one part no template can supply.
    5. Known limitations deserve a sentence of their own: stating boundaries is not exposing weakness, it demonstrates self-awareness and honesty at once. And once you have said it, it is hard to use against you — at most they ask which gap you would close first, which you already prepared.
    6. Expect the follow-up: 'which section took you the longest?' Answer 'key design decisions' and then walk through one on the spot. The question is nominally about READMEs, but it is an invitation to talk about your project — take it.

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

    1. 这题表面在问文档规范,实际在考「你有没有读者意识」。答成一串小标题清单(简介、安装、使用、贡献指南)会显得像背模板;面试官想听的是你知道读者是谁、他有多少时间、他在找什么。
    2. 先把读者说清楚再列结构,这一步就能拉开差距:看 README 的人预算大约三分钟,而且不打算 clone 下来跑。所以第一屏必须解决「这是什么」和「能不能跑」,深入的东西往后放。
    3. 然后给结构,并且为每一段说出它服务的是哪个读者:一句话定位(筛简历的人)、架构图(想快速建立心智模型的人)、快速开始(想验证能不能跑的人)、关键设计决策(面试官)、已知限制(面试官)、目录导读与许可(真的要读代码的人)。
    4. 重点落在两段上。「快速开始」的硬指标是三条命令之内跑起来,超了说明有隐性依赖;判据是拿一台没跑过的机器照着敲一遍,而不是在自己机器上试。「关键设计决策」每条要含「放弃了什么」,因为这是唯一无法从模板抄来的部分。
    5. 「已知限制」值得单独说一句:主动写出边界不是暴露短板,而是同时证明了自我认知和诚信。而且你先说了,对方就很难再拿它当把柄,最多顺着问「上生产你会先补哪个」——那是你准备好的题。
    6. 可以预期的追问:「你的项目 README 里最花时间的是哪一段?」答「关键设计决策」,然后现场讲一条。这题问的是 README,落点其实是让你讲项目,别错过这个递过来的机会。

    Key points

    • Start from the reader: a three-minute budget and no intention of cloning, so the first screen answers what it is and whether it runs
    • Seven sections: one-line positioning, architecture diagram, quick start, three key design decisions, known limitations, directory guide, license
    • Quick start must work in three commands or fewer, verified on a machine that has never run it
    • Every design decision states what was given up — the one part no template provides, and where interviewers pick their follow-up
    • Use Mermaid rather than screenshots: native GitHub rendering, text diffs, and it does not go stale

    答题要点

    • 先说读者:三分钟预算、不会 clone 下来跑,所以第一屏解决「是什么」和「能不能跑」
    • 七段结构:一句话定位、架构图、快速开始、关键设计决策 3 条、已知限制、目录导读、许可
    • 快速开始的硬指标是三条命令之内,且要在一台没跑过的机器上验证
    • 关键设计决策每条含「放弃了什么」,这是唯一抄不来的部分,也是面试官挑追问的地方
    • 架构图用 Mermaid 而不是截图:GitHub 原生渲染、改动是文本 diff、不会过期
  • Have you applied overseas? How does an English tech resume differ from a Chinese one?你投过海外岗位吗?英文简历和中文简历在写法上有什么不同?
    Common in ChinaCommon overseasBasic#behavioral#resume#global-market

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题看着像常识题,区分度藏在「你是真投过还是听说过」。只答「英文简历要简洁」是听说过;答得出「哪些东西在英文简历里绝对不能出现,以及为什么」的,才像真做过。
    2. 拆成两半答,顺序是「不该有的」在前、「该怎么写」在后。因为前者是硬约束,后者是风格偏好,先说硬的显得你分得清轻重。
    3. 不该有的那一半:不放照片、不写年龄和出生日期、不写性别、不写婚姻状况、不写身份证与户籍、不写期望薪资。原因要说到点子上——在美加英等地,招聘方为了规避雇佣歧视方面的法律风险,收到这些信息反而为难。说出「这是对方的合规顾虑」而不是「国外习惯这样」,是这题最能体现认知深度的一句。
    4. 该怎么写的那一半是五条格式硬要求:一页、反向时序、每条动词开头、每条带量化结果、技术栈单列一行。动词开头要给正反例——Built / Designed / Reduced / Cut 是对的,Responsible for、Helped with、Familiar with 是三个要避开的开头,因为它们分别在描述职责、描述协助、描述认知,都不是你的贡献。
    5. 补一条很多人漏掉的:单位和货币要写全(写 p95 latency 320 ms 而不是「延迟 320」,写每轮 0.0006 美元而不是「一轮 0.0006」)。海外面试官对量纲敏感,缺单位的数字他判断不了好坏。时态上也要一致:结束的项目用过去时,在推进的用现在时。
    6. 可以预期的追问:「你的英文简历是自己写的还是翻译的?」老实答自己写的,并说出你为此做的一个具体动作——比如把中文那份里的形容词全删掉之后重写,因为直译过来的形容词在英文里会显得空。

    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#career

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题看着像常识题,区分度其实在于你有没有真的按流程准备过。只答「海外有 behavioral、国内有八股」是在复述听说,面试官听不出你排练过。
    2. 先给结构这条主线,其余差异都是它的推论:国内通常是一天之内两到三轮,同一批人越问越深,单轮 60 分钟出头切成五段——自我介绍 3 分钟、项目深挖 25 分钟、手撕代码 20 分钟、场景与八股 10 分钟、反问 5 分钟;海外是拉长到几周的五个独立环节——recruiter screen 30 分钟、technical/coding 60 分钟、system design 60 分钟、behavioral 45 分钟、team match,每一环由不同的人负责,各判各的,最后合票。
    3. 由结构推准备策略,这一步才是答案的价值所在:同一批人越问越深,意味着国内的胜负手在项目深挖那 25 分钟,要练的是被追问三层还答得上;独立环节合票意味着海外任何一轮都能单独把你否掉,所以短板比长板重要,尤其是多数人从没排练过的 behavioral。
    4. 第三条差异是评价载体:国内更依赖面试官当场的主观印象,海外多数公司有结构化的评分维度和书面反馈,所以「边写边讲」「主动说出取舍与失败模式」这类能被写进反馈的行为,在海外权重更高。
    5. 要主动澄清一个常见误区:差异不是「海外不考算法」。coding 那 60 分钟照样是算法题,区别在于它明确要求你全程出声,沉默本身就会被扣分。
    6. 可以预期的追问:那准备时间怎么分配?答共同部分先练——项目深挖和系统设计两套流程都要考,投入产出比最高;剩下的按目标市场补,投海外就补 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 个可复用故事)
    • 海外更依赖结构化评分与书面反馈,所以边写边讲、主动说取舍这类可被记录的行为权重更高;但算法一样要考
  • What most commonly goes wrong in the self-introduction, and what does a good one look like?自我介绍环节最容易出的问题是什么?一段好的自我介绍应该长什么样?
    Common in ChinaCommon overseasIntermediate#self-presentation#communication

    How to reason about it · think before answering

    1. Start from what the interviewer is doing during those three minutes: judging whether you can structure a piece of speech unaided, and deciding which project to spend the next twenty-five minutes on. Once you see the second one, the answer stops being 'keep it short'.
    2. The usual failures share one root cause: telling it chronologically. Starting at university and reading the resume top-down means the three minutes expire before you reach the recent work, which is the only part anyone wants to hear.
    3. That gives the correct shape: reverse order, three blocks only — what kind of engineer you are now, one or two signature pieces of work with a number attached, and why this role. Land the main line in ninety seconds and leave room for follow-up rather than filling the slot. The silence is leverage, not waste.
    4. The second frequent failure is adjectives with no numbers. 'I built a high-performance agent service' carries almost no information; a sentence with a constraint, a goal, an action, and a metric moved from X to Y is what makes someone ask the next question.
    5. The third is the subtlest: seeding things you do not want to be asked about. Every technology you name is an invitation, so leave unfamiliar stacks out — and conversely, plant the topics you want to be asked about, since this is the only moment in the loop where you set the agenda.
    6. Expect this follow-up: if the interviewer cuts in with 'just briefly', you have already run long or drifted. Rehearse two versions, sixty and ninety seconds, and switch between them rather than compressing live — live compression usually deletes the conclusion too.

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

    1. 先看清面试官在这 3 分钟里做什么:一是看你能不能自己组织一段有结构的表达,二是决定接下来 25 分钟挖你哪个项目。看懂第二件事,答案就不是「讲短一点」这么浅了。
    2. 最容易出的问题有一个统一的根因——按时间顺序讲。从大学讲起、顺着简历从上往下念,于是 3 分钟到点时你还没讲到最近、最有价值的那段经历,而那恰恰是唯一有人想听的部分。
    3. 由此推出正确形态:倒序,只留三块——你现在是什么方向的工程师、一到两个带数字的代表作、你为什么来面这个岗位。90 秒讲完主线,把剩下的时间让给对方追问,而不是把 3 分钟填满。留白是主动权,不是浪费。
    4. 第二个高频问题是通篇形容词、没有一个数字。「我做过一个高性能的 Agent 服务」几乎不携带信息;换成一句带约束和指标的话(在什么约束下、为了什么目标、做了什么、把哪个指标从多少改善到多少),才会让对方接着问下去。
    5. 第三个问题最隐蔽:自我介绍里埋了自己不想被问的东西。你说出口的每一个技术名词都是一张邀请函,不熟的栈别写也别说;反过来,希望被问的点要主动埋进去,这是全场唯一由你控制议题的机会。
    6. 可以预期的追问:面试官打断你说「再简单说一下」,说明你已经超时或跑题了。所以要提前排练两个版本,一个 60 秒、一个 90 秒,现场直接切,不要临场压缩——临场压缩的结果通常是把结论也一起删掉了。

    Key points

    • The interviewer is doing two things at once: assessing structure and choosing which project to dig into
    • The common failure is chronological order, which burns the clock before reaching recent work; reverse it
    • Keep three blocks: current engineering focus, one or two signature results with numbers, and why this role
    • Landing the main line in ninety seconds and leaving room for follow-up beats filling all three minutes
    • Every technology you name is an invitation: omit unfamiliar stacks, plant the topics you want asked, and rehearse a sixty-second and a ninety-second version

    答题要点

    • 面试官在这 3 分钟里同时做两件事:判断你的表达结构,决定接下来挖哪个项目
    • 最常见的错是按时间顺序讲,时间用完还没讲到最近最有价值的经历;正确做法是倒序
    • 结构只留三块:现在的技术方向、一到两个带数字的代表作、为什么来面这个岗位
    • 90 秒讲完主线、主动留白给对方追问,比把 3 分钟填满更有利
    • 每个说出口的技术名词都是邀请函:不熟的不提,想被问的主动埋进去;提前排练 60 秒和 90 秒两个版本
  • After a mock interview, how do you assess yourself objectively instead of settling for 'that felt okay'?一次模拟面试之后,你怎么做一次客观的自我评估,而不是停在「感觉还行」?
    Common in ChinaCommon overseasIntermediate#self-assessment#deliberate-practice

    How to reason about it · think before answering

    1. This asks whether you have engineered your practice. 'Record it and listen again' is the passing floor; the discriminator is a repeatable rubric plus thresholds fixed in advance, because without a rubric two sessions are not comparable and improvement is unmeasurable.
    2. Objectivity requires reviewable evidence, so fix three things first: record audio or screen throughout, run the real time boxes, and score against the recording afterwards rather than on feeling at the buzzer. Self-assessment is at its most distorted in the minutes right after you finish.
    3. Then replace overall impression with fixed dimensions: self-introduction, project depth, coding, system design, and communication plus candidate questions, each scored 1 to 5 for a total of 25. What makes it work is writing anchor descriptions for what a 1, a 3, and a 5 look like — otherwise the same '4' means different things in different sessions.
    4. Set thresholds before scoring, which is the only defense against rationalizing afterwards: any dimension below 3 goes on the weakness list, and a total below 18 means rerunning the whole loop two days later instead of pressing on.
    5. The final step carries all the value: translate low scores into four columns — observation, root cause, smallest drill for tomorrow, and how to verify. An observation has to be a fact you can point at in the recording, with a timestamp and the actual words: 'explained it badly' does not qualify, 'eight minutes into the project story and still had not said what I personally did' does. The drill must fit in one day, and verification must be observable, ideally with a numeric bar.
    6. Expect the follow-up: how do you generate follow-up questions alone? Use a model as the interviewer, but write the interrogation rules into the instructions first — one question at a time, three consecutive layers of follow-up grounded in what you just said, and no praise, no evaluation, no supplying the answer. Without those rules it degrades into an encouraging assistant, which defeats the point.

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

    1. 这题在考你有没有把练习工程化。答「录下来多听几遍」只是及格线,真正的区分度在于有没有可重复的评分口径和事先定好的阈值——没有口径,两次模拟之间就没法比较,也就谈不上进步。
    2. 客观的前提是有可回放的证据,所以先固定三件事:全程录音或录屏、按时间盒计时、事后对着回放打分而不是结束时凭感觉打。刚讲完的十几分钟里自我评价偏差最大,讲得顺就全盘肯定,卡过一次就全盘否定。
    3. 然后用固定维度代替整体印象:自我介绍、项目讲解深度、编码、系统设计、沟通与反问,各 1 到 5 分,满分 25。关键是每个维度要写好 1 分、3 分、5 分各长什么样的锚点描述,否则同一个「4 分」在两次之间根本不是同一件事。
    4. 阈值要在打分之前定好,这是防止事后给自己找理由的唯一办法:任何单项低于 3 分就进弱项清单,总分低于 18 分就隔两天把整套流程重跑一次,而不是硬着头皮往下走。
    5. 最后一步才是全部价值所在——把低分翻译成四列:现象、根因、最小动作、怎么验证。现象必须是回放里能指着看的事实(带时间、带原话),「讲得不好」不算,「项目讲解到第 8 分钟还没说到我做了什么」才算;最小动作必须一天内做得完;验证必须可观察,最好带数字门槛。
    6. 可以预期的追问:一个人怎么产生追问?用大模型当面试官,但必须先把追问纪律写进指令——一次只问一个问题、基于我的回答连追三层、全程不评价不夸奖不给答案。不写纪律,它会退化成一个不停鼓励你的助手,那就失去了模拟的意义。

    Key points

    • Evidence before judgment: record throughout, run real time boxes, and score against the replay rather than on feeling at the buzzer
    • Use five fixed dimensions (intro, project depth, coding, system design, communication and candidate questions) scored 1 to 5 out of 25, each with anchors for what 1, 3 and 5 look like
    • Fix thresholds before scoring: any dimension below 3 goes on the weakness list, a total below 18 means rerunning the loop two days later
    • Translate low scores into four columns — observation, root cause, smallest drill, verification — where the observation is a pointable fact and the drill fits in one day
    • Practicing alone, use a model as interviewer but write the rules first: one question at a time, three layers of follow-up, no praise, no evaluation, no answers

    答题要点

    • 先有证据再有判断:全程录音或录屏、按时间盒计时、事后对着回放打分,不在结束当场凭感觉打
    • 用固定五个维度(自我介绍、项目讲解深度、编码、系统设计、沟通与反问)各 1 到 5 分、满分 25,并给每个维度写 1/3/5 分的锚点描述
    • 阈值先定后打:任何单项低于 3 分进弱项清单,总分低于 18 分隔两天重跑整套流程
    • 把低分翻译成四列:现象、根因、最小动作、怎么验证;现象必须是回放里能指着看的事实,动作必须一天内做得完
    • 一个人练时用大模型当面试官,但要先写死追问纪律:一次一问、连追三层、不评价不夸奖不给答案

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#concurrency

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题在考「你知不知道限流有多种语义」,而不是「你会不会写计数器」。只答出一种算法的人,会被默认没做过真正的流量治理。
    2. 先把四种按复杂度排开再逐个给弱点:固定窗口最省内存但有边界双倍;滑动窗口日志最精确但内存和请求数同阶;滑动窗口计数是近似解、内存回到常数;令牌桶允许突发、内存常数。这个排列顺序本身就是答案的骨架。
    3. 边界双倍要用具体数字讲,它是本题最常见的追问:限每分钟 100 次,用户在 12:00:59 打满 100 次,12:01:00 计数器清零又能打 100 次,跨边界的这 2 秒实际放行了 200 次。说不出这个例子,等于没答第一问。
    4. 结论选令牌桶,理由要落在业务形状上:真实流量本来就是突发的,令牌桶同时约束了长期速率(补充速度)和瞬时突发(桶容量),两个旋钮分别对应两个业务问题。实现上必须是惰性补充——取的时候按时间差现算,不要给每个用户起一个定时器,十万用户就是十万个定时器。
    5. 生产视角:单机内存版只在单实例下成立。多个网关实例共享配额时,「读余额 → 算补充 → 写回」三步之间一定有竞态,两个实例都读到「还剩 1 个」就会双双放行。修法是把三步塞进一段 Redis Lua 脚本,靠单线程执行整段脚本拿到原子性——用 Lua 不是为了快,是为了把三条命令粘成一条。
    6. 可以预期的追问:脚本里为什么不直接取当前时间?因为那会让脚本变得不确定,时间戳应该由调用方传进来;代价是各实例的时钟要大致对齐,这个取舍要主动说出口。

    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 一定有竞态;时间戳由调用方传入以保持脚本确定性
  • How would you build a scheduler that caps in-flight async tasks, and why is Promise.all or asyncio.gather not enough?怎么实现一个限制并发数的调度器?为什么不能直接用 Promise.all 或者 asyncio.gather?
    Common in ChinaCommon overseasIntermediate#concurrency#async

    How to reason about it · think before answering

    1. The hinge is the second half. They are checking whether you separate 'await a batch' from 'cap how many run at once' — similar API names, unrelated semantics.
    2. Name the wrong answer first: mapping 500 items to promises and awaiting them together runs at concurrency 500. Creating the promise already fired the request; awaiting only collects results. gather and CompletableFuture.allOf are the same trap in other accents.
    3. Then give the two correct shapes: a fixed set of workers pulling from a shared cursor (the JS idiom, where a worker is the slot), or a semaphore gating task start (asyncio.Semaphore, java.util.concurrent.Semaphore). Swift needs a manual window over a TaskGroup — fill limit slots, then add one task per result received.
    4. The real failure mode is slot leakage: release must happen in a finally, or the error must be collapsed into a result value inside the task. Code that misses this looks perfect on the happy path and only degrades once the downstream starts failing, which makes it one of the hardest bugs to trace.
    5. Tie it to agents: batch embedding, parallel tool calls, fan-out subtasks. The benefit is not only sparing the downstream — peak memory now scales with the concurrency limit instead of the task count.
    6. Expect the follow-up: what if tasks retry? Retries must happen inside the slot, otherwise a retry storm bypasses the limiter entirely. One level deeper: add jitter so failed tasks do not all come back at the same instant.

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

    1. 题眼在后半句。面试官在确认你分不分得清「等待一批任务」和「限制同时运行的任务数」——这两件事在 API 名字上很像,在语义上毫无关系。
    2. 先说破错误答案为什么错:把 500 个任务全部映射成 Promise 再一起 await,这段代码的并发度是 500。Promise 一被创建,它内部的请求就已经发出去了,await 只是在等结果;gather 和 CompletableFuture.allOf 是同一个坑的另外两种口音。
    3. 再给正确形状的两条路:固定数量的工人从同一个游标取任务(JS 的惯用法,槽位就是工人本身),或者用信号量挡在任务启动之前(Python 的 asyncio.Semaphore、Java 的 Semaphore)。Swift 要用 TaskGroup 自己开滑动窗口,先塞满 limit 个、每收一个结果补一个。
    4. 本题真正的失分点是槽位泄漏:acquire 之后必须在 finally 里 release,或者把错误在任务内部收敛成结果值。忘了这一步的代码在 happy path 上完全正常,只有下游开始报错时才会一点点变慢直到彻底卡死——这是最难查的那类 bug,因为症状出现在故障之后而不是之中。
    5. 落到 Agent 场景说收益:批量 embedding、并行工具调用、多路子任务都靠它。收益不只是「不打爆下游」,还有同时驻留的内存与并发度同阶而不是与任务数同阶。
    6. 可以预期的追问:如果任务本身还要重试呢?答案是重试要在槽位内部完成(占着槽位退避重试),否则重试风暴会绕过限流;再追一层就是给重试加抖动,避免所有失败任务在同一时刻一起回来。

    Key points

    • Promise.all and asyncio.gather only wait; the work started when each promise was created, so concurrency equals the task count
    • Two correct shapes: a fixed worker set pulling from a shared cursor, or a semaphore gating task start
    • The slot must be returned on every exit path — finally in Java, async with in Python, error-to-value inside a Swift task, try/catch inside the JS loop
    • A leaked slot shows up as gradual slowdown to a full stall once the downstream starts erroring, and is invisible on the happy path
    • The payoff is peak memory scaling with the concurrency limit rather than the task count; retries must stay inside the slot and carry jitter

    答题要点

    • Promise.all 与 asyncio.gather 只负责等待,任务在被创建的那一刻就已经启动了,并发度等于任务总数
    • 两种正确形状:固定数量的工人从共享游标取任务,或者用信号量挡在任务启动之前
    • 槽位必须在任何退出路径上归还:Java 写在 finally 里,Python 用 async with,Swift 把错误收敛成结果值,JS 在循环里 try 与 catch
    • 槽位泄漏的症状是「下游一开始报错就越来越慢直到卡死」,happy path 完全看不出来
    • 收益是同时驻留的内存与并发度同阶,而不是与任务总数同阶;重试要占着槽位做,并加抖动
  • Why can't you just call JSON.parse in a streaming response, and how would you parse incrementally?为什么流式场景下不能直接用 JSON.parse?你会怎么做增量解析?
    Common in ChinaCommon overseasDeep dive#streaming#json-parsing

    How to reason about it · think before answering

    1. Almost all the signal is in your first sentence. Whoever starts writing a state machine will spend twenty-plus minutes on something probably buggy; whoever first asks 'is it one complete JSON per line, or one big object split across chunks?' has already won half the question.
    2. Answer the why first: network chunking ignores syntax boundaries, so a single read often holds half a JSON document. Handing that to a parser only throws, and the exception carries nothing you can recover from.
    3. Then draw the distinction. Case (a) is SSE: each event is one line prefixed with data, holding one complete JSON object. This covers 99% of LLM work, and the fix is line buffering plus per-line parsing — keep the trailing fragment in the buffer and stitch it onto the next chunk.
    4. Case (b) — one large object arriving in pieces — is the only case needing real incremental parsing, and it rests on three state variables: bracket depth (back to zero means a complete object), whether you are inside a string (brackets in text must not count), and whether the previous character was a backslash (an escaped quote must not toggle string state). Drop any one and text containing brackets breaks the depth count.
    5. One trap almost nobody volunteers: chunks are split on bytes, and a CJK character takes three bytes in UTF-8, so a boundary can land mid-character. Use a streaming decoder — TextDecoder with the stream option, an incremental decoder in Python, InputStreamReader in Java — or you get a replacement character you can never recover. It is the half-line problem one layer down.
    6. Expect the follow-up: what about the terminator line? It is not JSON, so check for it and return before parsing. Feeding it to the parser is the single most common one-line bug in this question.

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

    1. 这题的区分度几乎全在你开口的第一句话。听到「流式 JSON 解析」就动手写状态机的人,会花二十多分钟写一个大概率有 bug 的东西;先反问一句「是一行一个完整 JSON,还是一个大对象被切成很多片」的人,已经赢了一半。
    2. 先回答为什么不能直接解析:网络分包不认语法边界,一次读取拿到的很可能是半个 JSON。直接扔给解析器只会抛异常,而且这个异常没有任何可恢复的信息。
    3. 然后做那个关键区分。情况 a 是 SSE:每条事件是一行以 data 开头的文本,行内是完整 JSON,LLM 场景 99% 是这一种,解法是行缓冲加逐行解析,二十行代码——把切分出来的最后一段(可能是半行)留在缓冲区里,等下一次读到更多数据再拼。情况 b 是单个大对象跨分片到达,才需要真正的增量解析。
    4. 情况 b 的核心是三个状态变量:括号深度(深度归零说明一个完整对象结束)、是否在字符串内部(字符串里的括号不能计入深度)、前一个字符是不是反斜杠(转义中的引号不切换字符串状态)。三者缺一不可,少一个遇到含括号的文本就算错深度。
    5. 还有一条几乎没人主动说、但一说就加分的坑:分片是按字节切的,一个汉字在 UTF-8 里占三个字节,边界可能落在中间。必须用流式解码器(TextDecoder 的 stream 选项、Python 的增量解码器、Java 的 InputStreamReader),否则会拿到一个永远补不回来的乱码字符。这是「半行缓冲」在字节层的同款问题。
    6. 可以预期的追问:那结尾那个终止标记怎么办?答案是它不是 JSON,必须在解析前先判断并直接返回,拿它去解析必然抛异常——这是这道题里最常见的一行 bug。

    Key points

    • Network chunking ignores syntax boundaries, so a read can hold half a document; parsing it throws an unrecoverable error
    • Ask which case it is first: one complete JSON per line (SSE, the overwhelming majority of LLM work) or one large object split across chunks
    • The first case only needs line buffering plus per-line parsing, keeping the trailing partial line for the next chunk
    • Only the second case needs a state machine, tracking bracket depth, inside-string, and escaped-previous-character
    • One layer down, a multi-byte UTF-8 character can be split across chunks, so use a streaming decoder; and the terminator line is not JSON, so check for it before parsing

    答题要点

    • 网络分包不认语法边界,一次读取可能拿到半个 JSON,直接解析必然抛异常且不可恢复
    • 先问清是哪一种:一行一个完整 JSON(SSE,占 LLM 场景的绝大多数)还是一个大对象跨分片到达
    • 前者只需行缓冲加逐行解析:把最后一段可能的半行留在缓冲区,等下一次读到更多数据再拼
    • 后者才需要状态机,核心是括号深度、是否在字符串内部、前一个字符是否为转义反斜杠三个状态
    • 字节层还有一个同款坑:UTF-8 多字节字符可能被分片切开,必须用流式解码器;结尾的终止标记不是 JSON,解析前要先判断

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#prioritization

    How to reason about it · think before answering

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.

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

    1. 这题看着像闲聊,其实在考「你会不会做优先级」。面试官想听的不是勤奋,是判断:一周时间是固定的,你怎么决定把它花在哪。答「每天复习八小时,从头过一遍」就是没有判断。
    2. 先给一条可复用的推导:复习的边际收益取决于「这一块你现在离能讲清有多远」,所以任何计划的第一步都必须是**测量**,而不是学习。没测量就排计划,等于闭着眼睛分配预算。
    3. 落到具体做法:第一天只做分诊——把所有题目出声过一遍,按「能讲清 / 看一眼能讲 / 讲不出」标三种颜色,看到不会的立刻跳过。这一遍的产出是一张分布图,不是知识。第二、三天只碰后两类,第四天只碰仍然讲不出的,最后两三天留给模拟和表达。
    4. 要主动说出「只标记不纠结」这条纪律和它的失败模式:碰到第一道不会的题当场去补,一道题吃掉半小时,做到第 20 道今天就没了,剩下的题连颜色都没有。这个细节最能证明你真的这样练过。
    5. 再补一个判据:判断「会」的标准必须可证伪——出声、限时 90 秒、不看提纲。默读产生的流畅感是题库给的,不是你的。
    6. 可预期的追问:如果分诊发现红题集中在同一块怎么办?答案是先补那一块的**上游**概念,而不是逐题补——同一块里的题往往共用一个没吃透的前置,补上游一道题能带亮五道。

    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 秒内讲完、不看提纲,默读不算
    • 分诊的产出是一张分布图,它决定后面几天该补技术还是补表达
    • 红题扎堆时先补共同的上游概念,比逐题补效率高得多
  • How do you turn scattered knowledge into a map that is actually useful for review?怎么把零散的知识点组织成一张便于复习的知识地图?
    Common in ChinaCommon overseasIntermediate#knowledge-organization#interview-prep

    How to reason about it · think before answering

    1. The load-bearing phrase is 'useful for review'. Most answers become 'group things by module and draw a mind map', which produces a table of contents, not a map — a contents page cannot tell you what to fix first. That is where candidates separate.
    2. Break it down: a graph has nodes and edges. Grouping nodes is cheap and almost everyone does it correctly; the information lives in the edges. So ask yourself how many edges your diagram has and what each one means. No answer means you drew a contents page.
    3. Give an operational rule for edges: draw A to B only when not understanding A blocks understanding B. 'Both are about message queues' does not qualify — that is sibling grouping. 'You cannot understand context compression without the context window' does. Course order does not qualify either; that is a calendar, not a dependency.
    4. Conclusion: use the map by painting your weak spots onto it. If a node is shaky, check whether its upstream is shaky too — repair upstream and several downstream nodes light up at once. That is the map's one advantage over a checklist: a checklist says what is broken, a map says where to start.
    5. Production angle: the same habit pays off at work. When debugging an incident, the dependency graph in your head decides whose logs you open first; without it you probe services one by one. Saying this shows the map is a working tool, not an exam prop.
    6. Expect the follow-up: how big should it be? Small enough to redraw on a whiteboard in five minutes. Past that you start maintaining the map instead of using it — merge nodes into themes and leave the detail in your question bank.

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

    1. 题眼在「便于复习」四个字。绝大多数人答成「按模块分类、画个思维导图」,那产出的是目录不是地图——目录任何一本书的前几页都有,它不能告诉你先补哪里。区分度就在这儿。
    2. 怎么拆:一张图有两种元素,节点和边。分层(节点怎么分组)是廉价的、几乎人人做得对;真正的信息量在边上。所以先问自己一个问题——我这张图上有几条边,每条边的含义是什么?答不上来就说明画的是目录。
    3. 给一条可操作的连边判据:只有当「不懂 A 就学不懂 B」时才连 A 指向 B。「A 和 B 都属于消息队列」不算,那是同层并列;「不理解上下文窗口就理解不了为什么要压缩」算。课程的先后顺序也不算——那是日历,不是依赖。
    4. 结论:地图的用法是把你的弱点涂上去。某个节点讲不清,先看它的上游是不是也红——是的话补上游,一次带亮一串。这就是地图相对清单的唯一优势:清单说哪里错了,地图说该从哪儿开始。
    5. 生产视角:这套东西在工作里同样有用。排查一个线上问题时,你脑子里那张「谁依赖谁」的图决定了你先看哪个服务的日志;没有这张图的人只能一个个试。面试时把这个类比说出来,会显得你不是为了背题才画图。
    6. 可预期的追问:那张图应该多大?答案是能在白板上 5 分钟画完——超过这个规模你会开始维护它而不是使用它,节点合并成主题,细节留在题库里。

    Key points

    • Grouping into layers is what any table of contents does; a map's information is in its edges
    • One rule for edges: draw one only when A is a genuine prerequisite for B — sibling topics and course order do not count
    • Paint your weak spots on the nodes and fix upstream first when reds cluster; one fix lights up several downstream nodes
    • Long cross-layer edges are the valuable ones — following them in an interview shows a system, isolated nodes only produce fragments
    • Keep it redrawable on a whiteboard in five minutes; finer detail belongs in the question bank, not the map

    答题要点

    • 分层只是分组,任何目录都做得到;地图的信息量全部在边上
    • 连边的判据只有一条:不懂 A 就学不懂 B 才连边,同类并列和课程顺序都不算
    • 把弱项涂到节点上,红点扎堆时优先补上游节点,一次带亮一串下游
    • 跨层的长边最值钱,面试时顺着长边讲能体现体系,孤立节点只能给出零碎答案
    • 规模控制在白板 5 分钟能画完,再细的内容留在题库里而不是图上
  • Walk me through what you have been working on recently and why you moved toward agent engineering, in three to five minutes.用 3 到 5 分钟讲一下你最近这段时间的成长路径,以及为什么转向 Agent 工程。
    Common in ChinaCommon overseasDeep dive#self-introduction#storytelling

    How to reason about it · think before answering

    1. This opens almost every interview and it is the one question you can fully pre-write. It tests selection, not history: three minutes cannot hold a month, so which three things you pick reveals what you think matters. A week-by-week recital is the common failure — it hands the judgment back to the interviewer.
    2. Structure it as origin, turn, evidence, direction. Origin: one sentence on where you were and what capped you. Turn: the concrete problem that pushed you toward agents, not 'I believe in the space'. Evidence: whichever of your projects maps best onto this role, framed as an engineering problem you solved. Direction: the kind of team and problem you want next.
    3. The evidence part has a hard requirement: give something checkable. A repository link, numbers you measured yourself, and the conditions you measured them under. 'I built an agent platform' and 'I split gateway from worker behind a message bus, and with three local replicas, killing the lease holder lets another worker take over once the lease expires' differ by an order of magnitude in credibility.
    4. Hold the integrity line yourself: these are learning projects, say so, and attach measurement conditions to every number (single machine, mock mode). Never present them as company work or quote scale you never ran — two follow-up questions expose it, and that kind of exposure is unrecoverable.
    5. Common mistake: spending the three minutes on technical depth. The opener's job is not to explain anything fully, it is to shape which threads the interviewer pulls over the next forty minutes — so end each part on a deliberate hook, such as 'lease renewal had to be atomic, which took a script', and stop there.
    6. Expect the follow-up: why not stay on your previous track? Answer with a concrete blocker you kept hitting, not with industry trends. Everyone can recite a trend; naming a specific problem shows you reasoned your way here.

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

    1. 这是几乎每场面试的第一题,也是唯一一道你能完全预写的题。它考的不是经历,是**取舍**:3 分钟装不下一个月,你选了讲哪三件事,直接暴露你认为什么重要。流水账式的「第一周我学了……第二周我学了……」是最常见的失败,它把判断权交回给了面试官。
    2. 怎么拆:套一条「起点 - 转折 - 证据 - 去向」的四段结构。起点一句话说清你原来的位置和它的天花板;转折说清是什么具体问题把你推向 Agent,不要用「看好这个方向」这种空话;证据是三个产出物中最能对上这个岗位的那一个,讲清楚它解决了什么工程问题;去向说清你想在什么样的团队继续解决什么问题。
    3. 证据那一段有个硬要求:**给出可被验证的东西**。仓库链接、你实测出来的数字、以及数字的测量条件。同一句话讲成「做了一个 Agent 平台」和讲成「gateway 和 worker 拆开、用消息总线解耦,本地三副本下杀掉持有租约的 worker,另一个能在租约到期后接手」,可信度差一个量级。
    4. 红线要自己守住:这三个是学习项目,说的时候就要说明是个人项目,数字要带测量条件(本地单机、模拟模式压测)。**不要把它讲成公司经历,也不要报没跑过的规模数**——面试官追问两句就穿帮,而且是不可挽回的那种。
    5. 常见误区:把这 3 分钟用来讲技术细节。开场白的目标不是讲透任何东西,是让面试官在后面 40 分钟里想问哪几个点——所以每段末尾都要故意留一个可追问的钩子,比如「租约续约那里我们用了一个脚本保证原子性」,停在这儿别展开。
    6. 可预期的追问:为什么不是继续做原来的方向?答案要落到具体问题上(原来的场景里你反复遇到什么做不了的事),而不是行业趋势——讲趋势的人到处都是,讲具体问题的人显得是自己想清楚的。

    Key points

    • Build the three minutes from origin, turn, evidence and direction — never a week-by-week recital
    • Ground the turn in one concrete thing you could not do before, not in a belief about the market
    • Make the evidence checkable: repository links, numbers you measured, and the conditions behind them
    • State plainly that these are personal learning projects; never dress them as company work or quote unmeasured scale
    • End each part on a deliberate hook so the next forty minutes land where you are strongest

    答题要点

    • 用「起点 - 转折 - 证据 - 去向」四段撑起 3 分钟,不要按周流水账
    • 转折要落到一个具体的做不了的问题上,而不是「看好这个方向」
    • 证据段给可验证的东西:仓库链接、自己实测的数字、以及测量条件
    • 明确说明这是个人学习项目,绝不包装成公司经历、绝不报没跑过的规模
    • 每段末尾留一个可追问的钩子,把后面 40 分钟引到你准备最充分的地方