如何构建一个不会失控的AI智能体:智能体循环、规划、工具调用、记忆与护栏
- 作者

- 姓名
- Nino
- 职业
- Senior Tech Editor
编写一个演示用的智能体只需要十行代码和一个周末。但生产级智能体则是完全不同的生物:它会在一个模棱两可的任务上无限循环、调用根本不存在的工具、在单个问题上消耗价值40美元的Token、带着盲目的自信返回错误答案,并且在你第一次让它接触写操作时删除它不该删除的数据。
将“我用工具连接了OpenAI API”与“这个智能体在生产环境中无人值守运行”之间拉开差距的,正是高级AI工程学的核心所在。本文将完整梳理智能体的本质、驱动其运行的循环、规划、正确的工具使用、记忆、反思,以及将玩具级项目转变为可信赖系统的安全护栏。
本文的所有示例均取自Mattrx——一个真实的多租户分析平台。其Insights智能体通过推理工具回答诸如“为什么租户X上周的转化率下降了?”之类的问题,该系统使用LangGraph进行编排,并通过FastAPI服务上的Langfuse进行追踪。我们将其从一个会无限循环且超预算的演示版,硬化成了p95延迟从4.2秒降至1.8秒且成本可控的生产级智能体。
为什么朴素的智能体总是失败
每个人构建的第一个智能体在演示时运行良好,但在生产环境中却屡屡碰壁,因为演示隐藏了五个致命问题:
- 无限循环:面对模糊不清的任务时,模型会在没有退出机制的情况下不断“思考”。
- 幻觉工具调用:当你只提供了
read_kpis时,它却试图调用send_email。 - 缺乏预算控制:一个复杂的问题可能会衍生出30次LLM调用,从而产生令人咋舌的意外账单。
- 静默失败:当工具抛出异常时,模型若无其事地忽略,最终返回一个自信但完全错误的答案。
- 没有刹车机制:一旦工具具备写权限,不受约束的智能体就会成为巨大的安全隐患。
抛开营销炒作的泡沫:智能体本质上是一个在循环中结合了工具与记忆的LLM,它不断决定自己的下一步行动,直到达成目标。我们需要区分人们常混淆的三种概念:
- 单次LLM调用:一个提示词,一个答案,由你编写控制流。
- 工作流或链(Workflow / Chain):固定的步骤序列,路径由你决定,具备可预测、低成本和易测试的特点。
- 智能体(Agent):由LLM在运行时决定执行路径。灵活性高,但不具确定性且更难约束。
高级工程师的直觉是:能用工作流解决的问题,绝不盲目上智能体。智能体只有在执行路径无法预知时,才配拥有这种不确定性。
智能体循环的解剖学
让我们看一个完全缺乏生产安全保障的朴素实现:
# 绝对不要将此代码直接部署到生产环境。缺乏退出机制、工具验证、预算控制与错误处理。
def naive_agent(task, tools):
messages = [{"role": "user", "content": task}]
while True: # (1) 存在无限循环风险
resp = llm(messages, tools=tools)
if resp.tool_calls:
for call in resp.tool_calls:
result = tools[call.name](**call.args) # (2) 工具幻觉会导致KeyError
messages.append(tool_result(call, result)) # (4) 工具抛错时直接崩溃
else:
return resp.content # (3) 缺少Token与步骤预算限制
十行代码藏了四个Bug。解决这些问题的方案不是更换一个更大的模型,而是引入结构:有界循环、经过验证的工具、预算、错误处理以及安全护栏。
核心模式是ReAct(Reason-Act-Observe,推理-行动-观察循环)。相比朴素版本,最关键的改进是设置硬性的步骤上限,确保它绝不会陷入无限循环:
def run_agent(task: str, tools: dict, max_steps: int = 8) -> str:
messages = [system_prompt(tools), {"role": "user", "content": task}]
for step in range(max_steps): # 硬上限 -> 防止无限循环
resp = llm(messages, tools=tool_schemas(tools))
if not resp.tool_calls:
return resp.content # 模型判定任务完成
for call in resp.tool_calls:
if call.name not in tools: # 拒绝幻觉工具
result = f"error: unknown tool '{call.name}'"
else:
result = safe_invoke(tools[call.name], call.args) # 内部包含try/except
messages.append(tool_result(call, result)) # 观察结果 -> 供下一步推理使用
return "stopped: step budget exhausted" # 安全失败,永不无限运行
那行for step in range(max_steps)正是区分“优雅放弃的智能体”与“凌晨三点刷爆账单的智能体”的关键所在。对一切资源进行限制。
规划:ReAct与“规划并执行”
ReAct每次只做一步决定,非常适合探索性任务,但容易走弯路。对于多步骤且形态可知的任务,“规划并执行”(Plan-and-Execute)通常更好:模型首先编写计划,然后执行,若现实情况与计划偏离则重新规划。
如何选择:开放式“自行摸索”的任务使用ReAct;而对于多步骤、前期结构化设计能降低成本与盲目性的任务,则采用规划并执行模式。
正确的工具使用方式
智能体的能力取决于其工具的质量,而大多数智能体故障本质上都是工具故障。一个生产级的工具应该是一个类型化的函数、清晰的Schema、严格的验证机制,以及能够教会模型如何纠错的异常处理。
@tool
def get_campaign_kpis(campaign_id: str, date_from: str, date_to: str) -> dict:
"""返回指定日期范围内某个活动的KPI(展示量、点击量、转化率、花费)。
日期格式为ISO-8601 (YYYY-MM-DD)。使用此工具回答性能相关问题。"""
validate_iso_date(date_from); validate_iso_date(date_to) # 永远不要信任模型的参数
require_tenant_scope(campaign_id) # 权限控制:智能体不能跨租户访问
return kpi_service.query(campaign_id, date_from, date_to)
五个核心原则:
- 描述即提示词:模型通过函数的docstring来选择工具——编写文档时要像写指令一样清晰。
- 约束Schema:使用枚举、指定格式和必填字段。
- 验证每个参数:工具代码是一个信任边界,与API端点完全相同。
- 提供具教导意义的错误:返回“错误:date_from在date_to之后”,而不是晦涩的堆栈跟踪,这样模型才能自我恢复。
- 精简且贴合任务的工具:大约10个意图级别的工具远胜过40个CRUD端点。
专业提示:当通过 n1n.ai 整合多个外部API时,请务必在工具输出之上维护一层严格的抽象。检索到的文档可能包含注入指令,因此应将工具输出视为纯数据,而非可执行命令。
记忆与反思机制
记忆分为两个层级:
- 短期记忆(工作记忆):当前任务的连续消息列表/草稿本。注意监控上下文窗口,在长任务中及时总结或精简旧步骤。
- 长期记忆:跨会话持久保存的事实(偏好设置、先前的结论、向量数据库),在相关时检索调用。
# LangGraph通过检查点(checkpointer)提供持久化的工作记忆与恢复能力。
agent = graph.compile(checkpointer=PostgresSaver(conn)) # 状态按thread_id持久化
context = memory_store.search(query=task, tenant_id=tenant, k=5) # 长期记忆 = 检索
允许智能体在返回结果前检查自己的工作——反思步骤会询问“这正确且完整吗?”,并能触发另一轮循环。它以额外的调用成本为代价捕获幻觉,因此应当将其限定在高风险输出场景中。
安全护栏与生产清单
以上所有内容赋予了智能体强大的能力,而安全护栏则确保它在无人值守时安全运行。
def guarded_step(state, call, tool):
if state.steps > MAX_STEPS: raise StopAgent("step budget") # 限制循环步数
if state.tokens > TOKEN_BUDGET: raise StopAgent("token budget") # 限制Token成本
if tool.name not in ALLOWLIST: return "error: tool not permitted" # 最小权限原则
if tool.is_destructive and not human_approved(call): # 确认写操作
return "paused: awaiting human approval"
result = safe_invoke(tool, call.args) # 确保循环永不崩溃
return validate_output(result) # 模式、PII与护栏检查
生产环境检查清单
- 步骤与Token预算:对单次运行的循环长度和消费设定硬性上限。这是不可妥协的。
- 工具白名单/最小权限:智能体只拥有该任务必需的工具(分析智能体不应拥有删除工具)。
- 引入人类介入(Human-in-the-loop):针对具破坏性或不可逆的操作,由智能体提出方案,人类进行确认提交。
- 输入与输出验证:对输入的参数以及输出的最终答案进行模式或PII扫描验证。
- 将工具结果视为不受信任数据:隔离返回的内容,防范间接提示词注入。
- 超时与降级策略:每个工具调用均设超时时间,失败时优雅降级为有用的提示信息。
- 全面追踪:确保每次运行均可观测。在通过 n1n.ai 路由LLM调用时,利用原生遥测技术监控智能体集群的Token消耗情况。
高级工程师的信号:当你将“智能体能够在无人值守的情况下执行不可逆操作”视为需要人工关卡的重大设计红线时,你就成熟了。
选择编排框架
- LangGraph:将智能体建模为显式图。最适合控制力、可检查性、持久状态以及人工介入检查点。
- CrewAI:高层次的、基于角色的多智能体框架,代码量极少。
- AutoGen:基于对话驱动的多智能体系统。
- Semantic Kernel / Microsoft.Extensions.AI:微软技术栈团队的.NET首选路径。
# LangGraph:智能体就是一个图。显式、可检查、可恢复。
graph = StateGraph(AgentState)
graph.add_node("reason", call_model)
graph.add_node(
"act", run_tools
)
graph.add_conditional_edges("reason", lambda s: "act" if s.tool_calls else END)
graph.add_edge("act", "reason") # 观察 -> 再次推理(循环)
agent = graph.compile(checkpointer=memory) # 持久化状态 + 恢复 + HITL暂停
前后性能指标对比
| 指标 | 朴素版本 (v1) | 硬化版本 (v2) |
|---|---|---|
| 智能体p95延迟 | 4.2秒 | 1.8秒 |
| 每个问题的成本 | 约 $0.90(无限制) | 约 $0.12(预算限制 + 缓存) |
| 失控循环次数 | 频繁 | 零(设硬上限) |
| 可调试性 | 无 | 每次运行完整追踪 |
性能的提升完全源于良好的架构设计——受约束的循环、类型化的只读工具、预算控制、输出验证和全面追踪,而不是更换了更聪明的模型。借助诸如 n1n.ai 这样支持智能缓存的高性能聚合器,您可以进一步优化延迟。
构建智能体只是第一步。让它在生产环境中值得信赖需要掌握三项技能:评估(测试非确定性内容)、可观测性(追踪运行成本、延迟及决策原因)以及成本工程(提示词/语义缓存、模型路由)。构建它、衡量它、观测它并降低其成本。
获取免费API密钥请访问 n1n.ai