从ChatBot到AI Agent
前言
2026年可以说是Agent爆发元年了,据博主身边统计学观察,不止是码农,很多本职工作跟代码完全没有关系的朋友也在用Agent,甚至我的老本行数学领域都被AI攻占了,截止我写这段话的时候,GPT跟Claude分别解决了单位距离猜想以及证伪了Jacobian猜想(据说是一个博士生在看世界杯的空闲用Fable5搞出来的,向前的赵诚不欺我)
博主目前的主力Agent是Openai的Codex,在沉迷了一段时间后,也想研究一下,LLM是如何从最简单的ChatBot,发展到现在的Agent的。
Prompt Engineering
首先让我们把时间拨回2023年,彼时ChatGPT横空出世,博主也是第一时间体验的用户之一,还记得当时注册就送18美金的API额度,有效期貌似是六个月吧,当时我就想着不能浪费啊,所以就去研究了一下OpenAI的官方文档,并且跟着做了一个最简单的Python程序用来调ChatGPT的API,这也让我对LLM有了最初步的认知。
所谓的LLM,就是一个输入文字,输出文字的机器,你可以把他理解成平常我们写的代码中的函数,或者数学上的函数,甚至是一个箱子。你给他输入若干文字,他给你输出若干文字,用户使用时认为的模型会记得我前面说的话,只不过是每一次调用程序都会把之前的历史记录作为输入发送给这个箱子而已,箱子本身什么都不会记得,这就是LLM的本质,至今未变。

那么面对这样一个被定义出来的新箱子,我们能研究的有这么两件事
- 给这个箱子什么样的输入,他会给出我们想要的输出
- 如何这个箱子可以放在一个系统中,作为一个完成系统的一部分被使用
不过当时的人们主要研究的还是第一件事,也就是如何让GPT扮演一只猫娘,其中的一些技巧我们也耳熟能详,例如设定角色,给GPT输出实例等等
1 | 你是一位资深律师,请用专业但通俗的语言解释这份合同的风险。 |
当时社区中也都在互相分享提示词,还出现了用于存放社区提示词的GitHub仓库,一副勃勃生机、万物竞发的景象,犹在眼前。随后,各家厂商也逐渐在自己的产品中加入类似的角色功能,而这就是Prompt Engineering,提示词工程。
Context Engineering
上下文管理
在Prompt Engineering的阶段,大家使用LLM的重点还是在如何写出好的提示词上,但是很快新的问题随之而来,那就是LLM的上下文窗口是有限的,讲人话就是我们输入给LLM这个箱子的内容是有上限的,如果我们强行给LLM输入超过上限的内容,那么就会被模型暴力截断,只保留最后的,不超过其上限的内容。
具体的表现比如,跟AI一直聊,聊到后面AI会忘记前面的内容,又或者,我们把一整本书扔给AI,然后问AI跟这本书相关的内容,AI无法全部回答正确。这里我要插一嘴,我曾经以为每聊一个话题开一个新对话是常识,直到我看到有人用GPT写毕业论文只在一个对话里聊,还有过年回家看到家里亲戚在用豆包,他的豆包只有一个对话,就,他用豆包到现在就没换过对话,问起来就是“这样他才能知道之前我说过什么”。
吐槽完了我们回到正题,刚才的吐槽中我说到,我曾经以为每聊一个话题开一个新对话是常识,这也就引出上下文的另外一个问题,就是上下文污染。简单来说就是我们用AI会聊到很多方面的话题,可能刚才还在跟他讨论哥德巴赫猜想,下一秒就在问他东京有什么好吃的,如果说这些内容全部挤在AI的输入里,哪怕没有超过上下文窗口,也会影响AI的输出质量,这就是上下文污染。
那么这里我又要开始吐槽了,在25年的时候,当时有段时间我的主力AI工具是Gemini,有一天我打开Gemini的时候,他跟我说他推出了一个Memory功能,可以跨对话存储记忆,然后我试用了一下,刚开始还没什么,知道我问了他一个无关紧要的问题,大概是类似于猪脚饭怎么做这种问题,具体问题我记不得了,就用猪脚饭做例子吧,然后后面聊天他就动不动就能扯到猪脚饭上,大概是我正在跟Gemini探讨一个概念,然后他突然来一句我们可以用猪脚饭来比喻,就是这种感觉。
于是我果断关了这个功能,其实这就是因为他的Memory没有调好,因为本质上Memory除了储存用户的习惯,偏好等等内容,更重要的是在什么时候,放什么内容进模型的上下文,我在跟你聊数学,聊代码,你把猪脚饭的记忆放进上下文了,那输出结果能好就有鬼了。
总而言之,就是大家意识到,只是研究Prompt该怎么写似乎已经不够用了,我们想要对LLM的输入,也就是上下文,做更加精细的管理,既然LLM本身没有任何记忆,我们可以自由的控制LLM的输入,那么我们就可以设计一个上下文管理系统,用于动态的管理上下文,这也就是所谓的Context Engineering,上下文工程。

