面灵AI→

小红书 Agent 开发一面:SSE、Streamable HTTP、MCP 版本兼容全问细节

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 自我介绍。
  2. 询问团队人数。
  3. 询问现在能否正常去实习。
  4. 拷打实习经历。
  5. 拷打项目经历。
  6. SSE 和 Streamable HTTP 的区别?
  7. 如何适配 stdio、HTTP、SSE、Streamable HTTP?
  8. 如何处理 MCP 版本兼容问题?
  9. ChatGPT 这种对话 Agent 的会话表和消息表字段怎么设计?
  10. MCP 的客户端和服务端交互完整流程?
  11. MCP 获取工具列表怎么做的?
  12. 如果 MCP 有很多工具,比如 100 个,Agent 怎么处理?
  13. Agent 怎么保证工具调用准确性?
  14. Agent 记忆模块怎么设计?
  15. 长期记忆和短期记忆分别怎么实现?
  16. 对话超过模型上下文窗口怎么处理?
  17. 压缩的具体方案?还能怎么细化?
  18. 分层记忆怎么分?
  19. 项目里有用到 MCP 吗?MCP 解决什么问题?
  20. 站在测试开发角度,怎么验证功能符合预期?
  21. 项目里用到什么组件?
  22. MQ 有哪些特性?
  23. Redis 缓存穿透、击穿、雪崩是什么?怎么解决?
  24. 布隆过滤器原理?误判怎么处理?
  25. 数据库和 Redis 如何保持数据一致性?
  26. 旧缓存删除后旧数据还没更新,请求把旧数据写回缓存怎么办?
  27. Agent 长期记忆和短期记忆如何设计?哪些内容作为何种记忆?
  28. 发生记忆冲突怎么处理?
  29. 用户姓名等信息被污染怎么处理?
  30. RAG 系统流程?如何提高准确率?
  31. 手撕:最长递增子序列。

具体项目追问

  1. 自动切题 Agent 中的 7 步 Pipeline 流程是什么?
  2. LangGraph 设计中的 3 个子图 Agent 怎么传递?
  3. 为什么拆成三个子图而不是一条 Pipeline?
  4. Skills 知识进化流程怎么搭建?
  5. 为什么不在 Prompt 中添加而用 Skills?

《参考解析》

MCP 的传输方式与版本兼容。 MCP 是让模型以统一协议访问外部工具与数据的标准,传输层有两代:早期是「HTTP + SSE」——客户端向服务端建一条 SSE 长连接接收消息,再用另一个 HTTP POST 端点发消息,两条通道分离、长连接容易被中间层掐断、也没法在无状态的网关后面水平扩展;新的 Streamable HTTP 把收发统一到单个 HTTP 端点上,响应可以是普通 JSON,也可以升级为流式(对客户端请求按需回 SSE 流),必要时用会话标识维持上下文,因而对负载均衡与断线重连更友好,还能兼容无状态部署。适配四种传输(stdio、HTTP、SSE、Streamable HTTP)的工程做法是抽象出统一的传输接口,让上层只看到「发送请求、接收消息、订阅通知」三个动作:stdio 用于本机子进程(最安全、零网络配置,适合桌面端与 CLI),HTTP 用于无状态一次性调用,SSE 与 Streamable HTTP 用于远程长会话,各自实现连接建立、心跳、重连与取消。版本兼容靠协议版本协商与能力声明:初始化握手时双方交换协议版本与各自支持的 capabilities,服务端按客户端版本决定可用特性,客户端对未知字段要与宽松解析(忽略而非报错),不支持的能力降级而不是直接失败;工具与资源的 schema 变更尽量只做向后兼容的追加,破坏性变更走新版本号并保留旧版本一段时间。

