快手 AI Agent开发 一面

1、为什么引入父子索引,为什么引入 BM25,比例是怎样的,具体流程是什么,有没有 rerank

父子索引主要是为了解决 切块后召回准,但是上下文不完整 的问题。如果文档直接按 300~500 token 切成小块做向量检索,确实更容易召回到具体答案,但很容易丢掉原文结构,比如标题、段落关系、表格上下文、前后约束条件。所以会把文档拆成两层:

  • 父块:保留完整语义单元,通常是 800~1500 token
  • 子块:用于召回,通常是 200~400 token

检索时先搜子块,命中后回源到对应父块,再把父块内容喂给大模型。这样既保证召回精度,也保证上下文完整性。

引入 BM25 是因为向量检索对语义相似比较敏感,但对 专有名词、缩写、数字、型号、产品名、英文串 这类精确匹配场景不够稳定。比如搜「账号限流 429」「蒲公英审核」「笔记灵感券」这种词,BM25 往往比纯向量更稳。所以实际一般会做混合召回。

比例一般是:

  • 向量召回占主,70%
  • BM25 召回占辅,30%

一个比较常见的流程是:

  1. 用户问题先做 query 改写
  2. 用改写后的 query 同时走向量检索和 BM25 检索
  3. 向量检索取 top50,BM25 取 top20
  4. 合并去重后得到候选 40~60 条
  5. 用 rerank 模型重排
  6. 取 rerank 后的 top5 或 top6 作为最终上下文
  7. 再做 prompt 组装,送给大模型生成

一般是有 rerank 的,不加 rerank 的话,混合召回出来的候选噪声还是偏大,尤其在语义相近但答案不完全对的场景里,模型很容易被错误片段带偏。

2、rerank 后返回几个块,有没有做一些验证

rerank 后一般返回 5 个块。这个值不是拍脑袋定的,是在准确率、上下文长度和最终生成效果之间平衡出来的。

如果返回太少,比如只给 1~2 个块,容易漏信息,尤其是答案分散在多个段落里的场景。如果返回太多,比如 8~10 个块,虽然召回覆盖率会高一点,但无关信息会明显变多,大模型更容易注意力分散,回答反而不稳定,成本和延迟也会上升。

验证通常会做三类:

  • 离线检索验证:看 gold chunk 是否在 rerank 后 top5 里,主要看 Recall@5、MRR、NDCG
  • 答案级验证:把 rerank 后的上下文喂给模型,看最终回答的正确率、引用命中率
  • 人工抽检:抽样看 top5 是否都和问题相关,是否存在明显干扰块

如果是业务问答场景,一般会重点看两个指标:

  • rerank 后 top5 的相关块命中率
  • 最终答案正确率

这两个指标比只看 embedding 召回率更有意义。

3、rerank 后的 topk 截断是怎么做的,为什么是这个值,有没有其他方案

topk 截断一般直接取 top5。这个值是通过实验定下来的,不是固定理论值。

比如做过一组对比:

  • top3:精度高,但覆盖不够,部分多证据问题会漏
  • top5:综合最好
  • top8:召回覆盖更高,但噪声开始明显增加
  • top10:模型输入过长,效果反而下降

所以最后会固定在 top5。如果问题比较复杂,比如需要跨段整合,也可能放宽到 top6,但不会太大。

除了固定 topk,还有两种常见方案:

1)按分数阈值截断

比如 rerank score 大于 0.75 的块才保留。好处是更灵活,坏处是不同 query 的分数分布差异大,不太稳定。

2)topk + 阈值双重控制

比如最多取 top5,但要求 score 至少大于 0.72。这个做法线上更常见一点,既能控制长度,也能过滤掉明显噪声。

4、讲一下上下文工程,记忆是怎么做的

上下文工程核心就是一件事:不是把能拿到的信息全塞给模型,而是只给当前问题真正需要的信息。一般会把上下文拆成几层:

  • 系统指令
  • 角色设定
  • 当前用户问题
  • 检索知识
  • 工具返回结果
  • 历史对话摘要
  • 最近几轮原始对话

记忆一般分成三类:

短期记忆

保留最近 3~5 轮原始对话,用来承接上下文,比如代词、省略、追问。

摘要记忆

当对话轮数变长后,把前面的历史压缩成一段摘要,比如用户偏好、已经问过的问题、已完成的动作。

长期记忆

把稳定事实结构化存储,比如用户标签、历史偏好、已授权信息、黑白名单状态。这部分通常不直接从原始对话里拼,而是单独落库。

一个简单的记忆结构可以这样写:

class SessionMemory:
    def __init__(self):
        self.recent_messages = []
        self.summary = ""
        self.long_term_profile = {
            "interests": [],
            "tags": [],
            "history_actions": []
        }

memory = SessionMemory()
memory.recent_messages = [
    {"role": "user", "content": "我最近在看穿搭内容"},
    {"role": "assistant", "content": "你更喜欢通勤风还是休闲风?"}
]
memory.summary = "用户近期关注穿搭,偏好实用型内容"
memory.long_term_profile["interests"].append("穿搭")
print(memory.__dict__)