有了这样一个上下文管理系统,每一次我们调用LLM时,不是无脑将所有现有的上下文全部塞给LLM,而是会先通过一个上下文管理系统,来决定将什么信息发给LLM,这个系统会拿到所有的可用上下文材料,例如,聊天记录,用户发送的材料,Memory等等,然后根据实际情况选择合适的内容组成上下文发给LLM。
例如用户问了一个关于代码的问题,那上下文管理可能会从Memory中调出用户关于代码风格,标准的偏好,从用户的内部知识库中调出代码遵循的规范等等,然后这个时候又发现用户已经在这个对话聊了很久了,上下文太长了,就要把过往的聊天记录压缩一下,然后将上面的所有东西拼装成上下文,发送给LLM。这样处理,我们上面提到的上下文窗口有限,以及上下文污染的问题,都可以得到有效解决。
上下文压缩
接下来我们会拆解几个Context Engineering中的技术,比如上下文压缩,不过显然写一段代码用于压缩文字,并且尽量不损失有效信息这个任务还是太难了,所以很自然的我们看可以想到交给LLM压缩,毕竟人家是语言模型,最擅长做这个了。我们完全可以写一段要求LLM对现有内容进行压缩的提示词,再将现有的上下文一并输入给LLM,让其进行压缩,然后再设定一个出发额度,比如上下文超过窗口的80%就触发一次压缩

这也对应了我们在最开始说的那两件事,给模型什么输入,以及模型如何作为系统的一部分,在这个简单的系统中,我们通过一个上下文管理系统,管理了对LLM的输入,同时LLM也作为这个系统的一部分,参与了对上下文的压缩工作。这也是现在的AI工具的一个缩影,自始至终我们只是多了一个名为LLM的箱子,能做的事情也就是研究给这个箱子什么输入,以及如何将这个箱子放进系统中。
RAG
下一个我们要介绍的技术是RAG,RAG的全称是Retrieval-Augmented Generation(检索增强生成),我们上面讲了上下文污染,得到的结论是模型拿到的上下文中最好只有跟问题相关的内容,但是如何判断,如何找到跟问题相关的内容呢,RAG就是其中一种解决方案。
RAG这个技术其实在大模型爆发之前就有了,这里我们只讲最传统的RAG,并且不会进行详细展开。因为说实话在2026年,最传统的RAG确实有点尴尬了,在下一个章节我们会引入Tool,现在的模型能力配合上Tool,在挑选上下文这件事情上表现确实优于RAG,而且即便是要用RAG,也是经过了改进的,当中加入了LLM的RAG,最传统的RAG之后可能只会存在于教科书跟大学生的作业与课设当中了。
言归正传,RAG分为两个阶段,准备阶段与检索阶段,准备阶段就是对现有的资料做预处理,大致分为三步,第一步Chunking,将资料切成小块,之后检索的结果其实也都是这些小块。第二步Embedding,将所有的文本转换为词向量,第三步就是将这些词向量存储进数据库。词向量简单接触过NLP领域的应该都不陌生,我记得CS224n的第二课就讲了词向量,如果没有接触过这个概念的读者我在此简单概述一下。
所谓词向量就是讲文本用一个个向量来表示,这个向量通常很高维,并且语义相近的词汇之间的距离也会很近,比如,Cat,Dog这些词会聚集在一片区域,且彼此之间距离很近,而Apple,Banana这些词会聚集在另一片区域。那么利用这个特性,我们要寻找资料中跟我们关心的问题相关的内容,只需要在问题对应的词向量周围寻找距离最近的Top N即可,这其实就是RAG的核心思路。
所以检索阶段的过程也很明了了,第一步,将问题通过Embedding转换为词向量,第二步,在该向量的周围寻找距离最近的Top-K个Chunks,第三步,将这些Chunks拼入上下文中。

