Skip to content
掌握度

时长约 60 分钟 · 12 题 + 反问。每题「问题 → 我的回答 → 完整答案 → 打分复盘」。总评:CodeAgent 全线、检索评测、大文件上传、MCP、FC 扎实;失分集中在 RAG 聊天助手模块的设计理由。

一面 ​

1. 自我介绍 — 9 分 ​

问题:请做个自我介绍,一到两分钟。

我的回答:

面试官您好,我叫 XX,是杭州某高校计算机技术硕士,27 届。竞赛方面拿过 ACM、CCPC 省赛银奖,蓝桥杯全国三等奖。技术栈主要分为后端和 AI 应用两块:后端熟练 Java、SpringBoot、MySQL、Redis、ES、Kafka;AI 应用方向掌握 MCP、Function Calling、ReAct 和 RAG 全链路开发。实习在某互联网大厂数据中台做了近半年后端研发,负责 MySQL 到 Hive 数据同步链路落地,包含 Binlog 采集、消息队列和 Spark 计算。核心难点是全量与增量数据重叠窗口问题,我们通过批次锚点 + 下游主键幂等去重,保障链路不丢不重,最终数据入湖延迟 P95 稳定在 3 分钟以内。个人做了两个 AI 项目:一个是终端 CodeAgent CLI,基于 ReAct 和工具调用,实现分层记忆、任务编排和 Git 快照安全回滚,同时做了动态阈值的上下文压缩解决长上下文超限问题;另一个 RAG 知识库,实现大文件断点续传,1GB 文件上传从 15 秒优化到 3 秒;向量 + BM25 混合检索,recall@5 从 63% 提升至 85%,支持流式回答和原文溯源。我应聘 AI 应用开发岗位,希望能把后端工程能力和大模型应用经验落地到业务中。

完整答案:定稿见 自我介绍。本场版本已接近满分,只剩两处口播修正:「分两种」→「分两块」;技能列表末尾的「等」删掉。

复盘:

  • ReAct 与前端 React 同音,面试官沉默时可自然补「就是推理加行动的 Agent 循环」消歧
  • 钩子全埋在弹药位,防薄的点(Ollama、摘要压缩)一个没提

2. CodeAgent CLI 整体介绍 — 8.5 分 ​

问题:解决什么问题?架构怎么分?执行流程怎么走?

我的回答:

CodeAgentCLI 主要是作为我在编码时的辅助工具,他可以帮我检查或者生成代码,可以进行代码审阅和任务排错等。整理架构主要分为 ReAct、Plan-and-Execute、Memory、Muti-Agent 和 HITL 这几大主要模块。一开始我只做了 ReAct,然后由于这个只能从局部进行编写设计代码,而不能从全局的思路进行代码开发,我又开发了 Plan。然后一般的模型都会将记忆写入上下文,不然每轮只是独立的对话,我又开发了记忆这个模块,这里涉及了短期记忆和长期记忆。然后如果全程只用一个智能体他可能陷入自我,所以又引入了编排者模式,引入了多智能体,不再是一个智能体单独工作。最后我发现既然是代码编写,那他不能一股脑的回答问题,有些偏离答案或者安全边际需要用户及时的去制止他,所以又开发了 HITL。整个执行流程大概是:用户输入任务,大模型先判断这是否是复杂任务,如果是复杂任务就列 plan,否则就走简单的 ReAct;plan 之后走的每一小步实际上还是小型的 ReAct,里面也可能涉及工具调用,直到模型输出没有工具调用时给出回答。这之间如果涉及修改代码或文件操作,可能还会触发 HITL 跟用户交互,看是否选择某个方案、是否赋予权限。最终所有任务节点走完,模型整理完答案返回。

完整答案:

CodeAgent CLI 是一个面向研发场景的终端 AI Agent——用户用自然语言下任务,它自己检索代码、改文件、跑命令,把任务做完。它解决的核心问题是让模型从「聊代码」变成「能安全地把活干完」。

