PERSONAL LAB / ai/agent开发
AI Agent 开发

16 项目怎么讲

前面十几篇讲的每个知识点,面试时最终都要落到”你在项目里怎么做的”。同一个项目,讲成”我用 LangGraph 和 RAG 做了一个 Agent”,和讲成”我先定义了不依赖模型自述的验收标准,然后评测时发现验收自己错了两次”,面试官的判断完全不同。

这篇要讲清楚:1 分钟、3 分钟、10 分钟三个版本的讲稿;每个技术决策为什么这么做;项目里所有能说的数字、出处和算法;主动讲局限怎么讲;简历上每一句话背后的证据和追问;30 个高频追问的回答方向。所有数字都来自 eval/REPORT.md、结果文件、runs.db 和 git 历史,文中有一个脚本可以把核心数字从原始文件里重新算一遍。

怎么读:

前置:整个系列。本篇里提到的每个知识点,都在前面对应的篇目里有详细讲解。

第一部分 速记页

项目一句话:CrossBuild Agent,给一个开源 C/C++ 仓库,Agent 自己调工具把它交叉编译到 aarch64-linux-musl,由程序独立验收产物。

数字卡片(背下来,每个都要能说出出处):

数字含义出处
5 个工具list_filesread_filewrite_filerun_commandsearch_knowledgeagent/tools.pybuild_agent.py
10 + 5 个项目评测集 10 个分三档(简单 4、中等 4、困难 2),留出集 5 个eval/projects*.json
25 → 40 步步数上限;只改这一项,libpng 29 步、re2 32 步通过(各一次)REPORT 第一版
9/10、10/10第一版 baseline 和 rag 的通过数,不能归因于知识库REPORT 第一版
3 次第一版两百多次工具调用里只查了 3 次知识库REPORT 第一版
2 次验收自身的 bug:空构建判通过;构建目录写死 build/REPORT 重跑、留出集
45 次留出集因验收 bug 作废重跑的运行数REPORT 留出集
30/30、29/30v2 baseline 和 rag;rag 失败那次是路径检查误拦REPORT v2
8 → 0toolchain 改环境变量后,“子目录找不到 toolchain”次数REPORT v3
60/60v3 两个配置全部通过,通过率失去区分度REPORT v3
15 条、48 条知识库条目数、检索测试集查询数(四类)REPORT 知识库
45/48混合检索 R@1,最高;重排同分、每条慢 30 多倍REPORT 知识库
15/15 × 3留出集三种配置全过;步数 14.2 / 15.7 / 16.8,差异在一倍标准差内REPORT 留出集
8 次留出集 30 次开知识库的运行只调用 8 次检索REPORT 留出集
30/30、29/29手写版和 LangGraph 版同配置;另有 1 次 402 余额不足不计REPORT LangGraph
65 行、126 行手写循环和 LangGraph 版的循环代码REPORT LangGraph
第 12 步杀、第 13 步续run 217 断点续跑,第 18 步通过,工具调用无重复REPORT LangGraph
288 次、41.6 倍runs.db 总运行数;输入 token 是输出的倍数runs.db
21 个单元测试数tests/

三个最有价值的故事(面试时优先讲):

  1. 验收标准写错了两次:只看退出码,空构建也判通过;构建目录写死,正确结果判失败
  2. “90% → 100%“不能归因:三条证据推翻了”知识库有效”的直觉
  3. cat 绕过凭据防护:两个工具功能重叠,防护只有弱的那个那么强

第二部分 易混对照

讲项目时最容易说错、说过头的地方:

容易说成实际应该说为什么
”加了知识库,通过率从 90% 提到 100%""第一版 90% 到 100%,但证据不支持归因于知识库;留出集上没有观察到可测量的收益”步数方向混杂、只查 3 次、救回的查询库里没有
”知识库没用""未能证明有效,瓶颈在经验覆盖和调用率,不在检索算法”没证明有效 ≠ 证明无效
”实现了沙箱""做了工具层检查,明确不是沙箱;构建脚本能绕过,要容器才行”README 原文
”LangGraph 版效果更好""两版结果看不出差别,LangGraph 多的是检查点和续跑能力”30/30 vs 29/29,步数 token 相近
”混合检索效果最好,所以用了""离线检索测试上混合最好;但端到端没带来可测量收益”离线分数高估实际作用
”步数少了,说明变好了""差距在一倍标准差内,只能算趋势”样本少、波动大
”Agent 成功率 100%""评测集上全部通过,说明评测集太简单、饱和了”通过率无区分度
”续跑不会重复执行工具""检查点之间能正确恢复;节点执行中途被杀会重复执行,需要幂等”检查点粒度是节点
”平均步数 13.1”先说清是哪一版、包不包括失败的运行第一版 REPORT 的 13.1 含失败运行,只算通过的是 11.8
”测试覆盖了所有情况""21 个测试覆盖越界、误删、验收、续跑等回归场景;没有注入测试”如实

第三部分 面试口述稿

3.1 一分钟版(自我介绍里带到项目)

