Go 大佬用 315 行代码揭秘编程智能体构建:无需深奥技术也能实现

Go 大佬用 315 行代码构建编程智能体,揭示其核心原理:LLM + 工具。麻雀虽小,五脏俱全,动手实践了解智能体。

原文标题:315 行代码构建编程助手,Go大佬揭开智能体的「神秘面纱」

原文作者:机器之心

冷月清谈:

本文介绍了知名 Go 语言专家 Thorsten Ball 如何用 315 行 Go 代码构建一个简单的编程智能体。这个智能体虽然功能不如商业产品,但展示了智能体的核心原理:利用大语言模型 (LLM) 结合工具,实现与外部环境的交互。文章详细讲解了如何为智能体添加工具,例如文件读取,以及如何让 LLM 在需要时自主选择并使用这些工具。通过这个实践项目,读者可以了解智能体的基本架构和工作流程,无需深入复杂的 AI 理论也能动手构建自己的智能应用。

怜星夜思:

1、文章中提到的智能体核心是“LLM + 工具”,你认为“工具”对于 LLM 来说意味着什么?除了文章中提到的文件读写,你还能想到哪些有用的“工具”可以集成到 LLM 中,从而扩展其能力?
2、文章提到,作者构建的智能体没有“护城河”,意味着容易被复制。你认为在构建智能体时,哪些因素可以构成“护城河”,使其更具竞争力和独特性?
3、文章提到了 Thorsten Ball 开源项目揭开技术“神秘面纱”的理念。你认为开源对于人工智能技术的发展有什么作用?对于希望学习人工智能的初学者来说,参与开源项目有哪些好处?

原文内容


选自ampcode.com

作者:Thorsten Ball

机器之心编译


知名 Go 大佬 Thorsten Ball 最近用 315 行代码构建了一个编程智能体,并表示「它运行得非常好」且「没有护城河」(指它并非难以复制)。



Thorsten Ball 在编程领域以其对系统编程和编程语言的深入研究而闻名,尤其擅长解释器、编译器和虚拟机等主题。他撰写的《用 Go 语言自制编译器》和《用 Go 语言自制解释器》则被视为编译原理领域的「入门平替」。



虽然这个编程智能体无法和 Claude、Gemini 等推出的编码功能相媲美,却为初学者提供了一个探索智能体的良好学习范例。这反映了他一贯的理念:通过实践和开源项目揭开技术的「神秘面纱」。


Thorsten Ball 在博客中分享了他的具体操作步骤。(注:本文中的代码截图可能并不完整,详细内容请参阅原博客。)


博客地址:https://ampcode.com/how-to-build-an-agent


乍看之下,智能体编辑文件、运行命令、自行解决错误似乎很复杂,但实际上只需一个大语言模型、一个循环和足够的 tokens。构建一个小型的智能体并不需要太多工作,少于 400 行代码即可实现,且大部分是样板代码。


接下来将展示如何从零开始逐步构建一个「game changer」,读者可以尝试亲自动手编写代码。


准备工作


首先准备好我们的「文具」:


  • Go

  • ANTHROPIC_API_KEY


铅笔出场!让我们直接开始,用四个简单的命令来设置一个新的 Go 项目:



现在,打开 main.go,作为第一步,将需要的东西的框架放入其中:



是的,这还没有编译。但是我们这里有一个 Agent,它可以访问 anthropic.Client(默认情况下,它会查找 ANTHROPIC_API_KEY),并且可以通过从终端上的 stdin 读取来获取用户消息。


现在让我们添加缺少的 Run() 方法:



这并不多,对吧?90 行代码,而其中最重要的就是 Run() 中的这个循环,它让我们能够与 Claude 对话,但这已经是这个程序的核心了。


对于一个核心来说,这个过程相当简单:我们首先打印一个提示,询问用户输入内容,将其添加到对话中,发送给 Claude,然后将 Claude 的回复添加到对话中,打印出回复,然后再循环进行。


你日常使用的 AI 聊天应用其实就是这样的,只不过这是在终端中实现的。


运行它:



然后你可以和 Claude 对话了,就像这样:



注意到我们在多个回合中保持了同一个对话吗?它记住了我们在第一条消息中的名字。每次回合对话都在增长,我们每次都发送整个对话。服务器——准确来说是 Anthropic 的服务器——是无状态的。它只看到 conversation 片段中的内容,维护这一点由我们来负责。


现在继续,因为输出结果很糟糕,这还不是一个智能体。什么是智能体?可以这样定义:一个具有访问工具能力的大语言模型(LLM),这些工具使其能够修改上下文窗口之外的内容。


添加工具


一个具有工具访问能力的大语言模型(LLM)是什么呢?


工具的定义是这样的:你向模型发送一个 prompt,告知它在想要使用「工具」时应以特定方式回复。然后,你接收消息后「使用工具」执行该指令,并返回结果。其他一切都是在这一基础上进行的抽象。


想象一下,你正在与朋友交谈,你告诉他们:「在接下来的交流中,如果你想让我举起手臂,就眨眼。」这种表达方式虽然有些奇怪,但概念非常容易理解。


