AI代理背后的肮脏秘密(演示)
原文:https://dev.to/sylwia-lask/the-dirty-secret-behind-ai-agents-demo--273d
长期以来,我一直觉得AI代理被某种神秘光环所笼罩。没人真正知道它们在做什么,它们可能很快就会接管世界,而如果你想自己构建一个……好吧,显然你需要一个框架。
如果我告诉你事实并非如此呢?你可以用大约80行代码构建自己的AI代理。
我不得不承认,这周并不轻松。工作上发生了很多事情。除此之外,我还收到了一个CFP的自动拒绝邮件。通常情况下,这件事只会占据我脑海大约五分钟,因为会议被拒本就是游戏的一部分。
但问题是……我实际上是被那个会议邀请的。“你已经被接受了,只需要在CFP中提交演讲细节就行。”因为那个邀请,我拒绝了另外两个会议的机会。唉,算了。至少我现在九月有空了。
无论如何,生活还要继续。今天是我的生日,所以作为我给自己(也给你们所有人!)的小礼物,我写了这篇文章。 希望你们喜欢!
我们真的需要框架吗?
让我们直入主题。
像LangChain、CrewAI或Mastra这样的框架并没有施展魔法。它们简化了对话记忆、工具执行、重试、回退等功能。
一旦你理解了底层机制,就更容易判断何时值得使用框架。即使你最终还是用了某个框架,你也会明白其内部运作原理。
所以我决定构建一个小型演示,看看一个AI代理到底需要多少代码。
我用Node.js写了一个大约80行代码的AI代理……好吧好吧,核心循环大约是80行。还有工具、提供者抽象层和一些外围逻辑。但拜托,“80行代码的AI代理”听起来好多了。
这里是仓库:
https://github.com/sylwia-lask/code-review-agent
既然是我的生日……如果你喜欢这个项目,欢迎给它点个⭐。当然,只有在你真正喜欢的情况下。
认识Steve
这个应用是一个简单的代码审查代理。嗯……也不完全是。
认识一下Steve:一位拥有15年审查他人代码经验的软件工程师。Steve不会轻信任何表面现象。当然,他有时可能有点讽刺……但很难反驳他的结论。
这是他的一次代码审查的样子:



或者当你提交空diff时:
目前,Steve审查的是本地的Git diff。这基本上意味着……他在审查自己。所以我想我已经构建了一个著名的自愈代理的原型,根据@nitsancohen770的说法,这个代理总有一天会取代我的工作。
如你所见,我基本上是在帮助自动化,让自己失业。也许是时候开始考虑退休了。
“代理就是一个While循环”
在我之前的文章中,我开玩笑说有人声称AI代理不过是一个while循环。好吧……我的甚至不是while循环。它是一个for循环,因为我想保护自己,避免意外创建无限循环,在token上浪费太多钱。
好吧,公平地说,循环本身实际上并不做任何事情。它只负责编排整个过程。但有趣的是,大多数代理框架在底层做的事情都非常相似。
编写循环本身是微不足道的。真正的挑战恰恰在你预期的地方。
构建AI代理始于……选择一个LLM。 这次我选择了Gemini API,因为这类项目的token价格极其便宜。我绝对有冲动用本地模型构建下一个版本,但是……等等。
我立刻遇到了一个问题:一些Gemini模型过载了,所以我不断收到503响应。这意味着我必须实现框架通常开箱即用的功能——一个简单的重试机制。
我的机制故意设计得非常基础。生产级框架通常提供更多功能,比如指数退避、抖动或自动回退到另一个模型。
下一个挑战当然是编写正确的提示词。在那之后,一切都变得出奇地简单。
代理实际上是如何工作的?
它比你想象的要简单得多。
第一步:发送提示词和可用工具
我们向模型发送两样东西:
- 用户的消息,
- 允许模型使用的工具列表。
这是请求的样子:
{
"model": "gemini-2.5-flash",
"contents": [
{
"role": "user",
"parts": [
{
"text": "请审查当前的git diff。"
}
]
}
],
"config": {
"systemInstruction": "你是Steve,一位拥有15年经验的高级软件工程师...",
"tools": [
{
"functionDeclarations": [
{
"name": "getDiff",
"description": "获取当前仓库的git diff...",
"parameters": {
"type": "OBJECT",
"properties": {},
"required": []
}
},
{
"name": "getFile",
"description": "从仓库中读取一个文件...",
"parameters": {
"type": "OBJECT",
"properties": {
"path": {
"type": "STRING",
"description": "相对于仓库根目录的文件路径"
}
},
"required": ["path"]
}
},
{
"name": "listFiles",
"description": "列出给定路径下的文件和目录...",
"parameters": {
"type": "OBJECT",
"properties": {
"path": {
"type": "STRING",
"description": "相对于仓库根目录的目录路径"
}
},
"required": ["path"]
}
}
]
}
]
}
}
第二步:等待模型的响应
模型可以通过以下三种方式之一响应:
- 纯文本,
- 请求调用工具,
- 或者两者兼有。
如果它返回纯文本,我们就完成了:那就是最终的代码审查。如果它要求我们调用工具,我们就进入第三步。
例如,我们可能会收到:
{
"candidates": [
{
"content": {
"role": "model",
"parts": [
{
"text": "让我们看看今天要处理什么烂摊子..."
},
{
"functionCall": {
"id": "call_001",
"name": "getDiff",
"args": {}
}
}
]
}
}
]
}
第三步:在本地执行工具
现在轮到我们的应用程序了。
我们在本地执行请求的工具。例如,运行git diff或从仓库读取文件,然后将结果作为对话中的另一条消息发送回模型。
这个演示中可用的工具是:
getDiffgetFilelistFiles
换句话说,一个好的代码审查者需要的一切。
第四步:重复直到模型完成
然后我们简单地回到第二步。为了避免陷入无限循环,我将最大迭代次数限制为10次。
不过,还有一个重要的细节。
看看下一个请求。注意,我们将整个对话历史发送回模型:
"请审查当前的 git diff。"
"让我们看看今天要处理什么问题……"
"来看看我们今天的'战况'……"
"diff --git a/src/auth.ts b/src/auth.ts --- a/src/auth.ts +++ b/src/auth.ts @@ -12,7 +12,7 @@ - if (password === storedHash) { + if (password == storedHash) { "
……就这样。LLM 自行决定何时使用哪个工具,以及何时完成任务。其余一切——执行工具、处理重试、强制迭代次数限制以及编排循环——都由我们的应用程序负责。
很简单,对吧?如果你喜欢可视化,ChatGPT 已经为我们准备了一张图
接下来做什么?
我打算未来至少再写两篇后续文章。一篇关于将 Steve 连接到 MCP,另一篇关于用本地 LLM 替换托管模型。
不过……别急。一步一步来。
额外内容:模型如何知道应该调用函数?
如果你只是为了学习如何构建 AI agent,现在可能可以停止阅读了。
这部分是为好奇者准备的。因为迟早会有人问:"Sylwia,你在说什么?你说你在构建自己的 AI agent,但你只是在用 Gemini SDK。你发送 JSON,收到 JSON。"
而且,正如 @darkwiiplayer 经常指出的那样,LLM 本质上是一个非常复杂的 next-token 预测器,而不是某种神奇的 JSON 生成器。
那么……当我们向模型发送 JSON 时,它如何知道该做什么?如果我们不使用 Gemini,而是使用某个旧的本地 Llama 模型,会发生什么?
Gemini SDK 是否秘密地预先添加了某些特殊提示?还是模型本身就是为了这个目的而训练的?
诚实的答案是,我们并不了解所有细节。我们所知道的是,像 Gemini 和 GPT 这样的模型原生支持工具调用。SDK 负责正确格式化请求并与 API 通信,而模型本身则理解工具声明,并在认为必要时生成函数调用。
换句话说,如果我们使用一个较旧的 Llama 模型,我们仍然可以编写一个提示,解释如何解析 JSON,并要求它以特定的 JSON 格式响应。
然后我们只需调用 JSON.parse()……
……前提是模型实际返回的是有效的 JSON,而不是 Markdown、解释,或者它决定添加的一些"有帮助"的注释。
话虽如此,值得一提的是,较新的开源模型也越来越原生支持工具调用。
尾声:由另一个 agent 编写的 agent
老实说。现在是 2026 年。这个 agent 很大程度上是由……另一个 AI agent 编写的。
由于我成为 AWS Community Builder 已经有一段时间了,我取消了 Claude Code 订阅,转而使用 Kiro:一个 AI IDE,内置了对多个 LLM 的支持,包括 Claude、GPT 和 Gemini。它的价格差不多,但得益于该计划,我可以免费使用它。
到目前为止,我非常喜欢它。感觉它有点像 VS Code 之上的一个 agent 驱动层,所以我很快就上手了。我也有一种感觉,我目前可能只使用了它实际功能的 10%,所以我确信未来我会写更多关于它的内容。
现在我只希望 AWS 最终能给我寄一些周边。 我特别想要他们的一件 T 恤。多年前,波兰实际上有一个名为 AWS 的政党,所以我非常想看看一些年长人士脸上的表情。
AWS……如果你们正在读这篇文章……我在等!!
最后的话
正如你所见,AI agent 不一定非得从框架开始。有时你只需要一个 LLM、工具、对话历史和简单的循环。其他一切都是便利性、生产环境加固和体验优化。
那么……
你觉得 Steve 怎么样?你喜欢这种构建 AI agent 的方法吗?
如果你喜欢这篇文章,也可以在 LinkedIn 上关注我。