我做了一个叫 CrossBuild Agent 的项目。给它一个开源的 C/C++ 仓库,它自己读项目结构、执行 cmake 命令、看报错、改配置,把项目交叉编译到 ARM64 的 musl 平台。选这个题目是因为我工作里做过二十多款工具的 ARM64 适配,知道这件事重复性高、又不是固定流程,适合用 Agent 做。

我在这个项目里最看重的是评测。成功与否不看模型怎么说,而是由程序重新构建一遍,检查产物是不是目标架构的 ELF 文件。我用 10 个项目分三档加 5 个留出项目,每个配置跑三轮,比较了加不加知识库、BM25 和向量混合检索、手写循环和 LangGraph。

过程中比较有价值的发现是:验收标准本身错过两次,都是读运行轨迹发现的;还有第一版里通过率从 90% 到 100% 的提升,我分析后认为不能归因于知识库,后面用留出集验证也确实没有可测量的收益。

3.2 三分钟版(“介绍一下你的项目”)

这个项目是一个做交叉编译的 Agent。背景是我工作里做过二十多款 C/C++ 工具的 ARM64 适配,还移植过 PyTorch 到 musl 平台,每个项目的依赖、编译选项、报错都不一样,没法写成固定脚本,但排查思路是有规律的,所以想试试让 Agent 来做。

怎么做的。 Agent 有五个工具:列目录、分页读文件、写文件、执行命令、查经验库。执行命令有白名单、路径检查和超时。循环是我手写的,有步数上限和卡住检测。结束后由程序独立验收:重新构建,并且在构建目录里找到 aarch64 架构的 ELF 产物才算成功。后来我又用 LangGraph 重写了一版循环,加了检查点和断点续跑,工具也封装成了 MCP server。

评测。 10 个开源项目分三档,另外 5 个完全没跑过的项目当留出集,每个配置跑三轮。第一版每配置只跑了一轮,看到加知识库后通过率从 9 个到 10 个,但我没有直接下结论,因为步数变化四个变好四个变差,知识库两百多次调用里只被查了 3 次,被救回的那个项目查的东西知识库里根本没有。所以结论写的是”未能证明有效”。

踩过的坑。 最大的教训是验收自己会错。第一次,旧验收只看 cmake 的退出码,复查时发现有两次运行一个目标都没编译也判了通过;第二次,留出集上 libarchive 九次失败八次,查下来是仓库自带一个叫 build 的源码目录,Agent 换了个目录名编译成功了,验收却写死去 build 目录找。这次 45 次结果全部作废重跑。另外还发现过一个安全问题,命令白名单里有 cat,参数又不检查路径,读文件工具上加的凭据防护被整个绕过了。

结论。 改完验收和工具缺陷后,两个配置 60 次全部通过,评测集已经没有区分度了;留出集上三种配置也都全过,知识库没带来可测量的收益,瓶颈在经验覆盖不足和模型很少主动查。LangGraph 和手写版结果一样,它多出来的价值是检查点和续跑。

3.3 十分钟版(技术面深挖,按顺序讲,每段一两分钟)

① 问题定义和为什么用 Agent

交叉编译每个项目路径都不同:有的关掉测试就能过,有的要先编依赖,有的依赖库 API 变了要打补丁。步骤没法事先定死,要根据报错决定下一步,所以用 Agent 而不是工作流。

我先定的是成功标准。不能让模型说”编译成功”就算,要程序独立判断。最早定的是重新执行 cmake --build 返回 0,后来发现不够,改成必须在构建目录里找到目标架构的 ELF 产物,静态库要拆开检查,CMake 自己在配置阶段生成的探测程序不算。

② Agent 循环和工具设计

循环是标准的:发消息给模型,模型返回工具调用,执行,结果作为 tool 消息放回去,直到模型不再调工具或者到步数上限。加了两个保护:一是步数上限,二是卡住检测,同一个工具调用(工具名加参数完全相同)重复超过 3 次就不再执行,所有调用都陷入重复时判定卡住;写文件成功之后命令的计数清零,因为改了文件再跑同一条命令是合理的。

工具设计上改过几次。读文件原来只保留末尾 100 行,长的 CMakeLists 前半部分模型看不到,第一版里有 6 次运行、15 次模型自己写 CMake 脚本分段读文件绕过去;改成从开头分页读之后,后面 120 次运行一次都没出现。命令输出截断时退出码固定放在第一行,完整输出存到工作目录里可以翻页读。报错信息也写成能指导下一步的,比如越界时说明”主机上的库不能用于交叉编译”。

③ 评测设计和两个验收 bug

评测集 10 个项目按依赖复杂度分三档。第一版每配置一轮,后来改成三轮,因为同一个项目三次跑的步数标准差平均有两三步,单次对比没有意义。判断差异我用了一个保守的规则:差距不到一倍标准差只算趋势。

两个验收 bug 前面说过。我从中得到的教训是,评测里最该怀疑的是评分器,而且这类问题只能靠读轨迹发现,看总分是看不出来的。每修一个我都补了回归测试。

④ 第一版的归因分析和步数实验

