老胡茶室
老胡茶室

无废话 Agent:理论篇

胡键

开发 LLM 应用,不可避免会用到 Agent。从本期开始,让我们把目光转向 Agent,毕竟大家不都说了么:“2025 是 AI Agent 元年”!

鉴于 Agent 的内容,我打算分成多篇来写:

  • 理论篇,毋庸置疑,聚焦于概念和原理,按照本系列一向的文风,自然会少说废话,直接将看似高高在上的理论术语以接地气的方式呈现。
  • 实践部分,则聚焦有代表性的工具(LangGraph、Agno 和 SmolAgent,每个工具单独成篇)和应用场景,看看典型的 Agent 做事风格和特点。

作为迷你系列的首篇,你将了解到:

  • 经典的 3W + 1H,即 What、Why、When 和 How。
  • Agent 相关的协议:AG-UI、MCP 和 A2A,它们的区别和使用场景。
  • 典型 Agent 架构,以及 Workflow 和 Agent 的取舍
  • 最后则说说 Agent 开发中的一些实践

本文不打算过份追求知识的深度,但尽可能在广度上覆盖 Agent 重要的信息。而且,个人觉得在如今 coding agent 发展迅猛的时代,知识广度的重要性远超单一知识点的深度。个中原因不言自明,各位可自行体会。

3W + 1H

What

关于 Agent,我曾在无废话 LangChain.js 中提到我特别喜欢 Agno 文档中的一幅图,见下:

Agent

它简单并清晰地回答了“何为 Agent” 这一问题:

Agent = LLM + Memory + Tools + Knowledge

其中:

  • LLM,为 Agent 提供核心引擎
  • Tools,为 Agent 提供外部可调用工具
  • Memory,为 Agent 提供状态管理,虽然每家各有各的术语,但基本上都可以归为长期记忆和短期记忆两类。
  • Knowledge,为 Agent 执行任务过程中所需的知识库

有些框架会将 Memory 和 Knowledge 合并为一个概念,但我个人觉得分开可以更好地帮助理解 Agent,即便大而化之为“知识”:

  • Memory 是与 Agent 本身有关的知识,如对话历史、用户偏好等,处于 instance 级别。
  • Knowledge 则是独立于 Agent 之外的客观知识,于具体 Agent 无关。

同时,联系到最近大热的 context engineering,你也可以说它们都是 context,但这种说法对于理解 Agent 并无太大帮助,故而在本文中不展开讨论。

Why

Agent 的价值何在?有必要在 LLM 之外再新造一个概念吗?简单的答案是:Yes!

从技术角度看,Agent 可视为一个内置了一套 prompt template 和配套支撑工具的微型框架,这个框架可以服务于一大类应用场景。由此,在开发时你不必每次再从头构建,只需直接复用即可。如同大多数前端开发者都在使用 React 这样的框架开发站点而非使用原始的 html / css / js 去手搓一个网站一般。

下图展示了 Agent 发展的关键历史阶段(来源:𝐋𝐋𝐌 𝐀𝐠𝐞𝐧𝐭𝐬 → ReAct, Toolformer, AutoGPT family & Autonomous Agent Frameworks):

Agent 发展历程

When

由于引入了状态,可以调用外部工具,并且可以访问外部数据,这使得 Agent 可以在更复杂的场景中发挥作用。比如,为广大开发者热爱的 Claude Code 和 Cursor 就是典型代表。其他场景,相信也不必我多说,大家自然都能想到。

Agent 既然这么好,那我直接无脑选用它岂非“永远正确”?!在此,我得给你当头一棒:非也,非也!

由于 Agent 本身是由 LLM 驱动的,而 LLM 的天然不确定性将导致它未必能 100% 按照你设想的方式去工作。比如工具调用,其决策权完全由 LLM 掌控,这与传统的编程方式有着本质区别。

因此,假如你追求的是 100% 的确定性和可控性,Agent 可能并非最佳选择。

关于这一点,我将在后续与 Workflow 的讨论中进一步展开。

How

Agent 的使用并不复杂,业内也有不少成熟的框架,而且在后续的实践篇中,我会深入介绍几个典型的 Agent 框架:

  • LangGraph:最流行且使用最广,python 和 js 版本都有,侧重状态图和工作流。
  • Agno:简约而不简单,python,侧重多 Agent 协同。
  • SmolAgent:会写代码的 Agent,python,侧重于用代码解决问题。

