文章目录
-
- 一、这是什么
- 二、为什么这个项目能帮你找实习
-
- 面试官想看什么
- 简历上可以怎么写
- 三、面试能聊的技术亮点
-
- 亮点 1:Agent 主循环——while(true) 而不是框架黑盒
- 亮点 2:手写 MCP 协议——150 行,零 SDK
- 亮点 3:三层上下文压缩——比"扔给 LLM 摘要"高一个维度
- 亮点 4:Subagent 隔离——在定义层切断递归
- 亮点 5:Prompt Caching——靠架构不靠 API
- 四、学这个项目的路线
-
- 第一周:跑起来,看懂主循环
- 第二周:改一个工具
- 第三周:准备面试话术
- 第四周:写进简历,开始投
- 五、和其他学习项目比
- 六、项目还有这些细节
- 总结
GitHub 仓库: https://github.com/cookie250825/mvp-claude-code
⭐ 觉得有用就 Star 一下,找实习时这就是你的项目经验。
今年 Java 后端的实习岗位,JD 里开始出现这些词了:大模型应用、Agent 开发、MCP 协议、LangChain。
你简历上写什么?“精通 Spring Boot、熟悉 Redis”——面试官一天看 50 份这样的简历。
我写了一个项目,专门给后端开发者的:纯 Java,2000 行代码,从零实现一个 AI 编程助手。 和 Claude Code 一样的架构,但每一行代码都是你自己能看懂、能讲清的。
不是套壳,不是调 API 玩具。是一个完整的 Agent 引擎——面试能讲 30 分钟的那种。
一、这是什么
你在终端里敲一行字:
> 帮我在 src 目录下找到所有用到 ThreadPoolExecutor 的地方,看看有没有忘记关的
它就去读文件、搜索代码、执行命令,几轮工具调用之后给你答案。像一个真正的工程师坐在键盘前面。
架构图和 Claude Code 完全一致:
#mermaid-svg-5sKVYbK9l3mYRABU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5sKVYbK9l3mYRABU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5sKVYbK9l3mYRABU .error-icon{fill:#552222;}#mermaid-svg-5sKVYbK9l3mYRABU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5sKVYbK9l3mYRABU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5sKVYbK9l3mYRABU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5sKVYbK9l3mYRABU .marker.cross{stroke:#333333;}#mermaid-svg-5sKVYbK9l3mYRABU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5sKVYbK9l3mYRABU p{margin:0;}#mermaid-svg-5sKVYbK9l3mYRABU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5sKVYbK9l3mYRABU .cluster-label text{fill:#333;}#mermaid-svg-5sKVYbK9l3mYRABU .cluster-label span{color:#333;}#mermaid-svg-5sKVYbK9l3mYRABU .cluster-label span p{background-color:transparent;}#mermaid-svg-5sKVYbK9l3mYRABU .label text,#mermaid-svg-5sKVYbK9l3mYRABU span{fill:#333;color:#333;}#mermaid-svg-5sKVYbK9l3mYRABU .node rect,#mermaid-svg-5sKVYbK9l3mYRABU .node circle,#mermaid-svg-5sKVYbK9l3mYRABU .node ellipse,#mermaid-svg-5sKVYbK9l3mYRABU .node polygon,#mermaid-svg-5sKVYbK9l3mYRABU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5sKVYbK9l3mYRABU .rough-node .label text,#mermaid-svg-5sKVYbK9l3mYRABU .node .label text,#mermaid-svg-5sKVYbK9l3mYRABU .image-shape .label,#mermaid-svg-5sKVYbK9l3mYRABU .icon-shape .label{text-anchor:middle;}#mermaid-svg-5sKVYbK9l3mYRABU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5sKVYbK9l3mYRABU .rough-node .label,#mermaid-svg-5sKVYbK9l3mYRABU .node .label,#mermaid-svg-5sKVYbK9l3mYRABU .image-shape .label,#mermaid-svg-5sKVYbK9l3mYRABU .icon-shape .label{text-align:center;}#mermaid-svg-5sKVYbK9l3mYRABU .node.clickable{cursor:pointer;}#mermaid-svg-5sKVYbK9l3mYRABU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5sKVYbK9l3mYRABU .arrowheadPath{fill:#333333;}#mermaid-svg-5sKVYbK9l3mYRABU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5sKVYbK9l3mYRABU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5sKVYbK9l3mYRABU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5sKVYbK9l3mYRABU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5sKVYbK9l3mYRABU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5sKVYbK9l3mYRABU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5sKVYbK9l3mYRABU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5sKVYbK9l3mYRABU .cluster text{fill:#333;}#mermaid-svg-5sKVYbK9l3mYRABU .cluster span{color:#333;}#mermaid-svg-5sKVYbK9l3mYRABU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5sKVYbK9l3mYRABU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5sKVYbK9l3mYRABU rect.text{fill:none;stroke-width:0;}#mermaid-svg-5sKVYbK9l3mYRABU .icon-shape,#mermaid-svg-5sKVYbK9l3mYRABU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5sKVYbK9l3mYRABU .icon-shape p,#mermaid-svg-5sKVYbK9l3mYRABU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5sKVYbK9l3mYRABU .icon-shape rect,#mermaid-svg-5sKVYbK9l3mYRABU .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5sKVYbK9l3mYRABU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5sKVYbK9l3mYRABU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5sKVYbK9l3mYRABU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
纯文本
含工具调用
批准
file/bash/search
task
MCP
用户输入
AgentLoop.process
BackgroundManager.drain\\n后台任务通知注入
microCompact\\n旧工具输出裁剪
token > 阈值?
LLM 摘要压缩
ContextBuilder.build\\n可缓存前缀 + 动态历史
AIService.streamingChat\\n流式输出 token
流式输出 ✅
ToolExecutionConfirmation\\n用户确认 y/n/a
ToolDispatcher 路由
工具类型
内置工具
SubagentRunner\\n独立上下文 + 防递归
MCPClient\\nJSON-RPC over stdio
ToolResult 写回 history
下轮循环
关键数字: 2000 行 Java、7 个 Maven 依赖、零 Spring Boot、零数据库。整个项目就是一个 jar 包,跑起来就行。
二、为什么这个项目能帮你找实习
面试官想看什么
后端实习面试,面试官心里有三个问题:
这个项目帮你同时回答这三个问题。
简历上可以怎么写
MVP Claude Code(开源) — 核心开发者
- 用 Java 从零实现类 Claude Code 的 AI 编程助手,2000+ 行代码
- 手写 MCP 协议客户端(JSON-RPC over stdio),零 SDK 依赖,支持接入外部工具生态
- 设计三层上下文压缩策略(Micro/Auto/Manual),解决 LLM 长对话 token 爆炸问题
- 实现 Subagent 子任务隔离机制,在工具定义层切断递归
- 技术栈:Java 17、LangChain4j、DeepSeek API、Picocli、Jackson
五条就够。 每一条都是面试官能追问的点——追问了你都能接住,这就是好简历。
三、面试能聊的技术亮点
下面这五个点,每个面试都能讲 5-10 分钟。我写项目时专门留了"面试话术"——docs/HARNESS_DESIGN.md 里每个组件都有。
亮点 1:Agent 主循环——while(true) 而不是框架黑盒
大部分 AI 项目靠 LangChain 的 AgentExecutor,几行配置跑起来,但面试一问细节就崩。
我是手写的:
for (int round = 1; round <= MAX_ROUNDS; round++) {
// 1. 排空后台任务通知
// 2. microCompact — 零成本裁剪旧工具输出
// 3. autoCompact — token 超阈值时 LLM 摘要
// 4. 流式调用 LLM
// 5. 工具调用?→ 确认 → 执行 → 写回结果
// 6. 纯文本?→ 返回给用户
}
面试官追问"为什么不用 AiServices"时,你能讲出可控性——框架是黑盒,手写循环让你在每一步都能插入自己的逻辑。
亮点 2:手写 MCP 协议——150 行,零 SDK
MCP(Model Context Protocol)是今年最热的 Agent 协议。大部分项目直接用 MCP SDK,这没问题,但面试没得聊。
我自己实现了完整的 JSON-RPC 握手:
// 标准 MCP 握手流程
send(initialize) → 声明协议版本和客户端能力
recv() → 获取服务端能力
send(initialized) → 握手完成通知
send(tools/list) → 拉取工具列表(含 JSON Schema)
send(tools/call) → 调用具体工具
用的全是 JDK 标准库——ProcessBuilder 管理子进程,BufferedReader/Writer 处理 stdio 通信。面试官问 MCP,你能从握手讲到子进程管理,10 分钟起步。
亮点 3:三层上下文压缩——比"扔给 LLM 摘要"高一个维度
LLM 的上下文窗口是有限的,对话长了怎么办?大部分方案就是"调 LLM 做个摘要"。
我设计了三层递进策略:
| Micro | 每轮 | 工具输出 >2000 字符 → 截到 500 | 零 |
| Auto | token > 阈值 | LLM 摘要 + 保留最近 10 条 | 一次 LLM 调用 |
| Manual | 用户命令 | 全量压缩 | 一次 LLM 调用 |
Micro 层是零成本的——大文件读出来几千字,又不是每轮都需要,截断保留前 500 字就够了。Auto 层才是 LLM 语义理解真正需要介入的场景。
面试话术: “多数上下文方案只有一层——要么滑动窗口丢消息,要么 LLM 摘要。我设计了三层递进,成本递增、按需触发。Micro 层零成本解决 80% 问题。”
亮点 4:Subagent 隔离——在定义层切断递归
Claude Code 有子 Agent,我的项目也有。子 Agent 的核心问题是怎么防止无限递归——子 Agent 里不能再创建孙 Agent 去干同一个活。
我的做法是在工具定义层切断:
父 Agent: Tools{file, bash, search, task} ← 有 task 工具
子 Agent: Tools{file, bash, search} ← 没有 task 工具
子 Agent 的工具列表里压根没有 task,LLM 不知道有这个工具就不可能调用。这不是在执行层拦截,是在能力层截断。
面试话术: “防递归不是加个 if 判断,而是在工具定义层切断。子 Agent 的 ToolSpec 列表里没有 task,LLM 的推理链路里永远不可能产生子 Agent 调用。”
亮点 5:Prompt Caching——靠架构不靠 API
每次调用 LLM,System Prompt 和工具定义占了上下文的前几千 token。对话越长,这部分重复发送越多次。
我把请求拆成了两层:
ChatRequest = [System Prompt + Memory + 工具声明] + [对话历史]
← 构造时固定,跨请求复用 → ← 每次拼接 →
前缀在构造时计算一次,后续每次 build() 复用同一个对象引用。LLM API 网关会逐字节比较相邻请求的公共前缀——前缀越稳定,缓存命中率越高。
不需要一行 cache_control API 调用,纯粹靠结构设计。
四、学这个项目的路线
如果你是大三/大四,Java 基础还行但没做过 AI 项目,建议这么走:
第一周:跑起来,看懂主循环
git clone https://github.com/cookie250825/mvp-claude-code.git
cd mvp-claude-code
cp config.yaml.example config.yaml
# 编辑 config.yaml 填上 DeepSeek API Key
mvn package -DskipTests
java -jar target/mvp-claude-code-1.0-SNAPSHOT.jar -i
然后对着 AgentLoop.java(80 行)看:用户输入进来后,每一步发生了什么。
第二周:改一个工具
给它加一个工具,比如 TimeTool——问 AI “几点了” 能回答当前时间。走通 “定义工具 → 注册到 Registry → LLM 调用 → 执行 → 写回结果” 的完整链路。
第三周:准备面试话术
docs/HARNESS_DESIGN.md 里有 10 个组件的设计思路 + 面试话术。选 3 个你最熟的,能讲 5 分钟以上就行——面试不是背八股,是讲你做过的东西。
第四周:写进简历,开始投
简历上放 4-5 条项目经验(参考上面第二节的模板),GitHub 链接放上去。面试官开始问 AI Agent,你就从 while(true) 循环开始讲。
五、和其他学习项目比
| 代码量 | 5000+ | 200 | 2000 |
| 能讲多久 | “我写了很多 CRUD” | “我调了 OpenAI SDK” | 30 分钟,逐层拆解 |
| 涉及概念 | MyBatis、Redis | 无 | Agent 循环 / MCP / 压缩 / 子 Agent / 缓存 |
| 面试区分度 | 低,人人都有 | 极低 | 高,面试官没见过 |
Spring Boot 商城是好项目,但那是"我会 Java"的证明。MVP Claude Code 是"我理解 AI Agent 架构"的证明。两个放一起,简历就完整了。
六、项目还有这些细节
聊聊你可能关心的:
为什么用 DeepSeek 不是 Claude? 便宜。DeepSeek 的 API 和 OpenAI 完全兼容,langchain4j-open-ai 模块直接对接。改两行配置就能换成任何一个 OpenAI 兼容的服务。
流式输出怎么做? 用 CompletableFuture 桥接异步和同步——流式 API 是异步的(onNext 实时打印 token),但 while(true) 循环需要等 AI 响应完才能继续。future.get() 阻塞等待,拿到完整响应后继续。
工具执行前怎么确认? 交互模式下弹 y/n/a 选择——y 执行这次、n 跳过、a 全部批准不再询问。用户拒绝后写入 history,LLM 看到后会自己调整策略,不会傻傻重试。
CHANGELOG 有完整迭代记录。 从 v1.0(核心引擎)到 v1.3(流式 + 确认 + 缓存),每个版本做了什么、为什么做,都在 CHANGELOG.md 里——这是"我有工程迭代能力"的证明。
总结
GitHub: https://github.com/cookie250825/mvp-claude-code
⭐ 有用就 Star,找实习加油。