第一版 baseline 9 个通过、rag 10 个。我给出了三条不能归因的证据。困难档那两个项目三轮里五次跑满 25 步,我看 libpng 的轨迹,它走的路是对的,识别出缺 zlib、去交叉编译 zlib,只是到第 25 步才刚开始配 zlib。所以我单独只改步数上限到 40,libpng 29 步、re2 32 步都过了。这是那一版里唯一的单变量实验,但每边只跑一次,只能说支持这个判断。

⑤ 知识库和检索对比

知识库从 160 次运行记录里提炼,按报错签名分组,让模型根据证据写候选条目,我逐条对着运行记录审核,7 条候选收了 5 条、合并 1 条、丢弃 1 条,最后 15 条。

检索测试集扩到 48 条,分四类:原始报错、见过的报错、刻意不用原词的改写描述、没见过的报错变体。结果 BM25 在原始报错上满分但改写类最弱,向量检索改写类最好但带具体库名时会排错,RRF 混合总分最高 45/48;加重排总分一样,每条查询从 33 毫秒变成 1.1 秒,所以没用。

⑥ 留出集:为什么换项目,结果是什么

新增的经验是从原来 10 个项目的运行记录里提炼的,再用这 10 个项目测就等于提前给了答案,所以另选了 5 个 Agent 从没跑过的项目。三种配置各三轮,全部通过,步数 14.2、15.7、16.8,差异在一倍标准差以内,方向还是开了知识库略多。30 次开知识库的运行只调了 8 次检索,查得最多的是 libarchive 的链接错误,库里根本没有这类经验。结论是瓶颈在覆盖,不在检索算法;离线检索测试是按已有条目写的,高估了实际作用。

⑦ LangGraph 重写和续跑

为了公平,两版共用工具、提示词、重复检测和验收,只换循环组织方式。结果看不出差别。LangGraph 代码多了一倍,但检查点有实际价值:我在编 re2 第 12 步时 kill 掉进程,续跑从第 13 步开始,直接用已经装好的 abseil,第 18 步编译通过,工具调用没有重复。要说明的是,杀的时机正好在节点执行完之后;如果杀在工具节点执行一半,恢复后已执行的工具会再跑一次,不能重复的操作要另外做幂等。

⑧ 安全和边界

评测跑 re2 时看到 Agent 执行 ls /opt/homebrew/Cellar 想找主机上的 abseil,而且执行成功了。查下来命令参数不检查路径,白名单里还有 cat,实测能读 /etc/hosts,读文件工具上加的凭据拦截被绕过。修复是参数路径也检查、去掉 cat。后来又把字符串前缀比较改成按路径层级比较。

但我在 README 里明确写了这不是沙箱。cmake、make 本身能执行任意代码,评测里模型就写过 CMakeLists 用 execute_process 读主机环境,参数检查管不到。要编不可信的仓库得放容器里。

⑨ 后端和可观测

FastAPI 加 SSE 推进度,Agent 在后台线程跑,事件经队列推给前端。同时只允许一个构建,拿不到锁返回 409。修过一个 bug:锁原来在响应生成器里释放,客户端一断开锁就放了,后台构建还在跑。改成在构建线程结束时释放,加了回归测试。每次运行和每次工具调用都记在 SQLite 里,前面说的那些发现基本都是翻这些记录找到的。

⑩ 局限和下一步

主动说几个没做好的:评测集已经饱和,需要依赖链更深、非 CMake 的项目;每配置三轮样本仍然少;没有容器隔离,子进程还能读到环境变量里的 API key;没记缓存命中和模型耗时,算不出真实成本;知识库要么扩大覆盖,要么改成报错后自动检索,而不是靠模型主动调用。

3.4 “你为什么做这个项目?”

两个原因。一是和我的工作经验直接相关,我在德科信息负责 C++ 工具的 ARM64 移植,做过二十多款工具和 PyTorch 运行时的适配,交叉编译里的坑我是真踩过的,所以我能判断 Agent 做得对不对,也能设计有意义的评测。二是这个任务的结果可以用程序客观验证,不需要人工打分或者模型打分,评测数字比较经得起追问,我想借这个项目把 Agent 的评测方法认真做一遍。

3.5 “项目里最难的是什么?”

我觉得最难的不是写代码,而是判断结果到底说明了什么。

比如第一版通过率从 90% 到 100%,最自然的写法是”加知识库提升了 10 个百分点”。但我去看了逐项目的步数、知识库的调用次数和查询内容,发现证据不支持。后来改验收、重跑、换留出集,每一步都在推翻或修正前面的结论,报告也改过好几次,还把早期写错的数字就地改正了。

另一个难点是评分器本身会错,而且错得很隐蔽。空构建判通过这个问题,总分上完全看不出来,是我复查复验输出时才发现是空的。

3.6 “如果重新做,你会怎么改?”

三点。第一,一开始就把验收标准写严并补测试,我前期的很多结果因为验收标准变了不得不作废重跑。第二,评测集一开始就选难一点的项目,并且从第一版起每个配置跑多轮,否则通过率很快饱和、单轮结果又没法比较。第三,工具执行一开始就放进容器,而不是先做工具层检查再发现挡不住。