各位可根据自己的口味和需求自行选择。

Agent 相关协议

作为新兴个体的 Agent ,它至少需要与外部世界里的三类事物进行交互:

  • 人类用户
  • 其他 Agent
  • 外部系统(如数据库、API 等)

由此,业内针对这三方面的交互制定了三种协议,见下图(来源:The Complete Guide to AI Agent Protocols: MCP, A2A, and AG-UI):

Agent 协议

其中:

  • AG-UI,侧重于 Agent 与人类用户交互,如果你在实现 Generative UI 或者涉及到 human-in-the-loop 的场景,AG-UI 是最佳选择。推荐:CopilotKit。
  • MCP,侧重于 Agent 与外部工具交互,具体可参见:
  • A2A,侧重于 Agent 与 Agent 交互,可查看 A2A SDK。

典型 Agent 架构

LangGraph 的文档网站对于典型 Agent 架构有一张很好的图,见下:

Agent 架构

我不打算对这张图进行太多解读,因为它跟一般的分布式系统架构大同小异。唯一不同之处就在于其中的 Agent 相比起一般的分布式系统组件,具有更强的智能性和自主性。

如果使用 LangGraph 来实现,就是构建不同的状态图(State Graph)。

最后强调一点:抛开单 Agent 不谈,Agent 之间的协作风格分为两大类:

  • 具有协调者的 Supervisor 架构,中心化架构
  • 自组织的 Network(也称 Swarm) 架构,去中心化架构

其他的无非就是在此之上的组合,LangGraph 对于这三类提供了预定义的支持:

  • create_react_agent,单 Agent
  • create_supervisor,Supervisor 架构
  • create_swarm,Network 架构

Workflow vs Agent

另一个经常与 Agent 一起出现的一个概念就是 Workflow。尤其是在 LangGraph 出来后,它声称自己是为 Agent 而生,但写代码时你却又发现是在定义状态图(实际就是 Workflow)。故而,导致不少初学者感到困惑。

简单的讲,Workflow 是一种架构风格,而 Agent 如前所述,在 LLM 上就是一种预定义的微型框架。写 LLM 应用中的 Workflow,你并不需要使用 Agent。

它俩最大的区别在于:确定性

  • workflow 是一组预定义的步骤,对于执行状况完全可控。即便 LLM 的输出带有不确定性,但通过加强 prompt 或者加上其他的约束条件,基本上你可以确保它能按照你的预期去执行。
  • agent,则不然。是否调用工具以及调用什么工具完全由 LLM 决定,而且由于其内部维护有自身的状态,即使调整 prompt 也未必能确保它按照你的预期去执行。

在实际开发中,我就发现有时 RAG Agent 会按期望查询知识库,有时则直接利用之前回答过的(相似但细节不同的)问题的上下文来直接作答。有时这是一种优化,而有时则是噩梦。

同时,在一开始用 Agno 写 Agent 时,我也发现 Agent 在使用工具时的随意性

agent 计划工具太随意,且来回浪费 token,即浪费钱。

想必各位在使用 Claude Code 或 Cursor 这类工具时也会发现其消耗 token 特别快,而这并非个例。

因此,在技术选型时:

  • 若追求确定性,则选择 Workflow。尤其是在输入内容可预知的情况下,Workflow 的优势更明显也更经济。典型如 RAG 中的 indexing pipeline。可参见:
  • 若追求灵活性和开放性,则选择 Agent,尤其是在输入内容不可预测,并且期望 Agent 能自主根据上下文决定如何跟外界打交道时。典型如各类 Chatbot。

注意,两者并不冲突,你完全可以将两者组合使用,实现你心目中的灵活性和确定性的平衡。而 LangGraph 则是这方面的一个很好的选择。

Agent 开发和使用实践

Prompt 和 Context

在 AK 的加持下,上下文工程红火起来了。但作为开发者,我建议咱们还是把两者区分一下更好。最起码你该区分:system prompt 和 context。将前者视为设定 Agent 基本行为的定海神针,而根据实际运行情况动态调整后者。

我尤其喜欢 dexhorthy 在其系列文章 12-factor-agent 中的做法:并非简单机械地把所有与 Agent 的历史对话都塞入上下文,而是将其进行结构化处理之后,再传给 Agent。这样可以更好地利用有限的上下文窗口,从而得到更好的输出。

这种做法类似你在和人沟通对话时的阶段性总结和提炼。这样,对方会更容易理解你的意图,使得对话更高效。

