面灵AI→

小红书 AI Agent 开发 一面:评测集、上下文压缩与 MQ 幂等

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

《面试题目》

项目与验收

  1. 先介绍你自己的工作。
  2. 你这里的 Agent 平台包括哪些功能?
  3. 选一个你独立完成的模块,从需求、方案、验收讲一下。
  4. 你怎么评估这套流程是合理、准确的?
  5. 如果让你设计边界,你会怎么设计?
  6. 哪些功能已经投入使用?
  7. 哪些真实功能已上线?收集到什么反馈?
  8. 还原一下那个并发异常问题。
  9. Service 里哪些字段被共享了?
  10. 怎样的执行顺序最终导致什么报错?

评测与标注

  1. 混合检索与 recall 的结果是否可信?
  2. 每道题大概涉及哪些相关文件?
  3. 正确答案谁来标注?怎么标注?
  4. 现在有 100 道题,换一批题这个机制还有效吗?

上下文与成本

  1. 上下文压缩降低 token 消耗是怎么统计的?
  2. 你怎么理解实际返回的 usage 字段?它代表什么?
  3. 节省的量包括哪些?
  4. 用户先用模型 A,后切到模型 B,摘要如何避免保留旧内容?
  5. 省了这么多 token 后,问答质量有没有下降?

人机协同与校验

  1. 工具调用里的用户确认,也就是 human-in-the-loop 是怎么做的?
  2. 用户说“用上次那个模型处理这张图”,但没说清模型和处理内容,Agent 如何补齐参数?
  3. 服务端是否要做类型、范围、资源存在性、权限等校验?

MQ 与一致性

  1. 你用 MQ,并通过任务 ID 和状态判断命令幂等;如果发送端一个、接收端多个,会怎样?
  2. 两个消费者同时接到待执行任务,如何避免多个消费者同时处理?
  3. 任务结果已写 MySQL,但 ACK 前消费者进程崩了,会怎样?
  4. 反问。

《参考解析》

平台模块和验收要讲成“能力闭环”,不是功能列表。 一个 Agent 平台通常包含:模型接入与路由、提示词/工具注册、会话与上下文管理、任务编排(planning/执行/重试)、知识库与检索、评测与标注、观测与成本、权限与审计。选一个独立模块时,按“需求—方案—验收”讲:需求来自谁、解决什么痛点、边界在哪;方案里说清数据模型、接口、失败处理、和上下游的契约;验收要有可量化指标,比如任务成功率、P95 延迟、单任务 token 成本、人工介入率、线上错误率,而不是“跑通了”。边界设计要覆盖输入(空值、超长、多语言、恶意注入)、输出(schema、引用、拒答)、权限(谁能调用哪个工具、能访问哪些数据)、执行(超时、重试、幂等、回滚)和人工(哪些操作必须确认)。上线反馈则要区分“功能上线”和“真实使用”:有没有真实用户、调用量、留存、bad case 类型,哪些反馈已经转成迭代项。

评测集的可信度取决于标注规范、一致性和抗污染,不是题目数量。 混合检索的 recall 要先把“相关”的定义写清楚:是文件级、函数级还是段落级;每道题标出标准答案涉及的文件/片段,多人独立标注,算一致率(如 Cohen’s kappa),分歧由第三人仲裁;题目按难度、业务域、问题类型分层,避免全是简单事实题。100 道题换一批后机制是否还有效,要看有没有“锚题”:保留一部分固定题做跨批次对比,新题按同样规范标注,否则换题后分数不可比。还要防污染:评测题不能出现在训练/提示词示例里,检索索引更新后要重跑。除了 recall,还要看 precision、MRR、答案正确率、引用命中率和端到端任务成功率;如果 recall 高但答案差,问题可能在重排、上下文拼接或生成,不要只盯检索。文件定位题则要单独统计 Top-K 命中率和平均排名,因为“找对文件”和“答对问题”是两件事。

token 统计要区分“账面节省”和“真实节省”,并把质量一起看。 模型返回的 usage 通常包含输入 token、输出 token、总 token,有些还细分缓存命中 token、推理 token;统计压缩效果时,要在同一会话、同一请求上对比“不压缩的基线 prompt”与“压缩后的 prompt”,而不是拿压缩后的输入和上一次输出比。节省量包括:历史对话被摘要后减少的 token、重复系统提示/工具描述被裁剪的部分、检索片段去重和截断、缓存命中带来的计费折扣;但也要减去摘要本身的生成成本、额外调用和延迟。按任务聚合而不是按单次调用聚合,否则多轮任务会被拆碎。质量是否下降,要用固定评测集看任务成功率、关键事实召回、人工评分、用户编辑率/追问率/拒答率,做压缩前后的 A/B;如果成本降了但追问率明显上升,实际总成本可能没降。回答时能给出一条“成本—质量曲线”,说明在哪个压缩比例下收益最大,会比单说“省了百分之多少”更有说服力。