第四部分 逐个详解

4.1 讲项目的结构

先说 STAR 是怎么来的。 早年的面试常问”你的优点是什么""如果遇到某某情况你会怎么做”,候选人很容易说漂亮话,面试官也很难判断真假。工业与组织心理学里有一条被广泛接受的经验:一个人过去在类似情况下怎么做,最能预测他以后会怎么做。于是有了行为面试,只问”你实际做过什么”。美国人才测评公司 DDI 说,STAR 是他们 1974 年随行为面试方法 Targeted Selection 一起提出的,用来让候选人把一段经历讲完整、让面试官追问时有章可循(DDI 的说明,这是 DDI 自己的说法)。技术面问”讲讲你的项目”,本质上也是行为面试。

STAR 是讲经历的常用结构:情境(Situation)、任务(Task)、行动(Action)、结果(Result)。用在技术项目上,我建议扩展成下面这样,重点放在”判断”上:

flowchart LR
    S[背景<br/>为什么做] --> T[目标<br/>成功标准是什么]
    T --> A[做法<br/>关键设计和理由]
    A --> E[验证<br/>怎么评测 数字]
    E --> F[发现<br/>意外 失败 修正]
    F --> L[局限<br/>没做好的 下一步]
部分要讲什么常见错误
背景为什么做、和自己经历的联系空泛(“AI 很火”)
目标可验证的成功标准没有标准,或者用模型自述当标准
做法关键设计和为什么罗列技术名词
验证评测方法、样本量、数字只有一个数,不说怎么来的
发现意外、失败、自己推翻过的结论只讲成功
局限主动说没做好的和原因不说,等面试官问出来

面试官判断”做过”还是”看过”,主要看后三部分。 能说出”我原以为……后来发现……所以改成……”的,几乎不可能是编的。

4.2 数字从哪来:自己能复算

面试官追问数字时,最怕说不清”怎么算的”。下面这个脚本从项目的原始结果文件和 runs.db 里把核心数字重新算一遍。保存为 recheck.py,在项目根目录运行。

import json
import sqlite3
from pathlib import Path
from statistics import mean

EVAL = Path("eval")


def load(pattern):
    runs = []
    for f in sorted(EVAL.glob(pattern)):
        runs.extend(json.loads(f.read_text()))
    return runs


def summary(name, runs, tiers=None):
    picked = [r for r in runs if tiers is None or r["tier"] in tiers]
    valid = [r for r in picked if not r["status"].startswith("crash")]
    passed = [r for r in valid if r["passed"]]
    steps = mean(r["steps"] for r in passed) if passed else 0
    print(f"{name:<28} 有效 {len(valid):>2}  通过 {len(passed):>2}  平均步数 {steps:.1f}")


summary("第一版 baseline", load("result_baseline.json"))
summary("第一版 rag", load("result_rag.json"))
summary("v2 baseline", load("result_v2_baseline_*.json"))
summary("v2 rag", load("result_v2_rag_*.json"))
summary("v3 baseline", load("result_v3_baseline_*.json"))
summary("v3 rag", load("result_v3_rag_*.json"))
summary("v3 baseline 简单+中等", load("result_v3_baseline_*.json"), {"简单", "中等"})
summary("LangGraph baseline 简单+中等", load("result_lg_baseline_*.json"), {"简单", "中等"})
summary("LangGraph baseline", load("result_lg_baseline_*.json"))
for cfg in ("baseline", "bm25", "hybrid"):
    summary(f"留出集 {cfg}", load(f"result_ho_{cfg}_*.json"))

conn = sqlite3.connect("file:runs.db?mode=ro", uri=True)
n, tin, tout = conn.execute("SELECT COUNT(*), SUM(prompt_tokens), SUM(completion_tokens) FROM runs").fetchone()
print(f"runs.db:{n} 次运行,输入 {tin:,},输出 {tout:,},比例 {tin / tout:.1f}")

实际输出(Python 3.14,只用标准库):

第一版 baseline                 有效 10  通过  9  平均步数 11.8
第一版 rag                      有效 10  通过 10  平均步数 12.4
v2 baseline                  有效 30  通过 30  平均步数 14.7
v2 rag                       有效 30  通过 29  平均步数 12.7
v3 baseline                  有效 30  通过 30  平均步数 13.6
v3 rag                       有效 30  通过 30  平均步数 13.2
v3 baseline 简单+中等            有效 24  通过 24  平均步数 10.8
LangGraph baseline 简单+中等     有效 24  通过 24  平均步数 10.5
LangGraph baseline           有效 29  通过 29  平均步数 13.4
留出集 baseline                 有效 15  通过 15  平均步数 14.2
留出集 bm25                     有效 15  通过 15  平均步数 15.7
留出集 hybrid                   有效 15  通过 15  平均步数 16.8
runs.db:288 次运行,输入 48,704,845,输出 1,169,783,比例 41.6