这个项目是一步步长出来的。第一版只有 ReAct 循环,模型边想边做,适合改个文件、查个问题这种局部任务;但全局性的活它容易跑偏——走一步看一步没有全局计划,所以加了 Plan-and-Execute:把目标拆成带依赖的任务图,拓扑排序查环,按依赖分批并行,每个节点内部还是一个小 ReAct,节点失败且进度没过半就带着已完成结果重新规划。之后解决记忆:会话内历史受窗口限制,跨会话的稳定事实没人管,所以分了短期会话、长期事实、项目规则三层,长期记忆按作用域隔离、按相关度召回。再往后,单体 Agent 既有能力盲区又没有质量把关,就加了 Multi-Agent——规划、执行、审查角色分离,审查不通过带着问题重试。最后是安全:改文件、跑命令这类操作不能让模型一股脑执行,所以危险操作先过人工审批,审批后面还有路径围栏和命令黑名单兜底,任务粒度的 Git 快照保证改坏了随时回滚。

一句话总结:单 Agent 管局部、Plan 管全局、记忆管跨会话、多 Agent 管质量、审批和快照管安全——五块各解决一个真实的问题,不是堆功能。

复盘:

  • 进化式叙事结构是对的,比 9-1 的功能清单强很多
  • 开头「辅助工具 + 用例枚举」自降身价,开口应是「面向研发场景的终端 AI Agent」
  • 记忆动机讲含混:模型不维护记忆,是应用维护历史;真实动机 = 会话内受窗口限制 + 跨会话稳定事实没人管
  • 「陷入自我」没说完:Multi-Agent 的动机是角色分工 + 质量审查
  • 漏了 plan 生成后的用户审查环节(执行 / 补充需求重规划 / 取消)
  • 关联:整体设计方案、plan和DAG

3. 上下文压缩 — 6.5 分 ​

问题:触发时机和阈值怎么定?怎么压?摘要失败了怎么办?

我的回答:

动态压缩触发之后,保留 system prompt 和用户最近对话三个轮次,然后将更早的对话数据交给 LLM 进行摘要总结。触发阈值不是一个固定的比例,而是根据模型的窗口计算的:从总窗口中预留摘要输出空间和安全缓冲,例如 200k 窗口大概会在 167k 左右进行压缩,也就是 83.5% 左右。如果摘要失败了会退化成滑动窗口,保留最近 20 轮对话。

完整答案:

先说触发。Agent 每轮调 LLM 之前评估一次主对话历史的 token 数——它才是真正决定请求大小的列表。阈值不是固定比例,是从模型窗口算出来的:窗口减去摘要输出预留、再减安全缓冲。200k 窗口大约 167k 触发,83% 左右。预留的道理:压缩本身还要调一次 LLM 生成摘要,得给它留输出空间;缓冲是因为 token 是估出来的——中英文按字符折算的启发式,不精确,必须留余量。

再说怎么压。两条硬规则:最近三个用户轮次原样不动;切割点强制落在 user 消息边界——历史里 tool_call 和 tool_result 成对出现,从中间切开,下一轮请求直接协议违约。切下来的旧消息整体摘要一次,原地重建成「system + 摘要 + 确认 + 最近三轮」。摘要也不是随便压,要求保留用户目标、已完成操作、已确认结论和未完成事项。

最后失败怎么办,三个选项里选跳过。重试:失败多是网络、限流这种瞬时问题,而压缩发生在马上要调 LLM 的热路径上,重试是拿用户的等待赌一个大概率还失败;硬截断:机械截断保不住工具对,也保不住目标结论,半截历史比完整但略超限更危险;跳过:零代价——阈值本来就留了缓冲,跳一两轮不会立刻撑爆,下一轮重新评估再试。原则就一句:失败无害,下次还有机会。

复盘:

  • 串台事故(本场最大扣分):滑窗 20 条是 RAG 项目的机制;CLI 的设计是跳过本次压缩、保留原始完整历史——与简历原话「摘要失败时跳过压缩,避免破坏原始消息历史」一致,且滑窗丢得比压缩更多,逻辑也不成立
  • 漏工具调用边界(简历写了「及工具调用边界」,被追问没讲出来)
  • 漏三选一论证——这是本题真正的出彩点
  • 关联:Memory系统「摘要失败为什么跳过」

4. 混合检索与评测 — 8.5 分 ​

问题:为什么混合检索?怎么实现?63%→85% 怎么验证的?

我的回答:

