公司未注明

面经合集

完整信息

16 条问法 · 15 道题

本篇目录

淘天 27 届暑期实习生正在招聘 各方向都有海量 HC 欢迎看我置顶帖子投递

面试题 11:代码生成 Agent 和普通代码补全(Copilot 式)的本质区别是什么?设计一个能完成"跨文件修改"任务的代码 Agent,核心难点在哪?

参考答案

本质区别在于决策粒度。

代码补全是行级决策——光标在哪,就在那个位置生成接下来几行代码。它的上下文是当前文件的局部内容,不需要做规划、不需要选择工具、不需要多步推理。本质上是一个"自动完成"功能,不是 Agent。

代码生成 Agent 是任务级决策——接收一个自然语言描述的需求("给这个项目加一个用户认证功能"),然后自主决定需要读哪些文件来理解现有架构、需要修改哪些文件、每个文件改什么、修改的先后顺序、改完之后怎么验证。它操作的粒度是整个代码仓库,面对的是一个多步规划和执行的问题。

跨文件修改的核心难点:

  1. 代码库理解——"看懂"整个项目

Agent 不可能把整个代码仓库塞进上下文窗口。一个中等规模的项目可能有几十万行代码,而窗口只有十几万 token。所以 Agent 必须有一套高效的代码检索策略:先通过目录结构和文件名建立粗粒度的"项目地图",然后根据任务需求有针对性地读取相关文件。这个过程本身就是多轮推理——读了 A 文件发现它依赖 B 模块,再去读 B,发现 B 的接口定义在 C 里。Agent 需要像人类开发者一样"跳转查看引用"。

  1. 修改一致性——改了 A 别忘了 B

跨文件修改最大的坑是不完整修改。改了一个函数的签名但没改所有调用方,加了一个新的数据库字段但没改对应的 ORM 模型和 API 序列化层。Agent 需要理解代码之间的依赖关系——函数调用链、类型引用、import 依赖、配置文件关联——才能确保修改的完整性。这比"能不能写对代码"难得多,考验的是 Agent 对代码结构的全局理解。

  1. 修改顺序——先改什么后改什么

实际开发中修改顺序很重要。比如你要重命名一个被广泛引用的接口,正确做法通常是先加新接口、再逐步迁移调用方、最后删掉旧接口。如果 Agent 直接一步到位把旧接口改名,中间状态下整个项目是编译不过的。Agent 需要能规划一个每步都保持项目可用的修改序列,而不是一次性输出所有改动。

  1. 验证闭环——怎么知道改对了

人类开发者改完代码会跑测试、跑编译、手动验证。Agent 也需要这个闭环:执行修改后,调用构建工具检查编译是否通过,跑现有测试看是否有回归,甚至读一下 lint 输出看是否引入了风格问题。如果验证失败,Agent 需要能分析错误信息、定位到自己哪步改错了、然后修复。这个"写 → 验证 → 修复"的内循环是代码 Agent 可靠性的关键。

追问 Q&A

Q:代码库太大看不完,Agent 怎么高效定位到需要修改的文件?

A: 分层检索。第一层用快速手段建立全局感知——读项目目录结构、读 README 和入口文件、读 package.json 或 build 配置了解项目组织方式。第二层根据任务做针对性搜索——用符号搜索找函数/类定义、用文本搜索找关键词引用、用文件名模式匹配找相关模块。第三层基于已读文件做关联跳转——发现 import 依赖就顺着读过去、发现接口调用就找实现方。整个过程就是 Agent 的多轮推理循环在代码检索这个场景上的具体应用。好的代码 Agent 通常还会维护一个"已读文件摘要"列表,避免重复读取同一个文件。

Q:Agent 生成的代码如何保持和现有项目的风格一致?

