逐日AI

面试题库

共 328 题,当前筛选 7 题。

30 天从前端工程师到 Agent 工程师

D1 LLM API 基础:messages/roles、token、流式、temperature;Agent 到底是什么

  • 为什么 LLM 应用几乎都用流式输出?SSE 和 WebSocket 该怎么选?Why do LLM apps stream responses, and how do you choose between SSE and WebSockets?
    国内高频海外高频进阶#streaming#protocol

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

    1. 第一问考的是对延迟指标的敏感度:要能区分「首字延迟」和「全文延迟」,并说出模型逐 token 生成决定了前者远小于后者。
    2. 把它翻译成产品语言:用户 1 秒内看到反馈 vs 对着空白等 20 秒,这是体验的分水岭,不是锦上添花。
    3. 第二问不要背优缺点表,先问自己「客户端需不需要频繁上行」——这一条几乎决定了答案。
    4. 只需要服务器往下推 token,SSE 就够:它跑在普通 HTTP 上,代理和负载均衡友好,还自带重连。需要语音、协同、频繁打断这类双向高频交互,才值得上 WebSocket。
    5. 给出多数产品的真实形态:请求走普通 POST,回复走 SSE,另配一个取消接口——顺势可以引到「POST 的 SSE 用不了 EventSource 的自动重连」这个坑。

    How to reason about it · think before answering

    1. The first half tests latency literacy: separate time-to-first-token from total latency and tie it to sequential generation.
    2. Translate to product terms: feedback within a second versus twenty seconds of blank screen.
    3. For the second half, skip the pros-and-cons table and ask whether the client needs frequent upstream messages.
    4. Server-to-client tokens only means SSE suffices: plain HTTP, proxy-friendly, with built-in reconnection. Voice, collaboration or frequent interrupts justify WebSockets.
    5. State the common shape: plain POST for the request, SSE for the reply, plus a cancel endpoint — which sets up the trap that POST-based SSE cannot use EventSource auto-reconnect.

    答题要点

    • 模型逐 token 生成,首字延迟远小于全文延迟;流式让用户 1 秒内看到反馈而不是等 20 秒
    • SSE 是单向、基于 HTTP 的文本协议,自动重连、穿透代理容易,天然适合服务器→客户端的 token 流
    • WebSocket 双向、更适合需要客户端频繁上行(语音、协同编辑、打断)的场景,但代理/负载均衡更麻烦
    • 多数聊天产品:请求用普通 HTTP POST,回复用 SSE;需要打断时再加一个取消接口

    Key points

    • Models emit tokens sequentially; time-to-first-token is far lower than full latency
    • SSE is one-way over HTTP with built-in reconnect and easy proxying, ideal for server→client token streams
    • WebSockets are bidirectional, better when the client sends often (voice, collaboration, interrupts) but harder to load-balance
    • Most chat products: plain POST for the request, SSE for the reply, plus a cancel endpoint
  • 流式回复到一半网络断了,前端和后端各要做什么?EventSource 的自动重连能用上吗?A streaming reply is cut off mid-way. What do the client and server each do, and can EventSource auto-reconnect help?
    国内高频海外高频进阶#streaming#reliability#sse

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

    1. 这题的陷阱在后半句。很多人背过「SSE 自带重连」,就直接答自动重连能救——那是错的,必须先分清两种 SSE 用法。
    2. 浏览器原生 EventSource 确实按规范自动重连:重连时带 Last-Event-ID 请求头,服务器用 id: 打点、用 retry: 设间隔;但它只能发 GET,且要求响应 Content-Type 是 text/event-stream。
    3. 而 LLM chat API 必须 POST(messages 要放在请求体里),所以实际用的是 fetch 加手写 SSE 解析——EventSource 那套自动重连一行都用不上。
    4. 于是前端职责变成:自己判定断流、自己重试、自己保存已收到的部分。后端职责是让重试是安全的——响应可续、副作用幂等。
    5. 给出续写策略并说清边界:把已收到的内容作为上下文构造续写请求;但工具调用块和思考块无法部分恢复,只能从最近的完整文本块续。
    6. 可预期追问:非 200 响应会重连吗?按规范不会——状态码不是 200 或 Content-Type 不对,连接直接判定失败;服务器还可以用 204 主动叫停重连。

    How to reason about it · think before answering

    1. The trap is the second half: people who memorized 'SSE reconnects automatically' answer yes, which is wrong.
    2. Native EventSource does auto-reconnect per spec, sending Last-Event-ID, with the server marking events via id: and setting the interval via retry: — but it only issues GET and requires Content-Type text/event-stream.
    3. LLM chat APIs require POST because messages go in the body, so real clients use fetch plus hand-written SSE parsing, where none of that machinery applies.
    4. So the client owns detection, retry and buffering of what arrived; the server's job is making retries safe — resumable output and idempotent side effects.
    5. Give the continuation strategy and its limits: feed the received prefix back as context, but tool-use and thinking blocks cannot be partially recovered — resume from the last complete text block.
    6. Follow-up to expect: does a non-200 reconnect? Per spec no — a non-200 status or wrong Content-Type fails the connection, and a 204 tells the browser to stop reconnecting.

    答题要点

    • 先区分两种 SSE:浏览器原生 EventSource 自动重连并带 Last-Event-ID,但只能 GET;LLM API 走 POST,用不上这套
    • 所以前端要自己检测断流、自己重试,并保留已收到的部分内容
    • 续写策略:把已收到的内容作为上下文发起新请求,让模型接着写,而不是整轮重来
    • 边界:tool_use 和 thinking 块无法部分恢复,只能从最近的完整文本块续
    • 后端要保证重试安全:响应可续、工具副作用幂等,并对已产生的用量正确计费

    Key points

    • Separate the two SSE modes: native EventSource auto-reconnects with Last-Event-ID but is GET-only; LLM APIs use POST and cannot rely on it
    • The client must therefore detect the break, retry itself, and keep whatever text already arrived
    • Continuation: send the received prefix as context so the model resumes rather than restarting the turn
    • Limits: tool_use and thinking blocks cannot be partially recovered; resume from the last complete text block
    • The server must make retries safe: resumable responses, idempotent tool side effects, correct billing for tokens already produced
  • 用户切到后台或者直接关掉网页,回来后怎么恢复那条还在生成的回复?The user backgrounds the app or closes the tab. How do you restore a reply that was still being generated?
    国内高频海外高频深入#streaming#reliability#architecture

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

    1. 先识别这题和「网络断了」不是同一个问题:客户端已经不存在了,任何写在前端的重试逻辑都不会执行。
    2. 由此推出唯一出路:生成过程必须能脱离这个客户端独立存活,也就是把流本身放到服务端持久化。
    3. 落到具体架构:发起请求时给这轮生成分配一个流 id,服务端一边把 token 推给当前连接,一边把同样的内容写进 Redis 之类的存储;会话记录里保存这个 activeStreamId。
    4. 恢复路径是另开一个 GET 端点:客户端带着会话 id 请求,服务端按 activeStreamId 找到那条流并接着推;找不到活跃流就返回 204,让前端知道没有需要恢复的东西。
    5. 说清代价,别只说方案:多了一份存储、一套过期清理、以及「同一条流可能被多个连接消费」的并发问题。
    6. 延伸:这套结构和普通聊天产品的「消息已持久化,重进会话直接读库」不同——区别在于回复还在生成中,需要的是可续的流而不是一条静态记录。

    How to reason about it · think before answering

    1. First separate this from a dropped connection: the client is gone, so no client-side retry will ever run.
    2. That leaves one option — the generation must outlive the client, which means persisting the stream server-side.
    3. Concretely: assign a stream id per generation; the server pushes tokens to the live connection while also writing them to storage such as Redis, and the chat record stores that activeStreamId.
    4. Recovery is a separate GET endpoint: the client asks with the chat id, the server locates the stream by activeStreamId and resumes; with no active stream it returns 204.
    5. Name the costs, not just the design: extra storage, expiry/cleanup, and concurrency when several connections consume the same stream.
    6. Extension: this differs from ordinary message persistence because the reply is still being produced — you need a resumable stream, not a static row.

    答题要点

    • 客户端已经不在了,前端重试无从谈起,必须让生成过程在服务端独立存活
    • 发起生成时分配流 id,服务端边推送边把内容写进 Redis,会话里记录 activeStreamId
    • 恢复走单独的 GET 端点:按会话 id 找到活跃流接着推,没有活跃流就返回 204
    • 代价:额外存储、过期清理,以及同一条流被多个连接消费的并发处理
    • 与「消息持久化后重新读库」的区别在于回复仍在生成中,需要的是可续的流

    Key points

    • The client is gone, so recovery must live server-side: the generation has to outlive the connection
    • Assign a stream id at start; the server writes tokens to Redis while streaming, and the chat stores activeStreamId
    • Resume through a dedicated GET endpoint that replays the active stream, returning 204 when there is none
    • Costs: extra storage, expiry and cleanup, and concurrent consumers of one stream
    • It differs from plain message persistence because the reply is still in flight, so you need a resumable stream
  • 移动端 App 里的对话,网络频繁抖动,你会怎么设计重连策略?On mobile, connectivity is flaky. How would you design the reconnection strategy for a chat feature?
    国内高频海外高频进阶#reliability#mobile#streaming

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

    1. 先说明移动端和浏览器的差别:网络在 WiFi 与蜂窝之间切换、App 会被系统挂起、后台执行时间受限,所以不能照搬网页那套。
    2. 重试节奏用指数退避加随机抖动。抖动这一条常被忽略,但它是防止大面积断网恢复后所有客户端同时涌上来把服务打垮的关键。
    3. 要设上限:最大重试次数与最大退避间隔,超过就转成显式的「重新加载」按钮交给用户,而不是无限静默重试。
    4. 区分「短暂抖动」和「真的没网」:监听系统的网络状态变化,没网时直接停止重试并进入离线态,等网络恢复事件再立刻重连,比盲目定时重试省电得多。
    5. 结合上一题的服务端持久化:App 被系统杀掉后重进,靠会话 id 请求恢复端点,而不是指望本地缓存拼出完整回复。
    6. 最后补发送侧:用户在离线时发出的消息进本地队列,恢复后按序重发,且每条带幂等键,避免重复发送。

    How to reason about it · think before answering

    1. Start with what makes mobile different: network switches between WiFi and cellular, the OS suspends apps, background time is limited.
    2. Use exponential backoff with jitter; jitter is the commonly missed part that prevents a thundering herd when a wide outage clears.
    3. Set ceilings: max attempts and max interval, then surface an explicit reload action instead of retrying silently forever.
    4. Distinguish a brief blip from being genuinely offline: subscribe to OS connectivity events, stop retrying when offline, and reconnect on the restore event — far cheaper on battery than blind timers.
    5. Combine with server-side persistence: after the OS kills the app, resume by chat id rather than reconstructing from local cache.
    6. Finally the send path: queue outgoing messages while offline and replay them in order, each with an idempotency key.

    答题要点

    • 移动端特殊性:WiFi 与蜂窝切换、App 被挂起、后台执行时间受限,不能照搬网页策略
    • 指数退避加随机抖动,抖动用于避免大面积恢复时的重连风暴
    • 设最大重试次数与最大间隔,超过后转为显式的重新加载入口,不做无限静默重试
    • 监听系统网络状态:离线直接停重试进入离线态,收到恢复事件再重连,比定时轮询省电
    • 回复恢复依赖服务端持久化,靠会话 id 请求恢复端点;发送侧用本地队列加幂等键按序重发

    Key points

    • Mobile differs: network handoffs, OS suspension, limited background time — do not copy the web strategy
    • Exponential backoff with jitter, where jitter prevents a reconnect storm when an outage clears
    • Cap attempts and interval, then hand the user an explicit reload instead of retrying forever
    • Listen to OS connectivity events: stop while offline, reconnect on restore, which saves battery over polling
    • Resume replies via server-side persistence by chat id; queue outgoing messages with idempotency keys
  • 用户主动点「停止生成」和网络意外断开,在服务端看起来都是连接没了,怎么区分处理?A user pressing stop and a dropped connection both look like a closed connection server-side. How do you tell them apart?
    国内高频海外高频深入#streaming#reliability#ux

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

    1. 先点破为什么要区分:主动停止是「用户不想要了」,应当立即释放算力并结束这轮;意外断开是「用户还想要」,理应保留结果供恢复。处理反了,用户要么白花钱,要么回来发现内容没了。
    2. 所以不能只靠 TCP 连接状态判断——它对两种情况的表现是一样的。必须有一个显式信号。
    3. 做法是给「停止」单独一个接口:前端点停止时先调这个接口,带上 run id,服务端据此把该轮标记为「用户取消」,再中止上游模型调用。
    4. 而单纯的连接关闭一律按「意外断开」处理:继续把已生成内容落盘、保留可恢复的流,等客户端回来续。
    5. 补一个现实约束:停止请求本身也可能因为断网而发不出去。所以服务端还需要兜底——比如流没有任何消费者超过一定时间就自行结束,避免算力空转。
    6. 延伸到计费:两种情况都要为已经产生的 token 计费,因为上游厂商已经收了钱;区别只在于要不要保留结果和是否继续生成。

    How to reason about it · think before answering

    1. Say why it matters: stop means the user no longer wants the output, so free compute and end the run; a drop means they still want it, so preserve the result for resumption.
    2. Connection state alone cannot distinguish them — it looks identical — so you need an explicit signal.
    3. Give stop its own endpoint: the client calls it with the run id before closing, and the server marks the run as user-cancelled and aborts the upstream call.
    4. Treat a bare connection close as an unexpected drop: keep persisting output and hold the stream for resumption.
    5. Add the real-world caveat: the stop request itself may fail to send when the network is down, so the server needs a fallback — end a stream with no consumer after a timeout.
    6. Extend to billing: both cases still owe for tokens already produced, since the upstream provider has charged; they differ only in whether output is retained.

    答题要点

    • 两者语义相反:主动停止要立即释放算力并结束,意外断开要保留结果等待恢复
    • TCP 连接状态无法区分,必须有显式信号:给停止单独一个接口,带 run id 标记为用户取消
    • 只收到连接关闭一律按意外断开处理,继续落盘并保留可恢复的流
    • 兜底:停止请求本身也可能发不出去,服务端需对长时间无消费者的流自行结束
    • 计费上两者都要为已产生的 token 记账,区别只在于是否保留结果、是否继续生成

    Key points

    • The semantics are opposite: stop frees compute immediately, a drop preserves output for resumption
    • Connection state cannot distinguish them, so add an explicit stop endpoint carrying the run id
    • Treat a bare close as an unexpected drop: keep persisting and hold the stream for resume
    • Fallback: the stop call may itself fail to send, so end streams with no consumer after a timeout
    • Both still bill for tokens already produced; they differ only in retention and whether generation continues

