Skip to content

01 项目口述稿(多版本) ​

用法:出声读,掐表。每个版本都标注了适用场景。 括号里的 【停顿】、【看面试官反应】 是提示,不要念出来。


版本 0:电梯版(20 秒,用于「简单介绍一下这个项目」) ​

「EasyCoding 是我用 Go 从零写的一个终端 AI 编程助手,对标 Claude Code。 核心是不依赖任何 Agent 框架,自己实现了 ReAct 循环、双协议 LLM 抽象、五层权限防御、MCP 工具接入和上下文压缩。 规模大概是 18 个包、1.8 万行 Go,go test ./... 全绿。」

备注:20 秒版本只做一件事——让面试官知道该往哪挖。最后那句「该往哪挖」是刻意的:你报了 5 个技术点,他就会从里面挑一个。


版本 1:一分钟版(自我介绍里嵌入) ​

「我最近做的一个项目是 EasyCoding,一个终端 AI 编程助手,全 Go 实现,对标 Claude Code。

它跟普通的『调 API 套壳』不一样的地方在于,我是把它当成一个 Agent 运行时 来写的: 【停顿】 第一,我做了一层协议无关的 Provider 抽象,Anthropic 和 OpenAI 两套 API 的差异全部收敛在一个接口后面,上层 Agent 代码完全不知道底下用的是哪家; 第二,我实现了完整的 ReAct 多轮闭环——思考、行动、观察、再思考,其中只读工具同轮并发、有副作用的工具串行保序; 第三,我做了一套五层权限防御,从危险命令黑名单一直兜到人在回路弹窗; 第四,通过 MCP 协议接入了社区工具生态; 第五,做了会话持久化和两层上下文压缩,让长会话不会爆 token。

整个项目 18 个包、1.8 万行 Go,只有三个入口依赖用了第三方库——LLM SDK、MCP SDK 和 TUI 框架,核心的 Agent 逻辑全是手写的。」

备注:

  • 一分钟版本的结构是「定位 → 五个技术点 → 规模和一个反差」。
  • 最后那句「核心全是手写的」是必须预留的伏笔——因为简历写了「从零手写」,面试官迟早会问「你用了什么库」,你在这里先主动交代,就变成「诚实」;等他问出来再交代,就变成「被抓」。
  • 五个技术点要按简历顺序报,面试官后面就会按这个顺序挖,你等于控制了节奏。

版本 2:三分钟版 ⭐ 主力稿(务必背熟) ​

「我讲一下 EasyCoding 这个项目。

先说为什么做。 我用 Claude Code 的时候发现它是个黑盒——我知道它好用,但不知道它为什么好用:它怎么决定调哪个工具、什么时候停下来、上下文快满了怎么办、危险命令在哪一层被拦住的。所以我就想用 Go 从零实现一遍,把这些问题拆开看清楚。 【停顿】 整体架构分五层。 最下面是协议层,一个 llm.Provider 接口,只有三个方法——返回供应商名、返回模型名、发起一轮流式对话;Anthropic 和 OpenAI 各自实现一遍,上层拿到的是一模一样的 StreamEvent。 往上是循环层,也就是 Agent 本体,实现 ReAct:一轮一轮地调模型,模型要么直接给文本结束,要么请求调工具,我执行完把结果回灌进历史,再进下一轮。 再往上是执行层,包括工具注册中心和一个权限引擎——工具执行前必须过权限,过了才真正跑。 再往上是状态层,管对话历史、上下文压缩、JSONL 会话存档、长期记忆。 最上面是扩展层——MCP、Skill、Hook、SubAgent。 【停顿】 有几个我觉得值得讲的设计决定。

第一个是工具并发模型。同一轮里模型可能一次请求好几个工具,我没有简单地全并发,也没有简单地全串行,而是做了「保序分批并发」:连续的只读工具——比如同时读三个文件、或者 glob 加 grep——打包成一个批次并发执行,遇到有副作用的工具就断批、串行执行它;不管怎么并发,结果都严格按模型请求的原始顺序回灌。这样既拿到了并发收益,又保证了语义。

第二个是权限的五层防御。顺序是:危险命令黑名单 → 文件沙箱 → YAML 规则引擎 → 四档模式兜底 → 人在回路弹窗。设计的核心是顺序不可交换——黑名单在最前面,而且任何配置、任何模式包括 Bypass 都绕不过它;沙箱在它后面,负责把文件操作锁在项目目录内;只有前两层都没拦,才轮到用户自己写的规则和模式兜底。最后如果判定是『需要确认』,就通过一个 channel 把请求丢给 TUI 弹窗,Agent 阻塞等人回答,用户可以在『允许本次 / 永久允许 / 拒绝』里选;选永久允许会把这条精确规则写进本地配置文件,跨会话生效。

