☰
保姆级教程:用LangGraph从零实现能自主查资料写报告的AI Agent
2026/10/20 5:44:50 网站建设 项目流程

这两年只要跟AI Agent沾边的需求,基本没有冷下来的时候。我见过不少同学,手上模型API调得飞起,一聊到Agent就卡壳——不是不知道概念,是不知道怎么把“让模型自己决定下一步做什么”这回事落到代码上。市面上的教程要么纯理论讲概念,要么丢一坨封装好的框架代码让你复制粘贴,跑完还是不懂为什么。这篇保姆级教程,我打算换一种写法:理论只讲够用的部分,剩下全部围绕一个能跑通的最小Agent实例展开,从状态怎么定义、工具怎么注册,到循环怎么控制、参数怎么调,每一行代码都说明它解决什么问题。不管你是刚入门想动手,还是已经在项目里踩过坑,应该都能从里面找到点有用的东西。

先说清楚一个很容易被忽略的事实:Agent开发的核心难点,从来不是“调用大模型API”,而是“让模型在一个可控的循环里,自主判断该调用哪个工具、如何根据结果继续推进”。这个循环一旦设计不好,写出来的东西要么是只会单轮对话的聊天机器人,要么是跑着跑着就死循环烧光token的失控程序。这篇文章要做的,就是带着你把这条链路完整走一遍。

1. 先想清楚:你写的到底是“聊天机器人”还是Agent?

1.1 没有工具调用的Agent都是“纸上谈兵”

很多人有一个误解:只要接入了大模型,这个东西就能叫Agent。实际上,你写一个prompt让模型输出一段文案,那是聊天机器人;你让模型根据用户需求自动判断需要查天气、订会议室、翻文档库,然后调用对应工具、汇总结果回复用户,这才是Agent。

两者的本质区别在于:聊天机器人是“一次问答”,Agent是“多轮决策循环”。在这个循环里,模型不再是单纯地生成文本,而是要像人一样思考:我现在需要哪些信息?我有哪些工具可以用?用完之后结果是否满足需求?如果不够,下一步该做什么?这个模式在学术界有个名字叫ReAct,也就是把推理(Reasoning)和行动(Acting)交织在一起。你可以把它理解成:模型一边想“我要做什么”,一边动手去查、去调,每做完一步再重新判断一次,直到任务完成。

所以,如果你设计的Agent没有工具调用能力,或者没有循环处理机制,本质上还是在一个“高级聊天框”里打转。真正的Agent开发,第一步就是要跳出“提问-回答”的线性思维,进入“思考-行动-观察-再思考”的循环架构。

1.2 三种主流Agent架构,选型先看场景

在动手写代码之前,有必要先了解目前主流的几种Agent架构模式。因为不同场景下,适合的架构完全不同。

第一种也是最常见的,就是上面说的ReAct循环架构。模型每轮迭代先输出一个想法(Thought),然后决定调用哪个工具(Action),工具返回结果后模型再观察(Observation),继续下一轮。这种架构适合任务链路较长、需要实时获取外部信息的场景。比如“帮我查一下最近三个月某行业动态并整理成报告”,这类任务没有固定步骤,模型需要动态判断查哪些关键词、哪些信息够了、哪些还得继续追。

第二种是计划-执行架构,英文叫Plan-and-Execute。这个模式的特点是:先让模型把任务拆解成一系列子任务,形成一份计划,然后按顺序执行。它跟ReAct的区别在于,ReAct是走一步看一步,计划-执行是先整体规划再逐步落实。适合任务流程相对固定、步骤清晰的场景。比如“每天早上帮我汇总某几个网站的更新内容”,模型可以先规划出“抓取网站A数据-抓取网站B数据-合并去重-生成摘要”,然后依次执行。

