很多人第一次把大模型接进工作流时都会纠结同一个问题:这件事到底该写个普通提示词调用 LLM,还是干脆搭一个 AI 智能体?答案其实不取决于模型有多强,而取决于任务的性质——你是只需要它“生成信息”,还是需要它“在外部系统里动手做事”。
先用一个问题区分:生成还是执行
判断标准可以压缩成一句话:任务是否需要跨工具、多步骤地改变外部状态。
如果任务只是把已有信息重新组织、总结、翻译、分类、写初稿,那么一次 LLM 调用通常就够了。比如把会议记录整理成摘要、给一段代码写注释、把用户反馈归类打标签,这些任务的输入输出都在同一个上下文里完成,不需要模型去查询数据库、调用 API 或修改文件。
反过来,如果任务需要“先查再算再写回”,比如“查一下本周新增的工单,按优先级排序,把超过 48 小时未处理的挑出来,给负责人发一封提醒邮件”,这就不是一次生成能解决的。它涉及读取数据、判断条件、调用邮件接口,中间任何一步失败都可能需要重试或换路径。这类任务才是 AI 智能体的主场。
IBM 对 AI 智能体的定义也强调这一点:智能体是一种通过使用可用工具设计工作流来自主执行任务的系统,功能范围不止自然语言处理,还包括决策、问题求解、与外部环境交互以及执行操作。换句话说,LLM 是智能体的“大脑”,但真正让它区别于普通调用的,是工具调用和自主编排能力。
一张判断清单
动手之前,可以拿下面几个问题过一遍:
| 判断项 | 偏向 LLM 直接调用 | 偏向 AI 智能体 |
|---|---|---|
| 是否需要访问外部系统 | 不需要,信息已在上下文里 | 需要查数据库、API、文件系统 |
| 步骤数量 | 一步生成 | 多步,且步骤顺序可能动态变化 |
| 是否需要根据中间结果调整 | 不需要 | 需要,比如查询失败要换关键词 |
| 失败后是否需要重试或换路径 | 不需要 | 需要 |
| 输出是否要写回外部状态 | 不写回 | 要发邮件、建工单、改记录 |
如果大部分落在左边,别急着上智能体。Anthropic 在构建智能体的经验分享里也提到,很多场景其实用简单的工作流就够用,智能体更适合那些步骤无法预先固定、需要模型自己决定下一步的任务。
从最小可用开始:只读、单工具
确定要做智能体之后,最容易踩的坑是一上来就设计多工具、多角色、带长期记忆的复杂系统。更稳妥的做法是先搭一个只读、单工具的最小版本,跑通基本循环,再逐步加东西。
所谓“只读”,是指这个工具只查询、不修改。比如只调用一个搜索接口,或者只读取一张数据库表的某几行。这样即使智能体判断错了,也不会造成实际破坏,你可以放心观察它的行为。
一个最小循环大致长这样:
- 接收用户任务描述。
- 把任务和可用工具的描述一起发给 LLM。
- LLM 决定是否调用工具,以及传什么参数。
- 执行工具,把结果返回给 LLM。
- LLM 根据结果决定继续调用还是给出最终回答。
用伪代码表示核心逻辑:
def run_agent(task, tools, max_steps=5):
messages = [{"role": "user", "content": task}]
for step in range(max_steps):
response = llm.chat(messages, tools=tools)
if response.tool_call:
result = execute_tool(response.tool_call)
messages.append(response.tool_call)
messages.append({"role": "tool", "content": result})
else:
return response.content
return "达到最大步数,任务未完成"
这里有两个关键点值得注意。max_steps 是防止智能体陷入无限循环的硬性刹车,没有它,一个判断失误的模型可能反复调用同一个工具。另一个是工具描述要写清楚,模型靠这段描述决定什么时候用、怎么传参,描述含糊是工具调用失败最常见的原因之一。

加上记忆与重试,并观察代价
最小版本跑通后,通常会遇到两类问题:一是智能体记不住前面已经查过什么,重复调用同一个工具;二是工具偶尔超时或返回空结果,整个任务就卡住了。这时候才需要引入状态记忆和失败重试。
状态记忆最简单的做法是把每一步的工具调用和结果都留在消息历史里,让模型在下一轮能看到“我已经查过 X,结果是 Y”。更复杂的做法是维护一个外部状态对象,记录已完成步骤和中间结论。对大多数单工具场景,前者已经够用。
失败重试要区分两种失败:可重试的和不可重试的。网络超时、限流这类错误可以退避重试;参数错误、权限不足这类错误重试多少次都没用,应该把错误信息返回给模型,让它调整参数或换一种方式。一个常见的做法是给工具调用包一层:
def execute_with_retry(tool_call, max_retries=2):
for attempt in range(max_retries + 1):
try:
return execute_tool(tool_call)
except RetryableError as e:
if attempt == max_retries:
return f"工具调用失败:{e}"
time.sleep(2 ** attempt)
except FatalError as e:
return f"无法重试的错误:{e}"
加完这些之后,一定要观察代价。多一次工具调用就多一次网络往返和一次模型推理,延迟和费用都会上升。一个实用的做法是在日志里记录每次任务的步数、工具调用次数和总耗时,跑一段时间后回看:如果平均步数接近 max_steps,说明要么任务太复杂,要么工具描述有问题。
边界、权限和日志
智能体一旦能执行操作,就必须考虑权限收敛。几个原则:
- 最小权限:只读工具就给只读账号,不要图省事用管理员密钥。
- 操作白名单:能调用的接口列成明确清单,而不是让模型自由拼接。
- 人工确认点:涉及发送、删除、支付这类不可逆操作时,先输出待执行动作让人确认。
- 完整日志:记录每次工具调用的输入、输出和时间戳,出问题时这是唯一能还原现场的东西。
日志还有一个容易被忽视的用途:它是你判断“这个智能体到底值不值得继续投入”的依据。如果日志显示它大部分时候都在做一次就能完成的事,那说明这个任务本来就不需要智能体。
常见踩坑与排查
工具调用一直失败:先检查工具描述是否说清了用途和参数格式,再检查模型返回的参数类型是否和实际接口匹配,比如字符串和数字混用。
智能体反复调用同一个工具:通常是状态没有正确回传,或者工具返回的结果里没有模型能判断“已完成”的信号。可以在工具结果里加一个明确的完成标记。
任务中途卡住不结束:检查 max_steps 是否设置,以及模型是否陷入了“调用—失败—再调用”的循环。把失败信息完整返回给模型,往往能让它自己换策略。
结果看起来对但实际错了:这是最危险的情况。只读阶段问题不大,一旦涉及写操作,务必加人工确认或二次校验。
回到最初的问题:该用 LLM 还是 AI 智能体,判断依据不是哪个更先进,而是任务需不需要跨工具执行多步操作。不需要,就用最简单的调用;需要,也先从只读、单工具的最小循环开始,跑通之后再逐步加记忆、重试和权限控制。每加一层能力,都回头看一眼它带来的延迟、成本和风险是否值得。

评论列表 (1条):
加载更多评论 Loading...