因为如果只是单纯的关键词检索,用户只要换种问法就会查找不到。比如知识库中有退换货规则,用户说我不想要这款产品了,这个时候就会检索不到。如果只有单纯的语义匹配,又会对专有名词、完全一样的词汇忽略,所以我采用了混合检索。这里主要做了三层:第一层是向量召回,先将用户的问题通过向量模型向量化,再根据查询向量做 HNSW 近似检索,把召回窗口设置成最终返回值的 30 倍,因为后面还会再筛。第二层是关键词加权限过滤,用户没有权限阅读的文件会被过滤掉,召回片段中没有涉及用户问题关键词的也会被过滤掉。最后一层是 rescore 二阶段重排,对召回窗口的结果用 BM25 做关键词重打分,和向量做线性融合,向量分权重 0.2、BM25 权重 1.0,关键词主导排序。这个数据是通过我手动从知识库中获取三十多个文档,以用户的视角对某个文档中的某条信息进行提问,并标注正确答案,看能否正确完整召回计算出来的。虽然说不是很科学,但得出混合检索比单检索好这个结论是足够的。

完整答案:

动机:企业知识库里用户问题和文档的表述往往不一致。纯关键词,同义改写就漏;纯向量,专有名词、术语编号这类精确词又不敏感。所以做混合,各干各擅长的:向量管找得全,关键词管排得准。

实现是三层,在一次 ES 请求里完成。第一层向量召回:问题过 embedding 模型,HNSW 近似检索,召回窗口放到最终返回量的 30 倍——因为后面关键词过滤和重排会大量淘汰,召回阶段宁可多拿。第二层过滤:一个 bool 查询,must 条件要求命中查询关键词,把纯语义但不沾字的候选杀掉;权限过滤放在 filter 子句——不参与打分、在查询层生效,越权文档根本不进候选集,这和检索完再在应用层过滤有本质区别。第三层 rescore 二阶段重排:对召回窗口用 BM25 重打分,和向量分线性融合,向量 0.2、BM25 1.0,关键词主导排序。

验证是控制变量的 recall@5 对比。基线是纯 BM25——代码里本来就有这条降级路径,权限过滤两边完全一致,唯一变量就是向量召回加重排,所以提升可归因。评测集从真实文档挑了三四十份,每份手写一条用户视角的问题,标注应命中的文件和块。跑下来纯关键词 63%,混合 85%。度量选 recall@5 是因为下游聊天拼上下文引用的就是 top5 量级,第五位之外等于没召回。局限我清楚:集子只有几十条、单人标注没有仲裁、只测召回不测精确率——支撑方向性结论够了,要指导调参得再扩。另外主动交代一个取舍:must 会漏掉零字面重叠的纯同义改写,当时是拿它换精确性;要放开就把 must 降成 should 配 minimum_should_match,有评测数据我再动这个旋钮。

复盘:

  • 三层实现完整、参数全、主动承认局限——本场结构最好的一答
  • 例子反噬:「我不想要这款产品了」是零字面重叠场景,自己的 must 实现同样会漏——换部分改写的例子,或主动亮取舍
  • 漏基线(纯 BM25 降级路径、权限一致、唯一变量)——评测可信度的核心
  • 漏 recall@5 为什么是 5:下游拼上下文引用的就是 top5 量级
  • 关联:知识库检索模块

5. 引用溯源 — 忘记 ​

问题:「编号即索引」怎么设计的?模型输出不存在的编号怎么办?

我的回答:忘记了。

完整答案:

出发点:防伪造不能靠提示词求模型「别编」,要把出处的真相从模型手里拿走。做法:检索结果拼进 Prompt 前先编号成 [1][2][3],模型只看得到编号;编号背后指向的真实出处——文件指纹、页码、锚点——存在服务端的映射表里,模型从头到尾接触不到。它回答时只能「引用几号」,写不了出处内容。回答完成后,完成通知把编号到出处的映射推给前端;用户点角标,后端查映射跳到原文预览。所以「伪造出处」在架构上被消掉了——不是模型不想编,是它没有编的材料。

输出不存在的编号:服务端映射里只有真实注入过的编号,模型编一个 [7],前端查引用详情查不到,就降级成普通文本——幻觉引用最多显示成一个无效角标,绝不会指向错误来源,危害被封在显示层。实测主流模型对编号引用遵从度很高,因为编号材料就在上下文里,照抄是成本最低的动作。