第三种是多Agent协作架构。把大任务拆给多个侧重点不同的子Agent,每个子Agent职责单一,它们之间通过消息传递来协作。适合复杂业务系统,比如一个Agent负责理解用户需求,一个Agent负责检索资料,一个Agent负责生成内容,最后再汇总。这种架构的优点是每个模块职责清晰、便于独立迭代,缺点也很明显——调试难度成倍增加,消息传递之间的状态管理复杂度很高。

对于一个刚开始学Agent开发的人来说,我的建议是:不要一上来就追多Agent架构,先把ReAct吃透。ReAct是基础中的基础,后面两种架构很多时候都是在ReAct的基础上做编排和封装。

1.3 保姆级教程的定位:从一个最小可用的Agent开始

我这篇教程的定位很明确:不贪多、不求大,围绕一个“查资料写报告”的典型场景,搭一个最小可用的ReAct Agent。为什么选这个场景?因为在这个场景里,Agent必须要调用外部工具才有意义。单纯的模型回答可能信息滞后,甚至凭空编造数据,而挂上搜索工具和网页内容解析工具之后,它能“真刀真枪”地获取信息再生成答案。这样你就能清晰地感受到:什么是工具调用、什么是循环决策、什么是基于观察结果修正下一步行动。

整个过程我会用Python实现,代码量控制在两百行左右。我尽量做到:每段代码都能解释清楚为什么这样写,每个抽象概念都能落到一处具体实现上。你不需要有很深的技术背景,只要能读懂基础的Python语法、了解函数和字典是什么,就可以跟着一步步来。

2. 技术选型:为什么我推荐用LangGraph搭Agent骨架

2.1 手写循环、LangChain AgentExecutor、LangGraph三选一

实现Agent的技术路线有好几条,我直接把我试过的三条路对比一下,你就知道为什么最终选了LangGraph。

第一条路是纯手写循环。思路很简单:自己写一个while循环,把“调用模型->解析输出->执行工具->拼接结果->再调用模型”这个流程串起来。优点是完全可控、没有黑盒,出问题能直接看到底是哪一步挂的。缺点是你要自己处理的东西太多了:模型返回的格式解析、工具执行结果怎么拼进上下文、最大迭代次数怎么控制、出错是重试还是退出……这些细节写起来不难,但非常琐碎,容易在边界条件上踩坑。对于学习入门,我反倒鼓励你自己手写一次这个循环,体验一次之后再上框架,你会对框架的设计思路理解深很多。

第二条路是直接用LangChain早期版本的AgentExecutor。它帮我们把循环封装好了,调用起来很方便,但问题在于内部逻辑比较黑盒,而且状态管理能力很弱。你想往里面加一个“遇到某类错误自动重试”或者“记录每一步中间过程”的逻辑,往往得靠hack或绕路,不太顺畅。

第三条路就是LangGraph。它的核心抽象是“图”:把Agent的各个处理环节定义为节点,节点之间用边连接,通过控制边的走向来实现循环和条件分支。本质上,它的设计比AgentExecutor更高一层——AgentExecutor把逻辑写死成了“工具调用循环”,而LangGraph让你可以自由地定义“什么时候该调用工具、什么时候该结束”。这非常符合Agent开发的实际需求,因为Agent的流程本来就不是线性的,它需要的是可配置的状态机。

我现在的建议是:学习期可以手写一次循环,生产项目直接用LangGraph。这篇教程就以LangGraph为主线,因为它能让你把注意力集中在“Agent的策略设计”上,而不是被“如何实现循环”这种基础设施问题分心。

2.2 环境准备与依赖安装

环境部分很简单,我默认你已经有Python环境,版本建议3.9以上。需要安装的库主要有这么几个:langgraph(图编排框架)、langchain-core(工具定义和消息模型的基础库)、langchain-openai(连接大模型API的适配器,这里用OpenAI协议是因为大多数兼容接口都走这个协议,厂商可以替换)。

安装命令直接抄这段:

pip install langgraph langchain-core langchain-openai

装完之后建议顺手装一个dotenv用来管理密钥,代码里不要硬编码任何密钥,这是基本素养:

pip install python-dotenv