逐段讲。

  • Path("eval").glob("result_v3_baseline_*.json"):按通配符找文件,* 匹配任意字符,于是 _1_2_3 三轮的结果文件都会被找到
  • sorted(...):让文件按名字顺序读,结果可复现
  • runs.extend(列表):把另一个列表的元素逐个追加进来。和 append 的区别是:append 会把整个列表当成一个元素加进去
  • from statistics import mean:求平均值
  • tiers=None:不传就不按难度档筛选;传一个集合(set) {"简单", "中等"} 时,r["tier"] in tiers 判断是否属于其中之一
  • r["status"].startswith("crash"):崩溃的运行(比如 402 余额不足)不计入有效运行
  • mean(... for r in passed) if passed else 0:只对通过的运行算平均步数;没有通过的就返回 0,避免对空序列求平均报错
  • 最后一段用只读模式打开 runs.db,10 篇讲过

和 REPORT 对照:

脚本输出REPORT是否一致
v2 rag 29/30、v3 各 30/30一致
v3 baseline 简单+中等 10.8、LangGraph 10.5LangGraph 对比表 10.8、10.5一致
LangGraph 有效 29”第 3 轮 re2 … 402 … 不计入”一致
留出集 14.2 / 15.7 / 16.8一致
288 次、41.6进度文档记录一致
第一版 baseline 平均步数 11.8REPORT 通过率表写 13.1算法不同

最后一行值得专门说:REPORT 第一版的平均步数 13.1 是 10 个项目全部算进去,包括失败时跑满 25 步的 re2;脚本只算通过的 9 个,得到 11.8。两个数都对,但讲的时候必须说清是哪个。面试时说出一个数字,要能回答”这个数怎么算的”。

自己改一改:

  1. summary 加一个参数,切换”只算通过的”和”全部算”,验证第一版 baseline 的 13.1
  2. 算一下 v3 baseline 困难档({"困难"})的平均步数,和 REPORT 的 25.0 对照

4.3 技术决策清单:每个”为什么”

面试官最爱问”为什么这么做”。下面每个决策都给出:做了什么、理由、证据、代价。理由要和证据对得上,不能事后编一个好听的。

决策理由证据或出处代价
用 Agent 而不是固定脚本每个项目依赖、选项、报错不同,下一步取决于上一步的报错评测集三档项目的轨迹差异很大;困难项目要先编依赖不稳定、贵,需要评测
程序独立验收模型会说”编译成功”但实际没有;要可复现、不依赖人工或模型打分旧标准下 json 两次空构建判通过,说明连退出码都不够验收自身会错,要测试
验收要求目标架构 ELF 产物退出码为 0 不代表编译了东西;主机编译也可能返回 0tests/test_regressions.pyVerify 测了空项目、主机构建、架构不对都判失败要解析 ELF、拆静态库
手写循环起步单 Agent 线性任务,几十行就够,每一步可控可调试65 行没有检查点
步数上限 40困难项目是嵌套任务,25 步不够只改上限,libpng 29 步、re2 32 步通过失败时成本上升;本质是愿意为失败付多少钱
read_file 从开头分页旧版只留末尾 100 行,前半部分看不到15 次绕路 → 0 次模型要多调几次
截断输出时保留退出码截断后看不出命令成没成功回归测试 test_exit_code_survives_truncation_and_log_is_readable
toolchain 改环境变量模型在子目录传相对路径找不到 toolchain,还被路径检查误拦8 次 → 0;误拦消失;这是 v2 → v3 唯一的改动依赖 CMake 3.21+
知识库做成工具让模型调用只在需要时查,不占上下文模型很少主动调用(第一版 3 次、留出集 8 次)
BM25 起步条目少(最初 8 条)、查询多是带关键词的报错原文,关键词匹配就够,而且快、没有依赖48 条测试里 BM25 原始报错类 15/15,每条 0.1ms改写描述类弱(13/17)
用混合检索、不用重排混合总分最高;重排总分一样但慢 30 多倍45/48;33ms vs 1126ms
留出集经验是从原 10 个项目提炼的,再用它们测等于提前给答案5 个新项目又发现一个验收 bug,45 次作废重跑
每配置 3 轮同一项目重复跑步数波动大,单轮对比没意义同项目 3 次标准差平均 2.5 到 3.4 步成本三倍
用 LangGraph 重写验证框架实际带来什么,而不是凭印象;岗位要求常见结果无差别;续跑实测成功代码翻倍、线程问题
两版共用工具和验收保证对比公平,只变循环写法check_and_run 共用
工具封装成 MCP server同一套工具和边界检查给其他客户端用tests/test_mcp.py客户端的确认和审计不受控
工具层检查但不做容器学习项目、工具链在 macOS 主机上;明确写出边界README “不是安全沙箱”构建脚本能绕过;子进程能读环境变量
同时只允许一个构建Agent 设置进程级环境变量,工作目录按项目名409;锁在构建线程结束时释放只能单用户