映射本身有三级容灾:当前会话内存、Redis 生成快照、历史行的 JSON 字段——在线、断线重连、查历史三个场景引用都能还原。代价是模型不能改写引用描述,灵活性低一点;但知识库场景可溯源远比灵活重要。

记忆口诀:编号进 Prompt,真相在服务端,伪造只掉角标。

6. Git 快照回滚 — 8.5 分 ​

问题:怎么做到不碰用户 Git 历史?为什么不用 git stash / 项目内开分支?

我的回答:

每个任务前我都拍两张快照,任务开始前同步拍,任务结束之后异步拍。用户点回滚时,我不做任何的 checkout 和 reset,而是从快照仓库把目标版本的文件清单读出来,和当前工作区做差异化比对,多出来的删掉,变了的写回,这里全程是文件的操作。恢复之前还会给当前状态拍一张恢复前的快照,保证回滚这个操作也是可以撤销的。快照信息放在一个独立的快照仓库中,用的是 JGit,在用户目录下按路径哈希建一个隐藏仓库,让他的工作目录指向用户项目根,但所有的提交都写进外面这个隐藏仓库——加文件、存版本扫的都是用户的代码,写的都是我的仓库。用 git stash 或者开分支,会有可能把用户暂存内容顶掉。

完整答案:

隔离怎么做:快照仓库不放项目里,放用户目录下按路径哈希定位的隐藏目录。JGit 打开时 gitDir 指向独立仓库、worktree 指向用户项目根——所以 add、commit 扫的是项目文件,但 index、对象、提交全写进我的仓库,代码里根本没有打开项目 .git 的路径;.git 本身还在排除列表里,连快照都不扫。哈希分两段:父目录一段、项目根一段——不同父目录的同名项目不撞车,项目搬家自动生成新历史,不会错绑旧快照。

时机分两种:任务开始前同步拍、结束后异步拍。pre 必须同步——它决定回滚基准,异步的话 Agent 可能已经开始改文件,基准就脏了;post 只做对比审计,异步落盘不拖慢最终回复。决定回滚基准的要严格时序,只做记录的可以放宽。

回滚不做任何 checkout、reset——Git 在这里只当内容寻址存储,不做版本控制操作。从快照仓库读目标树,和当前工作区做差异:多出来的删、变了的写回、空目录清掉,全程文件操作。恢复前先拍一张 pre-restore 快照,回滚本身也可撤销;而且只允许恢复到 pre-turn——post-turn 里可能已经包含改坏的产物,不满足「回到任务开始前」的语义。

为什么不用 stash 或开分支:stash 是栈语义,pop/apply 直接改用户工作区,还和用户自己的 stash 共享命名空间,可能把人家暂存的内容顶掉;项目内开分支要动 HEAD 和 ref,用户的分支、reflog 全被碰——「不碰用户 Git 历史」是红线,两条路都封死。

复盘:

  • 机制全对、无事实错误
  • 双快照只讲做法没讲为什么——「决定回滚基准的要严格时序,只做记录的可以放宽」
  • 面试官给了两条替代路只堵一条:开分支动 HEAD/ref 也要封死
  • 缺题眼句:「Git 在这里只当内容寻址存储,不做版本控制操作」
  • 关联:Git快照回滚

7. chunk 切分 — 7 分 ​

问题:文档的 chunk 怎么切?为什么这么切?(9-1 栽过的原题)

我的回答:

文档的 chunk 我们会按语义进行切分,默认最大的 chunk 大小是 512,会先按段落分,如果段落超出这个大小再按句子分,最后再到词,这样可以尽量把相关的内容不被拆分,放到同一个 chunk 中。

完整答案:

切块是结构感知的分级切分,不是无脑定长。文本先按段落切,段落超过上限——512 个 token——再降级按句子切,句子还超再到词,目标始终是语义完整的内容待在同一个块里。512 是权衡:块太大,一个块混多个主题,向量表示被稀释、检索精度降;太小,语义碎片化,召回了也缺上下文。相邻块保留一部分重叠,因为切句会切断指代——「它」「上述规定」指的东西可能被切到两块里。

两个增强。第一,每个块带页码和锚点:PDF 走页级解析,块知道自己在第几页哪一段——这是回答能溯源到「第 X 页」的地基,和引用机制是同一条线;切块前还先滤掉页眉页脚、图片说明这类噪音,扫描件走 OCR 兜底。第二,父子块:检索时用子块匹配保精度,命中后把所在父块喂给模型保上下文——检索粒度和生成粒度分离。当前实现是页级切块带锚点,父子块是明确的演进方向,等评测数据显示「命中了但不够用」时上。