A: 风格一致性来自两个方面。隐式学习:Agent 在读取现有文件时,已经在上下文中"看到"了项目的代码风格——命名规范、缩进方式、注释习惯、错误处理模式。只要上下文里有足够的代码样本,LLM 通常能自动模仿。显式约束:把项目的 lint 规则、代码规范说明注入系统提示词,或者在生成后直接跑一遍 linter/formatter 做自动修正。实际中后者更可靠——不要完全依赖 LLM 的模仿能力,用工具做确定性保障。

面试题 12:代码 Agent 的"写 → 运行 → 修复"循环应该怎么设计?失败修复最多尝试几轮,为什么?

参考答案

循环结构:

代码 Agent 的核心执行循环不是简单的"一次生成",而是一个带反馈的迭代修正过程:生成代码 → 执行验证(编译/测试/lint)→ 分析错误输出 → 修正代码 → 再次验证。这个循环持续直到验证通过或达到重试上限。

每一步的关键设计:

验证手段的选择:不同的验证给 Agent 提供不同质量的反馈信号。类型检查/编译错误是最高质量的反馈——精确到行号、错误类型明确、修复方向清晰。测试失败次之——能知道哪个行为不符合预期,但定位到代码层面需要 Agent 做推理。Lint 警告最弱——通常是风格问题,不影响功能。Agent 应该按编译 → 测试 → lint 的顺序验证,先保证能跑起来,再保证行为正确,最后处理风格。

错误信息的处理:编译器/测试框架的输出往往非常长。直接把完整输出塞进上下文会浪费大量窗口空间。需要做智能截取——提取错误类型、错误位置、关键错误信息,去掉冗余的堆栈和无关的警告。保留足够 Agent 定位问题的信息量,但不塞无用的噪声。

修复时的上下文管理:每轮修复后,之前的错误版本代码还有没有价值?通常第一轮的错误版本值得保留(帮 Agent 理解"什么是错的"),但多轮累积的历史版本就应该压缩了,否则上下文里全是废弃代码。

重试上限的设定:

经验上 3 轮是一个合理的上限,背后的逻辑是:

第一轮修复:通常是修语法错误、缺少 import、类型不匹配这类明确的编译问题。成功率很高,因为错误信息指向性强。

第二轮修复:通常是修逻辑错误——第一轮编译通过了但测试失败。Agent 需要理解测试的预期行为和实际行为的差异,做更深层的推理。成功率明显下降。

第三轮修复:如果前两轮都没修好,说明 Agent 对这个问题的理解可能从根本上就偏了。再多给几轮通常是在同一个错误方向上反复打转。此时更合理的策略是退一步——放弃当前方案、重新审视整体思路、甚至换一种实现方式。

超过 3 轮还在修同一个问题,几乎一定是初始方案选择错误而不是修修补补能修好。此时应该把失败的尝试作为"反面经验"总结,重新规划。

追问 Q&A

Q:Agent 修复代码时经常引入新 bug——改好了 A 又搞坏了 B,怎么解决?

A: 核心问题是 Agent 做局部修复时缺乏全局影响评估。应对方式有两个层面。验证层面:每次修复后不只跑失败的那个测试,而是跑全量测试(或至少跑与修改文件相关的测试集)。这样新引入的回归会被立即发现。策略层面:限制每次修复的范围——修复时只改必要的部分,不做"顺便优化"。修改范围越小,引入新问题的概率越低。如果一次修复需要改很多地方,应该拆成多个小步骤,每步之后都做验证。

Q:测试通过了就代表代码正确吗?代码 Agent 的验证还有什么盲区?

A: 测试通过只能证明"覆盖到的场景是对的",无法证明代码没有问题。几个常见盲区:测试覆盖不足——如果原项目的测试覆盖率低,很多行为根本没有被验证。Agent 改坏了也不会有测试失败。非功能性问题——性能退化、内存泄漏、并发安全问题,这些普通测试很难发现。语义正确但设计不合理——代码能跑、测试通过,但实现方式是一个 hack 而不是正确的设计。自动化手段很难判断这类问题。所以生产级的代码 Agent 通常还需要在自动验证之外加一层人工审核——先由 Agent 自动完成到"测试全通过"的程度,最后由人类开发者做 code review 确认设计合理性。