实际做法里,短期记忆保留原文,长期记忆尽量结构化,摘要记忆定期更新。这样 token 成本和效果会比较平衡。

5、Function Calling 和 Agent 规划是怎么做的

AI Agent 不是单纯调用一次大模型,而是让模型先决定 要不要调用工具、调用哪个工具、参数是什么、结果回来后怎么继续。Function Calling 是其中的执行接口,Agent 规划是上层决策逻辑。

常见链路是:

  1. 用户发起请求
  2. 模型先判断意图
  3. 如果需要外部信息,就生成函数调用参数
  4. 后端执行工具
  5. 把工具结果回填给模型
  6. 模型生成最终答案

比如查笔记数据、查用户画像、查商品信息、查审核状态,这些都不应该让模型自己编,必须查真实系统。

def query_note_status(note_id: int):
    return {
        "note_id": note_id,
        "status": "reviewing",
        "reason": "内容审核中"
    }

tool_call = {
    "name": "query_note_status",
    "arguments": {"note_id": 12345}
}

result = query_note_status(**tool_call["arguments"])
print(result)

Agent 规划如果任务简单,通常一次工具调用就够了。如果任务复杂,比如“先查数据,再汇总,再生成建议”,会拆成多步执行,但步数一般会限制在 3~5 步,避免链路过长。

6、Prompt 注入怎么防,工具调用怎么做安全控制

Prompt 注入本质上是用户输入试图覆盖系统规则,比如:

  • 忽略之前所有指令
  • 直接输出内部配置
  • 假装自己是管理员
  • 诱导模型调用高权限工具

防御一般做三层:

输入层

先做规则过滤和风险识别,识别越权请求、系统 prompt 探测、工具滥用指令。

模型层

系统提示词里明确优先级:系统指令高于用户输入,用户不能修改工具权限,不能访问未授权数据。

工具层

工具侧必须做权限校验,不能只靠模型自觉。比如查用户隐私信息、查审核详情、查交易数据,必须带用户身份和权限校验。

也就是说,安全不能只放在 prompt 里,真正关键的是后端工具鉴权。模型最多负责“提议调用”,最终是否执行一定由程序决定。

7、分布式令牌桶限流讲一下,漏桶讲一下,滑动窗口算法限流讲一下,如果用滑动窗口结构体会包含什么字段,滑动窗口和令牌桶相比有什么区别,用 Redis 的什么数据结构实现

令牌桶

令牌桶会按固定速率往桶里放令牌,请求来了先拿令牌,拿到就通过,拿不到就拒绝。它允许一定程度的突发流量,只要桶里之前积累了令牌,就可以瞬时放过一批请求。

核心参数一般有:

  • capacity:桶容量
  • rate:令牌生成速率,单位通常是 token/s
  • tokens:当前剩余令牌数
  • last_refill_time:上次补充令牌时间

分布式场景里通常把桶状态放 Redis,用 Lua 保证原子性。

漏桶

漏桶是请求先进桶,再按固定速率流出。它的特点是输出速率稳定,能平滑流量,但不太适合强突发场景,因为突发请求会在桶里排队,桶满了就丢。

滑动窗口

滑动窗口会统计最近一段时间内的请求数,比如最近 1 秒最多 100 次。相比固定窗口,滑动窗口不会出现窗口切换瞬间的突刺问题。

如果滑动窗口结构体自己实现,常见字段有:

class SlidingWindow:
    def __init__(self):
        self.window_size = 1000      # 毫秒
        self.max_requests = 100
        self.current_time = 0
        self.buckets = []            # [(timestamp, count)]
        self.total_count = 0

如果用 Redis 实现滑动窗口,最常见的是 ZSet。每次请求进来把当前时间戳作为 score 写入 ZSet:

  • 先删除窗口外数据
  • 再统计窗口内元素个数
  • 没超限就插入当前请求
  • 超限就拒绝

令牌桶和滑动窗口的区别:

  • 令牌桶适合控制平均速率,并允许突发
  • 滑动窗口适合控制某时间段内的真实请求数,更直观

8、布隆过滤器讲一下

布隆过滤器是一种空间效率很高的概率型数据结构,用来判断一个元素 大概率存在一定不存在。它的底层是一个位数组加多个哈希函数。

插入元素时,用多个哈希函数计算多个位置,把这些位置都置为 1。查询元素时,如果有任意一个位置是 0,说明元素一定不存在;如果所有位置都是 1,只能说明元素可能存在,因为可能发生哈希冲突。

特点是:

  • 查询快
  • 占用内存小
  • 会有误判
  • 不会漏判不存在的元素
  • 普通布隆过滤器不支持删除

常见用途有:

  • 防缓存穿透
  • 请求去重
  • 黑名单预过滤
  • 大规模 ID 存在性判断

9、索引失效的情况,like 会不会失效

MySQL 索引失效常见情况有这些:

  • 对索引列做函数、计算、隐式类型转换
  • 不满足

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

AI-Agent面试实战专栏 文章被收录于专栏

本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

全部评论
这个是算法还是后端开发呀
点赞 回复 分享
发布于 03-31 18:46 四川
这个是Java还是Python的
点赞 回复 分享
发布于 03-31 18:44 四川