背后是同一个判断:切块的所有决策都要往下游看——元数据服务溯源和权限过滤,块大小服务向量质量和上下文完整。怎么切不是解析器的事,是检索和生成两个下游倒逼出来的。

复盘:

  • 比 9-1 进步(有了语义分级切分),但作为自己标记过的原题,深度还不够赢
  • 512 无单位无理由;overlap 反而消失了(切句切断指代)
  • 漏页码锚点元数据(溯源线)和父子块(9-1 复盘自己记的标准答案)
  • 关联:文件上传模块

8. MCP — 8 分 ​

问题:MCP 是什么?接入后架构有什么变化?本地和 MCP 工具调用链路的区别?

我的回答:

MCP 全称是模型上下文协议,主要是将工具调用的接口进行统一,就像 USB 一样。主要有两种传输形式:一种是 stdio 本地传输,还有一种是 HTTP 远程传输。接了 MCP 之后,我的工具调用不仅能调用本地已有的工具,还能调用其他网上提供的工具,只不过我将外部工具都做了一层需要人工确认许可的权限,通过加上前缀和本地工具进行区分,防止覆盖。本地工具是应用内的函数,自己写自己定义的;而 MCP 是把工具从应用中拆下来,变成独立的服务进程,任何支持 MCP 的客户端都能直接使用。MCP 工具连接上来之后会被包装成和本地工具一样的形式,注册进同一个工具注册表,在模型侧看来和本地工具没有任何差别。

完整答案:

MCP 是 Model Context Protocol,模型上下文协议,解决的问题是模型用工具时每家接口各一套、接一个接一次。它把这层统一了——类比 USB-C:工具提供方把能力封装成 server,写一次,任何支持 MCP 的客户端都能即插即用。协议规定了三类能力:tools、resources、prompts,我项目里主要用 tools。传输两种:本地 stdio 管道,远程 Streamable HTTP。

接入后架构上最大的变化是工具从编译期写死变成运行时可扩展:MCP 工具是独立服务进程,连接时走初始化和工具发现,把每个工具的 name、description、inputSchema 动态拉回来,包装成和本地工具统一的接口,注册进同一个工具注册表——模型侧完全无差别,选工具照样靠描述路由,一套提示词兼容两类工具。

调用链路的区别:本地工具是进程内函数调用;MCP 是跨进程——参数按 schema 序列化成 JSON-RPC,经 stdio 或 HTTP 发给 server 执行再回传,多了一个故障面,要处理 server 挂掉和超时。安全上的区别:所有 MCP 工具统一加 mcp__ 前缀,一物两用——命名空间隔离防止覆盖本地工具,同时前缀本身就是审批路由的 key:外部 server 不确定会干什么,带这个前缀的默认全部过人工审批;本地工具只有写文件、跑命令那几个危险操作要审批。

复盘:

  • 「统一注册表、模型侧无差别」「外部工具默认审批」——有真实接入经验的答案
  • 协议面只答了 tools,还有 resources、prompts
  • 链路机制层漏了:跨进程 JSON-RPC、初始化 + 工具发现、server 故障面
  • 远程传输准确名:Streamable HTTP;mcp__ 前缀一物两用
  • 关联:人工审批

9. ReAct 自主检索 vs 固定流水线 — 5.5 分 ​

问题:为什么让模型自主决定检索?自主的代价怎么控制?

我的回答:

我用了白名单的机制,像单纯寒暄类的内容不过检索,然后把查询分流交给模型判断而不是代码分类器。其他的我忘记了。

完整答案:

固定「先检索再回答」的流水线有两个毛病。一是成本:每次对话都强制检索,问「你好」也要打一次 embedding 加一次 ES。二是质量不可控:检索结果无论好坏全塞给模型,检索失败或者结果不搭时,回答直接被污染。