关于”为什么 BM25 起步”要注意:上表的理由是根据项目实际情况整理的,和最初的知识库规模、查询特点对得上。面试时如果被追问”当时是怎么想的”,按事实说”条目少、报错文本关键词明显,先用最简单的,后面再用测试集对比”,不要说成事先做过调研。

4.4 主动讲局限

主动说局限不是示弱,是展示判断力。关键是说清楚原因和影响,最好带上”如果要做会怎么做”。

局限原因影响如果继续做
评测集饱和10 个项目里 8 个关掉测试就能过;修正验收后全部通过通过率无区分度,只能比步数和 token加依赖链更深、非 CMake、需要 try_run 的项目
样本少一轮完整评测十分钟左右、上百万 token大部分差异只能算趋势更多轮次;配对比较和置信区间(09 篇实验)
知识库没带来可测量收益覆盖不足、模型很少主动查投入的检索工作没有端到端回报报错后自动检索附在结果后;扩大覆盖(但不能用留出集的经验)
不是沙箱工具层检查拦不住构建脚本不能编不可信仓库容器、断网、不传环境变量
子进程能读 API keysubprocess.run 没有限制环境变量恶意构建脚本能拿到 key只传必要环境变量;容器
没记缓存命中和模型耗时最初只记了 token 总数算不出真实成本和延迟分解按步记录 usage 全字段
没有告警单机项目余额耗尽后又连续崩了 4 个项目连续崩溃告警、余额监控
SSE 没有心跳没在 Nginx 后部署过部署后长命令可能断连定期发注释行
只有单用户进程级环境变量、全局锁不能多人用任务队列、每任务独立环境
通过率只跑了一个模型成本不知道换模型结论是否成立换模型跑同一评测

讲的时候的句式:“这个我没做好,原因是……,它的影响是……,如果继续做我会……”。

4.5 简历逐条对照

简历(resume/resume_ai_v1.md)上 CrossBuild Agent 的每一条,都要知道证据在哪、会被怎么追问。

简历原文要点证据最可能的追问回答要点
建模为多步决策任务;程序独立验收,要求构建通过且产出目标架构 ELF,不采信模型自述,也不依赖人工或模型打分build_agent.verify;REPORT 重跑一节”验收会不会错?“错过两次,讲两个 bug 和修复、回归测试
手写 Tool Calling 循环,含步数预算、重复调用检测与卡死判定build_events”卡死怎么判定?“工具名加参数完全相同算同一个调用,超过 3 次就不再执行、直接提示换思路;出现过的不同调用超过 2 种、且每一种都超过上限时判卡住并结束;写文件成功后清掉命令的计数
LangGraph 重写,SQLite 检查点支持断点续跑,两版同配置各 3 轮结果一致graph_agent.py;REPORT LangGraph”一致是什么意思?""续跑会重复执行吗?“30/30 vs 29/29(1 次 402 不计),步数 token 相近;节点粒度,中途被杀会重复
路径层级校验、命令白名单、超时终止,输出截断保留退出码并落盘分页tools.py”安全吗?“讲 cat 绕过;明确不是沙箱
toolchain 改环境变量后,子目录配置找不到工具链由 8 次降为 0REPORT v3”8 次是怎么统计的?“v2 两个配置各 4 次,从 trace 里按报错统计;v3 唯一改动所以因果清楚
工具封装为 MCP servermcp_server.pytests/test_mcp.py”MCP 是什么?为什么做?“12 篇;复用同一套工具和边界
从 160 次运行记录提炼报错经验并人工审核,知识库扩至 15 条REPORT 知识库;knowledge/mine_runs.py”怎么提炼的?审核标准?“按报错签名分组 → 模型写候选 → 对照运行记录审核;7 条收 5 合 1 丢 1
48 条分类查询对比,混合 Recall@1 45/48 最高,重排同分但慢 30 余倍,未采用REPORT 检索对比”为什么分类?""RRF 是什么?“分类才看出互补;RRF 按排名融合,k=60
10 个项目三档加 5 个留出项目,每配置重复 3 轮eval/”为什么要留出集?“防泄漏
修正空构建误判、构建目录写死两处验收问题后重跑REPORT”怎么发现的?“复验输出为空;libarchive 9 败 8 报错一致
留出集上知识库未带来可测量收益,定位瓶颈为经验覆盖不足而非检索算法REPORT 留出集”那做 RAG 有什么意义?“方法跑通、定位了瓶颈、给出了改进方向;如实结论比虚高数字有价值
SQLite 记录每步工具调用、耗时与 token,界面经 SSE 实时展示与回放trace.pyserver/app.py、Vue 组件”记了什么?没记什么?“10 篇;没记缓存命中、模型耗时
21 个测试覆盖越界、误删、验收与续跑tests/ 下 4 个文件”误删是什么?“prepare 拒绝 .. 这类项目名,否则会删到工作区外

4.6 关于”代码是你自己写的吗""做了多久”

这两个问题在 2026 年的面试里越来越常见,要提前想好怎么如实回答。

事实(来自 git 历史):项目的 16 个提交集中在 2026-09-11 到 09-13,提交信息里都带有 AI 编码工具的共同作者标记。