第三个是上下文的两层压缩。第一层不做任何 LLM 调用,是纯本地的:单条工具结果超过 5 万字节,就把它落盘到会话目录,历史里只留一段头部预览加一句『完整内容已保存到某路径,要看请用文件读取工具读』。第二层才是 LLM 摘要:超过阈值就把整段历史发给模型,让它按九个固定小节产出一份摘要,再拼上三段恢复信息——最近读过的文件快照、当前可用工具列表、以及一句『不要凭摘要猜代码,要读原文』的边界提示。这两层都有兜底:第一层的替换决策记在一个账本里,同一个工具结果一旦决定替换就不允许翻转,避免历史反复变动破坏缓存;第二层有熔断,连续三次摘要失败就跳闸,不再自动压缩,避免陷入死循环。 【停顿】 最后说规模:18 个包,101 个非测试文件、1.4 万行 Go 代码,加上测试一共 1.8 万行,go test ./... 全绿。」

备注:

  • 这是主力稿。它有三个「【停顿】」标记,作用是给面试官留出插话的窗口——好的面试是对话,不是背稿。如果他在第一个停顿处插话问「Provider 接口三个方法够吗」,你就顺着答,这比你把 3 分钟背完效果更好。
  • 讲「为什么做」很重要。字节的面试官非常吃「自驱 + 追问本质」这个叙事,而不是「我做了个功能」。
  • 三分钟里我故意只深讲了 3 个点(工具并发、五层权限、两层压缩),另外 2 个(协议、MCP)留给面试官挖——不要一次讲完,讲完了他就没得问,反而会去挖你不熟的地方。

版本 3:八分钟版(讲架构 + 演示流程) ​

三分钟版之后,如果他说「再详细讲讲」,用这个。这个版本的重点是带他走一遍「一次完整的用户请求」。

「我按『用户敲一行字之后发生了什么』来串一遍,这样最清楚。

第一段:输入。 用户在 TUI 输入框里敲完回车,我先判断是不是以 / 开头——是的话走命令分发,一共有 13 条内置命令,分成四类:纯本地查询(比如 /status)、改界面状态的(比如 /plan 切计划模式)、注入提示词触发一轮 LLM 的(比如 /review)、以及 Skill 类。不是 / 开头就当成普通消息,先过一遍 Hook 的 UserPromptSubmit 事件——Hook 是个外部命令执行器,可以在这时候拦截,被拦了就不进历史、不发请求。

第二段:进入 Agent 循环。 消息追加到对话历史,然后调用 Agent.Run,它返回一个事件 channel。这里有个设计选择:我没有用 Bubbletea 的 program.Send 从后台推消息,而是让 TUI 在每次 Update 之后返回一个阻塞读 channel 的 tea.Cmd,读到事件就处理、处理完再返回一个新的读命令,形成自循环。这样做的好处是——agent 侧的 channel 是无缓冲的,TUI 不读,agent 就阻塞在发送上,天然形成背压,不会出现 UI 卡死但 agent 还在疯狂跑的情况;同时事件里带了 ctx.Done() 分支,取消的时候 agent 不会被卡死在发送上。

第三段:一轮请求内部。 每一轮开始,先做上下文管理——估算当前 token、必要时压缩;然后按当前权限模式选工具集,计划模式下只给模型只读工具;然后拼系统提示。这里有个细节:系统提示我分成两段,一段是稳定的固定模块,一段是环境信息,因为 Anthropic 的 Prompt Cache 是按前缀命中缓存的,稳定的那段打上缓存断点,每次都命中;环境段每轮重建——为什么要每轮重建?因为用户可能在某一轮激活了一个 Skill,下一轮它必须立刻生效。

第四段:流式返回。 模型流式吐文本,我这边把文本增量直接发给 UI 显示,工具调用的增量参数由 SDK 的 Accumulator 拼完,流结束后一次性拿到完整的工具调用列表。

第五段:执行工具。 先按只读/副作用分批,然后每一个都过权限引擎的四层判定。判定结果如果是 Deny,我就把拒绝原因当成工具结果回灌给模型——注意这里不中断循环,模型看到『你被拒绝的原因是路径在项目外』,它会自己换个路径重试。如果是 Ask,就走人在回路。执行完所有工具,结果按原始顺序回灌进历史,进入下一轮。

第六段:收尾。 什么时候停?三种情况:模型不再请求工具、只回文本,这是自然完成;连续三轮整轮都请求了不存在的工具,判定为幻觉,主动停;或者撞到 25 轮迭代上限。这三条路径都会保证历史末尾是一条 assistant 消息——因为如果最后是 user 或 tool 消息,下一次请求 Anthropic 会直接返回 400。

第七段:落盘。 对话历史的每一次追加都会同步写进 JSONL 文件,写完 fsync 一次。用 JSONL 而不是数据库,是因为它天然支持崩溃后恢复——最坏情况只丢最后一行,而且外部工具 tail -f 就能看。」

备注:

  • 八分钟版的技术密度很高,但如果你能完整讲下来,面试官会直接改变对你的评级。因为它证明你不只知道「有什么」,还知道「数据怎么流动」。
  • 讲的时候一定要画图(如果有白板)。画一条竖线,左边写「TUI」,右边写「Agent」,中间画两个箭头标「事件 channel」和「审批 channel」。这个图一画出来,后面的所有追问你都能指着图回答。
  • 第七段「为什么用 JSONL 不用 SQLite」是个钩子,面试官很可能顺着问,答案在 05 文档里。