所以用 ReAct 把决定权交给模型:需要事实依据时它主动调检索工具,拿到结果觉得不够还能再查一轮,闲聊就直接回答。但自主性有代价——循环失控,模型可能无限调工具。笼子是两层预算加强制收尾:最多四轮、最多八次工具调用,预算耗尽还有一轮强制收尾——把工具列表清空,逼模型基于已有结果作答,防止它陷在工具循环里。检索原则本身是「知识库优先」的强制检索,配五类免检索白名单:企业内部知识模型其实没见过,是幻觉高发区,所以默认都查,纯寒暄这类才免检——分流交给模型判断,不写死代码分类器。

还有个安全细节:检索工具内部就是权限混合检索,模型自主调工具等于自动带上了用户的权限边界——自主性不会引入越权。

记忆骨架:聊天助手模块所有设计都是「自主性是一笔交易」的对仗——收益(省成本、质量可控、多轮迭代)换代价(循环失控),代价有笼子(预算 + 强制收尾)。

10. 大模型链路稳定性 — 忘记 ​

问题:做了哪几层?和标准熔断器差距在哪?

我的回答:忘记了。

完整答案:

大模型是系统里最不可控的外部依赖,我做了四层,从用户侧往依赖侧排。

第一层兜底回复:LLM 调用异常统一捕获,用户看到的是「AI 服务暂时不可用,请稍后重试」的友好提示,不是 5xx 堆栈。第二层功能降级:embedding 服务不可用时,检索自动退化成纯 BM25 加同样的权限过滤——功能保留、语义能力降级,系统还能用。第三层多维限流:IP 级、账号级、每日次数、token 余额四道闸,把故障流量挡在依赖调用之前——限流不只是省成本,是故障时不让流量继续打已经半死的外部依赖。第四层供应商热切换:密钥失效或服务劣化时,管理端秒级切到备用模型,密钥加密落库、不用重启——这条在换供应商时真实验证过,换供应商只改配置不改代码。

和标准熔断器比的差距我清楚:现在没有失败率的自动统计,没有熔断打开和半开探测——切换是运维手动触发,重试是固定退避不是自适应。演进方向是按依赖维度引入 Resilience4j:失败率过阈值自动打开,冷却后放少量请求探测,恢复自动闭合——把「人盯着切」变成「系统自己收敛」。

记忆口诀:兜底管体面、降级管功能、限流管流量、热切管供应商;差距一句话——熔断器是自动的,我的是手动的。主动讲差距是加分项:把「做过」和「做全」分开。

11. 大文件上传 — 8.5 分 ​

问题:分片上传和断点续传怎么实现?进度记在哪?15s→3s 优化的是什么?

我的回答:

分片是靠前端切分的,会被切成每次 5MB 的文件,然后 4 并发传给后端。后端通过 Redis、MySQL 和 MinIO 三个共同去判断该分片是否存在:Redis 里有个位图,专门存储某个分片是否已经存在,记录进度;MySQL 里存分片元数据;MinIO 里存分片内容本身。查询按开销从低到高:先查 Redis,然后是 MySQL,最后是 MinIO。如果三方都有,说明分片存在,直接跳过;只有 Redis 中没有,说明只是缓存丢了,回填位图即可;剩下的走分片重传逻辑,先把脏状态清掉,再当新分片处理。写入也是有顺序的:先写 MinIO,然后是 MySQL,最后是 Redis,这样可以保证任何一步失败都可以恢复。断点续传的原理也一样:先查当前状态,解析出已传列表返回给前端,前端再从中扣除进行上传。1GB 从 15s 到 3s 是分别单流上传和四并发上传对比的结果。

完整答案:

前端选完文件先分块算 MD5 做指纹,按 5MB 切片、四个并发上传。5MB 不是随便选的:合并用的 composeObject 走 S3 分片拷贝协议,每个源分片最小 5MiB,对齐的就是这个下限。

后端幂等去重靠三个存储共同判断:Redis 位图记进度、MySQL 记分片元数据、MinIO 存分片内容,查询按开销从低到高——先查位、再查库、最后才发网络请求做对象存在性检查。三种结果:三方都在,幂等返回不重复存;只有 Redis 缺,缓存丢了,从数据库回填位图照样幂等返回;其余不一致,清脏状态当新分片走完整写入。写入顺序是先 MinIO、再 MySQL、最后标位图:内容写失败干净重试,元数据写失败留的孤儿对象会被确定性路径复写,位图失败直接吞——数据库才是事实源。整个上传没有一处依赖分布式事务,靠写入顺序加幂等收敛。