D7 封装成服务:Fastify + SSE + Docker(dg P07);W1 复盘

  • 流式返回大模型回复,你会选 SSE 还是 WebSocket?为什么?For streaming LLM responses, would you pick SSE or WebSocket, and why?
    国内高频海外高频基础#sse#streaming#api-design

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

    1. 这题的题眼是「怎么选」,不是「有什么区别」。只背出「SSE 单向、WebSocket 双向」拿不到分,因为那是文档第一段。
    2. 先问自己一个问题,它几乎决定了答案:这条连接上客户端需不需要频繁上行?聊天补全是「一次请求、一路往回推」,上行只有最开始那一次,完全落在 SSE 的形状里;协同编辑、实时游戏、语音这种双向高频才轮到 WebSocket。
    3. 然后给 SSE 的三条实际好处:它就是普通 HTTP,鉴权头、Cookie、限流、日志、CDN、反向代理这一整套现成设施全部照用;服务端只是往响应里写字节,不需要额外的连接管理;协议是纯文本,出问题 curl 一下就能看。WebSocket 走的是升级后的独立协议,前面那套东西大多要重做一遍。
    4. 接着说 SSE 的两个真实限制,主动说破比被问出来强:一是浏览器原生的 EventSource 只能发 GET,而大模型接口必须 POST,所以真实前端都是 fetch 手写解析,规范里那套 Last-Event-ID 自动重连一行都用不上;二是 HTTP/1.1 下同域并发连接数有限制,多个标签页各开一条长连接会互相挤占,HTTP/2 之后这条基本消失。
    5. 结论要落到一句可判断的话:单向推送选 SSE,双向高频选 WebSocket;拿不准就先用 SSE,因为它的退路是加一个上行接口,而 WebSocket 的退路是重做整套基础设施。
    6. 可以预期的追问:那大模型产品里的「停止生成」按钮怎么办?答案是它根本不需要走同一条连接——另发一个普通的 POST 请求带上这次生成的 id,服务端收到就中止上游,SSE 那条连接自然结束。这个追问很能区分有没有真做过。

    How to reason about it · think before answering

    1. The hinge is 'how would you pick', not 'what is the difference'. Reciting 'SSE is one-way, WebSocket is two-way' scores nothing — that is the first paragraph of any doc.
    2. Ask one question that nearly decides it: does the client need frequent upstream messages on this connection? Chat completion is one request followed by a long push, which is exactly SSE's shape. Collaborative editing, realtime games and voice are what WebSocket is for.
    3. Give three practical wins for SSE: it is ordinary HTTP, so auth headers, cookies, rate limiting, logging, CDNs and reverse proxies all keep working; the server just writes bytes into a response, with no separate connection lifecycle to manage; and the wire format is plain text, so curl is your debugger. WebSocket runs an upgraded protocol where most of that tooling has to be rebuilt.
    4. Volunteer SSE's two real limits before they are raised. First, the browser's native EventSource can only issue GET, while model endpoints require POST, so real frontends hand-roll the parser with fetch and the spec's Last-Event-ID auto-reconnect never applies. Second, HTTP/1.1 caps concurrent connections per origin, so several tabs each holding a stream compete; HTTP/2 largely removes this.
    5. Land on a decision rule: one-way push means SSE, high-frequency bidirectional means WebSocket, and when unsure start with SSE — its escape hatch is adding one upstream endpoint, while WebSocket's escape hatch is rebuilding your infrastructure.
    6. Expect the follow-up: what about the 'stop generating' button? It does not need the same connection — send a plain POST carrying the run id, have the server abort upstream, and the SSE stream ends on its own. This one separates people who shipped it from people who read about it.

    答题要点

    • 先判断上行频率:一次请求、一路往回推的场景(聊天补全)用 SSE,双向高频(协同编辑、语音)用 WebSocket
    • SSE 就是普通 HTTP,鉴权、限流、日志、代理、CDN 这套设施全部照用,排查时 curl 就够
    • SSE 的限制要主动说:EventSource 只能 GET,而模型接口必须 POST,所以自动重连用不上;HTTP/1.1 下同域连接数有限
    • 拿不准先选 SSE:加一个上行接口就能补足,而换 WebSocket 要重做整套基础设施
    • 「停止生成」不用走同一条连接,另发一个 POST 带 run id 让服务端中止上游即可

    Key points

    • Decide by upstream frequency: one request plus a long push (chat completion) fits SSE; high-frequency bidirectional traffic needs WebSocket
    • SSE is plain HTTP, so auth, rate limiting, logging, proxies and CDNs all still apply, and curl is enough to debug it
    • Name SSE's limits yourself: EventSource is GET-only while model endpoints need POST, so spec auto-reconnect does not apply; HTTP/1.1 also caps per-origin connections
    • When unsure start with SSE — adding one upstream endpoint is cheaper than rebuilding infrastructure around WebSocket
    • A stop button does not need the same connection: POST the run id and abort upstream, and the stream ends by itself
  • 把一个本地跑的 Agent 脚本改造成生产服务,接口层要重点考虑哪些事?When turning a local agent script into a production service, what does the interface layer have to get right?
    国内高频海外高频进阶#api-design#service-architecture#streaming

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

    1. 这题考的是「你知不知道脚本里有哪些隐含假设」。答成一份笼统的清单(鉴权、日志、监控)拿不到分,要说出脚本时代默认成立、服务里立刻不成立的那几条。
    2. 先把假设列出来,这是最能体现工程视角的一步:只有一个用户(历史可以放模块级变量)、串行执行(不会有两个请求同时改一份状态)、输入可信(参数是自己敲的)、进程和会话同生共死(Ctrl+C 之后不用交代)。四条在服务里全部不成立,而第一条最难查,因为它在本地单人测试时表现完美。
    3. 然后给出四个必须做的决定:接口形状(一次性 JSON 还是流式推送)、会话标识(客户端带 sessionId 还是服务端发 cookie,以及历史存哪里)、鉴权与限流(谁能调、多久能调一次、单次 token 上限)、错误怎么表达。
    4. 第四条要单独展开,它是这题真正的区分点:流式接口一旦写出 200 和第一个字节,状态码就已经发出去了,之后模型超时、余额不足、上游 500,都只能在流里补发一个约定好的 error 事件。所以推流之前必须把能校验的全部校验完,那是你最后一次能用状态码好好说话的机会。
    5. 再补一条生产视角:服务要有健康检查接口。没有它,编排系统和负载均衡就没法判断这个实例能不能接流量,滚动发布时会把请求打给一个还没起好的进程。
    6. 可以预期的追问:单次请求的 token 上限为什么要在接口层限制?因为 Agent 的成本是请求方触发、你来买单,不设上限就等于把钱包交给调用方——限流限的不只是 QPS,还有每次调用能烧多少钱。

    How to reason about it · think before answering

    1. This tests whether you can name the assumptions hidden in a script. A generic checklist (auth, logging, monitoring) scores nothing; name the assumptions that silently break.
    2. List them first: one user (so history can live in a module-level variable), serial execution (no two requests mutating the same state), trusted input (you typed the arguments yourself), and a process whose life equals the session's. All four break in a service, and the first is hardest to catch because single-user local testing looks perfect.
    3. Then give the four decisions: response shape (single JSON versus streamed events), session identity (client-supplied id versus server cookie, and where history is stored), authentication and rate limiting (who may call, how often, and the per-call token ceiling), and how errors are expressed.
    4. Expand the last one — it is where this question is actually won. Once a streaming endpoint has written 200 and the first byte, the status code is already on the wire, so a later timeout, out-of-credit or upstream 500 can only surface as an agreed error event inside the stream. Validate everything you can before the first byte, because that is your last chance to speak in status codes.
    5. Add a production note: ship a health endpoint. Without one, orchestrators and load balancers cannot tell whether an instance is ready, and rolling deploys send traffic to a process that has not finished booting.
    6. Expect the follow-up: why cap tokens per request at the interface layer? Because agent cost is triggered by the caller and paid by you — no cap means handing your wallet to the client. Rate limiting is about money per call, not just QPS.

    答题要点

    • 脚本的四个隐含假设在服务里全部不成立:单用户、串行、输入可信、进程与会话同生共死
    • 会话状态必须按 sessionId 隔离,且要意识到放进程内存意味着重启即丢、无法水平扩容
    • 四个接口决定:响应形状、会话标识、鉴权与限流(含单次 token 上限)、错误表达方式
    • 流式接口推流之后无法用状态码报错,必须约定一个流内的 error 事件,并把校验全部前置到第一个字节之前
    • 提供健康检查接口,否则编排系统无法判断实例能不能接流量

    Key points

    • A script's four assumptions all break in a service: single user, serial execution, trusted input, and a process that dies with the session
    • Session state must be keyed by session id, and in-process storage means data is lost on restart and blocks horizontal scaling
    • Four interface decisions: response shape, session identity, auth and rate limiting including a per-call token ceiling, and error semantics
    • A streaming endpoint cannot report errors by status code after the first byte, so define an in-stream error event and move all validation ahead of it
    • Expose a health endpoint, or orchestrators cannot tell whether the instance is ready for traffic