建议的回答方向:

开发时我用了 Claude Code 这类 AI 编码工具,git 提交记录里也能看到。代码主体是在几天里迭代出来的。

我自己负责的是这些判断:任务怎么定义、成功标准怎么定、评测集怎么分档、为什么要多轮和留出集、每一轮结果说明了什么、哪些结论不能下。比如”90% 到 100% 不能归因于知识库”、发现验收判错、决定作废 45 次结果重跑,这些都是看数据和轨迹之后做的决定。

代码我都能讲清楚,您可以挑任意一段问。

要做的准备:既然这样回答,就必须真的能讲清每一段代码。本系列 04、05、07、10、11、12、14 篇逐段讲过项目的工具层、循环、检查点、记录、安全检查、LangGraph 版本、MCP server 和后端,面试前至少把这些段落对着源码过一遍。

不要做的:不要否认用了 AI 工具,git 历史是公开的;也不要把”用 AI 写代码”说成主要能力。

4.7 容易踩的表达坑

例子改成
夸大因果”加了知识库效果提升了""没有观察到可测量的差异”
数字不说算法”平均 13 步""v3 baseline 10 个项目 3 轮、只算通过的,平均 13.6 步”
技术名词堆砌”用了 LangGraph、MCP、RAG、RRF、Milvus”每个说清楚解决了什么问题、结果如何
把没做的说成做了”有沙箱""工具层检查,明确不是沙箱”
回避失败只讲最终 60/60先讲验收 bug 和重跑,60/60 反而说明评测集要加难度
讲太细停不下来一开口就讲 ELF 解析先讲结构,面试官感兴趣再展开
背稿痕迹重一字不差念记住要点和数字,用自己的话讲

第五部分 对照项目

本篇的”对照”是把系列里每篇的知识点,对应到项目讲稿里的哪一段:

系列篇目讲稿里的位置面试时能说的项目证据
01 大模型基础成本输入是输出的 41.6 倍,每步重发历史
02 模型 API 工程局限402 余额不足崩溃;没有告警
03 Prompt 工程工具设计知识库工具描述改写后调用从 3 次到 5 次,仍然低
04 工具调用与工具设计十分钟版 ②分页读、截断保留退出码、错误信息引导
05 Agent 循环十分钟版 ②步数上限、卡住检测、写文件后清零
06 上下文工程工具设计读文件分页、输出截断;41.6 倍
07 状态持久化十分钟版 ⑦run 217 续跑;节点粒度
08 RAG十分钟版 ⑤⑥48 条四类、45/48、覆盖是瓶颈
09 评测十分钟版 ③④⑥两个验收 bug、三条证据、留出集、一倍标准差规则
10 可观测十分钟版 ⑨runs.db 两张表;没记缓存命中
11 安全十分钟版 ⑧cat 绕过、不是沙箱、环境变量
12 框架与协议十分钟版 ⑦65 vs 126 行;MCP server
13 多 Agent追问为什么没用:串行依赖、共享目录
14 AI 应用后端十分钟版 ⑨SSE、锁、409、断开放锁的 bug
15 系统设计题追问按八步框架讲项目

第六部分 追问清单

30 个高频追问,按主题分组。回答方向里的数字都在第一部分数字卡片里。

#追问回答方向
1为什么选交叉编译这个题目工作经验相关,能判断对错;结果可程序验证
2为什么用 Agent 不用脚本路径不固定,下一步依赖报错
3用的什么模型,为什么deepseek-flash,便宜,评测要跑几百次;没做多模型对比,是局限
4成功标准怎么定的程序重新构建 + 目标架构 ELF 产物;讲演变
5验收会错吗两个 bug、怎么发现、回归测试
6模型会不会钻验收空子run 44 给 header-only 项目加示例目标
7卡住怎么判断重复调用计数;写文件后清零
8步数上限怎么定的25 → 40 的单变量实验;成本权衡
9工具设计改过什么读文件分页、截断保留退出码、toolchain 环境变量
10知识库怎么建的运行记录 → 分组 → 模型候选 → 人工审核
11为什么检索测试集分四类原始报错对 BM25 有利;改写和变体才测得出差异
12RRF 怎么算各路排名倒数加 k 求和,k=60
13为什么不用重排总分一样、慢 30 多倍
14知识库到底有没有用未能证明;三条证据;留出集无差异;瓶颈在覆盖和调用率
15那 RAG 做得有什么意义方法跑通、离线和端到端分层、定位瓶颈
16为什么要留出集防止提前看到答案
17每个配置跑几轮?够吗3 轮;不够,只能说趋势;一倍标准差规则
18结果有统计显著性吗没做正式检验;配对重抽样区间跨过 0(09 篇实验)
1960/60 说明什么评测集饱和,要加难度
20LangGraph 和手写有什么区别结果一样;代码量、控制流、检查点
21续跑会重复执行工具吗节点粒度;需要幂等
22踩过 LangGraph 什么坑线程和 SQLite、递归上限默认值、invalid_tool_calls
23MCP server 做了什么复用工具层;越界返回 isError
24安全上发现过什么问题cat 绕过;路径前缀;.. 项目名
25你的 Agent 安全吗拦得住什么、拦不住什么、应该靠容器
26后端怎么设计的线程 + 队列 + SSE + 锁;断开放锁 bug
27成本多少288 次输入 4870 万 token;没记缓存命中,只能粗估
28如果上线要改什么容器、任务队列、多用户、告警、心跳
29最难的是什么判断结果说明什么;评分器会错
30代码是你写的吗、做了多久如实说用了 AI 编码工具、几天迭代;讲自己的判断;能讲清每段代码