断点续传:续传前先调状态接口,服务端一次 GET 拉整个位图解析出已传列表——1GB 文件的位图才 25 字节,一次往返拿全部进度;位图丢了就从 MySQL 重建,所以 Redis 怎么挂进度都不丢。前端拿全集减已传,四并发只补缺失分片。

15s 到 3s 归因两半:四个并发把带宽吃满,15 ÷ 4 ≈ 3.75 和实测吻合;重传粒度从整文件降到单分片,弱网收益最大。

复盘:

  • 全场最扎实:三方校验判断矩阵、开销排序、写入顺序可恢复,全部准确
  • 数字只答了测量没答归因(4 并发吃满带宽 + 重传粒度变化)
  • 5MB 缺出处(S3 协议下限 5MiB);断点续传漏「Redis 怎么挂进度都不丢」的自愈卖点
  • 关联:文件上传模块

12. Function Calling 原理 — 8 分 ​

问题:模型怎么「调用」本地函数?它执行函数吗?参数不合法怎么办?

我的回答:

一开始工具会被全部写入工具注册表中,然后将工具列表名字、描述这些作为内容写入 system prompt 中。模型根据这些描述判断当前是否会调用其中某些工具。模型本身是不会执行这些工具的,这些工具的执行本质上还是通过应用来完成实现的。完成之后会将 result 写入 prompt 回传给模型,这个时候模型就能知道当前工具的执行情况以及结果,判断下一步该做什么。如果模型返回的参数不合法,会在 tool_result 中返回参数不合法返回给模型,模型下次 ReAct 之后就会自主进行修正。

完整答案:

Function Calling 的本质:模型不执行任何东西,只产出「调哪个工具、带什么参数」的结构化意图,执行永远在应用侧。工具注册时声明 name、description、inputSchema,这些定义随请求发给模型;模型要调工具时,输出符合 schema 的结构化 JSON 参数,放在协议专门的 tool_call 字段里返回——应用能可靠解析,不用从文本里抠。

应用拿到 tool_call 先按 schema 校验,合法就执行真正的函数,结果作为 tool_result 回传,模型看到结果再决定下一步——继续调工具或输出最终答案,这个循环就是 ReAct 的骨架。一轮里模型也可能返回多个互不依赖的工具调用,可以并行。

参数不合法有两道。第一道 schema 校验,不过关就不执行,把「哪个参数、什么错」具体地作为 tool_result 回传——错误信息具体,模型下一轮基本能自我修正;第二道语义层,参数合法但值越界(路径跑出项目根这种),安全层直接拒绝,不交给模型。反复修正失败达到上限就终止本轮上抛,不能无限循环。

复盘:

  • 「模型不执行、执行在应用侧」的边界答对了——这题的及格线
  • 漏 JSON Schema(「参数不合法」能成立的前提);校验有两道(schema 校验回传 + 语义层安全拦截 + 硬上限)
  • 可补亮点:一轮可并行多个无依赖工具调用

反问环节 ​

我的反问:「我这轮表现有哪些不足,哪里需要补充学习。」

复盘:模拟可以这么问,真面试是低价值反问——把面试官架在评判位,多半得到客套话。备三个业务/技术向反问:

  1. 团队 RAG / Agent 落地在什么阶段?模型选型和效果评测怎么做?
  2. 这个岗位入职半年的主要产出是什么?校招生带教节奏如何?
  3. 团队怎么看 Agent 自主性和安全的平衡?

总复盘 ​

得分曲线:9 / 8.5 / 6.5 / 8.5 / 忘 / 8.5 / 7 / 8 / 5.5 / 忘 / 8.5 / 8,均分 7.8。

强项(保持):CodeAgent 全线、RAG 检索与评测、大文件上传、MCP、FC——笔记厚的地方全部扛住了深挖。

弱项(一条主线):三次失分全在 RAG 聊天助手模块的设计理由,外加一次跨项目串台。行动项:

  • [ ] 重读 聊天助手模块,背三个口诀:编号进 Prompt / 自主性是交易 / 兜底降级限流热切
  • [ ] 修正串台:滑窗 20 条属 RAG,CLI 压缩失败 = 跳过保留原历史
  • [ ] 补 chunk 策略 + 父子块 + 页码锚点线
  • [ ] 准备三个业务向反问

本站仅供个人学习使用,请勿外传