记忆分层、压缩与污染治理。 短期记忆是当前会话的上下文(最近若干轮原文加工具结果),长期记忆是跨会话的事实与偏好(用户姓名、岗位、常驻城市、明确说过的约束、历史结论),通常存向量库加结构化字段并带时间戳与来源。分层可以这样切:工作记忆(当前任务的目标、计划与中间产物)、会话记忆(本轮对话的原文与滚动摘要)、用户记忆(跨会话的稳定事实与偏好)、知识记忆(可检索的外部资料,与用户无关)。压缩方案要具体到策略:按轮次滚动摘要(保留结论与未完成事项、丢弃寒暄)、按主题切分再做分主题摘要、工具长输出落盘只留摘要与引用、把结构化事实抽出来写成键值对(这些不该被摘要,直接进结构化存储)、以及用「摘要 + 最近几轮原文 + 检索到的相关历史片段」三段拼装上下文。追问「还能怎么细化」时可以补:摘要要带时间与来源便于追溯、要保留否定与条件(「不要打电话」「只在工作日」这类),并且要用固定 prompt 与模型版本以保证摘要可复现。记忆冲突的处理原则是「新的覆盖旧的、但要留痕」:同一事实按时间取最新,同时保留历史版本与变更原因;置信度不同就保留高置信度并提示存在冲突;用户显式纠正必须最高优先级并立即生效。用户姓名等信息被污染(模型把别人的名字写进记忆)要靠来源校验与写入闸门:只有用户明确陈述或从可信字段抽取的事实才允许写入长期记忆,模型推测一律不写;写入前做一次一致性校验(与已有资料是否矛盾),并对敏感字段做白名单;出问题后要能按来源追溯到具体会话并批量回滚。

缓存与 RAG。 缓存穿透是查不存在的数据导致每次都打到数据库(用布隆过滤器前置拦截、空值缓存加短 TTL);击穿是热点 key 过期瞬间大量请求同时回源(用互斥锁或单飞、热点 key 逻辑过期不物理删除、提前预热);雪崩是大量 key 同时失效或缓存整体不可用(过期时间加随机抖动、多级缓存、限流降级与熔断)。布隆过滤器用多个哈希函数把元素映射到位数组,判断「不存在」一定准确、判断「存在」有误判,所以它只适合做前置拦截;降低误判要靠增加位数组长度、调整哈希个数并估算实际元素量,同时支持删除要用计数布隆过滤器。数据库与 Redis 的一致性主流方案是 Cache-Aside:读先查缓存、未命中查库回填并设随机过期;写先更新数据库再删除缓存(不是更新缓存),必要时叠加延迟双删或订阅 binlog 异步失效。「旧缓存删除后旧数据又被写回」这个经典窗口的解法有几种:延迟双删(写库后延迟再删一次)、给缓存写入加版本号或时间戳(回填时若库中版本更新则丢弃旧值)、用 binlog 驱动的失效队列保证删除顺序、以及把「读回填」做成带短锁的单飞操作把并发回填串行化。真正的兜底是对账与监控,而不是假设缓存永远一致。RAG 流程与提准确率的方向在别处已详述,这一问的要点是:检索侧做混合召回加 rerank、切片要保语义边界并带元数据、查询侧做改写与多查询、生成侧强制引用原文并做事实核查、评测侧建分层评测集并用 badcase 回流。

手撕:最长递增子序列与项目追问。 LIS 的标准解法是贪心加二分:维护一个数组 tails,其中 tails[i] 表示长度为 i+1 的递增子序列的最小结尾值,遍历每个数时用二分找到第一个不小于它的位置并替换,若位置在末尾则长度加一,时间 O(n log n)、空间 O(n);要返回具体序列就再记录每个元素的前驱下标。经典 O(n²) DP 是 dp[i] = max(dp[j]) + 1 (j < i 且 nums[j] < nums[i]),适合先讲清题意与状态定义,再优化到二分。注意区分「子序列」与「子数组」——子数组要求连续,用另一套解法;另外「严格递增」与「非递减」在二分判断上是 >= 还是 > 的区别,写之前先确认。项目追问部分值得提前准备的逻辑是:把流程画成 7 步 Pipeline 并说明每步的输入输出与失败处理;多个 LangGraph 子图之间通过共享状态对象与显式边传递(谁写哪个字段要约定清楚,否则会出现互相覆盖);拆成三个子图而不是一条 Pipeline 的理由通常是职责隔离与可独立重试(每个子图有独立的失败恢复与评测);Skills 本质是把「某类任务的做法、脚本与约束」封装成可版本化、可检索、可复用的能力包,比塞进 prompt 的好处是可控、可测、可增量进化(改能力不用改主提示词,也不会一次把所有细节都占满上下文)。