第七部分 闭卷自测

1. 不看稿,说出一分钟版的三个要点。

答案

一是项目是什么、为什么做(交叉编译 Agent;工作中做过二十多款工具的 ARM64 适配);二是看重评测(程序独立验收、10+5 个项目、每配置三轮、比较了知识库、检索方式、框架);三是有价值的发现(验收自身错过两次;90% 到 100% 不能归因于知识库,留出集也无可测量收益)。

2. “90% → 100% 不能归因于知识库”的三条证据是什么?

答案

步数变化方向混杂(4 个变好、4 个变差、2 个持平);两百多次工具调用只查了 3 次知识库;被救回的 re2 查的内容当时库里没有。另外每个配置只跑了 1 轮。

3. 两个验收 bug 分别是什么、怎么发现的、影响是什么?

答案

一:只看 cmake --build 退出码,json 两次空构建判通过;复查复验输出为空发现;旧数字作废,按新标准重跑。二:验收写死 build/ 目录,libarchive 仓库自带 build/ 源码目录,Agent 换目录名编译成功却判失败;9 次失败 8 次、报错都是 could not load cache 发现;45 次结果作废重跑。

4. 为什么 toolchain 改环境变量这个改动”因果清楚”,而困难档步数下降”只能算趋势”?

答案

v2 到 v3 只改了这一处,“子目录找不到 toolchain”从 8 次到 0、误拦消失,在轨迹里能直接对应到改动。困难档步数每格只有 6 次,标准差 5 到 8 步,差距不到一倍标准差,也没法在轨迹里直接对应。

5. 实验里第一版 baseline 平均步数算出来是 11.8,REPORT 写 13.1,谁错了?

答案

都没错,算法不同。REPORT 把 10 个项目全算上,包括失败时跑满 25 步的 re2;脚本只算通过的 9 个。讲数字时必须说清算法。

6. 为什么要做留出集?留出集上发现了什么?

答案

新增经验是从原 10 个项目的运行记录提炼的,再用它们测等于提前给了答案。留出集上:又发现构建目录写死的验收 bug;修复后三种配置全过,步数差异在一倍标准差内;30 次开知识库只调 8 次检索,查得最多的 libarchive 链接错误库里没有,瓶颈是覆盖不是算法。

7. LangGraph 版和手写版的对比结论是什么?续跑实验证明了什么、没证明什么?

答案

结果无差别(30/30 vs 29/29,另 1 次 402 不计),代码 65 vs 126 行,LangGraph 多出的是检查点和续跑。续跑证明了检查点之间的状态能正确恢复、已完成的工具调用不重复;没证明节点执行中途被杀也不重复,按机制分析是会重复的。

8. 说出至少五个项目的局限,并用”原因—影响—如果继续做”的句式讲其中一个。

答案

评测集饱和、样本少、知识库无可测量收益、不是沙箱、子进程能读 API key、没记缓存命中和模型耗时、没有告警、SSE 无心跳、只能单用户、只跑了一个模型。例:评测集饱和,原因是 10 个项目里 8 个关掉测试就能过,影响是通过率无区分度只能比步数和 token,如果继续做会加依赖链更深、非 CMake、需要 try_run 的项目。

9. 简历上”混合检索 Recall@1 45/48 最高,重排同分但慢 30 余倍,未采用”会被怎么追问?

答案

为什么测试集分类(原始报错对 BM25 有利,改写和变体才测得出差异);RRF 怎么算(按排名倒数融合,k=60);为什么不用重排(总分一样,33ms 变 1126ms);那端到端效果呢(留出集无可测量收益,离线分数高估实际作用)。

10. 被问”代码是你自己写的吗”,怎么如实回答?

答案

如实说用了 AI 编码工具(git 历史可见)、几天迭代完成;重点讲自己负责的判断:任务和成功标准定义、评测设计、多轮和留出集、结果解读、推翻错误结论、决定作废重跑;表示能讲清任意一段代码,并确实提前准备好。

延伸阅读

  1. 项目仓库:github.com/0612like/crossbuild-agentREADME.mdeval/REPORT.md
  2. 00 学习路线与面试地图 — 回到系列目录,按面试重要度复习
  3. 09 评测 — 项目评测全过程时间线
  4. 15 系统设计题 — 用八步框架讲项目

这是系列的最后一篇。回到 00 学习路线与面试地图,按面试前的复习顺序过一遍。

Related · Agent 开发
⎇ main ai/agent开发 21 节 230 notes UTF-8