1. 先搞清楚 AI Agent 到底在解决什么问题
1.1 从“会聊天的模型”到“能办事的系统”
很多人第一次接触 AI Agent,脑子里浮现的是“更聪明的聊天机器人”。这个理解不算错,但远远不够。聊天机器人解决的是“信息问答”,你问它答,对话结束,任务也就结束了。而 AI Agent 解决的是“任务闭环”——你给它一个目标,它自己去拆解步骤、调用工具、检查结果、遇到问题重试,直到把事办完。
举个具体的例子。你让一个纯 LLM 应用“帮我查一下上周的销售数据并生成周报”,它大概率会回复你一段“我无法访问你的数据库”之类的话。但如果你把这件事交给一个配置好的 AI Agent,它会先调用数据库查询工具拉取数据,再用代码解释器做汇总统计,接着调用文档生成工具产出周报,最后通过邮件或消息接口把结果发出去。整个过程你只需要说一句话。
这中间的差别,就是Workflow 编排和工具调用带来的。Agent 不是一个模型,而是一套“模型 + 工具 + 记忆 + 编排逻辑”的系统工程。Anthropic 在 2024 年底发布的那篇关于构建高效 Agent 的文章里,把这个思路讲得很透:最成功的 Agent 实现,往往不是最复杂的框架,而是最简单、最可控的组合模式。
1.2 为什么现在大家都在聊 Agent
三个条件同时成熟了。第一,LLM 的推理能力和指令遵循能力到了可用的水平,尤其是支持Function Calling和结构化输出的模型越来越多。第二,工具生态起来了,无论是搜索、代码执行、数据库连接还是各类 SaaS 接口,都有现成的 SDK 可以接。第三,成本降下来了,以前跑一次复杂推理要几毛钱,现在几分钱甚至几厘钱就能搞定。
这三个条件缺一个,Agent 都跑不起来。早两年做类似尝试的人应该记得,那时候模型经常“幻觉”出根本不存在的工具名,或者把参数格式写错,整个流程动不动就断。现在虽然还会出问题,但至少在一个设计良好的 Workflow 里,成功率能做到 90% 以上。
1.3 这篇文章适合谁看
如果你是完全没接触过 Agent 的新手,这篇文章会从最基础的概念讲起,告诉你一个 Agent 由哪些部分组成,每一部分为什么必须存在。如果你已经用过一些 Agent 框架但总觉得“不太听话”,这篇文章会帮你理清 Workflow 编排的核心逻辑,让你知道问题出在哪个环节。如果你正在做AI Agent 开发的技术选型,文章里关于框架对比和工具选型的部分可以直接参考。
我自己的经验是,学 Agent 最快的方式不是先看框架文档,而是先用最原始的方式手搓一个最小可用的版本。你只有亲手处理过“模型返回的 JSON 解析失败”这种问题,才会真正理解为什么需要结构化输出约束。下面我就按这个思路,从零开始拆。
2. 一个 AI Agent 的最小构成与核心原理
2.1 四个必备组件:模型、工具、记忆、编排
把 Agent 拆开看,核心就四样东西。
模型是大脑,负责理解意图、做决策、生成内容。选模型的时候不要只看跑分,要看它在你的具体任务上的表现。有些模型通用能力强但工具调用格式老出错,有些模型专门针对 Function Calling 做了优化,实际用起来反而更稳。
工具是手脚,让 Agent 能跟外部世界交互。最常见的工具包括:搜索引擎、代码执行器、文件读写、HTTP 请求、数据库查询。工具的定义要尽可能清晰,每个工具只做一件事,参数说明要写明白。我见过太多人把工具设计得过于复杂,一个工具干五件事,结果模型根本不知道怎么传参。
记忆分短期和长期。短期记忆就是当前对话的上下文,决定了 Agent 能“记住”多少轮之前的交互。长期记忆通常用向量数据库或知识库来实现,让 Agent 能跨会话记住用户偏好或领域知识。LLM Wiki 知识库这类方案就是典型的长期记忆实现。
编排是骨架,决定这些组件按什么顺序、什么条件组合起来。最简单的编排就是“模型输出 → 解析 → 调工具 → 把结果塞回模型 → 再输出”,复杂一点的会有分支判断、循环重试、并行执行。
2.2 ReAct 模式:Agent 最基础的思考循环
ReAct 是 Reasoning + Acting 的缩写,是目前大多数 Agent 的底层逻辑。它的工作流程是这样的:
- 模型接收用户输入和当前上下文
- 模型输出一段“思考”(Thought),说明它打算做什么
- 模型输出一个“动作”(Action),指定要调用的工具和参数
- 系统执行工具,拿到“观察结果”(Observation)
- 把 Observation 追加到上下文,回到第 1 步
- 直到模型认为任务完成,输出最终答案
这个循环看起来简单,但实际跑起来坑很多。最常见的问题是模型在 Thought 阶段想得很好,但 Action 阶段参数格式写错了。比如它想调用search(query="AI Agent"),结果输出成search("AI Agent"),少了参数名。这时候就需要在系统提示词里把工具调用的格式约束死,或者用支持结构化输出的模型来强制 JSON Schema。
提示:ReAct 循环一定要设最大迭代次数。我一般设 10 到 15 次,超过就强制终止并返回当前结果。不然遇到模型“钻牛角尖”的情况,它会一直循环调用同一个工具,烧钱又费时。
2.3 Workflow 编排:把单步能力串成完整流程
单个 ReAct 循环能解决“查个天气”这种简单任务,但真实业务往往需要多步骤协作。比如“分析竞品动态并生成报告”这件事,拆开看至少包括:搜索竞品新闻、抓取关键页面内容、提取核心信息、对比分析、生成结构化报告、发送给相关人员。
Workflow 编排就是把这些步骤按依赖关系组织起来。有两种主流做法:
一种是代码编排,用 Python 或 TypeScript 把每一步写成函数,用 if/else 和循环来控制流程。优点是可控性极强,调试方便,适合逻辑固定的场景。缺点是灵活性差,流程一变就要改代码。
另一种是模型编排,让一个“规划模型”先输出任务分解和步骤依赖,再由执行器按计划调用各个子 Agent 或工具。优点是灵活,能处理没见过的任务组合。缺点是稳定性差,规划模型本身可能出错,而且多一层调用就多一层延迟和成本。
我的建议是:核心业务流程用代码编排,边缘的、探索性的任务用模型编排。比如客服 Agent 的“查订单 → 判断状态 → 回复用户”这条主线,用代码写死最稳。但用户问了一个没预设过的问题,可以交给模型编排去尝试解决。
2.4 为什么 Anthropic 强调“简单优先”
Anthropic 在那篇 Agent 构建指南里反复强调一个观点:能用简单 Workflow 解决的,不要上复杂 Agent。很多人一上来就想搞“全自主 Agent”,结果发现模型在开放环境里根本不可控,今天能跑通的任务明天就失败。
他们的建议是先用 Prompt Chaining(提示链)把任务拆成固定步骤,每个步骤用 LLM 处理,步骤之间用代码传递数据。如果 Prompt Chaining 不够用,再考虑 Routing(路由)——根据输入类型分发给不同的处理链。再复杂一点用 Orchestrator-Workers(编排器-工作者)模式,由一个主模型分解任务,多个子模型并行执行。最后才考虑完全自主的 Agent。
这个渐进思路非常实用。我自己的项目里,80% 的需求用 Prompt Chaining 加简单条件分支就能搞定,根本不需要 Agent 框架。剩下 20% 才需要真正的 Agent 能力。
3. 从零搭建一个 AI Agent 的完整实操
3.1 环境准备与技术选型
先明确一点:不要一上来就选框架。我见过太多人花一周时间研究 LangChain、AutoGPT、CrewAI 的区别,结果连一个能跑通的 Demo 都没写出来。正确的做法是先用手写的方式实现一个最小 Agent,理解每个环节在干什么,然后再根据需求选框架。
基础环境很简单:
python -m venv agent-env source agent-env/bin/activate pip install openai httpx pydantic模型接口方面,OpenAI 的 API 格式目前是事实标准,大多数模型服务商都兼容。如果你用 Anthropic 的 Claude,接口略有不同但逻辑一致。国内的话,通义千问、DeepSeek、智谱都有兼容 OpenAI 格式的接口,切换成本很低。
注意:如果你在调用 Anthropic 服务时遇到
unable to connect to anthropic services或failed to connect to api.anthropic.com这类报错,先检查网络出口和 API Key 配置。另外claude doesn't look like an anthropic model: expected a gateway model route这个错误通常出现在用第三方网关转发请求时,模型名称映射没配对。建议先用官方 SDK 直连测试,确认基础链路通了再上网关。
3.2 定义工具:让模型知道它能做什么
工具定义是整个 Agent 的地基。一个工具包含三部分:名称、描述、参数 Schema。
tools = [ { "type": "function", "function": { "name": "search_web", "description": "搜索互联网获取最新信息。当需要查询实时数据、新闻或未知信息时使用。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "搜索关键词,尽量具体" }, "max_results": { "type": "integer", "description": "返回结果数量,默认5", "default": 5 } }, "required": ["query"] } } } ]描述字段非常关键。模型就是靠这段文字来判断“什么时候该用这个工具”。我踩过的坑是描述写得太笼统,比如只写“搜索功能”,结果模型在需要计算的时候也去调搜索。后来改成“搜索互联网获取最新信息,不适用于数学计算”,误调用率立刻降下来了。
参数 Schema 用 JSON Schema 格式,required字段一定要标清楚。如果某个参数有默认值,在 description 里说明,模型会自己决定要不要传。
3.3 实现 ReAct 循环:核心执行引擎
下面是一个最简版的 ReAct 循环实现:
import json from openai import OpenAI client = OpenAI() def run_agent(user_input, max_iterations=10): messages = [ {"role": "system", "content": "你是一个助手,可以使用工具来完成任务。每次只调用一个工具。"}, {"role": "user", "content": user_input} ] for i in range(max_iterations): response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="auto" ) msg = response.choices[0].message # 没有工具调用,说明模型认为任务完成 if not msg.tool_calls: return msg.content # 把模型的回复加入上下文 messages.append(msg) # 执行每个工具调用 for tool_call in msg.tool_calls: func_name = tool_call.function.name func_args = json.loads(tool_call.function.arguments) # 实际执行工具 result = execute_tool(func_name, func_args) # 把结果塞回上下文 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(result) }) return "达到最大迭代次数,任务未完成"这段代码的核心逻辑就是:模型输出工具调用 → 执行 → 结果回传 → 模型继续决策。max_iterations是安全阀,防止死循环。
execute_tool函数根据工具名分发到具体实现:
def execute_tool(name, args): if name == "search_web": return search_web(args["query"], args.get("max_results", 5)) elif name == "run_code": return run_code(args["code"]) else: return f"未知工具: {name}"3.4 加入记忆:让 Agent 记住上下文
短期记忆直接靠 messages 列表维护就行。但要注意上下文长度限制,超过模型窗口后需要做截断或摘要。我的做法是保留最近 10 轮完整对话,更早的用 LLM 压缩成一段摘要放在系统提示里。
长期记忆需要向量数据库。流程是:把领域知识切块 → 用 Embedding 模型转向量 → 存入向量库 → 查询时先做相似度检索 → 把相关片段拼进提示词。这就是RAG的基本流程。
LLM Wiki 知识库是 RAG 的一种变体,它把知识组织成结构化的 Wiki 页面,每个页面有明确的主题和关联关系。查询时先定位到相关页面,再提取内容。相比纯向量检索,Wiki 结构能保留更多上下文和层级关系,适合知识体系比较复杂的场景。
3.5 编排多步骤 Workflow
单轮 ReAct 只能处理简单任务。复杂任务需要把多个 ReAct 实例或固定步骤串起来。下面是一个“竞品分析报告”的 Workflow 示例:
def competitor_analysis_workflow(company_name): # 步骤1:搜索竞品新闻 news = search_agent(f"{company_name} 最新动态 2025") # 步骤2:提取关键信息 key_points = extract_agent(news) # 步骤3:对比分析 analysis = analyze_agent(key_points, company_name) # 步骤4:生成报告 report = generate_report_agent(analysis) return report每个xxx_agent内部都是一个完整的 ReAct 循环。步骤之间用代码传递数据,这样比让一个模型从头到尾自己规划要稳定得多。
如果步骤之间有条件分支,比如“如果新闻数量少于3条,就扩大搜索范围”,直接在代码里写 if/else 就行。Workflow 编排的精髓就是把不确定的交给模型,把确定的交给代码。
4. 实际开发中绕不开的坑与排查方法
4.1 工具调用格式错误:最常见也最烦人
模型返回的 JSON 解析失败是最高频的问题。表现包括:参数名拼错、缺少必填字段、JSON 格式不合法、把字符串当数字传。
排查思路分三步。第一,检查工具定义是否清晰,参数名是否容易混淆。第二,在系统提示词里加一句“调用工具时严格按照 JSON Schema 输出,不要添加额外字段”。第三,如果模型本身对 Function Calling 支持不好,考虑用支持结构化输出的模型,或者用 Instructor 这类库做输出约束。
我实测下来,GPT-4o 和 Claude 3.5 Sonnet 在工具调用上的稳定性明显好于小模型。如果成本敏感,至少用 7B 以上且专门做过 Function Calling 微调的模型。
4.2 循环调用同一个工具:模型“卡住了”
有时候模型会反复调用同一个工具,比如一直搜索同一个关键词。原因通常是工具返回的结果没有提供足够的新信息,模型不知道下一步该干什么。
解决办法有两个。一是在工具返回结果里加一个提示,比如“如果以上结果不包含所需信息,请尝试更换关键词或使用其他工具”。二是在系统提示里加约束:“同一个工具连续调用不要超过2次,如果2次都没有得到有用信息,请换一种方式或直接告知用户无法完成”。
4.3 上下文爆炸:Token 消耗失控
多轮 ReAct 循环会让上下文迅速膨胀。一次搜索返回 2000 字,调三次就 6000 字,再加上系统提示和历史对话,很容易超过模型窗口。
我的做法是:工具返回结果先做一次摘要再塞回上下文。比如搜索结果只保留标题和前 200 字摘要,完整内容存到外部文件,需要时再按需读取。这样能把单次工具返回的 Token 消耗降低 70% 以上。
另外,设置合理的max_iterations和单次工具返回长度上限也很重要。我一般限制工具返回不超过 1500 字,超过就截断并提示模型“结果已截断,如需完整内容请指定更精确的查询”。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决思路 |
|---|---|---|---|
| JSON 解析失败 | 模型输出格式不规范 | 打印原始返回内容 | 加格式约束提示词,换结构化输出模型 |
| 工具调用参数错误 | 工具描述不清晰 | 检查 Schema 和 description | 细化参数说明,加示例 |
| 循环调用同一工具 | 结果无新信息 | 查看工具返回内容 | 加调用次数限制,优化返回摘要 |
| 上下文超长 | 工具返回内容过多 | 统计 Token 消耗 | 摘要后再入上下文,设长度上限 |
| 任务未完成就退出 | 模型误判完成 | 检查最终输出 | 在提示词中明确完成条件 |
| 响应延迟高 | 迭代次数过多 | 记录每轮耗时 | 减少迭代,并行化独立步骤 |
4.5 几个让我少走弯路的实操心得
第一,先写测试用例再写 Agent。把典型输入和期望输出列出来,每次改完提示词或工具定义就跑一遍。没有测试用例的 Agent 开发就是盲人摸象。
第二,日志要打全。每一轮的模型输入、输出、工具调用参数、工具返回结果都要记录。出问题的时候,回看日志比瞎猜快十倍。
第三,工具宁少勿多。一开始只给 3 到 5 个核心工具,跑通了再逐步加。工具越多,模型选错的概率越大。
第四,系统提示词要短而精。我见过有人写 2000 字的系统提示,结果模型把关键约束都忘了。核心约束放在最前面和最后面,中间放示例。
第五,别追求一次完美。Agent 开发是迭代过程,第一版能跑通主流程就行,边界情况慢慢补。我第一个 Agent 只能处理三种固定问法,但跑通之后扩展起来就快了。
5. 进阶方向:从能跑到好用
5.1 加入人工确认环节
完全自主的 Agent 在生产环境里风险很高。我的做法是在关键操作前加人工确认。比如 Agent 要发邮件、改数据库、执行支付,先弹一个确认框让用户点“同意”再继续。这样既保留了自动化效率,又避免了误操作。
实现上就是在 Workflow 里插入一个human_approval节点,Agent 执行到这个节点时暂停,把待确认内容展示给用户,用户确认后继续,拒绝则走回退分支。
5.2 多 Agent 协作
复杂任务可以拆给多个专职 Agent。比如一个“电商运营 Agent”可以拆成:选品 Agent、定价 Agent、文案 Agent、客服 Agent。每个 Agent 有自己的工具集和提示词,通过一个协调器来调度。
这种架构的优点是每个 Agent 可以独立优化,缺点是通信成本高,而且协调器本身可能成为瓶颈。我的建议是:只有当单个 Agent 的工具超过 10 个、提示词超过 1500 字时,才考虑拆分。否则一个 Agent 加清晰的 Workflow 编排就够了。
5.3 评估与监控
Agent 上线只是开始,持续监控才是关键。需要跟踪的指标包括:任务完成率、平均迭代次数、工具调用成功率、Token 消耗、响应延迟。
我一般会做一个简单的 Dashboard,每天看一遍。如果发现完成率下降,就去查日志看是哪类任务出了问题。如果 Token 消耗突然上升,通常是某个工具返回内容变长了,或者模型开始频繁重试。
5.4 关于框架选择的个人看法
LangChain 生态最全但抽象层太厚,出问题不好排查。LlamaIndex 在 RAG 场景下很好用,但 Agent 编排能力偏弱。CrewAI 的多 Agent 协作设计很优雅,但定制化空间有限。Spring AI 适合 Java 技术栈的团队,跟 Spring Cloud 集成很顺。
我的选择是:核心逻辑手写,辅助功能用库。比如工具调用的解析和重试逻辑自己写,向量检索用 LlamaIndex,Web 服务用 FastAPI。这样既保持了可控性,又不用重复造轮子。
6. 一个完整的最小可运行示例
6.1 项目结构
mini-agent/ ├── main.py # 入口 ├── agent.py # ReAct 循环 ├── tools.py # 工具定义与实现 ├── memory.py # 记忆管理 └── config.py # 配置6.2 核心代码串联
config.py放模型配置和 API Key:
MODEL = "gpt-4o" MAX_ITERATIONS = 10 MAX_TOOL_RESULT_LENGTH = 1500tools.py定义工具:
def search_web(query, max_results=5): # 实际实现调用搜索 API results = do_search(query, max_results) # 截断过长内容 return truncate(results, MAX_TOOL_RESULT_LENGTH) def run_code(code): # 在沙箱中执行代码 return execute_in_sandbox(code)agent.py是核心循环,就是前面 3.3 节的代码加上日志和错误处理。
main.py启动:
if __name__ == "__main__": while True: user_input = input("你: ") if user_input == "exit": break result = run_agent(user_input) print(f"Agent: {result}")这个最小版本大概 200 行代码,能处理“搜索信息并总结”这类任务。跑通之后,你可以逐步加工具、加记忆、加 Workflow 分支,慢慢扩展成完整的系统。
6.3 测试与验证
跑通之后先做三组测试。第一组是正常任务,比如“搜索今天 AI 领域的重要新闻并总结”。第二组是边界情况,比如“搜索一个不存在的公司”。第三组是异常情况,比如故意让搜索 API 返回错误,看 Agent 能不能优雅处理。
每组测试记录:是否完成任务、迭代了几轮、消耗多少 Token、有没有报错。这些数据是你后续优化的基准。
7. 关于成本与性能的平衡
7.1 Token 消耗的主要来源
一个典型的多轮 Agent 任务,Token 消耗分布大概是:系统提示词占 10%,用户输入占 5%,模型思考输出占 20%,工具调用参数占 10%,工具返回结果占 55%。可以看到,工具返回结果是最大的消耗源。
优化方向很明确:压缩工具返回内容。搜索类工具只返回摘要,代码执行类工具只返回关键输出,数据库查询类工具限制返回行数。我一般会把工具返回控制在 500 到 1500 字之间,具体看任务复杂度。
7.2 模型选择的性价比考量
不是所有环节都需要用最贵的模型。我的做法是分层:规划任务分解用强模型,执行具体工具调用用中等模型,结果摘要用便宜模型。这样整体成本能降 40% 到 60%,而效果下降不明显。
具体来说,任务规划用 GPT-4o 或 Claude 3.5 Sonnet,工具调用用 GPT-4o-mini 或 Claude 3 Haiku,摘要用更便宜的模型。当然这需要你的 Workflow 支持多模型切换,代码编排模式下很容易实现。
7.3 缓存策略
很多 Agent 任务有重复性。比如每天早上查同样的数据源、生成同样格式的报告。这种场景可以加缓存:把工具调用参数做哈希,如果相同参数在有效期内调用过,直接返回缓存结果。
我实测下来,在日报生成场景下,缓存能减少 60% 以上的工具调用次数,Token 消耗直接砍半。缓存有效期根据数据更新频率来定,新闻类设 1 小时,统计数据设 1 天。
8. 我踩过的三个印象最深的坑
8.1 工具描述里的一个错别字导致整个流程崩溃
有一次我写了一个send_email工具,描述里把“收件人”写成了“收件入”。结果模型在调用时死活传不对参数,因为它从描述里学到的就是错的。排查了两个小时才发现是描述里的错别字。从那以后,我每次改完工具定义都会让另一个同事帮忙读一遍。
8.2 忘记设最大迭代次数导致账单暴涨
早期做的一个 Agent 没设迭代上限,结果遇到一个模型无法处理的任务,它连续调用了 200 多次搜索工具,一晚上烧掉了几十美元。第二天看到账单的时候心都在滴血。现在我的所有 Agent 都强制设max_iterations,默认 10,最多不超过 20。
8.3 上下文截断把关键信息截掉了
为了控制 Token,我一开始简单粗暴地按消息数量截断,保留最近 10 条。结果有一次 Agent 在第 3 轮拿到了关键数据,到第 12 轮需要用时已经被截掉了,导致任务失败。后来改成按 Token 数动态截断,并且优先保留包含工具调用结果的消息,问题才解决。
9. 后续可以继续深挖的方向
如果你已经跑通了一个基础 Agent,接下来可以往这几个方向深入。
评估体系:建立自动化的评估流水线,每次修改提示词或工具定义后自动跑测试集,量化对比效果变化。这是从“能跑”到“可靠”的关键一步。
多模态能力:让 Agent 能处理图片、PDF、表格等非文本输入。很多真实业务场景的数据不是纯文本,比如发票识别、图表分析。
领域知识注入:通过 RAG 或微调把行业知识灌进 Agent。通用模型在专业领域往往不够准,注入领域知识后效果提升很明显。LLM Wiki这类结构化知识库在这方面有天然优势。
安全与权限:给 Agent 加上操作权限控制,不同用户能调用的工具不同,敏感操作需要审批。这在企业环境里是必须的。
可观测性:接入 LangSmith 或自己搭一套追踪系统,把每一轮的输入输出、耗时、成本都可视化。出了问题能快速定位,优化也有数据支撑。
Agent 这个方向变化很快,每隔几个月就有新工具和新模式出来。但底层逻辑是不变的:清晰的工具定义、可控的编排流程、合理的记忆管理、持续的评估优化。把这四件事做好,不管用什么框架都能搭出好用的 Agent。