然后在你的项目根目录建一个.env文件,把模型的密钥和接口地址填进去。注意,这里有个关键配置项:如果你用的不是官网直连,而是某个中转网关或者云服务商的兼容接口,那么base_url一定要填对。我见过太多人代码写得没问题,最后卡在base_url配错上,报一个莫名其妙的401或者404。

LLM_API_KEY=你的密钥 LLM_BASE_URL=https://你的接口地址 MODEL_NAME=你的模型标识

注意:不同模型的上下文长度和支持的工具调用格式不同。建议动手前先确认你选的模型支持function calling特性,否则后面工具调用的逻辑跑不起来。

2.3 项目目录结构设计

代码别一上来就堆一个文件里,后面你加工具、加状态、做持久化的时候会想哭。我习惯用这样一个目录结构:

agent_demo/ ├── .env # 环境变量,不入库 ├── tools/ │ └── search.py # 工具定义:搜索、网页内容解析 ├── agent/ │ ├── state.py # Agent状态定义 │ ├── nodes.py # 各处理节点的实现 │ └── graph.py # 图编排,把节点拼起来 └── main.py # 入口,跑一个对话Demo

这个结构看起来很常规,但它把不同的关注点分开了:state管“数据长什么样”,nodes管“每一步做什么”,graph管“这些步骤怎么串”,main管“用户怎么交互”。分工清楚之后,你后面做调试、加功能,都会很顺手。别嫌麻烦,Agent项目的复杂度增长非常快,前期多分一个文件,后期少掉一把头发。

3. 从零实现一个能“查资料写报告”的Agent

3.1 定义状态:Agent的“记忆骨架”

Agent和普通函数的本质区别在于,它是一个有状态的循环。每一次迭代,它都需要知道:目前已经收集了哪些信息、还没有解决什么问题、消息历史上已经有哪些对话。在LangGraph里,这些数据都放在“状态”对象里,每个节点都可以读取和修改状态内容。

我常用的最小状态设计只有两个字段:一个放消息列表,一个放中间过程记录。为什么不直接放一个自定义变量保存结果?因为LangGraph的状态机制要求你明确定义字段类型和变更方式,这样它才能帮你处理并行、分支等复杂流转。如果你只用一个全局变量,循环一多根本管不住。

直接看一下代码,这是agent/state.py的完整内容:

# agent/state.py from typing import Annotated, TypedDict from langgraph.graph.message import add_messages class AgentState(TypedDict): # 消息历史,用 add_messages 注解表示新消息会追加到列表中 messages: Annotated[list, add_messages] # 存放 Agent 每一步中间决策过程的日志,便于排查 trace: list[str]

这里最需要理解的是Annotated[list, add_messages]这个写法。它的意思是:每当一个节点返回包含messages字段的更新时,旧消息不会被覆盖,而是自动追加进去。如果你不加这个注解,默认行为是整体覆盖,那样你的Agent只会记住最近一轮的状态,相当于失忆了。这个点是LangGraph新手最容易踩的坑,我第一次用的时候就因为忘了加注解,调试了半天才发现Agent把前面的聊天内容全忘了。

trace字段我用来记录模型的每一步思考过程。为什么要单独存它?因为大模型API返回的内容里,通常包含推理过程、调用工具请求、最终回答等多个部分。如果你只把最终回答存进消息列表,后面出了问题想排查,回溯的时候什么都看不到。把中间过程单独存一份,等于给Agent装了个行车记录仪。

3.2 让模型学会使用工具:注册搜索与网页解析工具

Agent能不能干活,很大程度上取决于你给了它什么工具。这里我定义两个非常实用的工具:一个是关键词搜索,负责从搜索引擎拿搜索结果列表;一个是网页正文解析,负责打开指定的URL,提取主要内容。这两个工具配合起来,Agent就具备了“自己去找资料、自己读资料”的能力。

先定义工具函数,用@tool装饰器标记一下:

# tools/search.py import requests from langchain_core.tools import tool @tool def web_search(query: str) -> str: """根据关键词搜索网页,返回前5条搜索结果标题和链接。""" # 这里以某个通用搜索接口为例,实际使用时换成你自己申请的搜索服务即可 params = {"q": query, "num": 5} resp = requests.get("https://your-search-provider.example/api", params=params, timeout=10) results = resp.json().get("items", []) return "\n".join(f"{i+1}. {item['title']} - {item['link']}" for i, item in enumerate(results)) @tool def fetch_page_content(url: str) -> str: """打开指定URL,提取网页正文前2000个字符。""" resp = requests.get(url, timeout=10) text = resp.text # 实际项目里推荐用 BeautifulSoup 做正文提取,这里简化处理 return text[:2000]

工具定义的重点其实不在函数实现,而在那段文档字符串。别小看它,工具描述是大模型判断“什么时候该用这个工具、参数怎么填”的唯一依据。我见过很典型的反面教材:有人把工具描述写得极度简单,比如只写一句“搜索”,结果模型在需要搜索时不知道这个工具是干嘛的,或者压根不调用;还有人在描述里不写清楚参数格式,模型传参时乱填。写工具描述的原则是:说清楚工具能干什么、输入参数是什么格式、输出大概长什么样。

这也是后面我们要在系统提示词里反复强调工具清单的原因。LangGraph会在每次循环时把这些工具的名字和描述注入到模型请求里,模型是根据这个信息来决定调用哪个工具的。

3.3 Agent主循环:ReAct模式的完整落地

节点定义好之后,下一步就是把它们串成一个图。先定义三个核心节点:调用模型的节点、执行工具的节点、判断是否需要继续的节点。

下面这段是整个Agent的核心,也是建议你仔细看的重点:

# agent/nodes.py import json from langchain_openai import ChatOpenAI from langgraph.prebuilt import ToolNode # 初始化模型,关闭流式输出以便稳定获取结构化返回 llm = ChatOpenAI( model="你的模型标识", temperature=0.3, streaming=False, ) def call_model(state: AgentState): """调用大模型,模型可能返回直接回答,也可能要求调用工具。""" # 系统提示词:明确告诉模型它有工具可用,以及什么情况下必须用工具 system_prompt = """你是一个只能通过工具获取实时信息的智能助理。 用户提出的问题,如果涉及事实信息,你必须先调用工具查询,不要凭空编造。 当信息不足时,可以多次调用工具,直到信息足够再回答。 回答时用简洁的中文,并标注信息来源。""" response = llm.invoke([ {"role": "system", "content": system_prompt}, *state["messages"] ]) # 把模型的思考过程追加到 trace,方便回溯 trace = state.get("trace", []) trace.append(f"模型输出: {response.content}") return { "messages": [response], # 追加模型输出到消息历史 "trace": trace, } def execute_tools(state: AgentState): """从模型返回结果中解析工具调用,并执行对应工具。""" last_message = state["messages"][-1] # 解析工具调用请求,ToolNode 可以直接处理标准格式 tool_node = ToolNode([web_search, fetch_page_content]) result = tool_node.invoke(last_message) trace = state.get("trace", []) trace.append(f"工具执行结果: {result}") # 将工具结果追加到消息历史 return { "messages": result, "trace": trace, } def should_continue(state: AgentState): """判断是否还需要继续循环:有工具调用就继续,否则结束。""" last_message = state["messages"][-1] # 如果消息里带着工具调用请求,说明还需要执行工具,继续循环 if last_message.tool_calls: return "continue" return "end"

这里有几个关键点要展开讲。

system_prompt的作用比大多数初学者以为的重要得多。有些教程只是简单写一句“你是一个助手”,这种提示词用在Agent里基本等于没有。你需要明确告诉模型:什么时候必须用工具、用工具的边界是什么、信息不足时如何处理。质量高一点的提示词能大大减少模型瞎编的概率。