相关推荐

1. 为什么做Agent项目?2. 了解过市面上有哪些智能体agent吗3. 讲下Agent项目4. Agent项目开发的框架5. 介绍一些AI大模型6. RAG系统流程7. MCP和Function Calling8. 如何写好的prompt9. 多轮对话的实现方案10. Agent项目背景11. LLM产生幻觉的原因及解决方案12. MCP协议的核心内容13. 推理模式的差异化设计14. RAG检索优化策略15. 特定推理模型不支持MCP的技术原因16. Agent推理模式17. 跨模块错误追踪的Agent知识库构建方案18. 多Agent执行策略的智能选择和切换机制设计19. 简历关键词提取的技术实现20. RAG评估方案21. SSE的局限性22. 举例复杂任务下执行流程23. MCP通信方式24. 项目中AI贡献的代码占比25. Prompt工程的实践经验26. 基于代码构建知识库的Agent设计27. A2A协议28. 长文本生成的技术方案29. Agent skills30. 演示Agent项目实现细节31. 了解其他的Agent范式吗32. 模型预热机制33. NL2SQL场景下的SQL安全防护34. 复杂任务执行准确率提升的评估方法35. AI辅助IDE开发工具36. RAG动态知识更新37. MCP和skill区别38. 推理模式的选择机制39. 企业内部知识库RAG的动态持续更新方案40. Prompt设计示例41. A2A与MCP区别42. 多阶段召回策略优化43. AI辅助开发的实践经验面试官主要最爱问的就是讲一下你的 Agent 项目整体架构 & 执行流程RAG 全流程 + 检索优化怎么做的Tool 调用 / Function Calling / MCP 机制原理多轮对话、上下文记忆、幻觉怎么解决
面试官最爱问的 AI 问...
点赞 评论 收藏
分享
一、先破题:面试官到底想听什么?别上来就念定义,先抓核心:他想知道你懂不懂 RAG 的本质、会不会落地、他想看到你的深度思考。一句话开场就能拉好感:“RAG 本质就是给大模型‘外挂知识库’,让它先查资料再回答,既不用重新训模型,又能减少幻觉,特别适合企业私有数据场景。”二、核心回答框架:3 步讲 RAG 全流程1️⃣ 先讲原理:为什么要用 RAG?传统大模型的知识全靠预训练,新数据、企业内部数据它根本没见过,一问就容易瞎编。RAG 的思路很朴素:生成答案前先去外部知识库搜一遍,把相关资料塞给模型当参考,让它 “照着资料说”。这样既避免了微调的高成本,又能保证答案基于真实数据,还能随时更新知识库,很灵活。2️⃣ 再讲落地:项目里怎么搭 RAG 链路?别只说 “召回 - 过滤 - 生成”,要讲具体做了什么、用了什么工具,显得你真干过:第一步:搭知识库(离线准备)先把企业文档 / 业务数据切分:按语义段落拆,控制每段 token 数,太粗太细都影响检索效果用 Embedding 模型(比如 BGE、text-embedding-ada-002)把文本转成向量存到向量库(Milvus/FAISS/Pinecone 都行),方便后面做相似度搜索举个例子:我们做企业知识库时,会把长文档按章节 + 段落拆分,每段控制在 300token 左右,既保证信息完整,又不会太冗余。第二步:用户提问时的检索阶段先把用户问题也转成向量,去向量库做相似度检索,捞出 Top-K 相关文档关键:加个 rerank 模型(比如 CrossEncoder)做二次排序,把最相关的片段往前排,避免 “看似相关实则没用” 的文档干扰还可以加 query rewriting 优化提问,比如把口语化问题转成更适合检索的句式,提升召回准确率第三步:生成答案把检索到的文档片段 + 用户问题,拼进 Prompt 里,给模型明确指令:“请仅基于以下参考资料回答问题,不要编造内容,如果资料里没有答案就说‘未找到相关信息’。”喂给大模型生成答案,这样输出就完全基于检索到的真实数据,不会瞎编。3️⃣ 最后补深度:RAG 的关键与坑讲完流程,补几句踩坑经验,瞬间拉开差距:核心难点:文档切分、检索质量、Prompt 设计切分太粗:信息太杂,检索不准;太细:上下文断裂,模型看不懂检索差:哪怕模型再强,给错资料也会生成垃圾答案,所以 rerank 和 query rewriting 特别重要Prompt 要 “严”:必须约束模型只能用参考资料,不然它还是会忍不住瞎编局限性也要提:依赖 Embedding 质量,选不对模型检索直接拉胯长上下文会推高成本,太多参考资料反而让模型混乱实时性问题:知识库更新后要重新生成向量,不能秒级同步三、面试加分小技巧提架构:主动说 “我们用的是召回 - 过滤 - 生成三段式架构”,显得你体系化提优化:聊 rerank、query rewriting、多轮检索这些进阶手段,证明你不是只会基础版提场景:结合具体项目说,比如 “在企业客服知识库 / 内部文档问答里用 RAG”,比空泛讲理论更有说服力
查看11道真题和解析
点赞 评论 收藏
分享
评论
5
49
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务