一、引言:AI Agent 不是魔法,全是工程
自从 AutoGPT 横空出世,再到 Devin 引爆软件工程圈,人们在使用 AI Agent 时,总会产生一种极其强烈的"拟人化"错觉:仿佛真的有一个关在机箱里的赛博程序员,他在孜孜不倦地看代码、敲键盘、等编译。
但如果你真的掀开底层源码的盖头,你会发现这一切并没有什么不可思议的魔法。当下所有市面上光鲜亮丽的 Agent 产品,剥去花哨的 UI 外衣,其骨架出奇的一致。
在整个体系中,大语言模型(LLM)仅仅是一个极其出色但双眼被蒙住的"纯算力大脑"。它接受一串文本,吐出另一串文本;它自己看不到电脑里的文件,也不能直接连上外网查阅文档。
为了让这个"大脑"对物理世界产生干涉,现代 AI 架构给出了一个极其精简的第一性原理公式:
Agent = LLM + Tool Use (工具调用) + 循环 (Loop)
这就是 AI 行动的基石。只要你懂得了写这层粘合代码,你自己也能在本地手搓出一个能帮你干活的私人 AI 助理。
二、智能体的心脏:The Agent Loop (单体引擎)
"One loop & Bash is all you need."
到底什么是 Agent Loop?说白了,它就是一段你在大学刚学会编程时就写过的 while(true) 死循环。
1. 架构流转:大模型是如何"动"起来的?
让我们用一张图来看清一次典型的 Agent 任务流转机制:
如执行 Bash, 查询数据库] Execute --> AppendToolResult[5. 将工具真实执行的输出
追加回 messages 数组结尾] AppendToolResult --> Request Response -->|stop_reason != 'tool_use'| Exit([6. 刹车成功!跳出循环,输出最终回答]) end
2. 核心源码揭秘
如果我们把官方教程中最小闭环的 Typescript 源码提炼出来,你会惊叹于它的单薄------驱动人工智能改变世界的引擎,不到 30 行代码:
typescript
// 智能体的心脏机制
async function agentLoop(messages: Anthropic.MessageParam[]): Promise<void> {
while (true) {
// 步骤 1:带着全量记忆(历史上下文)请求大模型
const response = await client.messages.create({
model: "claude-3-7-sonnet",
messages: messages,
tools: TOOLS, // 💡 把你写好的 Bash、ReadFile 等能力告诉模型
max_tokens: 8000,
});
// 步骤 2:把模型这轮的思考和指令存回"大脑"
messages.push({ role: 'assistant', content: response.content });
// 步骤 3:决定生死的核心刹车片 (Brake)
// 如果模型没有主动要求调用工具,说明它认为任务大功告成,立刻中止死循环!
if (response.stop_reason !== 'tool_use') {
return;
}
// 步骤 4:干脏活累活
const results = [];
for (const block of response.content) {
if (block.type === 'tool_use' && block.name === 'bash') {
const command = block.input.command; // 剥离出 LLM 要执行的 Shell 语句
const output = runBash(command); // 真实依靠宿主机执行命令
results.push({
type: 'tool_result',
tool_use_id: block.id,
content: output,
});
}
}
// 步骤 5:将外界真实回传的运行结果存回大脑,供下一轮继续推理
messages.push({ role: 'user', content: results });
}
}
3. Loop 背后的两个残酷真相
① "刹车片"机制(stop_reason)
注意看代码中的步骤 3。这个循环永远不会自己退出,唯一的"刹车踏板"掌握在 API 返回对象的 stop_reason(停止原因)字段上。 只要模型认为当前已有的信息不足以回答你的问题,它就会固执地给出一个 tool_use,程序就会默默地去跑脚本获取报错或日志。当信息拼凑完整时,新一轮它返回的 stop_reason 变成了 end_turn,于是程序跳出死循环。这就是 Agent 能自主规划并连环执行的根本机制。
② 大模型的先天顽疾:"无状态失忆症"
大家看源码中的步骤 1 和 步骤 5 会发现一个动作:我们在不断地 push 新内容进 messages 数组参数,并且在每一轮死循环里,把整坨包含所有聊天历史的 messages 全盘甩给大模型 。 为什么要这么做? 因为 LLM 本身的 API 是"无状态 (Stateless)"的。就如同一个患有重度失忆症的天才,它发完上一条指令就全忘了。必须靠我们在本地端用 while 循环把"执行的中间结果"像滚雪球一样积累起来喂给它。
这也是为什么 Agent 跑深了会产生"Token 爆炸"和严重的"幻觉偏移"。 为了解决这些因"无状态"无限堆叠引发的灾难级痛点,业界并未止步于此。在原生的循环引擎之上,现代 Agent 围绕着"拯救上下文"进化出了四大核心维度的续命机制:
- 规划 (Planning) :在动手写代码前,强制设定一步工具调用让大模型写下
TODO List(项目 S03 的核心)。由于 LLM 是基于前文预测下一个词的,把规划写在上下文中,能让它在几百轮对话后依然拥有不被冲刷掉的"思想锚点"。 - 压缩 (Compact) :物理意义上的瘦身(项目 S06 的核心)。当
messages数组达到几万 Token 的水位红线时,系统会触发一次"记忆整理",把前文几十轮的废话对话,用模型精练压缩成几十个字的摘要,再塞回上下文中接着跑。 - 动态技能 (Skills):也就是 RAG(检索增强生成)的应用。我们不把几十万字的公司规范全塞进系统初始提示词里,而是等模型需要时,再按需精准注入,极大节省了宝贵的上下文空间。
- 多智能体 (Teams):这是最高维度的终极解法------解决上下文爆炸最好的方式,就是当场换个脑子里没装过垃圾的新人干活。
这也就是我们接下来要重磅探讨的:认知层面的进阶 (Skills) 与 结构层面的重组 (Teams)。
三、给 AI 植入"肌肉记忆":Tools 与 Skills 到底有啥区别?
在 Agent 的世界里,经常能听到 Tools(工具)和 Skills(技能)这两个词。很多人刚开始玩常常会懵圈,这其实是两个完全不同维度的"外挂"。
1. Tools(工具):干活的"作案工具"
Tools 极其原子化。大模型就像个只会下达指令的"巨婴总裁",它说"给我查下当前目录有啥文件",底层代码(Tool)就立刻替它执行本地命令,然后把字符串原样塞回给大脑。
在源码里,给大模型配发"作案工具"的代码是非常直白(且写死)的 JSON Schema:
typescript
// 纯纯的物理输出工具,发给大模型的一把"工兵铲"
const TOOLS: Anthropic.Tool[] = [{
name: 'bash',
description: '执行本地终端命令。注意防范 rm -rf /',
input_schema: {
type: 'object',
properties: {
command: { type: 'string' },
},
required: ['command'],
},
}];
2. Skills(技能):大脑里的"祖传 SOP"
有了工兵铲大模型就能敲出代码了吗?不行,它第一天去财务部也会傻眼,因为它根本不知道你们公司报销打车费的奇葩流程。这时就需要 Skills!
Skills 本质是纯文本 Markdown 教程档(或者是业务 SOP)。比如你甩给它一份《公司内网祖传代码跑通秘籍.md》。 我们来看一段极其经典的伪代码演示,大模型是如何被"动态灌输"技能的:
typescript
// 当任务是:"帮我用 NextJS 写个鉴权"
const task = "帮我用 NextJS 写个鉴权";
// 1. RAG 显灵:先去技能库里找到对应的《祖传 SOP.md》
const skillDocs = loadSkill("nextjs-auth-best-practices.md");
// 2. 动态塞进它的大脑(System Prompt)
const SYSTEM_PROMPT = `
你是一个高级前端。
遇到不懂的,请严格按照以下指南进行:
<skills>
${skillDocs}
</skills>
`;
这种设计太绝妙了:把"抓取的动作"硬编码成极速的 Tools,把"干活的套路"变成纯文本的 Skills。 你在公司写再多恶心的规范流,直接转成 PDF 喂给它,新来的 AI 管培生当场就能原地满级。
四、从"全栈大冤种"到"黑心包工头":Agent Teams (多智能体协同)
在单体 Agent 架构里,你让 AI 既读业务文档,又重构前后端代码,还要跑测试修 Bug......这就好比让一个程序员连续 996 熬大夜,它的"上下文记忆数组(messages)"会彻底爆炸,连自己叫啥都忘了,开始胡言乱语。
为了避免把 AI 逼疯,我们得让它从"全栈大冤种"升级成"包工头"(Lead)。这就是当下大热的 Teams 多智能体协同架构!
这里可以提炼出一个极其经典的系统底层公式:
Agent Teams = Agent Nodes (干活节点) + MessageBus (异步信使) + Protocols (强校验协议)
仅仅把几个大模型实例实例化并拉进同一个代码库里,绝对不叫团队。真正的多智能体架构,必然是建立在节点分离、异步解耦、以及强制规范沟通的强盛三角关系之上。
1. 团队是怎么跑起来的?(核心流转图)
不要把多智能体想得太复杂,本质上它就是开个小号在后台跑线程,然后大家用文本文件当"微信群"聊天:
2. 源码里的核心魔法与踩坑警告
🪓 魔法一:"撒豆成兵"的 HR 系统 (TeammateManager)
在 Lead 的视角里,生成一个小弟是完全不阻塞自己主线程的,它用的是底层 setImmediate 异步挂起:
typescript
spawn(name: string, role: string, prompt: string): string {
// 1. 把新人写入企业花名册 config.json
this.config.members.push({ name, role, status: 'working' });
this.saveConfig();
// 💡 2. 开启异步线程:打工人自己跑自己的 Agent Loop 死循环
const immediate = setImmediate(() => {
runTeammateWorker(name, role, prompt, '').finally(() => {
// 干完活了,状态改成 idle (摸鱼待命)
this.updateStatus(name, 'idle');
});
});
return `Spawned '${name}'`;
}
🪓 魔法二:生死攸关的"阅后即焚" (Drain 机制)
作为全公司的"微信文件传输助手",MessageBus 收发消息全靠读写本地 .jsonl 文件。打工人拿完信件后,有一步极其暴力的骚操作:
typescript
readInbox(name: string): Message[] {
const inboxPath = path.join(this.inboxDir, `${name}.jsonl`);
// ... 1. 读取里面的 JSON 消息 ...
// 🚨 2. 坑点预警:拿完即毁 (Drain inbox)
fs.writeFileSync(inboxPath, '');
return messages;
}
如果不清空文件,由于循环(Loop)一直在高频运行检测,Agent 会在每一轮都读到历史遗留消息,最后直接被旧指令搞到当场死循环暴雷!
🪓 魔法三:老板去哪儿了?(幽灵信箱彩蛋)
在架构实测中,我们发现了一个极其搞人、但在 AI 圈屡见不鲜的操作事故(幻觉投递)。 打工人由于过度聪明,做完了任务想汇报,于是瞎猜老板的微信号应该叫 team_lead:
json
// 打工人发出了虚空的呐喊
{
"name": "send_message",
"input": {
"to": "team_lead",
"content": "老大,我做完了!"
}
}
而系统设定里包工头的大名写死了叫 lead。由于 MessageBus 没有验证收件人,这导致打工人把汇报发到了虚无的 team_lead.jsonl 里,而正牌老板死盯着空荡荡的 lead.jsonl 大骂下面的人工作效率低。 (这就告诉我们:在系统级 Prompt 里,一定要把内部通讯花名/ID 给生硬地限死!)
五、工业级落地的护城河:填平工程暗礁 (S08, S10, S12)
如果在上面的第四章,你以为只要把大模型分配成不同的线程(Teams),这个系统就能完美运行了,那也太理想化了。在真实的项目工程里,还有无数个"暗礁"在等 Agent 翻船。
为了让 AI 程序员进化成可以真正接管生产环境的最终形态,底层的脚手架还需要打上这三个极度硬核的工程补丁。
1. 拒绝死等:长耗时任务与后台异步流 (S08)
🤔 为什么需要这个? 想象一下,大模型现在决定去跑一个 npm install 或者长达 10 分钟的端到端测试。如果在单次循坏里用最原始的模式硬跑强行阻断等待,大模型的 Loop 就会长久卡死,轻则浪费巨额算力,重则导致 API 超时响应断开。
🛠️ 源码解法 (Background Tasks): 高级的 Agent 绝对不会在"前台"死等。当遇到耗时任务时,它的底层 Tools 会派生出一个完全独立的后台进程,并立刻给模型主循环光速返回一句:"任务已在后台启动,PID: 1234"。 随后,大模型在它的 While 循环里该干嘛干嘛(去改别的文件)。过了几轮循环后,它再调用专门的查询工具瞄一眼:"PID 1234 的那个测试跑完没?" 这才是真正的极客范。
2. 消除幻觉灾难:强约束通信协议 (S10)
🤔 为什么需要这个? 在上一章的彩蛋里我们体验过了,如果你放任大模型在文本信箱里用自由发挥的散文发消息,它能给你瞎编出无数个"幽灵信箱"。自然语言太奔放了,用来做高并发内部调度,绝对是灾难。
🛠️ 源码解法 (Team Protocols): 能用"强类型"约束的,绝对不交给大模型自由发挥。系统底层强制拉起了 JSON 通信协议,大模型要找老板,发出的请求必须带有严格的反序列化标识:
json
{
"type": "plan_approval_request",
"data": { "plan_id": "9527", "content": "老大,下周建表方案,请您批复" }
}
在底层代码执行前,配合 TypeScript 的反向校验,一旦查无此人,或者打工人漏填了必填参数,代码会当场拉响警报,勒令大模型重发一次 JSON,这大幅降低了系统架构崩塌的概率。
3. 世界防御边界:Git 沙盒与物理隔离 (S12)
🤔 为什么需要这个? 如果你直接把本机的最高 bash 权限交给一个还在处于试用期的 AI 程序员,一旦它在一通幻觉下把你的 src 目录给 rm -rf 删干净,或是往 main 分支推了一堆满是 Bug 的代码,这绝对是所有老板的血压飙升瞬间。
🛠️ 源码解法 (Worktree Isolation): 像 Devin 这种工业级的 AI 系统,绝对不会在你的正式工作区里作案 。 它在接手危险任务时,会在本地悄悄拉出一个完全物理隔离的沙盒(利用 git worktree 机制),把所有的工作内容映射进一个极其隐蔽的独立开发分支和安全沙盒目录里。
它在这个单独拷贝的隔离环境里无论怎么造次、怎么写八面漏风的死循环代码都无所谓。只有当它在这个独立环境里彻底跑通了所有测试用例,它才会优雅地向最高指挥官(你)打包发送一个代码合并请求(Merge Request)。
六、连接真实世界的超级总线:MCP (Model Context Protocol)
当你给模型加了查文件的工具之后,如果第二天咱们要接入本地的 MySQL 数据库,甚至调起 Playwright 开启无头浏览器去填表呢? 如果在以前,你得没日没夜地给大模型写针对特定工具的胶水适配层(Function Calling Schema 适配)。如果换了模型基座,胶水代码还得重写。这就像回到了 2010 年,满屋子都是不同尺寸的诺基亚和索爱充电线。
为了终结圈地运动,Anthropic 扔出了开源的终极杀器:MCP(模型上下文协议)。
1. AI 界的"Type-C 接口"
MCP 本身不提供任何实际的执行能力,它只是一个基于 JSON-RPC 标准的数据通信协议。
它是怎么做到"即插即用"的?看这张架构流转图:
2. 剥开外衣:极致解耦
大模型其实根本不懂 Node.js 怎么启动浏览器,也不懂数据库握手包。它只在标准格式下看到了一个名叫 query_db(sql: string) 的虚拟接口。
MCP 实现了物理隔离与智力的完美解耦!你今天写好了一个 MCP Server 去操作局域网的 CI/CD 平台,明天不管是接入 Claude 还是 ChatGPT,统统一秒钟零代码对接。
不仅仅是 API 工具,MCP 还统管了 Agent 赖以生存的三大命脉:
- Tools(工具):执行具体的动作(跑 SQL)。
- Resources(资源):提供海量的静态死数据流(读一个 5GB 的日志文件库)。
- Prompts(提示词模版):标准化的角色设定(插上个 U 盘,模型当场学会公司发版 SOP 模版)。
七、结语:告别魔法,拥抱工程
从不到 30 行 while(true) 的初代单兵大冤种(The Agent Loop ),到具备"文件传输总线+阅后即焚"机制的多线程外包公司(Agent Teams ),再到使用标准数据线捅开外部大门的终结者(MCP 协议)。
希望随着对这整条链路的开颅解剖,那些看似高不可攀的 AI 黑科技框架滤镜已经在你眼前彻底粉碎了。
目前人工智能业界,真的没有天外飞仙的变戏法。所有惊艳的产品,都是建立在:
极其优秀的基座智力 (LLM) + 精准不坑人的原子级工具外挂 (Tools) + 严丝合缝甚至需要手动补错填坑的代码脚手架 (Harness)
之上所产生的结果。
最后,引用本系列起点的这句金言来结束这趟旅程: "One loop & Bash is all you need."
少一点对庞大 AI 框架(LangChain / AutoGen)中眼花缭乱参数的迷信。当你真正掌握了底层的这行 while 循环本质,你甚至在这个周末,就能用最原生的 Javascript 在本地手搓出一个能帮你找 Bug 的超级代码管家。