模型切换后摘要被旧内容污染,根因是摘要没有版本和来源信息。 如果摘要只存一段自然语言,模型 A 生成的结论、术语和假设会被模型 B 继续沿用,即使 B 的能力和风格不同。工程上要给摘要打元数据:生成它的模型/版本、时间、来源消息范围、置信度;切换模型时,优先用新模型对原始对话重新摘要,至少对关键结论做一次重写;如果原始对话太长,就保留结构化记忆(实体、约束、已确认事实、未完成事项)和最近原文,摘要只作辅助。检索时按模型版本过滤旧摘要,或者把旧摘要标成“历史参考”,不让它进入系统指令。用户显式切换模型时,可以提示“摘要将由新模型重建”,并保留回滚。验证方法是构造一批跨模型切换的对话,比较切换前后的答案一致性、旧结论残留率和任务成功率;如果旧摘要里有关键事实,重建时要保证事实不丢,只是表达和推理链更新。

Human-in-the-loop 的关键是“按风险分级 + 明确确认对象 + 可审计”。 读操作、低风险查询可以自动执行;写操作、花钱、删除、对外发送、修改权限这类高风险动作必须让用户确认,确认界面要展示工具名、参数、影响范围和预期结果,最好有 diff/预览,而不是只弹“是否继续”。确认要有超时、一次性 token、幂等键,防止重复提交;用户拒绝后要能把拒绝原因回传给 Agent,让它重新规划。参数补齐则靠对话状态和槽位:从当前会话、历史会话、用户偏好里解析“上次那个模型”“这张图”的指代,缺关键参数时先追问,不能猜高风险参数;有默认值时也要在确认页显式展示。服务端校验是最后一道闸:类型、范围、枚举、资源是否存在、当前用户是否有权限、配额和频率限制、业务规则、幂等键,全都要在服务端做,不能信任模型生成的参数;校验失败返回结构化错误码和可读原因,让 Agent 能修正或向用户解释。

一个生产者、多个消费者时,先看他们是不是同一个消费组。 集群消费模式下,同一消费组内 MQ 会把消息分给其中一个消费者,不会广播给所有实例;广播模式才会每个实例都收到一份,不同消费组则各自收到一份。所以“接收端多个”本身不是问题,问题在于消费逻辑是否假设“只有我会处理”。防重复处理要用业务状态做 CAS:UPDATE task SET status='RUNNING', owner=?, lease_until=? WHERE id=? AND status='PENDING',受影响行数为 1 的实例才执行;或者用唯一约束插入执行记录、Redis 分布式锁(带 TTL 和续租)。同一任务要保证顺序时,按任务 ID 分区到同一队列,单消费者串行处理。消费者崩溃在“结果已写 MySQL、ACK 前”这个窗口,消息会被重投,消费端必须先查状态:如果已是 SUCCESS 就直接 ACK;如果是 RUNNING 且租约过期,说明上一个消费者死了,可以接管并做幂等重放。这里本质是“至少一次投递 + 幂等消费”,不要指望 MQ 层单独给你端到端 exactly-once;再配 outbox、状态机、补偿和对账,才能覆盖写成功但 ACK 丢失、写一半失败、重复消息这些情况。

Service 里的并发异常,八成是单例 Bean 上放了请求级可变状态。 Spring 默认单例,如果 Service 用成员变量保存当前用户、当前请求参数、临时结果,两个请求会互相覆盖:请求 A 写入字段,请求 B 覆盖,A 后续读到 B 的值,轻则返回错数据,重则空指针或状态机错乱。还原时要画出执行顺序:A 设置字段 → B 设置字段 → A 读取(已是 B 的值)→ A 报错/写错;再用日志里的 request id 和线程名确认交错。修复原则是 Service 无状态:请求数据放方法参数或局部变量,必须跨方法传递时用 ThreadLocal 并在 finally 清理,或者用 request scope/prototype;共享的缓存/Map 要换成线程安全结构并明确并发语义。验证可以写并发测试,用 CountDownLatch 让多个线程同时进入,观察是否复现;上线后加请求级日志和告警。回答这类题不要只说“加锁”,先判断状态该不该共享,再谈锁的粒度和代价。