这就是最简单的RAG的流程,当然这个流程中有非常多的改进点,比如Chunking要怎么切比较好,如果是按固定字符切,一是会损失信息,导致检索结果不准,二是放进上下文中也不是很干净的上下文,有一种策略就是引入LLM进行重写,将资料粗略切块,然后让LLM进行总结,重写,这样词向量的质量高了,放进LLM的上下文还变得更简洁更干净。
再比如用户给出的问题直接做Embedding效果不一定好,因为一个问题中可能包括很多个小问题,最好是每一个细分的问题各自检索,这时也可以引入LLM进行重写,将问题重写为一个或多个细分的问题,并且分别进行检索。
但是即便如此,RAG还是有一些从根上无法解决的问题,比如RAG是需要做预处理的,如果资料库频繁变动,比如一个正在开发的大型项目代码,那就很灾难了,每次变动都需要重新跑一遍准备过程。这也就是为什么RAG现在越来越少用了,在后续章节中我们还会继续提到。
Tool & Loop
Tool Call
在大家研究如何管理Context的同时,有些人也开始研究另一件事情,就是如何给LLM接上手脚,让他能够与现实世界进行交互,而这就引出了下一个概念Tool,也就是工具,接下来讲的内容都是原理导向,也就是本质上这件事情是这么运转的,而实际上工程上如何落地,如何保证稳定性,要更加复杂。
我们举一个最简单的例子,联网搜索,LLM自然是没有联网搜索的能力的,所以我们希望联网搜索这个功能成为一个可以被LLM使用的工具,让LLM需要的时候选择调用,这样就可以让LLM实现联网搜索的功能了。首先,我们当然要把联网搜索这个工具做出来,例如我们可以写一段代码,用于接收给定的搜索关键词,然后返回一定数量的搜索结果
1 | def web_search(query): |
代码中可能调用了Google的API之类的,具体实现我们这里就省略,总之现在我们就有了这样一个联网搜索的Tool。
接着,LLM应该如何调用这个Tool呢,LLM只是个输入输出都为文字的机器,所以大家就想到,让LLM输入一段特殊的结构化文字,用于表达自己想要调用某个Tool,而代码检测到这个特殊的结构化输出,不会返回给用户,而是运行对应的Tool。
具体到我们这个例子,在Prompt中我们要加入关于这个Tool的内容,让LLM知道有这个工具,以及如何调用这个工具,例如
1 | 你有一个工具: |
这样在LLM判断需要调用该工具时,他就会输出约定好的结构,向Agent传递要调用什么工具,给出需要的参数等等信息,例如
1 | { |
此时代码检测到这个特殊的结构化输出,按照要求调用联网搜索工具,得到搜索结果,将搜索结果放入上下文中,再调用一次LLM。这个时候,如果LLM判断信息足够回答用户的问题,则返回输出,反之,LLM也可以再次调用工具,这就是LLM调用Tool的基本运行逻辑。

当然,这里我要再次强调,原理上是这么个原理,实际落地会复杂很多,而且各家厂商的协议,规定都可能不同,Tool Call能力现在也被AI厂商加入了训练当中,各厂对自家模型的调教也有很大区别,如果要尝试开发LLM应用,请以官方API文档为准。
Agent Loop
从上面的例子我们能看到,在LLM拥有Tool Call的能力之后,在一个任务的执行过程中,LLM被调用了不止一次,而且是由LLM进行自主判断的,整个系统的工作流程被重构为一个循环,这种工作流程被称为Agent Loop。这也是目前的Agent的核心技术,由LLM自主调用工具,自主判断,循环运行,直至LLM判断任务完成时再退出循环。
这也可以解释为什么在Agent时代,Token的消耗量会指数级增长,因为在这种新的流程下,一个任务会循环多次调用LLM,我们这里举的例子比较简单,现在的Agent处理一个任务经常是几十上百次的LLM调用,这个Token的消耗量自然就水涨船高了。

RAG已死?
Tool与Agent Loop的引入,除了让LLM真正拥有跟现实世界交互的能力,也就是输出端,也进一步完善了Context Engineering,即输入端。这里我们再回到RAG的例子,RAG的本职工作就是对现有的资料做筛选,判断哪些内容应该真正的进入上下文,但是RAG只有一次机会,如果筛选的效果不好,那么就没有下一次机会了,LLM必须做出输出,但是在引入Tool的情况下就不是了。
这里我们就要回收前面RAG小节提到的伏笔了,在引入Tool之后,Agent有一种更好的检索上下文的方法,那就是通过调用Bash Tool,在Shell中运行命令来检索文件,查看文件内容,主要是ls与grep这两个命令,就像人类工程师一样。这种检索方式不需要做RAG的预处理流程,信息也不是被Chunking切碎的,最重要的是在Loop中可以返工,如果一次检索的信息不够,那就再来一次,对于Vibe Coding这种文件快速变动的场景来说,简直再适合不过了。

所以社区中也出现了RAG已死的声音,确实放眼现在主流的Vibe Coding工具,也基本都在抛弃RAG,最开始Cursor就是走的RAG的路线,现在也逐渐放弃RAG,转向grep路线了,而Claude Code则从一开始就是走的grep路线,Codex同理,或许之后只有大量的静态文档知识库之类场景才有RAG的用武之地吧。
对 Context Engineering 的完善
Tool可以让LLM具有读的能力,我们也不要忘了还可以让LLM具有写的能力,此时有人就提出了这样的想法,让LLM借助Tool,自主维护一个文本文件,作为上下文的一部分进行使用。具体的来说,这个文件中可以包括项目的基本情况,运行环境,用户的使用偏好,个人情况,或者放入一些用户常用的工作流,有Agent使用经验的读者应该已经看出来了,这就是现在用Agent都绕不开的AGENTS.md,Skill,Memory这些功能。
本质上,这些功能都是一个个的文本文件,虽然可以手写,但是大家基本都会让Agent自主生成与更新维护,而且回顾文章前面的发展历程,我们会发现这些功能本质上就是Prompt Engineering与Context Engineering的结合,例如,我们都知道AGENTS.md一般用于存放一个项目的基本信息,希望让AI遵守的规则等,并且Agent在每一个任务中都会读取改文件作为上下文,这不就是一份写好的模板Prompt吗。再比如Skill,本质上就是讲一套工作流整理成Prompt,只不过他的架构会比Prompt复杂一些,同时,只有在需要使用到对应的Skill的时候,Agent才会调用Skill,也就是将其放入上下文中,这就是Prompt Engineering与Context Engineering的结合,将优质的,符合用户习惯与需求的Prompt存储起来,并且由Context管理系统进行调度,而且这些提示词还可以由Agent进行更新迭代。

Harness Engineering
现在,让我们进入最后一个章节,在引入Tool之后,目前这个系统已经可以被称为一个Agent了,不过要将一个Agent真正投入生产环境,并不是一件简单的事情。只要是深度用过一段时间LLM的都会意识到,LLM本身就是一个很不稳定的机器,他会乱编,无脑认同你的观点,等等问题,那么如果让LLM自己跑Agent Loop,一旦出现幻觉,或者判断错误,就会让整个任务失控,而且Loop中一个任务是要多次调用LLM的,可想而知这样的一个系统不出错几乎是不可能的。
当然这个问题可以通过增强模型能力来解决,不过我们在Agent系统的设计上也有很多可以做的事情,这也就来到了目前的最后一个阶段Harness Engineering,Harness的本意是马具,之所以这么命名就是想表达,我们这是在给Agent套上各种约束,这样Agent才可以在生产环境中稳定运转。
我们从一个最简单的Agent Loop开始,讲讲最常见的问题,首先就是LLM的幻觉问题,Loop中的工具调用都是由LLM通过结构化输出调用的,那么就有可能出现编造Tool,参数缺失,不合法等等情况,那么我们在Harness中就可以对结构化输出进行检查,保证LLM的调用请求是合法的,正确的。
然后,幻觉问题还可能导致Loop陷入无限循环,因为是否结束循环是由LLM自己判定的,这样不是一个稳定的行为,那么设置一个循环次数上限,Token消耗上限,运行时间上限等等,都是可以在Harness中用于约束Agent的。
下一个问题是如何处理报错,Tool是很可能出现报错的,例如网络波动会影响需要联网的Tool,LLM生成的Shell命令也不一定是正确的,还有可能命令执行的时候会遇到权限问题等等,而且在Agent中,Tool报错并不代表任务需要停止,大家在使用Agent的时候会发现,如果遇到了报错,Agent会想办法去解决,比如提权,绕过,换用其他工具或者命令等,所以调用Tool中产生的报错信息应该是返回给Agent让他自行判断的。
而Harness要做的应该是这么两件事,第一,处理一些简单的报错,例如网络波动造成的超时,可以先重复尝试几次,是在无法连接再返回给LLM,第二,防止LLM进入无意义重试,如果LLM多次调用同样的工具,同样的参数,同样的报错,那么这时应该由Harness进行拦截。
上面说的都是稳定性问题,接着我们来说说另一个很重要的问题,权限与安全性问题,这应该是各位开发者最关注的问题,我记得不久前就出现过Codex错误删除整个项目的情况,在此先呼吁大家要用git管理版本,记得备份,不要完全相信Agent的版本管理。
然后我们来说权限的问题,Harness比较常见的约束有,第一,使用沙箱,物理上限制Agent的权限,例如限制Agent只在当前工作目录中,以及部分Agent专用的临时目录中有完整权限,Agent使用的Shell也不是高权限账号,无法运行一些高权限,高风险命令。第二,引入人工干预,只要不是开最高权限使用Agent的读者,应该都会遇到Agent询问是否允许运行某某命令的请求,这就是引入人工干预,让人类工程师判断是否是危险命令,当然我知道很多人现在都开着最高权限在用Agent工具,哪怕有一些人工确认的环节,也是AI端上来什么都说yes,笑って,不过博主本人还是比较保守的,基本都是开着次高权限在用,大多数情况下也没有危险命令需要我进行确认。
最后,我们来说说Agent的管理问题,主要有两个方面,第一个是状态管理,Agent Loop是一个可以做到长时间运行的系统,如果运行过程中出现中断等问题,那么整个Loop重新跑一遍就很麻烦了,所以Harness会对Loop进行状态管理,会对Loop的中间状态进行存储,虽然不一定是每一个Loop都存,但是大部分关键节点是会被保存的,这样如果出现中断,或者Loop跑偏等情况,就可以利用状态存储,从中间状态继续运行。第二个方面是质量管理,一个长时间运行,且自带不稳定因素的系统,如何提高该系统的交付质量呢,答案就是我们上文提到的Context Engineering,AGENTS.md,Skill,Memory这些功能都是提高交付质量的功能,现在一般大家也会把这些都视为是Harness的一部分。

总的来说就是,Harness就是套在Agent Loop外面的约束,目的就是让Agent Loop可以更加稳定的运行,并且交付更好的结果,同时还可以安全的投入生产环境,不过Harness这个概念目前还在不断发展,各种新的技术层出不穷,很多也并未开源,博主在这里也只能讲一下最基本的策略。
结语
文章的最后,博主再提一遍我自己的总结,第一,LLM就是一个输入是文字,输出是文字的机器,第二,从ChatBot到Agent,我们本质上做的只有两件事,给LLM更好的输入,以及在系统中将LLM放在合适的位置,希望对各位读者有帮助。
最后的最后,本文不是由AI生成的文章,而是每个字都由博主手搓的(经典AI味不是而是,不过文章中的配图使用了GPT image 2进行生成,手动作图还是太累了,这也是博主的首次尝试,效果嘛,只能说差强人意,后续我也想研究更稳定出图的方案,或者等AI再一次净化,那就先写这么多,じゃね。——写于我的24岁生日