ToolNode是LangGraph预置的节点,它会自动检查最后一条消息里有没有工具调用请求,如果有就执行对应工具,并返回一个包含工具执行结果的消息对象。这样就不用自己解析tool_calls字段了,省掉很多重复代码。但你要理解它在背后做的事情:它本质上就是一个“读消息-解析请求-执行工具-返回结果”的节点。

最后是组装图。agent/graph.py的代码很简短,却是整个Agent的骨架:

# agent/graph.py from langgraph.graph import StateGraph, END from agent.state import AgentState from agent.nodes import call_model, execute_tools, should_continue def build_agent(): graph = StateGraph(AgentState) # 注册节点 graph.add_node("model", call_model) graph.add_node("tools", execute_tools) # 设置入口节点 graph.set_entry_point("model") # 设置流转条件 graph.add_conditional_edges( "model", should_continue, { "continue": "tools", # 有工具调用 -> 去执行工具 "end": END, # 没有 -> 直接结束 } ) graph.add_edge("tools", "model") # 工具执行完回到模型,进入下一轮循环 return graph.compile()

流转关系其实就一个循环:模型节点 -> 判断是否有工具调用 -> 有就去执行工具 -> 工具结果送回模型 -> 再判断……直到模型觉得信息够了,直接给出最终答案。

这个设计好在哪里?好处是你可以非常直观地看到Agent的“思考过程”和“行动过程”是分离的。模型节点负责思考,工具节点负责行动,判断节点负责决定是否继续。以后你想加一个“自动查资料后必须总结摘要再回答”的逻辑,只需要在工具节点后面插入一个摘要节点,不需要动其他任何地方。

3.4 跑通第一个Demo:看Agent如何自主完成一个调研任务

代码都写完之后,跑一个具体任务来验证一下。入口文件main.py长这样:

# main.py from agent.graph import build_agent def main(): agent = build_agent() user_input = "帮我调研一下当前主流的AI代理框架有哪些,各自的核心特点是什么,整理成一份简要报告" result = agent.invoke({"messages": [{"role": "user", "content": user_input}], "trace": []}) print("\n=== Agent执行过程追踪 ===") for step in result.get("trace", []): print(step) print("\n=== 最终回答 ===") print(result["messages"][-1].content) if __name__ == "__main__": main()

你跑起来之后会看到类似这样的过程:模型先输出“我需要搜索一下当前主流的AI代理框架”,接着调用web_search工具,拿到搜索结果;然后模型分析搜索结果,发现信息还不够具体,于是再调用fetch_page_content工具,打开几个页面;等关键信息收集得差不多了,模型才生成最终报告。

注意看trace日志,这一步非常有用。你能直观地看到Agent每个决策背后的依据,这也是我坚持要单独存trace的原因。如果没有这个字段,你永远不知道模型走到哪一步、为什么半天没出结果。

4. 参数调优与常见问题排查

4.1 影响Agent“智商”的5个参数

Agent代码跑通只是第一步,能不能稳定高效地干活,还得看参数怎么调。我把实际项目中影响最大的几个参数列出来,每个都说一下我的经验和建议。

第一个是temperature。这个参数控制回答的随机性。在Agent场景里,我强烈建议调低,一般0.2到0.4之间。为什么?因为Agent需要的是稳定的工具调用决策,而不是发散性创意。你把温度调到0.8以上,模型就可能在同一个问题上反复横跳,这次决定调用工具,下次决定不调用,输出极不稳定。

第二个是max_iterations概念。在LangGraph里它不是显式参数,但你自己心里要有一条“最大循环次数”的线。我最常用的做法是在should_continue函数里加一个计数器,超过10轮就直接强制结束。否则遇到那种永远觉得自己信息不够的Agent,它会不断调工具,把对话上下文撑爆,token烧得你心慌。

第三个是模型上下文长度。工具调用会消耗大量token,因为每轮循环都要把之前所有消息重新发给模型。如果你用的是较小上下文窗口的模型,跑超过5轮之后就可能提示超出上限。解决思路有两个:要么换更长上下文的模型,要么在状态里维护一个滑动窗口,只保留最近几轮的消息。这个我在后面落地部分会细说。