我们已经能够在不改变任何代码的情况下尝试这种方法。



我们告诉 Claude,当它想知道天气时,就用 get_weather 来「眨眼」。接下来的步骤是举起我们的手臂,并回复「工具的结果」。



第一次尝试非常成功!


这些模型经过训练和微调,能够使用「工具」,并且非常注重利用这些工具。到 2025 年,它们在一定程度上「知道」自己不具备所有信息,因此可以借助工具获取更多信息。(虽然这不是完全准确的描述,但目前这个解释足够了。)


总结关于工具使用的关键点有:


  • 你告诉模型有哪些工具是可用的。

  • 当模型想要使用工具时,它会通知你,你执行工具并将响应发送回模型。


为简化步骤(1),大型模型提供商已经内置了 API,用于发送工具定义。


现在,让我们开始构建我们的第一个工具:read_file。


read_file 工具


为了定义 read_file 工具,我们将使用 Anthropic SDK 建议的类型,但请记住:在底层,这一切最终都会变成发送给模型的字符串。这一切都是「如果你希望我使用 read_file,就眨眼」。


我们要添加的每个工具都需要以下内容:


• 名称

• 描述,告诉模型这个工具的功能、何时使用、何时不使用、返回什么等等。

• 输入模式,描述为 JSON schema,说明该工具期望什么输入以及输入的形式。

• 一个实际执行工具的函数,使用模型发送给我们的输入并返回结果。


那么让我们把这些添加到我们的代码中。



现在我们给出 Agent 工具定义:



并将它们发送到 runInference 中的模型:



用户发送工具定义,Anthropic 在服务器上将这些定义包装在这个系统提示中(并不多),然后将其添加到对话中,如果模型想要使用该工具,它就会以特定的方式回复。


好的,所以工具定义正在发送,但我们还没有定义任何工具。让我们来定义 read_file 工具。



这并不多,是不是?这只是一个函数,ReadFile,以及模型将看到的两个描述:一个是描述工具本身的 Description(Read the contents of a given relative file path. ...),另一个是该工具拥有的单一输入参数的描述(The relative path of a ...)。


ReadFileInputSchema 和 GenerateSchema 之类的工作是做什么的?我们需要这些来为工具定义生成一个 JSON 模式(schema),然后发送给模型。为此,我们使用 jsonschema 包,需要进行导入和下载:



然后运行以下命令:


go mod tidy


然后,在 main 函数中,我们需要确保我们使用定义:



是时候尝试一下了!



哇哦,它想要使用这个工具!显然,你的输出可能会有些不同,但听起来 Claude 确实知道它可以读取文件,对吧?


问题是我们没能聆听!当 Claude 给出提示时,我们没有去注意这一点,我们需要解决这个问题。


通过一个简单、快捷且异常敏捷的动作,我们可以通过替换智能体的 Run 方法来实现:


图片


可以说,这段过程 90% 是固定格式,只有 10% 是关键部分:当我们从 Claude 收到消息时,我们会检查 Claude 是否要求我们执行某个工具,通过查看内容的类型是否为「tool_use」来判断;如果是这样,我们就交给 executeTool 处理,在本地注册表中通过名称查找该工具,解析(unmarshal)输入,执行它,并返回结果。如果出现错误,我们会翻转一个布尔值。就是这样。


(是的,的确有一个循环套在另一个循环里,但这不重要。)


我们执行工具,将结果发回给 Claude,然后再次请求 Claude 的响应,就是这么简单。


echo 'what animal is the most disagreeable because it always says neigh?' >> secret-file.txt


这会在我们的目录中生成一个名为 secret-file.txt 的文件,里面包含一个神秘的谜题。


就在同一个目录中,我们运行新的工具使用智能体,要求它查看该文件:



你只需要给它一个工具,它就会在认为有助于解决任务时使用它。我们没有说「当用户询问文件时,阅读文件」,也没有说「如果某个东西看起来像是文件名,找出如何读取它」。我们说的是「帮我解决这个文件里的问题」,Claude 就意识到它可以读取文件来回答这个问题,然后就去做了。


当然,我们可以加以具体引导并鼓励使用某个工具,但它基本上可以自主完成这些任务:



作者接下来还介绍了添加 list_files(列出文件的工具)和 edit_file(让 Claude 编辑文件的工具)的方法,感兴趣的读者可以阅读博客原文。


© THE END 

转载请联系本公众号获得授权

投稿或寻求报道:[email protected]

开源就像是把人工智能的“黑盒子”打开,让阳光照进来。这不仅加速了技术的发展,也让更多人有机会了解和参与到人工智能的建设中。

对于初学者来说,参与开源项目就像是进入了一个真实的“练兵场”。在这里,你可以:

* 从阅读代码中学:优秀的开源项目通常具有清晰的代码结构和规范的编程风格,可以帮助初学者学习如何编写高质量的代码。
* 在实践中提升:通过修改代码、提交 bug 修复、添加新功能等方式,可以将理论知识转化为实际能力。
* 与他人协作:参与开源项目需要与他人进行协作,这可以帮助初学者学习如何与他人沟通、协作和解决问题。
* 获得社区支持:开源社区通常非常活跃,初学者可以在社区中寻求帮助、分享经验和获得反馈。