面试题 13:代码 Agent 在处理存量代码时,怎么避免"破坏性重构"?什么是最小化修改原则?

参考答案

问题的根源:

LLM 生成代码时有一个固有倾向——它倾向于按照自己"认为最好"的方式重写代码,而不是在现有代码的基础上做最小改动。当你让 Agent "修复函数 A 的一个 bug"时,它可能把整个函数重写了,顺便改了变量命名、调整了代码结构、加了它认为有用的注释。表面上看代码"更好了",但实际带来几个严重问题:

diff 不可审查:人类 reviewer 看到一个函数被整个替换,根本无法快速判断哪些改动是修 bug 的、哪些是"顺手重构"的。审查成本急剧上升。

引入隐性回归:重写的代码可能改变了原始代码的某些隐含行为——边界条件处理、错误码返回、副作用顺序——这些可能不在测试覆盖范围内。

git blame 污染:大面积重写后,整个函数的 git blame 都指向 Agent 的那次提交,历史归因信息丢失。

最小化修改原则:

核心思想是只改必须改的,不碰不需要碰的。

在系统提示词中明确约束 Agent 的行为——"你的修改应该尽可能小,只改变实现需求或修复 bug 所必需的代码。不要重命名现有变量,不要调整代码格式,不要添加额外的注释,不要重构你没有被要求重构的代码"。

关键实现策略:

精确定位修改点:Agent 在做修改前,先明确列出"需要改动的具体位置和改动内容",形成一个修改计划。这个计划本身就可以作为 review 的依据——改 5 处比重写整个文件更容易审查。

使用 diff 而不是全文替换:Agent 的输出格式应该是"在第 X 行,把旧内容改成新内容",而不是"这是整个文件的新版本"。这在技术上约束了 Agent 只能做局部修改,从机制上防止了大面积重写。

修改后自动对比:Agent 完成修改后,自动生成 diff 并检查——修改的行数是否合理?有没有碰到不应该碰的文件或函数?如果 diff 范围明显超出任务需要,触发自查或人工确认。

追问 Q&A

Q:有时候修复一个 bug 确实需要重构一部分代码,怎么判断这个重构是"必要的"还是"过度的"?

A: 判断标准是能不能在不重构的情况下完成任务。如果 bug 的根因是某个数据结构设计不合理,不改结构就没法修——这就是必要的重构。如果 bug 只是一个条件判断写错了,但 Agent 顺便把整个函数的控制流都重新组织了——这就是过度的。实践中可以让 Agent 做两步决策:先尝试最小修改方案,如果发现做不到或者做出来明显是 hack,再提出重构方案并说明为什么。关键是让重构成为一个有意识的决策而不是无意识的习惯。而且当需要重构时,应该把重构和功能修改分成两个独立的步骤——先重构(保持行为不变)、跑测试确认没有回归,再做功能修改。不要混在一起。

面试题 14:代码 Agent 需要理解多少项目上下文才"够用"?如何设计代码库的索引策略?

参考答案

够用的标准不是"读了多少",而是"理解了相关的依赖链"。

一个需求可能只涉及改一个文件,但要改对它,Agent 可能需要理解这个文件的上下游——它被谁调用(影响面评估)、它依赖了什么(接口约束)、相关的类型定义(参数和返回值)、对应的测试文件(验证方式)。这条依赖链就是"够用"的上下文。

索引策略的分层设计:

第零层:项目骨架

不读具体代码,只看结构——目录树、入口文件、package.json/go.mod/pyproject.toml 等配置文件。目的是让 Agent 快速建立"这个项目的大概组织方式"的认知:是前后端分离还是单体?用了什么框架?模块怎么划分的?这一层的信息量很小但价值极高,几乎每次任务都需要。

第一层:符号索引

对代码库做静态分析,提取函数名、类名、接口定义、导出符号,构建一个轻量的符号表。Agent 做任务时先在符号表中搜索相关名称,快速定位到文件和行号,然后再有针对性地读具体代码。这比让 Agent 盲目遍历文件高效得多。

