面试题库
共 328 题,当前筛选 4 题。
课程全部30 天从前端工程师到 Agent 工程师5 天提示词工程零基础Claude 高效使用:从对话到 Claude CodeCodex 与 OpenAI Agents SDK 高效使用7 天 MCP:把工具接进任何 Agent7 天 Agent Skills:把经验做成可复用能力5 天上下文工程14 天 RAG:从检索到可信回答14 天用 Agent 搭一条 AI 短剧生产线
标签
全部#rate-limiting4#evaluation30#cost29#reliability24#architecture22#agent-skills21#security18#observability15#streaming12#error-handling11#idempotency10#system-design10
还有 362 个标签收起标签
#api-design9#coding-agent9#distributed-systems8#multi-agent8#rag8#chunking7#debugging7#pipeline-design7#structured-output7#agent-loop6#mcp6#operations6#prompt-injection6#sse6#tool-calling6#tool-design6#context5#context-engineering5#deployment5#embeddings5#hybrid-search5#message-bus5#scheduling5#system-prompt5#agentic-rag4#behavioral4#client4#concurrency4#consistency4#framework-design4#ingestion4#prompt-engineering4#retrieval4#routing4#trade-offs4#abstention3#agents-sdk3#caching3#communication3#context-window3#cost-control3#data-quality3#failure-modes3#image-generation3#interview-prep3#langgraph3#latency3#llm-as-judge3#llm-basics3#long-context3#model-migration3#model-routing3#orchestration3#ordering3#progressive-disclosure3#prompt-basics3#prompt-caching3#protocol3#provider-abstraction3#ranking3#recall3#redis-streams3#responses-api3#resume3#retry3#runtime3#scalability3#skills3#state-management3#statelessness3#subagents3#timeline3#versioning3#workflow-engine3#access-control2#agent-design2#async-task2#auth2#checkpointing2#citation-verification2#citations2#claude-md2#code-review2#compaction2#context-rot2#contextual-retrieval2#cost-tradeoff2#data-modeling2#database2#distribution2#fallback2#ffmpeg2#golden-set2#grounding2#interview-process2#long-term-memory2#media-pipeline2#memory2#multi-hop2#multi-tenancy2#oauth2#prioritization2#project-storytelling2#prompt-techniques2#query-rewriting2#react2#refusal2#reproducibility2#rerank2#retrieval-quality2#retrospective2#scripts2#sharding2#state-machine2#tool-permissions2#tooling2#tools2#tracing2#transport2#tts2#ux2#vector-database2#verification2#workflow2#agent-basics1#agent-quality1#agent-sdk1#agents-md1#altitude1#analytics1#approvals1#architecture-review1#async1#atomicity1#attention-budget1#av-sync1#backoff1#bi-encoder1#bm251#budget-control1#build-vs-buy1#cancellation1#candidate-selection1#capacity-planning1#career1#chain-of-thought1#ci1#circuit-breaker1#claude-code1#cli-design1#client-integration1#cloud1#compliance1#compression1#configuration1#confused-deputy1#consistent-hashing1#content-hash1#content-safety1#context-assembly1#context-compression1#context-management1#copyright1#coreference1#correctness1#cost-accounting1#cost-analysis1#cost-optimization1#cross-encoder1#cross-model1#customer-support1#dag1#decision-making1#decomposition1#degradation1#deliberate-practice1#design1#diagnostics1#dimensions1#docker1#documentation1#embedding-migration1#encoding1#engineering-judgement1#engineering-tradeoffs1#error-propagation1#escalation1#eval1#event-driven1#evidence1#failure-analysis1#fairness1#faithfulness1#fan-out1#feedback-loop1#fencing-token1#few-shot1#filter-pushdown1#filtering1#fine-tuning1#forking1#four-elements1#framework-selection1#frontend1#global-market1#graph-rag1#guardrails1#hallucination1#handoff1#handoffs1#headless1#hnsw1#hooks1#human-in-the-loop1#hybrid1#hyde1#incremental-recompute1#incremental-sync1#index-maintenance1#index-routing1#indexing1#information-retrieval1#instruction-hierarchy1#integration1#intent-routing1#interrupt-merge1#invalidation1#isolation1#iterative-scan1#ivfflat1#json-parsing1#json-schema1#just-in-time1#jwt1#knowledge-organization1#labeling1#latency-budget1#lease1#least-privilege1#llm-output-quality1#long-session1#loop-guard1#maintenance1#mcp-basics1#mental-model1#messages-api1#metadata1#methodology1#metrics1#migration1#mobile1#model-selection1#moderation1#modularity1#multi-turn1#multimodal1#nodejs1#normalisation1#notifications1#ocr1#offline-testing1#openai1#overlap1#pagination1#parent-child1#pdf-parsing1#performance1#permissions1#persistence1#pgvector1#pipeline-reliability1#portfolio1#primitives1#priority-queue1#proactive-messaging1#product-engineering1#production-readiness1#prompt1#prompt-assembly1#prompt-bloat1#prompt-design1#prompt-limits1#prompt-surface1#prompt-template1#prompt-versioning1#prompting1#protocol-versions1#quality1#quality-check1#quantization1#query-transformation1#quiet-hours1#rag-basics1#rank-fusion1#reasoning1#reconnect1#redis1#reflection1#replay1#reporting1#retrieval-failure1#retrieval-metrics1#retry-semantics1#retry-strategy1#review1#risk-assessment1#rollback1#rollout1#rrf1#safety1#sampling1#sandbox1#sandboxing1#scaling1#schema-design1#schema-validation1#scoping1#secrets-management1#self-assessment1#self-introduction1#self-presentation1#self-reflection1#server-design1#service-architecture1#session-management1#sessions1#similarity1#skill-authoring1#skill-description1#skill-design1#spec1#split-brain1#stakeholder-communication1#star1#state-persistence1#stateless1#stdio-transport1#stopping-criteria1#storytelling1#subagent1#subscriptions1#subtitles1#task-graph1#team-governance1#test-set1#test-strategy1#testing1#thresholds1#timezone1#token-accounting1#token-budget1#tool-budget1#tool-execution1#tool-naming1#tool-schema1#trust-boundary1#tuning1#validation1#vector-index1#workflow-design1#xml-tags1#zero-downtime1
30 天从前端工程师到 Agent 工程师
D20 定时 Job 与主动关怀:时区、quiet hours、每日上限、通知 provider 抽象
quiet hours 和每日发送上限这两条规则你会怎么实现?它们应该放在链路的哪一步判断?How would you implement quiet hours and a per-user daily cap, and where in the pipeline should they be evaluated?
国内高频海外高频进阶#rate-limiting#quiet-hours#cost-control分析过程 · 先想清楚再作答
- 这题有两个题眼,很多人只答了前一个。第一个是「怎么判断」(细节题),第二个是「放在哪一步」(架构题),后者才是拿分点。
- 先讲 quiet hours 的判断。把时刻折成从午夜起算的分钟数之后,绝大多数人第一次都会写成 start 小于等于 now 且 now 小于 end。这对午休那种同日区间是对的,对 22:00 到 08:00 恒为 false——start 是 1320、end 是 480,条件永远不成立,于是半夜照发。正确写法是 start 小于 end 时用「且」,start 大于 end(跨午夜)时换成「或」。这个 bug 恶劣在只在跨午夜的配置上错,用 13:00 到 14:00 写的单元测试全绿。
- 接着是命中之后怎么办:推迟,不是丢弃。判据不该由发送方临时决定,而应该由消息自己带一个过期时刻——过期时刻早于窗口结束的丢弃,其余一律推迟到窗口结束。限时取消提醒过了今晚就没意义,账单提醒明早发一样有效。另外要提一句惊群:所有推迟的消息会算出同一个到期时刻,要加一个按用户标识哈希得出的抖动(不能用随机数,否则线上复现不了)。
- 再讲每日上限的两个细节。一是「一天」必须是**用户本地日历日**,写成 UTC 日的话东八区用户早上八点前发的会算进昨天的额度。二是必须先占坑再判断——原子自增拿返回值比上限,超了再把名额还回去;先查后写在并发下两条候选会同时读到同一个值然后一起发出去。渠道明确拒绝时也要把名额还回去。
- 最后是架构题那一半,也是最值钱的一段:三道闸必须在**生成内容之前**判断,不是在发送那一步。顺序错了程序照样跑通、发出的消息也一样,唯一区别是每条被拦下的消息你都已经付过一次模型调用的钱。按 1000 用户每天各 3 条候选、拦掉四成、单条约 0.00075 美元算,一个月白花约 27 美元,比这批用户的正常对话开销还高,而且监控上完全看不出来——只有把候选数和实际发送数并排摆出来才看得见差额。
- 可以预期的追问:三道闸内部谁先谁后?答时区、安静时段、每日上限,上限必须最后。因为被安静时段推迟的消息明早才发,不该占掉今天的名额;顺序反了用户会发现自己明明没收到几条却被限流了。
How to reason about it · think before answering
- There are two hinges here and most candidates only answer the first. One is how to evaluate the rules (a details question), the other is where in the pipeline (an architecture question) — the second is where the points are.
- Start with quiet hours. Once you fold times into minutes-from-midnight, almost everyone first writes start less-or-equal now and now less-than end. That is correct for a same-day window like a lunch break, but for 22:00 to 08:00 it is always false: start is 1320, end is 480, the condition never holds, and you push at 3am. The fix is to use and when start is before end, and or when start is after end. What makes this bug nasty is that it only misfires on the cross-midnight config, so a unit test written around 13:00 to 14:00 passes.
- Then what to do on a hit: defer, do not drop. The decision should not be the sender's mood — attach an expiry to each candidate and drop only when it expires before the window ends, deferring everything else to the window's end. A thirty-minute cancellation warning is worthless tomorrow; a billing summary is just as valid at 8am. Mention the thundering herd too: every deferred message resolves to the same due instant, so add jitter derived from a hash of the user id, never a random number, or you cannot reproduce incidents.
- Now two details on the daily cap. First, the day must be the user's local calendar day; keying on the UTC date charges an East-Asian user's 8am message to yesterday's budget. Second, increment first and check the returned value, then give the slot back if it exceeded — a read-then-write races, letting two candidates read the same count and both go out. Return the slot on a hard rejection from the channel as well.
- Finish with the architecture half, which is the valuable part: all three gates must run before the content is generated, not at the send step. Get the order wrong and the program still works and sends the same messages; the only difference is that you paid for a model call on every message you then threw away. At 1000 users, three candidates each per day, forty percent blocked and roughly $0.00075 per message, that is about $27 a month wasted — more than the normal conversational spend for the same cohort — and it is invisible in monitoring. Only putting candidate count next to sent count reveals the gap.
- Expect: what order do the three gates run in? Timezone, quiet hours, daily cap, with the cap last. A message deferred to tomorrow morning must not consume today's quota; reverse the order and users get rate-limited despite having received almost nothing.
答题要点
- 跨午夜的安静时段:start 小于 end 用「且」,start 大于 end 换成「或」,朴素写法对 22:00-08:00 恒为 false
- 命中安静时段是推迟到窗口结束而不是丢弃;只有自带的过期时刻早于窗口结束才丢
- 推迟会造成惊群,要加按用户标识哈希得出的抖动,不能用随机数
- 每日上限的「天」必须是用户本地日历日,不是 UTC 日
- 计数要先占坑再判断(原子自增后比上限,超了还回去),先查后写在并发下会超发
- 三道闸必须在生成内容之前判断,装晚了每条被拦的消息都已经付过模型调用的钱(示例量级约 27 美元每月);闸内顺序是时区、安静时段、每日上限,上限最后
Key points
- Cross-midnight quiet hours need or when start is after end; the naive and version is always false for 22:00-08:00
- On a hit, defer to the end of the window rather than drop; only drop when the message's own expiry precedes that
- Deferral causes a thundering herd, so add jitter hashed from the user id, never a random value
- The day in a daily cap must be the user's local calendar day, not the UTC date
- Increment atomically then compare and release on overflow; read-then-write over-sends under concurrency
- Run all three gates before generating content — otherwise every blocked message has already been paid for (about $27/month at the example scale); order them timezone, quiet hours, daily cap, with the cap last
D29 弱项补强 + 编码热身:rate limiter、LRU、并发控制、流式 JSON 解析
限流器有哪几种常见算法?各自的优缺点是什么?如果只能落地一种,你选哪个?What are the common rate limiting algorithms, what are their trade-offs, and which one would you actually ship?
国内高频海外高频基础#rate-limiting#concurrency分析过程 · 先想清楚再作答
- 这题在考「你知不知道限流有多种语义」,而不是「你会不会写计数器」。只答出一种算法的人,会被默认没做过真正的流量治理。
- 先把四种按复杂度排开再逐个给弱点:固定窗口最省内存但有边界双倍;滑动窗口日志最精确但内存和请求数同阶;滑动窗口计数是近似解、内存回到常数;令牌桶允许突发、内存常数。这个排列顺序本身就是答案的骨架。
- 边界双倍要用具体数字讲,它是本题最常见的追问:限每分钟 100 次,用户在 12:00:59 打满 100 次,12:01:00 计数器清零又能打 100 次,跨边界的这 2 秒实际放行了 200 次。说不出这个例子,等于没答第一问。
- 结论选令牌桶,理由要落在业务形状上:真实流量本来就是突发的,令牌桶同时约束了长期速率(补充速度)和瞬时突发(桶容量),两个旋钮分别对应两个业务问题。实现上必须是惰性补充——取的时候按时间差现算,不要给每个用户起一个定时器,十万用户就是十万个定时器。
- 生产视角:单机内存版只在单实例下成立。多个网关实例共享配额时,「读余额 → 算补充 → 写回」三步之间一定有竞态,两个实例都读到「还剩 1 个」就会双双放行。修法是把三步塞进一段 Redis Lua 脚本,靠单线程执行整段脚本拿到原子性——用 Lua 不是为了快,是为了把三条命令粘成一条。
- 可以预期的追问:脚本里为什么不直接取当前时间?因为那会让脚本变得不确定,时间戳应该由调用方传进来;代价是各实例的时钟要大致对齐,这个取舍要主动说出口。
How to reason about it · think before answering
- This question tests whether you know rate limiting has several distinct semantics, not whether you can write a counter. Naming only one algorithm reads as never having run real traffic.
- Lay the four out by complexity and attach a weakness to each: fixed window is cheapest but has the boundary burst; sliding window log is exact but its memory grows with request count; sliding window counter is an approximation with constant memory; token bucket allows bursts with constant memory. That ordering is the skeleton of a good answer.
- Make the boundary burst concrete, because it is the standard follow-up: with a 100-per-minute limit, a client can spend 100 at 12:00:59 and another 100 the instant the counter resets at 12:01:00 — 200 requests inside two seconds, double the quota.
- Pick the token bucket and justify it by traffic shape: real traffic is bursty, and the bucket gives you two independent knobs — refill rate caps the long-run rate, capacity caps the burst. Implement it with lazy refill: compute the top-up from the elapsed time when a token is requested, never run a timer per user.
- Production angle: the in-memory version only holds for a single instance. Across gateway replicas, read-compute-write has a race and two replicas can both see 'one token left' and both allow. Fix it with a Redis Lua script so refill and deduction happen in one atomic step — Lua is not for speed here, it is for gluing three commands into one.
- Expect the follow-up: why not read the clock inside the script? Because that makes the script non-deterministic. Pass the timestamp in from the caller, and say the cost out loud — replica clocks now have to be roughly aligned.
答题要点
- 四种算法:固定窗口(省内存但边界双倍)、滑动窗口日志(精确但内存与请求数同阶)、滑动窗口计数(近似、常数内存)、令牌桶(允许突发、常数内存)
- 固定窗口的边界双倍:跨窗口交界的 2 秒内可以放行两倍配额,下游是数据库或模型 API 时足以打穿
- 落地选令牌桶:补充速度管长期速率、桶容量管瞬时突发,两个旋钮对应两个真实业务约束
- 必须用惰性补充:取令牌时按时间差现算,不要为每个 key 起定时器
- 分布式版把补充与扣减写进一段 Redis Lua 脚本,先 GET 再 SET 一定有竞态;时间戳由调用方传入以保持脚本确定性
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
14 天用 Agent 搭一条 AI 短剧生产线
D9 并发与配额:多集同时开机,还不能把厂商额度打爆
多个任务共用一家厂商的额度,你会怎么设计限流?Many jobs share one vendor's quota. How would you design the rate limiting?
国内高频海外高频进阶#rate-limiting#concurrency#scheduling分析过程 · 先想清楚再作答
- 这题的题眼是「共用」两个字。只回答一个令牌桶算答了一半,面试官想听的是你按什么维度分桶、以及桶之外还需要什么。
- 先给维度:限流要按「厂商 + 接口类别」分桶,不能全局一个桶。同一家的图像和语音是两个独立的配额池,混在一起会让紧的那个把松的那个也拖住。
- 再给部件:一个桶不够,要两个。令牌桶管速率(一分钟发几次,数字是厂商定的),信号量管并发(同一时刻挂着几个,数字是你自己定的用来保护内存和钱包)。只有速率控制的话,快速返回的接口一分钟能发几百次;只有并发控制的话,二十个请求同时在飞会把内存挂满。
- 然后是关键的工程细节:拿许可的动作必须是非阻塞的。如果任务先被派出去、再在执行流里等令牌,工作槽会被一批低优先任务占死,优先级就静默失效了。正确结构是调度器派活之前先问闸门要许可,拿不到就跳过它去看下一个候选。
- 最后落到取值:并发上限不该按 CPU 核数定,这条线几乎没有本地计算,全在等网络;它该按「一次失败要重跑多少东西」和厂商配额来定。
- 可预期的追问是「桶的状态放哪」。单进程放内存就够;多进程要放 Redis,用一个原子脚本取令牌,否则每个进程各限各的,加起来照样超。
How to reason about it · think before answering
- The hinge is the word shared. Naming a token bucket only answers half of it; the interviewer wants your bucketing dimension and what else sits around the bucket.
- Dimension first: bucket per vendor and per API family, never one global bucket. Image and speech quotas at the same vendor are separate pools, and merging them lets the tight one throttle the loose one.
- Then the parts: one bucket is not enough. A token bucket caps rate (calls per minute, a number the vendor sets); a semaphore caps concurrency (how many are in flight, a number you set to protect memory and spend). Rate alone lets a fast endpoint fire hundreds per minute; concurrency alone lets twenty downloads pile up.
- The engineering detail that decides everything: admission must be non-blocking. If a job is dispatched first and then waits for a token inside the worker, low-priority work pins every worker slot and priority silently stops working. Ask the gate before dispatch, and skip to the next candidate when it says no.
- Finally the numbers: concurrency should not track CPU cores, since this pipeline is almost all network wait. Derive it from vendor quota and from how much work one failure forces you to redo.
- Expected follow-up: where does bucket state live. In-memory is fine for one process; across processes it belongs in Redis behind an atomic token-take script, or each process limits itself and the sum still blows the quota.
答题要点
- 按「厂商 + 接口类别」分桶,一家的图像和语音各一道闸门。
- 每道闸门两个部件:令牌桶控速率(厂商给的 RPM),信号量控并发(自己定的在飞上限)。
- 准入必须非阻塞,拿不到许可就把任务留在队列里,绝不占着工作槽干等。
- 先抢并发票再取令牌,顺序反了会白白扣掉配额。
- 多进程部署时桶的状态要外置到 Redis,用原子操作取令牌。
Key points
- Bucket per vendor and per API family; image and speech at one vendor get separate gates.
- Each gate has two parts: a token bucket for rate (the vendor's RPM) and a semaphore for in-flight concurrency (your own number).
- Admission is non-blocking: if the gate says no, the job stays queued instead of holding a worker slot.
- Take the concurrency slot before the token, or a rejected admission silently burns quota.
- Across processes, move bucket state to Redis and take tokens atomically.
收到限流响应之后,除了退避重试还该做什么?Beyond backing off and retrying, what else should happen when you get rate limited?
国内高频海外高频深入#rate-limiting#error-handling#retry分析过程 · 先想清楚再作答
- 这题在考你有没有真在生产里被限流打过。只答「指数退避加抖动」是标准答案的前半段,面试官等的是后半段。
- 先把限流摆正位置:它不是错误,是信号。它告诉你此刻的发送速率超过了厂商愿意接受的速率。既然是信号,就该有反馈动作,而不只是重试。
- 第一个动作是主动降速:把令牌桶罚一档,接下来一两个窗口只发一半令牌。不降速的话,退避结束后你会用同样的速度再撞一次,重试次数越多越糟。
- 第二个动作是别在退避里占着执行流。正确做法是把任务重新入队并记一个「不早于」时间戳,槽位立刻还回去给别的任务。
- 第三个动作是分类:限流和服务端错误可以重试,鉴权失败、余额不足、参数错误、内容审核不通过一次都不该重试——重试只会让你在一分钟里把同一个错误犯五遍,还白占配额。MiniMax 这边 1002 是限流、1039 是 TPM 维度的限流,1004 鉴权、1008 余额、2013 参数、1026 和 1027 是内容审核。
- 第四个动作是把限流次数记进指标。撞得多说明闸门配小了或者配大了,这个数字是你回头调参数的唯一依据。
- 可预期的追问是「退避上限怎么定」。定在业务能等的时间上,超过就转降级:换更小的分辨率、更短的时长,或者干脆排到下一批。
How to reason about it · think before answering
- This question separates people who have actually been throttled in production. Exponential backoff with jitter is only the first half of the answer.
- Frame it correctly: throttling is a signal, not an error. It says your current send rate exceeds what the vendor will accept right now, so it deserves a feedback action, not just a retry.
- Action one is to slow down on purpose: penalize the bucket so the next window or two issues half the tokens. Without that, you finish the backoff and hit the same wall at the same speed.
- Action two is to not hold an execution slot while waiting. Requeue the job with a not-before timestamp and hand the slot back immediately.
- Action three is classification. Throttling and server errors are retryable; auth failure, insufficient balance, invalid parameters and content-policy rejections are not, and retrying them just repeats one mistake five times while consuming quota. At MiniMax, 1002 is rate limiting and 1039 is the token-per-minute variant, while 1004 is auth, 1008 is balance, 2013 is bad parameters and 1026 or 1027 are content rejections.
- Action four is to record throttle counts as a metric. That number is the only evidence you have when you later retune the gate.
- Expected follow-up: how to cap the backoff. Cap it at what the business can wait for, then degrade instead of retrying: smaller resolution, shorter duration, or push the job into the next batch.
答题要点
- 把限流当信号:退避的同时给令牌桶降档,接下来的窗口只发一半令牌。
- 退避期间把任务重新入队并记一个不早于时间戳,工作槽立刻还回去。
- 退避要带抖动,否则同时被限的任务会同时醒来再撞一次。
- 严格区分可重试与不可重试:鉴权、余额、参数、内容审核一次都不重试。
- 把限流次数记成指标,它是回头调闸门参数的唯一依据;退避到上限就转降级而不是继续重试。
Key points
- Treat throttling as a signal: back off and also penalize the bucket so the next window issues fewer tokens.
- Requeue with a not-before timestamp instead of sleeping inside the worker slot.
- Add jitter, or everything throttled together wakes together and collides again.
- Separate retryable from non-retryable: auth, balance, bad parameters and content rejections get zero retries.
- Emit a throttle counter as a metric, and switch to degradation once backoff hits its ceiling.