卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 大模型面经

  • Transformer原理

  • RAG检索增强

  • 模型微调

  • Agent智能体

  • AI编程

  • 真实面经

    • 字节Agent开发四面面经
    • 字节番茄小说Agent实习一面
    • 阿里淘天Agent研发岗面经
    • 百度Agent开发岗面经

    # 百度 Agent 开发岗面经:工具从十个变成上千个,还能选对吗?

    面经时间:2026年9月24日

    前面那篇阿里淘天 Agent 研发岗面经,重点是代码 Agent 如何读仓库、改文件、运行测试和完成交付。

    这次看百度 Agent 开发岗。问题从个人背景和实习项目切入,接着追问:前端按钮和 Agent Chat 怎么分工,零散功能算 Skill 还是 Tool,十个工具怎么选,上千个工具又怎么办。后半场还有两道 Coding,以及 Java 集合、Go Channel。

    能调用工具只是起点。工具变多、对话变长、调用失败以后,系统还能不能把任务做完,才是这组题的主线。

    原始面经只有问题,没有候选人的完整经历和作答。

    以下为补充的参考回答,第一人称为口述示例。全文用“内部工单助手”串联技术题:支持查工单、查知识库、生成处理建议和创建工单,功能规模、个人职责及完成度都要按自己的经历替换。

    本次面经问题:

    • 一、介绍个人背景与实习经历
    • 二、具体介绍一下实习项目
    • 三、如何理解 Agent?开发会遇到哪些问题?前端入口与 Chat 怎么分工,零散功能属于 Skill 还是 Tool?
    • 四、十个工具时怎么识别正确 Tool?扩展到几百、几千个后怎么提高命中率?
    • 五、MCP 与普通 API 调用有什么区别?为什么 Agent 生态广泛使用 MCP?
    • 六、持续对话导致上下文变长怎么办?短期上下文和长期摘要怎么分层?
    • 七、当前实习状态如何?
    • 八、什么时候能到岗?
    • 九、实习地点有什么偏好?
    • 十、Coding:滑动窗口最大值
    • 十一、Coding:和为 K 的连续子数组个数,如何从枚举前缀和优化到哈希表?
    • 十二、Java 与 Go 分别用到什么程度?
    • 十三、介绍一下 Go Channel
    • 十四、ArrayList 与 LinkedList 的底层结构、查询复杂度和适用场景有什么差异?
    • 十五、反问环节

    # 二、具体介绍一下实习项目

    以工单助手为例,我会先讲业务:用户原来要在工单页面和知识库之间来回切换,现在希望直接问“这个报错以前怎么处理”,拿到相关工单、知识依据和处理建议。

    系统里保留两个入口。前端表单提供明确的查询、创建操作,Chat 入口负责理解自然语言、补齐条件和组织多步查询。两边复用同一套业务服务,权限、字段校验和状态流转规则也放在服务端。

    一轮对话中,模型根据问题和工具定义提出调用请求;执行器校验工具名、参数和权限,真正访问业务服务,再把结果交回模型。比如先根据工单编号查询详情,再用错误信息查知识库,最后给出带来源的建议。需要创建工单时,先整理标题、描述和目标项目,按产品约定确认后再执行。

    个人贡献要落到具体模块:如果我只负责工具层,就重点讲 Schema、错误返回、超时和调用记录。项目效果则用真实样本说明怎么验收,例如抽取历史问题,检查工具选型、参数和最终结果;没有统计过成功率,就不报提升比例。

    # 三、如何理解 Agent?开发会遇到哪些问题?前端入口与 Chat 怎么分工,零散功能属于 Skill 还是 Tool?

    我理解的 Agent,是模型围绕目标,根据当前状态选择动作,并利用执行结果继续推进任务的系统。模型提出下一步动作,执行器真正调用工具,业务系统决定操作是否成功。 一次工具返回成功,也不一定代表整个用户目标已经完成。

    开发里我会把问题分开排查:意图理解错了,要补业务上下文或澄清;工具选错了,要检查描述和候选范围;参数缺失,要明确必填字段;执行超时,要区分查询与写操作,不能无条件重试。创建工单超时后,可能服务端已经创建成功,需要按请求标识查询结果或利用幂等机制防止重复创建。

    前端和 Chat 的分工取决于操作是否明确。用户知道要创建什么工单,表单填写更直观;用户只知道“上次那个登录故障又出现了”,Chat 更适合帮助定位、检索和整理信息。Chat 也可以返回表单让用户补充字段,不必把所有交互都做成聊天。

    至于零散功能,我会看职责,不能按功能大小分类。get_ticket、search_knowledge、create_ticket 这种有明确输入输出、能被执行器调用的能力适合做 Tool;“排查登录故障”包含查现象、检索历史、核验证据和输出建议,更适合组织成 Skill。

    这里的 Skill 指可复用的任务指导与资源包,具体格式取决于实现,可以包含步骤、模板和脚本,也可以调用多个 Tool。Tool 提供可执行动作,Skill 提供完成一类任务的方法;Skill 本身不授予执行权限。 更细的边界可以看Agent Skill 面试详解。

    Agent Skill工程定位

    这张图对应的是职责关系:任务指导进入上下文以后,底层动作仍要经过工具执行层。

    # 四、十个工具时怎么识别正确 Tool?扩展到几百、几千个后怎么提高命中率?

    十个左右的工具,我会先评估工具定义的 Token 开销。如果规模可控,可以把当前用户有权使用的工具定义交给模型,通过工具名、描述、参数 Schema 和使用条件帮助它选择。工具数量只是参考,不能只按“十个”判断一定能全部加载。

    描述要能区分相近工具。例如 get_ticket 用于已知编号查详情,search_tickets 用于按关键词和状态搜索,create_ticket 会产生新工单。用户说“查一下 123 号工单”,应该直接查详情;说“最近有没有类似问题”,才需要搜索。缺少关键信息时允许追问,工具选择并不要求每次强行命中一个。

    模型选定后,执行层还要校验参数类型、业务字段和权限。Schema 合法只代表参数结构正确,项目是否存在、工单是否属于当前用户可见范围,仍然要由服务端检查。

    扩展到几百、几千个工具,我会增加工具目录和按需加载机制:权限与业务范围过滤 → 检索候选工具 → 重排候选 → 加载完整定义 → 模型选择并生成参数。 目录保存工具名称、用途、适用条件、参数摘要和版本;必要时按业务域分层,但跨域问题要允许召回多个域,避免第一层选错后再也找不到正确工具。

    工具检索可以结合关键词和语义匹配。用户明确说出工具名、错误码时,精确匹配很有用;表达业务目标时,语义召回能补充。候选数量要根据评测调整,没召回合适工具时,可以在预算内扩展检索或澄清,不能让模型编造工具名。

    命中率要分层看。我会用包含相似工具、缺参数、跨域任务和无可用工具的标注问题,分别看候选 Recall@K、最终选型准确率、参数合法率和任务成功率,同时记录延迟与 Token 开销。多步任务还要检查是否漏掉必要工具。正确工具进入候选集,和用户任务真正完成,是两件需要分别验证的事。

    # 五、MCP 与普通 API 调用有什么区别?为什么 Agent 生态广泛使用 MCP?

    我会先区分层次:普通 API 提供具体业务能力,MCP 提供统一的能力接入与交互协议。MCP Server 可以封装已有的工单 API,两者可以在同一条调用链里配合。

    直接对接 API 时,应用自己处理接口映射。MCP 则约定客户端与服务端的初始化、能力协商,以及工具发现和调用等交互;服务端还可以提供资源、提示模板。Host 管理客户端连接,Server 可以是本地进程,也可以是远程服务。这些边界见 MCP 架构规范 (opens new window)。

    以查工单为例,宿主通过 MCP 获取工具定义,提供给模型;模型提出 get_ticket 调用请求,宿主经客户端发给 Server,Server 再访问工单服务并返回结果。工具定义由协议传递,如何选工具仍由应用和模型完成。 具体发现与调用方法见 MCP Tools 规范 (opens new window)。

    如果问为什么生态愿意采用,我会从接入复用解释:同一个服务端实现可以被兼容的宿主复用,应用接入不同业务系统时也有统一的交互方式。这能减少重复适配,但认证、权限范围、工具语义和版本兼容仍需要工程处理。

    对于只有几个固定内部接口的系统,直接封装成工具也可以。是否引入 MCP,要看是否需要跨宿主复用和统一接入,不能把“用了 MCP”当成工具选型准确或执行可靠的证明。

    # 六、持续对话导致上下文变长怎么办?短期上下文和长期摘要怎么分层?

    我会按 Token 预算管理上下文,给模型输出和后续工具结果预留空间。工具定义、历史消息、检索内容和工具返回都要计入,不能等请求已经超限再删除前半段聊天。

    短期上下文保留当前目标、最近相关对话、未完成动作和必要证据。长工具结果先分页或截取相关字段,原始结果保存在外部,模型需要时再按标识读取。工单查询返回几百条日志时,先给错误摘要和日志位置,不把所有内容塞进下一轮。

    较早的对话压成任务摘要,保留已经确认的条件、关键决策、待办、执行状态和证据引用。比如“已查到工单 123,用户仅要求给建议,尚未授权创建新工单”,这比“正在处理登录故障”更有恢复价值。摘要覆盖前后要检查这些关键信息,原记录留存以便反查。

    跨会话长期记忆另行管理,只保存有长期价值且来源明确的信息,例如用户确认的偏好和项目约定,并记录作用域、时间与来源。一份旧会话摘要不应直接成为永久事实库。 订单状态、工单状态之类实时信息,使用前重新查询;记忆读取要按用户和项目隔离,冲突时回到原始证据确认。

    压缩还不能破坏调用协议。尚未完成的工具请求及其对应结果应保持可关联,已完成的历史调用才适合整体归档。若创建工单的响应丢失,需要保留“结果未知”和请求标识,恢复后先查状态,不能被摘要改成“创建失败”再执行一次。

    更完整的上下文预算与按需读取,可以接着看上下文窗口与 Token 管理面试详解。

    # 十、Coding:滑动窗口最大值

    我先确认题意:给定数组 nums 和窗口大小 k,返回每个长度为 k 的连续窗口中的最大值。下面约定空数组或非法 k 返回空数组。

    暴力做法是每个窗口遍历一遍,时间复杂度 O(nk)。我会用双端队列保存候选元素的下标,下标从队首到队尾递增,对应值严格递减,队首就是当前窗口的最大值。

    右边界向右走时,先移除已经离开窗口的队首,再从队尾移除不大于当前值的元素,最后把当前下标入队。旧元素比当前值小,而且更早过期,后续窗口不需要再用它作为最大值。

    import java.util.ArrayDeque;
    import java.util.Deque;
    
    class SlidingWindowMaximum {
        public static int[] maxSlidingWindow(int[] nums, int k) {
            if (nums == null || k <= 0 || k > nums.length) {
                return new int[0];
            }
            int[] answer = new int[nums.length - k + 1];
            Deque<Integer> deque = new ArrayDeque<>();
            for (int i = 0; i < nums.length; i++) {
                while (!deque.isEmpty() && deque.peekFirst() <= i - k) {
                    deque.removeFirst();
                }
                while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {
                    deque.removeLast();
                }
                deque.addLast(i);
                if (i >= k - 1) {
                    answer[i - k + 1] = nums[deque.peekFirst()];
                }
            }
            return answer;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    [1,3,-1,-3,5,3,6,7]、k=3 的结果是 [3,3,5,5,6,7]。相等值保留更晚的下标,因为它过期更晚;使用下标也方便准确判断元素是否离开窗口。

    每个下标入队一次、出队最多一次,所以总时间复杂度 O(n),队列辅助空间 O(k),不含返回数组。验证时还要覆盖 k=1、k=n、重复值、全负数和单调数组。

    # 十一、Coding:和为 K 的连续子数组个数,如何从枚举前缀和优化到哈希表?

    我先确认数组是否允许负数。如果允许,就不能直接套上一题的滑动窗口,因为窗口扩张后总和可能变小,缩小后也可能变大,无法靠总和与 K 的大小决定指针怎么移动。

    定义 prefix[0]=0,prefix[j] 表示前 j 个元素之和。区间 [i,j) 的和是 prefix[j]-prefix[i]。枚举所有 i<j,判断这个差是否等于 K,时间复杂度仍然是 O(n²);前缀和只让单次区间求和变成了 O(1)。

    继续变形:prefix[i]=prefix[j]-K。当右端点确定时,我只关心此前有多少个前缀和等于当前前缀和减 K。用哈希表保存出现频次,就能替代内层枚举。

    import java.util.HashMap;
    import java.util.Map;
    
    class SubarraySumEqualsK {
        public static long subarraySum(int[] nums, long k) {
            Map<Long, Long> count = new HashMap<>();
            count.put(0L, 1L);
            long sum = 0;
            long answer = 0;
            for (int value : nums) {
                sum += value;
                answer += count.getOrDefault(sum - k, 0L);
                count.put(sum, count.getOrDefault(sum, 0L) + 1);
            }
            return answer;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    初始的 0 → 1 代表空前缀,用于统计从下标 0 开始的合法区间。必须先查频次、再记录当前前缀和,否则 k=0 时会把长度为零的区间算进去。哈希表记录的是次数,同一个前缀和出现多次,会对应不同的起点。

    例如 [1,-1,0]、k=0 有三个答案:[1,-1]、[0] 和整个数组。再如三个零、k=0,答案是 6,不能用 Set 代替频次表。

    哈希表查询和更新按期望 O(1) 计,总时间复杂度为期望 O(n),空间复杂度 O(n)。这里用 long 保存前缀和与答案;如果题目接口要求返回 int,应先确认数据范围。空数组返回 0,示例约定输入数组非 null。

    # 十二、Java 与 Go 分别用到什么程度?

    我会按实际完成过的工作回答。假如主要使用 Java,就讲自己写过的业务接口、集合、并发和排障;Go 如果只做过练习,就明确说明学习范围,不包装成生产经验。

    以工单助手的示例分工来说,业务接口可以由 Java 服务承载,独立工具服务也可以用 Go 实现。但架构里出现一种语言,不代表我熟悉它的全部生态,需要能说清自己具体写了哪些模块。

    如果被问迁移能力,我会结合具体实现比较:Java 的异常、线程池和同步方式,与 Go 的错误返回、goroutine、Channel 和 context 取消传播分别怎么用。熟悉程度用能独立完成和排查的任务来说明,比给自己打分更有依据。

    # 十三、介绍一下 Go Channel

    Channel 是 Go 中有类型的通信机制,可用于 goroutine 之间传递值和同步。无缓冲 Channel 的通信要等发送和接收双方就绪;有缓冲 Channel 在未满时允许发送,在非空时允许接收。它不是创建 goroutine 的机制。

    比如多个 goroutine 查询不同知识源,可以把结果发到结果通道,再由汇总方接收。需要控制并发数量时还要设置 worker 数或信号量,不能以为有了缓冲区就限制住了 goroutine 总数。

    我会特别注意关闭语义:通常由负责发送生命周期的一方关闭;多个发送方要等所有发送结束后,再由协调者关闭。接收方不能因为自己不想收了,就擅自关闭其他 goroutine 仍在发送的通道。关闭的含义是“不会再发送”,不是清理内存。

    操作 行为
    向 nil Channel 发送或从中接收 永久阻塞
    向已关闭 Channel 发送 panic
    从已关闭 Channel 接收 先读完缓冲值,再立即返回零值,ok=false
    关闭 nil 或已关闭的 Channel panic

    这些行为以 Go 语言规范 (opens new window)为准。select 可以等待多个通信操作,多个分支就绪时会选择其中之一;不能依赖它实现业务优先级。

    调用外部工具时,我还会用 context 传递取消和超时。消费者提前退出后,生产者若一直阻塞在发送上,就可能泄漏 goroutine,所以发送也要能响应取消。共享状态是否有数据竞争,仍要看完整同步关系,不能仅凭使用了 Channel 就判断安全。

    # 十四、ArrayList 与 LinkedList 的底层结构、查询复杂度和适用场景有什么差异?

    以 JDK 21 为例,ArrayList 基于可扩容数组,LinkedList 基于双向链表。前者通过下标访问数组槽位;后者每个节点保存元素及前后节点引用,按下标访问需要遍历。

    操作 ArrayList LinkedList
    按下标查询 O(1) O(n),从较近的一端遍历
    按值查找 O(n) O(n)
    尾部追加 均摊 O(1),扩容时 O(n) O(1)
    按下标在中间插入、删除 O(n),涉及元素搬移 O(n),需要先定位节点
    迭代器已定位后的插入、删除 通常 O(n),涉及搬移 修改节点链接为 O(1)

    复杂度和结构可对照 JDK 21 的 ArrayList 文档 (opens new window)与 LinkedList 文档 (opens new window)。不能笼统说“LinkedList 增删都是 O(1)”;按下标操作时,定位成本也要算进去。

    如果工单列表主要是加载后遍历、按位置读取,我通常选择 ArrayList。它的引用槽位集中存放,也少了链表节点的额外开销;这里集中存放的是引用,不能据此说所有元素对象也连续存放。

    如果已经用迭代器定位到位置,需要连续修改相邻节点,可以考虑 LinkedList。若只是做栈或双端队列,我通常先考虑 ArrayDeque,上一道单调队列就是例子。实际取舍还受数据规模和访问模式影响,两个 List 都不能直接当成线程安全容器使用。

    # 十五、反问环节

    我会问与岗位工作直接相关的问题,比如:“团队目前的 Agent 主要服务什么场景?现在更需要解决工具接入、任务成功率,还是效果评测的问题?”这能帮助判断岗位的实际重点。

    如果时间允许,再问:“实习生通常负责哪一段链路,交付如何验收?团队会怎么记录和复盘一次 Agent 任务失败?”比起泛问技术栈,这些问题更容易了解日常工作方式。

    到岗和地点已经聊清楚,就把剩余时间留给团队职责和技术问题。面试结果、轮次和部门信息,材料没有提供,这里不作推断。

    # 这场面试真正考什么?

    这场面试把“工具能不能调通”继续往下追到了选择、权限、失败恢复和记忆保真。后面的单调队列、前缀和与语言基础,也要求候选人能解释每一步为什么成立。

    录友可以拿自己的项目练一遍:同一句需求分别从按钮和 Chat 进入,画清谁理解意图、谁执行动作、谁校验结果,再模拟一次超时和一次历史摘要遗漏。每个环节都能指出具体状态和代码位置,回答才有支撑。

    Last Updated: 10/20/2026, 10:23:59 PM

    ← 阿里淘天Agent研发岗面经

    评论

    验证登录状态...

    侧边栏 侧边栏
    夜间模式 夜间
    卡码简历 卡码简历
    代码随想录 代码随想录
    卡码投递表 卡码投递表🔥
    2026实习校招群 2026群
    添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
    支持卡码笔记 支持卡码笔记
    鼓励/支持/赞赏Carl 卡码笔记赞赏码
    1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
    2. 希望大家都能梦想成真,有好的前程,加油💪