第二层:依赖图

基于 import/require/include 关系构建文件间的依赖图。Agent 确定了核心修改文件后,可以沿着依赖图向上(调用方)和向下(依赖方)扩展,自动找出所有可能受影响的文件。这一层解决了"改了 A 还要改什么"的问题。

第三层:语义索引

对代码块做向量嵌入,支持自然语言检索。当 Agent 需要找"处理用户认证的逻辑"但不知道具体函数名时,可以用语义搜索。这一层的精度不如符号搜索,但覆盖面更广,适合模糊搜索。

实际执行中的组合使用:

Agent 接到任务后,先用第零层了解项目结构,再用第一层或第三层定位到核心文件,读完核心文件后用第二层扩展出影响范围,最后有针对性地读取所有相关文件。整个过程在 Agent 的多轮推理循环中动态进行,而不是一开始就试图读完所有东西。

追问 Q&A

Q:索引需要多频繁更新?Agent 自己的修改会让索引过时吗?

A: 这是一个真实的工程问题。Agent 在一次任务中可能修改了多个文件,此时符号索引和依赖图就过时了。短期的解法是任务内增量感知——Agent 自己维护一个"本次修改的文件清单和改动摘要",在后续推理中参考这个清单而不是过时的索引。长期的解法是索引系统支持增量更新——文件修改后只重新索引变化的文件及其直接依赖,而不是全量重建。在实践中,精确的实时增量索引成本很高。常见的折中方案是:任务开始时做一次全量快照,任务过程中靠 Agent 的工作记忆跟踪变化,任务结束后触发增量索引更新。

面试题 15:如何评估一个代码生成 Agent 的实际能力?有哪些维度和基准?

参考答案

不能只看"能不能生成正确代码",要看完整的开发循环。

代码生成的正确率只是冰山一角。真实开发场景中,Agent 的价值体现在从理解需求到交付可用代码的完整链条上。

评估维度:

维度一:任务完成率

给定一组明确的编程任务(实现功能、修复 bug、重构代码),Agent 能成功完成多少比例。这里"成功"的定义是——生成的代码能编译通过、测试全部通过、满足需求描述。这是最基础的指标。业界常用的 benchmark 有 SWE-bench(从真实 GitHub issue 中提取的 bug 修复任务)和 HumanEval(函数级代码生成)。

维度二:修改精准度

Agent 完成任务时实际修改了多少代码?改动是否精准?和人类开发者完成同一个任务的 diff 相比,Agent 的 diff 是否有大量多余改动?改动越精准,说明 Agent 对代码库的理解越好。可以用"多余修改率"——Agent 的 diff 行数与最优 diff 行数之比——来量化。

维度三:自主程度

Agent 完成任务过程中需要多少次人工干预?完全自主完成的比例是多少?需要人工提示、纠正方向、指出遗漏的比例分别是多少?自主程度越高,Agent 在实际开发流程中的价值越大。

维度四:效率

完成一个任务需要几轮 LLM 调用?总 token 消耗是多少?端到端延迟是多少?两个 Agent 都能完成同一个任务,但一个用了 5 轮、另一个用了 25 轮,成本和体验差异巨大。

维度五:安全性

Agent 生成的代码有没有引入安全漏洞?SQL 注入、XSS、硬编码密钥、不安全的依赖——这些都需要自动扫描检查。一个功能正确但有安全漏洞的代码,在生产中是不可接受的。

追问 Q&A

Q:SWE-bench 跑分高就代表真实场景好用吗?