版本 4:如果面试官问「这个项目最难的地方是什么」 ​

「最难的不是写功能,是决定边界在哪。我举一个具体的。

上下文压缩我一开始想做成『超过阈值就摘要』,做完发现两个问题: 一是摘要本身也要花 token,如果历史特别长,摘要请求自己就会超窗口——所以我加了一个 PTL 重试,摘要请求被拒时,先丢最旧的一组用户回合重试,前三次每次只丢一组,再失败就按 20% 的比例丢,直到塞得进去; 二是压缩会破坏 Prompt Cache——历史一变,后面的缓存全失效,反而更贵、更慢。所以我做了两个防御:第一层的不做 LLM 调用的替换是带账本的,同一个工具结果一旦决定落盘替换,就不允许再变回去,保证历史内容稳定;第二层摘要才真正重写历史,所以触发阈值留了 13000 token 的安全余量,尽量晚触发。

所以就这一个功能,我改了四五个版本。我觉得这才是 Agent 工程真正难的地方——不是算法,是一堆互相冲突的约束里找平衡点。」

备注:这个回答是为字节量身定做的。字节的面试评价体系里非常看重「你在冲突中做了什么取舍」。不要答「最难的是并发」或「最难的是架构设计」这种空话。


版本 5:如果面试官问「这个项目有多少是你自己写的 / 用 AI 写的」 ​

这是你最需要准备的一题。诚实但不自贬。

「代码主要是 AI 辅助写的,这是实话——2025 年写 1.8 万行 Go,不用 AI 是不现实的。 但我想区分两件事:AI 能帮你写代码,不能帮你决定架构。

这个项目里所有关键的设计决定都是我自己做的,而且都能追溯到具体的理由: 比如 Provider 接口为什么只有三个方法、为什么系统提示要拆成可缓存和不可缓存两段、为什么工具并发要保序分批而不是简单全并发、为什么压缩要分两层而且第一层不做 LLM 调用、为什么黑名单必须在沙箱和规则之前且不可配置放开、为什么权限引擎只做前四层而第五层交给 Agent 编排。 这些决定 AI 不会主动帮你想,你甚至要在 AI 给出一个能跑的版本之后,把它推倒重来。

我也很清楚 AI 写的代码有什么风险,这个项目里我自己就找出了几个问题:比如 MCP 工具的权限通配规则其实是失效的,因为 extractTarget 没有 MCP 分支导致匹配目标恒为空串;比如后台 Agent 的工具白名单里 Skill 工具名大小写不一致,导致白名单形同虚设。这些问题 AI 写的时候不会告诉你,得靠你真正理解每一层的数据流才能发现。」

备注:

  • 绝对不要否认用了 AI。 2025–2026 年的面试官问这个问题时,心里已经有答案了。否认 = 不诚实,直接出局。
  • 这段话的杀伤力在最后一段:你主动报出了两个真实存在的、需要读懂代码才能发现的 bug。这一下就把「AI 写的」变成了「我 review 过的」,性质完全不同。
  • 这两个 bug 的细节在 00 文档的「软肋 3」和「软肋 3.5」,被追问时照着答。

版本 6:如果面试官说「我觉得这就是个玩具项目」 ​

「我理解这个判断,从用户量上它确实是个人项目。 但我想说它在工程密度上不是玩具: 18 个包、1.8 万行 Go,五层权限里每一层都有对应的规格文档和验收清单,go test ./... 全绿,有 -race 跑过的并发测试,有 CI,也有 tmux 端到端的手工验收流程。 而且它刻意覆盖了几个真实难点:协议差异的收敛、工具执行的并发与顺序语义、上下文的预算管理、权限的分层拦截、外部进程的生命周期管理——这些在任何一个真要上线的 Agent 产品里都是核心问题,不是在简历项目里编出来的需求。

当然我也承认它和生产级的差距在哪:没有多租户,没有可观测性——没有分布式 trace、没有指标上报,权限沙箱是参数级的、不是进程级的。这些我在文档里都写了。」

备注:

  • 这个回答的模板是「承认边界 + 搬出密度证据 + 主动列出生产级差距」。最后那段「主动列出差距」是反直觉但极其有效的——它把「被质疑」变成了「我在做自我评估」。
  • 千万别硬顶说「这不是玩具」。也别真的自贬说「是的,就是个玩具」。要重新定义评价维度。

附:口述时的三个技术细节提醒 ​

  1. 项目名要说清楚:简历写 EasyCoding,仓库也是 EasyCoding,但代码里的 module 名和二进制名是 mewcode,README 也写 MewCode。面试官如果打开 GitHub 看到 MewCode 会疑惑,所以第一次提到时说一句:「项目英文名是 MewCode,仓库叫 EasyCoding。」
  2. 报数字要精确:不要说「大概一万多行」,要说「101 个非测试文件、14,082 行;加测试一共 17,984 行」。精确的数字 = 你真的数过。
  3. 说「25 轮」不要说「10 轮」:这是简历里的错误,必须纠正(见 00 文档校正 1)。

➡️ 下一篇:02-简历五大技术点拆解.md

持续学习,持续构建。