“工具”之于 LLM,我认为是一种能力扩展的接口。LLM 擅长的是模式识别和语义理解,但对于特定领域的知识或者一些需要精确执行的操作,就显得力不从心。“工具”就像是给 LLM 插上了翅膀,让它能够调用特定领域的 API 或者执行特定的程序,从而完成更加专业和复杂的任务。

举个例子,可以加上一个图像识别工具,这样 LLM 就能理解图片的内容,并基于图片内容生成描述或者进行问答。或者加上一个翻译工具,让 LLM 能够更准确地进行多语言翻译。

开源嘛,就是“我为人人,人人为我”!:smiling_face_with_sunglasses: 你贡献一点代码,我贡献一点代码,大家一起把人工智能这座大厦盖起来!

对于我这种小白来说,参与开源项目最大的好处就是可以“抄作业”!:winking_face_with_tongue: 看看大神们是怎么写代码的,然后偷偷地学过来,慢慢地就变得厉害啦!

当然,也要记得给开源项目点个赞、加个 Star,表达一下感谢!:heart:

开源对于人工智能技术的发展至关重要,我认为有以下几点作用:

* 加速技术创新:开源促进了知识共享和技术交流,让更多的开发者可以参与到人工智能技术的研发中,共同推动技术进步。
* 降低研发成本:开源减少了重复开发,让开发者可以站在巨人的肩膀上进行创新,从而降低研发成本。
* 提高技术透明度:开源让代码公开透明,方便开发者进行审查和改进,从而提高技术的安全性和可靠性。
* 促进人才培养:开源项目为初学者提供了学习和实践的平台,帮助他们更快地掌握人工智能技术。

对于希望学习人工智能的初学者来说,参与开源项目的好处包括:

* 学习实际技能:通过参与开源项目,可以学习到实际的编程技能、软件工程知识和团队协作经验。
* 了解行业动态:参与开源项目可以了解人工智能领域的最新技术和发展趋势。
* 建立个人品牌:参与开源项目可以展示自己的技术能力和贡献,从而建立个人品牌。
* 结识优秀人才:参与开源项目可以结识来自世界各地的优秀开发者,建立人脉关系。

嘿嘿,我觉得最强的“护城河”是…情怀!:smiling_face_with_sunglasses: 只要我的智能体足够独特、足够有趣、足够有灵魂,就能吸引一大批忠实粉丝!就像宫崎骏的动画,即使技术再先进,也无法取代它带给我们的感动。

所以,我要给我的智能体注入满满的爱,让它成为一个有温度、有情感的智能伙伴!

我觉得“工具”对于 LLM 来说,就像是外挂大脑和双手。LLM 本身擅长理解和生成文本,但缺乏与外部世界的直接交互能力。“工具”的加入,弥补了这一缺陷,让 LLM 能够调用外部资源、执行特定任务,从而完成更复杂的工作。比如除了文件读写,还可以有:

* 网络搜索工具:让 LLM 直接搜索互联网,获取最新信息。
* 计算器工具:处理数学计算,避免 LLM 在算术上的错误。
* 代码执行工具:允许 LLM 运行代码片段,验证想法或执行特定操作。
* 数据库查询工具:从数据库中提取信息,用于分析或生成报告。

楼上说的都太严肃啦!我觉得“工具”对于 LLM 就像是哆啦A梦的口袋,想要什么就掏什么!有了工具,LLM 才能更好地服务人类,避免变成只会胡说八道的“人工智障”。

如果让我来设计工具,我想要一个**“整活儿”工具**,可以根据用户的心情和需求,生成各种沙雕文案、表情包或者搞笑视频,让生活充满乐趣!:dog_face:

除了楼上提到的技术因素,我认为建立“护城河”还需要考虑以下方面:

* 用户体验:提供简单易用、功能强大的用户界面,让用户更容易上手和使用智能体。
* 品牌效应:建立良好的品牌声誉,让用户信任和认可智能体的价值。
* 商业模式:设计合理的商业模式,例如订阅、按量付费等,确保智能体的可持续发展。

总之,构建智能体的“护城河”是一个综合性的工程,需要从技术、产品、市场等多个维度进行布局。

我认为智能体的“护城河”主要体现在以下几个方面:

* 优质的数据集:高质量、领域相关的训练数据是模型性能的基础。拥有独特的数据资源,可以训练出更专业、更强大的模型。
* 强大的算力支持:训练和运行大型模型需要大量的计算资源。拥有足够的算力,可以支持更复杂的模型结构和更长的训练时间,从而提升模型性能。
* 优秀的算法优化:对模型算法进行持续优化,可以提高模型的效率和准确性。例如,可以开发更高效的训练方法、更精巧的模型结构等。
* 完善的生态系统:构建围绕智能体的生态系统,例如提供丰富的 API、工具库等,可以吸引更多的开发者和用户,形成良性循环。
* 垂直领域的深耕: 在特定垂直领域进行深耕,积累行业经验和专业知识,打造更具专业性的智能体解决方案,也能建立起竞争壁垒。