在进行 Vibe Coding 时,你也可以采用类似的做法:在完成任务的过程中,定期让它回顾当前进展,对比预期目标,再规划下一步行动。比起你一路催它赶路,不断 PUA 它(这类网上所谓的经验,不妨当笑话去看。),这种方式更能让你得到预期的结果。

尤其是在你发现 Agent 陷入死循环时,此时不妨让它 step back,思考接下来如何行动。即使你想新开会话,那么这种回顾也是有意义的,你打开让它把这种回顾写下来,作为下一次会话的 context。

不要给 Agent 塞入过多的工具

工具过多,不仅可能会意外导致 Agent 选择错误的工具,同时也潜在带来了更多地中间交互,导致宝贵的上下文窗口被浪费掉。

同时,不要天真地指望在塞给 Agent 有先后调用次数的工具之后,靠你的 prompt 就能确保它能按照你的预期去调用工具。还记得上面 workflow 和 agent 的讨论吗?我敢担保:期望越大,失望越大!

如果有此类需求,建议将 Agent 进行拆分,然后使用 workflow 来加强确定性。并且,这样还带来了一个额外的好处:增加了 Agent 的可组合性。

这跟在 Class 设计时不宜包含过多职责的道理是一样的。

由此,也可以衍生出另一个经验:不要将你的 restful API 直接一比一地映射到 Agent 的工具上(如 MCP Server)。

尽可能控制输入

另一种天真的想法是:指望 Agent 能像你期望的那样能自动获取内容,比如给它一个 URL,它就能自动获取内容。放弃幻想!最早遇到这个情况的时候,还是我在写自用浏览器插件 tsw的时候。

链接 VS 内容

我第一次尝试的是:链接,因为图省事是我的原则。

一开始,貌似 ok,但仔细看过摘要内容之后,发现效果并不理想,而且还大概率是它在胡说八道。

于是乎,老老实实回归传统,解析链接,获取内容再传入。

首先,并不是所有底层模型具有直接访问外部系统的能力;其次,就目前的技术水平,它未必如你所愿获得期望内容。第三,即便你可以通过工具确保一旦访问内容,它就返回期望格式,但是,别忘了前面说的:调不调用工具完全看 Agent 的心情。

这么多不确定性,又有潜在浪费 token 浪费钱的风险,何必呢?!

如果你能控制输入,尽可能自行获取内容,然后将其作为 context 传入 Agent。这样会带来更高的确定性且更经济。

了解新兴的安全风险

做 Web 开发的开发者对于 OWASP Top 10 应该不陌生。同样,他们针对 LLM 也发布了类似的安全风险列表,见下图(来源:OWASP Top 10: LLM & Generative AI Security Risks):

LLM 安全风险

无独有偶,排在第一位的依旧是 Injection 类攻击,只是这次不再是 SQL Injection,而是 Prompt Injection。

面对这类新型攻击,一般的 LLM 框架中都引入了 Guardrail 的概念,并且像 LangGraph 本身也有相关的 Hook 来帮助你实现 Guardrail。

同时,对于敏感操作,建议引入 human-in-the-loop 环节,让人类用户来确认,而非完全放权。

而对于代码执行,则建议使用沙箱环境来隔离 Agent 的执行环境,避免潜在的安全风险。尤其是 MCP 的引入,对此类风险的防范更是至关重要,参见无废话 MCP

任务 Context 拆分影响 Agent 的架构选择

前段时间有两篇观点看似矛盾的文章引起大家的热议:

前者提倡多 Agent 协同,后者则认为单 Agent 更好。看似矛盾,但其实其核心还是在于任务的 Context 是否可以拆分。

  • 如果能,且相互之间没有太多依赖,那么多 Agent 协同可以带来更高的效率和灵活性。因为有些任务可以并行处理。
  • 如果不能,或者依赖较多,那么单 Agent 更好,因为省掉了上下文切换的开销。也能让 Agent 更好地理解任务。

这里有一个非常好的视频对此有解读,建议各位观看。

总结

相信通过本文,你会对 Agent 有一个更清晰的认识。除了网上已经广为熟知的哪些 Agent 的资料(OpenAI 和 Anthropic 都有),我还强烈推荐阅读:12-Factor Agents - Principles for building reliable LLM applications。你一定会从中受益匪浅,只是请注意:它不适合初学者 😄!

付费内容

本文包含付费内容,需要会员权限才能查看完整内容。