第四个是工具描述里的细节量。前面讲过,工具描述直接影响模型调用的准确率。我建议在描述里补充“什么时候不要用这个工具”的反向说明。比如搜索工具里写“当用户只是闲聊或询问观点时,请不要使用此工具”,这样能大幅减少模型的误调用。

第五个是system prompt里的“失败动作”定义。你要告诉模型:如果工具调用报错,是重新试一次,还是换个工具,还是直接告诉用户失败?没有这个预设,模型可能卡在一个报错循环里无法自拔。我见过最极端的案例,模型在一个404的网页上连续调了8次工具,就是因为我没告诉它“打不开就换一个链接”。

4.2 运行Agent最常见的4个报错

写代码必踩坑,我来总结几个我在实际运行Agent时遇到最多的报错,顺带附上排查思路。

第一个是“工具调用格式错误”。如果你看到模型返回里出现了tool_calls字段为空,但模型文本中又写了“我要用搜索工具”之类的话,大概率是模型没有遵循工具调用协议。排查方向:确认模型是否支持function calling、确认工具描述是否足够清晰、确认模型版本是否太老。有时候换个更新的模型版本,问题立刻消失。

第二个是“工具执行超时”。网络请求是Agent链路里最不稳定的环节。处理办法很简单:给每个工具函数设置timeout参数,一般8到10秒比较合理。同时建议在工具内部加try...except,遇到异常时返回一个错误信息字符串,而不是让异常直接打断整个流程。模型读到错误信息后还能自行判断是重试还是换方案,这样Agent就具备了一定的容错能力。

第三个是“上下文超长”。截图里经常出现的maximum context length exceeded就是这个问题。工具调用会产生大量中间输出,尤其是抓网页的时候,一整页HTML可能就有几万token。我的经验是:工具返回的内容一定要截断,比如网页正文最多返回2000到3000字符,搜索列表最多返回5到10条。宁可信息少一点,也不要让模型被冗余内容干扰、导致上下文爆掉。

第四个是“循环不收敛”。Agent一直在调用工具,就是不输出最终答案。这不一定是bug,更可能是你的判断逻辑有问题。排查思路:先把trace完整打印出来,看每轮模型到底在纠结什么。通常原因无非两种:模型觉得信息还不够、或者工具结果没有满足它的需求。针对前者,在system prompt里强调“信息足够即可回答,不要追求完美”;针对后者,检查工具返回结果的质量,看看是不是搜索接口返回的内容太稀疏,模型没法用。

4.3 一套我能直接用的“避坑清单”

这几个问题我单独整理成一张速查表,建议你收藏下来,真出问题了直接对着找原因。

现象可能原因解决方案
模型不调用工具模型不支持function calling,或工具描述不清晰换支持工具调用的模型,重写工具描述
模型乱传参数工具描述没有说明参数格式在描述中补充参数类型和示例
工具执行报错中断没有捕获异常工具内部包try,异常时返回错误文本
Agent死循环不结束缺少最大迭代控制在循环判断中增加次数限制
上下文超长工具返回内容过大截断工具输出,压缩网页正文
最终回答有幻觉模型信息不足就急着回答系统提示词中强调先查询再回答

提示:模板里有一类定位很通用的问题,就是“模型信息不足就急着回答”。这个问题在真实场景中出现频率极高,解决手段就是那句我很常说的提示词:“如果不确定,请明确告诉用户你还缺什么信息,而不是编造一个数字。”这一句话能省掉很多后期校对时间。

5. 从“能跑”到“能交付”:Agent落地的最后一公里

5.1 会话持久化:让它记住用户是谁

很多人把Demo跑通之后就以为完了,其实从“能跑”到“能交付”,中间还有大量工作需要补。第一个要补的就是会话持久化。

在我上面写的最小Demo里,用户每次发起请求,messages状态都是在内存里新建的,也就是说Agent不记得上一轮对话。这在真实项目里是不可接受的。用户肯定希望Agent记得他上一句说了什么,甚至记得他三天前的偏好。