A: 不一定。SWE-bench 的价值在于提供了一个可量化、可复现的横向对比基准,但它和真实开发场景有几个显著差距。任务颗粒度:SWE-bench 的任务是"修复一个具体 issue",而真实开发中很多任务是模糊的——"这个页面加载太慢了""用户反馈说导出功能有问题",Agent 需要自己做问题定位。代码库复杂度:SWE-bench 选取的项目虽然是真实开源项目,但 Agent 面对的上下文通常已经被预处理过(问题描述比较清晰、定位范围相对明确)。真实场景中 Agent 面对的是完全陌生的、文档可能不完善的私有代码库。评估维度单一:SWE-bench 只看"测试是否通过",不评估代码质量、修改精准度、安全性。所以跑分要看,但不能只看跑分。更有价值的评估方式是在自己团队的真实项目上做 A/B 测试——让 Agent 处理一批真实任务,对比有 Agent 和没有 Agent 时开发者的效率差异。

面试题 16:设计一个能处理"模糊需求"的代码 Agent——用户只说"这个页面太慢了",Agent 该怎么做?

参考答案

这道题考察的是 Agent 从"模糊问题"到"具体行动"的推理链路设计。

"太慢了"不是一个可执行的指令。Agent 必须先把它转化为具体的、可度量的问题,然后才能开始修复。整个过程分为几个阶段:

阶段一:问题定义——"慢"是什么意思?

Agent 需要先收集信息来定义问题的边界。首先确认是哪个页面、什么操作慢(首次加载?交互响应?数据请求?)。如果用户没说清楚,Agent 应该主动提问而不是猜测——"你说的慢是首次打开页面的加载速度,还是页面内某个操作的响应速度?"一个能主动提问澄清需求的 Agent,比一个闷头做事做错的 Agent 有用得多。

阶段二:问题定位——慢在哪?

确认了"什么慢"之后,Agent 需要用工具做诊断而不是凭直觉猜。读取相关页面组件的代码 → 找到数据请求逻辑 → 检查是否有明显的性能反模式(在渲染循环中发请求、没有做缓存、加载了过大的依赖、无意义的重复渲染等)。如果能跑 profiling 工具就更好——用 bundle 分析工具看包大小、查看 API 响应时间日志。关键是用数据定位瓶颈,而不是凭经验猜测。

阶段三:方案选择——怎么解决?

定位到瓶颈后,可能有多种修复方案。比如数据请求慢:加缓存?做懒加载?优化查询?Agent 应该给出几个方案和各自的取舍,让用户选择,或者在无法询问的场景下选择最安全(侵入性最小)的方案。性能优化领域有一个原则——先摘低垂果实,不到万不得已不做架构级改造。

阶段四:执行与验证

做出修改后,必须量化验证——不是"我觉得快了",而是"修改前 API 响应时间 800ms,修改后 200ms"或者"包体积从 2.1MB 降到 1.3MB"。有数据支撑的优化结论才有说服力。

这道题的评估重点不是性能优化知识本身,而是 Agent 面对模糊输入时的处理策略——会不会主动澄清需求、会不会用工具做诊断而不是猜测、方案选择是否有理有据。

追问 Q&A

Q:如果 Agent 的诊断方向错了——花了很多轮在排查前端渲染问题,但实际瓶颈在后端接口,怎么办?

A: 这是多步推理中典型的"方向性错误"问题。应对机制有两个。事前:在诊断阶段不要过早收窄范围。Agent 应该先用轻量手段对所有可能的瓶颈做一次快速排查(网络请求耗时、包大小、渲染次数各看一眼),而不是一上来就钻进某一个方向。事中:设置诊断轮数阈值。如果在某个方向上探索了 N 轮还没找到明确瓶颈,Agent 应该自动退出当前方向,总结"在前端渲染层面没发现明显问题",转向下一个可能性。关键是让 Agent 有"我可能搞错方向了"的自我觉察能力,而不是沿着错误方向一路走到底。这可以通过在系统提示词中明确引导:"如果连续 3 步没有取得实质性进展,请暂停并重新评估问题方向。"

本篇目录
  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09
    有时候修复一个 bug 确实需要重构一部分代码,怎么判断这个重构是"必要的"还是"过度的"?
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
    如果 Agent 的诊断方向错了——花了很多轮在排查前端渲染问题,但实际瓶颈在后端接口,怎么办?