LangGraph官方给了一个方案叫checkpointer,它可以把状态快照持久化到存储后端。你可以先简单地持久化到本地SQLite,后面再换到Redis或数据库,对应接口基本不变。加上持久化之后,Agent的“记忆”就能跨会话存在,不再是一句问完就没。

这个环节容易被忽略,但它恰恰是Agent产品能不能让用户感到“智能”的关键。记忆不只是保存聊天记录,还包括保存Agent中间推理状态。用户中途打断、切换话题、重试某一步,都需要系统能从正确的状态继续。持久化做得好的Agent,使用体验会上一个明显台阶。

5.2 可观测性:看得到Agent在“思考”什么

实话说,Agent应用和传统后端应用最大的不同,就是它的内部行为不可控性很高。传统接口调用是确定的:输入什么参数,返回什么结果。Agent不一样,同样的用户输入,模型可能这次调工具,下次不调;这次走三步结束,下次走十步还没完。在这种情况下,如果你没有足够强的可观测性,排查问题的成本会高到离谱。

建议在还清阶段就养成往trace里写日志的习惯。我上面代码里已经有了一个雏形,但你还可以做得更细:给每轮循环编号、记录工具名称和入参、记录工具返回的单向哈希值、记录每轮耗时。这些信息对于一个线上Agent的故障排查、效果归因、成本分析都非常有价值。

更进一步,你可以把trace里的关键事件同步到日志系统。在Agent运行出问题的时候,你不需要让用户复现操作过程,直接搜日志就能定位是哪一步决策出了问题。这个习惯越早养成越好,项目规模一大,你会深刻体会到“可观测性就是生产力”这句话。

5.3 成本与并发优化:别让Agent烧光你的Token预算

最后说一个大家很容易忽略但迟早要面对的问题:成本。

Agent调用模型的token消耗远超普通聊天应用。原因在于,每轮工具循环都要把全部历史消息重新发给模型,跑一个复杂任务可能相当于普通对话十几次的消耗。我自己见过一个反面的案例,一个Agent单次任务烧掉了几十万token,最后产出的报告质量还很一般。听完那个数字,你就会明白成本控制有多重要。

成本优化有几个立竿见影的做法。第一,压缩工具返回内容,能不抓全文就不抓全文,优先返回摘要。第二,控制循环次数,超过8轮还搞不定的任务直接提示用户“任务过于复杂,需要简化描述”。第三,消息历史做滑动窗口,只保留最近4到6轮的对话,把老消息的完整细节落地到结构化存储里。第四,模型选型上区分“指挥模型”和“干活模型”:负责决策的模型用能力最强、也最贵的那款;负责简单文本处理的任务,换成便宜的小模型就够了。

并发优化同样是重点。Agent的循环特性决定了它的单次任务耗时可能长达几十秒,这时候同步调用接口肯定扛不住高并发。生产级方案是把任务提交到异步队列,Agent跑任务的过程通过轮询或回调来更新进度,这样用户侧体验更好,服务端资源利用率也更高。

写在最后的几句实在话

如果你完整地跟着搭完这个Agent,你会发现真正花时间的不是写代码,而是调提示词和调工具。我到现在做Agent项目,超过一半的时间还是花在跟模型“讲清楚什么时候该干什么”上。这是Agent开发跟传统软件开发很不一样的地方——你面对的是一个概率系统,你的每一句描述、每一个例子、每一条工具说明,都会影响模型的表现质量。

最后分享一个小习惯:每次修改完提示词或工具描述,我会跑同一组测试用例,然后把结果记录下来。没有这组对比数据,你根本分不清这次调整到底让Agent变好了还是变差了。Agent开发本质上是一场持续调优的过程,拥有自己的评估用例集,才是项目可持续迭代的基础。希望这篇保姆级教程能帮你少走一些弯路,尽快写出真正能落地生花的Agent应用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询