1. Agent七要素:一个能独立干活,而不是只会聊天的系统
先聊个观察。这一两年我看了很多AI Agent项目,从研究原型到生产系统都有。有一个现象挺明显的:能用LLM做“聊天机器人”的人很多,但能把Agent做成“能独立干活”的系统的人,少很多。差距在哪?不在于会不会调模型,而在于有没有把Agent当成一个系统来设计,而不是当成一次API调用来写。
我自己复盘过好几个Agent项目,包括基于LangGraph写的工作流编排、基于FastAPI + LangChain的服务端实现,以及几个给业务团队用的自动化工具。总结下来,一个真正能跑的Agent,至少要过一遍这七个要素的检查清单,少一个,后面都会以某种形式出问题。
1.1 大脑:大模型内核是决策起点
第一要素,也是大家最熟的:Agent需要一个LLM内核。它负责理解用户的自然语言指令,根据上下文生成行动计划,并对执行结果做解读。这里的关键不是“接一个大模型”,而是“怎么选大模型”,这个问题我放在后面的决策点展开。
先说一个容易踩的坑:很多人一开始就把GPT或Claude挂上去,结果一问复杂问题就崩。原因不是模型不行,而是你还没给模型搭好“思考的环境”。Agent的大模型不是在“生成一句话”,而是在“做决策”——它要先理解用户问题,再回忆任务目标、结合工具返回结果、判断下一步动作。这个链路里,上下文的长短、工具描述是否清晰、系统提示的结构是否合理,都会直接影响模型是否能做出正确决策。
我常跟团队讲一句话:模型是你的员工,你要把你期待的执行逻辑、边界、输出约束都写在员工手册里。员工手册就是System Prompt加上Function Description,这块写不好,后面全盘崩。
1.2 目标与角色:系统提示不是写作文,是定规矩
第二个要素,是目标和角色的定义。很多初级项目不写System Prompt,或者随便写两句“你是一个助手”。这等于招了个能力很强的实习生,但你什么也没交代,他当然天马行空。
真正可复用的Agent,系统提示必须包含四部分:角色画像(你是谁)、任务边界(你负责什么、不负责什么)、执行风格(直接给结果还是逐步分析)、输出格式(JSON还是Markdown)。
我见过一个项目,Agent偶尔会“跑偏”——用户问A,它答B,后来排查发现是系统提示里夹了一大段无关的业务介绍,模型把重心带偏了。把系统提示当作一段“有约束力的工作说明书”来对待,模型的表现会稳定非常多。还有一个实操技巧:系统提示要尽量结构化,用编号、分隔线、块状描述,模型对这种格式的遵循程度远高于散文式提示。
1.3 记忆:上下文窗口不等于记忆
第三个要素是记忆。很多人把记忆等同于“把所有对话都塞进上下文窗口”。这在Demo阶段可行,一旦上生产就完蛋,Token成本、响应延迟、上下文漂移会同时爆发。
Agent的记忆至少分三层:短期记忆(当前任务上下文)、长期记忆(用户偏好、历史结论)、外部记忆(知识库、向量数据库、业务数据库)。工程实现上,短期记忆就是上下文窗口,长期记忆要落到向量库或KV存储,外部记忆则是通过检索工具做实时访问。
我常用的做法是:把Agent的执行过程拆成“任务单元”,每个任务单元只保留命中任务的上下文,结束后把关键结论压缩成摘要写入长期记忆。这个做法能控制上下文膨胀,也能让Agent在多轮任务之间保持一致性。实测下来,同样的任务,用了打包压缩的Agent比纯堆上下文的版本,Token消耗降低了40%左右,而且回答准确率反而更高。
1.4 规划:把大任务拆成可执行动作
第四个要素是规划能力。简单说,Agent要能把“帮我整理一份市场调研报告”拆成“搜索行业数据→筛选平台→整理成PPT结构→输出文档”这样一连串可执行的动作。
规划有一种最朴素也最好用的实现方式,叫ReAct(Reason + Act):让模型先思考(Reason)下一步该干什么,然后再调用工具执行(Act),根据返回结果再次思考,如此循环。这个模式在LangChain和LangGraph里都有现成封装。
但这里有个常见认知误区:不是所有任务都需要“规划”,也不是所有Agent都要“会规划”。简单任务(查天气、算算术)走固定流程就行,甚至不需要AI来规划。规划能力的引入一定要匹配任务的复杂度。给一个“回邮件”的Agent加复杂规划器,纯属给自己找麻烦,徒增延迟和Token消耗。
1.5 手与脚:工具调用是Agent真正“落地”的支点
第五个要素,工具调用。这是Agent从“只说不做”变成“真干活”的核心一环。工具可以是API、数据库查询、代码执行器、文件读写器、浏览器操作等。
工程实现上,工具调用有两种主流方案。一种是LLM的Function Calling原生能力:模型根据用户咨询选择调用哪个函数,并填充参数,应用侧拿到参数发给对应服务。另一种是让模型生成代码再执行(Code Interpreter模式),适合数据分析、图表生成类场景。
我做工具封装时有个习惯:把每个工具的能力描述写清楚,包括输入参数的类型、约束、示例值,以及执行成功返回什么、失败了抛什么错误。这个描述会直接参与模型的推理,写不清楚的后果就是——模型生成一堆无效的调用参数,返回404你都不知道为什么。你可以用“给我一个工具”的视角来理解:新工具不是端上来就行,你得告诉模型这工具怎么用、什么时候用、不能干什么。
1.6 反馈循环:没有执行结果回灌,Agent就是“半瞎”
第六个要素是反馈循环。Agent执行一个动作之后,必须把结果拿回来重新喂给模型,模型基于真实结果判断下一步怎么走。没有这个循环,Agent只能靠“猜”来组织后续动作,那基本上等于让员工闭着眼睛工作。
实现反馈循环的核心是状态管理。以LangGraph为例,它把Agent的执行过程抽象成图:节点是“模型思考”“调用工具”“读取结果”,边是状态转移。每一步执行结果都更新State对象,模型节点读取最新状态继续推理。这个设计保证了Agent的行为是可追踪、可回溯、可回滚的。
这里要提一个实战问题:工具返回的结果通常很乱,有JSON、HTML、错误栈等等。反馈给模型之前,一定要做结果清洗和裁剪。否则模型会被大段的原始HTML干扰,甚至被错误信息带偏,输出创意但无用的内容。我通常在工具层加一个“适配器”,把各种返回统一成简洁的文本结构,提取关键词和结论,再回灌给模型。
1.7 安全与约束:给Agent上护栏,而不是裸奔
第七个要素最容易被忽视:安全与约束。LLM的输出是被概率驱动的,永远有不可控风险。放任Agent去调外部API、操作数据库、自动发消息,一旦出错,后果可能很严重。
约束手段从软到硬排:系统提示规则(最弱)、参数控制(温度调低、输出长度限制)、结构化输出约束(JSON Schema)、工具权限白名单(操作层阻断)、执行审批(关键动作人工确认)。我经历过一次事故:Agent在自动回复邮件时,错把“会话总结”发给了客户。排查发现是我当时图方便,给“发送邮件”工具开了全量权限,没有加入审核环节。后面凡是“高影响动作”(发送、删除、支付),全部改成“需审批模式”,Agent执行到这一步会自动生成请求,等人确认后再真正执行。
安全是整个Agent工程里最容易返工的部分。前面偷懒省一分钟,后面可能要花一周去收拾。
2. 七个决策点:从“能跑”到“能用”的工程分水岭
七要素解决的是“Agent需要有什么”,七个决策点回答的是“你在具体项目里怎么选、怎么做”。我把这些年反复纠结过的问题,收敛成了七个决策点,覆盖架构选型、模型选择、工具设计、并发策略、可观测性等工程维度。
2.1 决策一:工作流、单Agent,还是多Agent编排
第一个决策,也是最根本的架构决策:任务到底应该走固定工作流,还是交给单Agent自由发挥,还是拆给多个Agent协作。
我给出的建议,基于一个非常朴素的判断标准——任务流程是否确定。流程完全确定(比如“读取上传文件→数据清洗→生成报表→邮件推送”)就走工作流,不要用Agent。流程部分确定、部分需要动态决策(比如“分析用户意图→调用合适工具→根据结果调整方案”)就用单Agent。任务本身是解耦的、可以并行处理(比如“同时分析三个竞品→汇总结论”)就考虑多Agent。
很多团队一上来就堆多Agent,结果多个Agent互相踢皮球、上下文互相覆盖、费用飙升。我见过一个系统用5个Agent协作,实际效果还不如一个带5个工具的Agent。多头协作的收益只在任务能真正并行、或者角色差异足够大时才成立。否则,多Agent只是徒增开销和复杂度。
2.2 决策二:模型怎么选——推理型、通用型,还是小模型
模型选型是Agent效果的上限决定因素,分享前面说过的“决策起点”的延伸。
我现在的选择标准很直白:任务的推理复杂度决定模型档位。需要多步规划、复杂工具组合、长文档理解的任务,选推理型强模型(o系列或Claude的思考模式)。常规任务(问答、分类、摘要)用通用模型。高并发、低延迟的场景用小模型或专用模型。
这里想强调一点:不要把“最强模型”当默认答案。实际项目中,我经常给同一个Agent配两个模型:一个负责“规划”(推理型,慢但准),一个负责“执行细节”(通用型,快且便宜)。这个思路能显著降低成本。实测一个客服Agent,采用大模型规划+小模型生成话术的方式,API费用降了约35%,用户基本无感知。
2.3 决策三:工具的粒度怎么定——一个工具还是一组动作
工具设计是Agent工程里最影响体验的部分。工具粒度太粗(例如“处理报告”),模型不知道具体怎么填参;粒度太细(例如“打开文件A”“读取第3行”“写入第5列”),模型走流程容易出错,调用次数暴增。
我的经验是:工具的粒度以“一个业务动作”为单位,而不是一个API接口。比如你要做一个“数据看板”Agent,不应该暴露“postLog”“getLogById”“updateLog”这类接口,应该封装成“analyzeTodayTraffic”或“queryAbnormalLog”。因为Agent需要的不是“操作接口”,而是“达成目标的动作”。LLM理解“分析今天流量异常”的成功率,远高于理解“先getXXX再parseXXX再compareXXX”这种逻辑。
工具描述还有一个细节:写清“什么时候不能用”。比如一个工具只能处理2024年之后的数据,你一定要在描述里写清楚“早于2024年的数据返回错误,请告知用户暂不支持”。这能避免Agent在错误方向上浪费多轮调用。
2.4 决策四:记忆放在哪——上下文窗口还是外部存储
我前面提过记忆分层,这里聊具体的工程决策。
第一优先级是尽量避免“所有东西都塞进上下文”。上下文是Cost(成本)和Noise(噪声),不是“记忆库”。如果你要做多轮对话Agent,方案是用外部存储保存关键信息:Redis存会话状态和临时变量、PG存业务数据、向量库存语义记忆。每次对话开始时,通过检索把“与此轮相关的摘要”注入上下文,而不是把历史全量灌进去。
我自己的一个笔记类Agent,历史会话超过50轮,平均每轮注入的上下文不到3K Token。做法是:每次对话结束时用模型生成一段结构化的摘要(包括提到的实体、偏好、结论),存到JSON里;下一轮对话开始时,先选择最近3次摘要加上向量检索的命中结果一起注入。这样用户换话题后,旧细节虽然不在上下文里,但触达关键的“锚点信息”还在。这个设计跑了一段时间,感觉非常稳定。
2.5 决策五:同步调用还是异步编排——Agent怎么扛并发
顺带把热搜里“AI Agent怎么扛并发”这个苦水一起倒了。Agent和普通API不一样,它的单次请求链路很重:多次LLM推理、工具调用、状态流转,耗时通常在几秒到几分钟。如果每个用户请求都同步阻塞等Agent跑完,后端很快就被拖垮。
我的并发策略分三层。第一层是HTTP接入层用FastAPI的异步协程池(async def),让I/O等待期间不占线程。第二层是把耗时长的Agent任务丢到消息队列(Celery或Redis Queue)里异步执行,前端用WebSocket或轮询拿结果。第三层是LLM调用层做并发限制,避免一个用户把模型QPS打满,影响其他用户。
实测一个并发峰值约2000的客服Agent,采用异步编排之后服务稳得很,单容器QPS上去了,上一版同步阻塞版一压测就502,问题根本不在于模型多快,而在于编排模式。不要把Agent做成“串行阻塞”单体,宁可把它做成“接单 + 干活 + 出结果”三段式。
2.6 决策六:自由度怎么限——给Agent多大操作权限
这直接关系到我前边说的安全要素。决策核心是:Agent能在什么范围内自由决策,在什么边界必须被卡死。
我的默认实践是“宽进严出”:工具调用链路内,让Agent尽量自主,不要每一步都弹确认框,否则体验极差;但凡是涉及“对外部系统产生永久性影响”的动作(发邮件、改数据库、删文件、推送消息、下单支付),一律走审批。
另一个限制维度是参数校验:Agent生成的工具参数,在真正执行前必须过一层Schema校验层。比如Agent调数据库工具,SQL语句必须校验只允许SELECT,不允许DELETE。这一层不能靠模型自觉,要在代码层面硬卡住。AI越强,越要给它清楚划出“可以做”和“不能做”的篱笆。
2.7 决策七:可观测性怎么做——Agent能白盒化吗
最后一个决策点:你如何知道Agent在干什么、为什么这么干。LLM推理本质上是黑盒,但在工程层,你完全可以把Agent的“决策线索”变成白盒。
实现这个目标的方式:全链路追踪。每条Agent执行记录要包含用户原始意图、每一步状态变化、每个工具调用的输入输出、每次LLM推理的输入token、最终响应与耗时。用了LangGraph的话,天然能拿到这些trace数据;用LangChain也支持LangSmith或OpenTelemetry。
我强烈建议从第一天就接上可观测性,而不是出了问题才加。因为Agent的bug很难靠单测覆盖,它的故障分布是“概率性”的,同一句话十次有九次正确、一次错误,只有靠全链路追踪去回放才能定位。没有trace的Agent排查,等于在大海捞针。
3. 工程实现实录:一个能跑通业务的Agent落地过程
理论聊得差不多了,我直接拆一个真正落过地的项目例子。这个项目的需求是“做一个客户反馈的自动分类和跟进Agent”,输入是客户留言,输出是分类标签、紧急度评分、建议回复话术,必要时自动创建工单。
我选了FastAPI + LangChain + LangGraph的组合,重点讲实现的关键路径和代码骨架,你可以直接照着把结构搭起来。
3.1 场景定义与Agent形态选择
第一步,明确问题边界。这个任务的核心是“分类”和“决策”,流程相对固定:读留言→分类→评分→生成回复→(条件)建工单。按我的决策标准,这类“流程确定但内容需要理解”的任务,最适合的不是纯自由Agent,也不是硬编码工作流,而是“动态路由 + 固定节点”的混合模式。
这里最关键的是加分:分类结果不是唯一的动作,还要触发后续分支。用LangGraph的条件边来实现“如果是退款问题就走退款工单节点,如果是技术咨询就走FAQ检索节点”,比在一个Agent里写死逻辑要清晰得多。这个设计的好处是,核心逻辑可以被测试覆盖,LLM只负责“理解内容”,路由和动作由代码控制。
3.2 工具层的设计与落地
这个项目需要两个外部动作:查询历史工单(只读)和创建新工单(写操作)。
按照决策三的粒度原则,我把“查历史工单”封装成工具search_tickets,参数是客户ID和关键词;“创建工单”封装成create_ticket,参数是标题、描述、优先级。每个工具都写了清晰的描述,包括何时使用、参数含义、返回结构。
创建工单这个操作是高影响动作,按“决策六”的原则,我没有让Agent直接写库,而是让它生成一个“工单草稿”,存在待确认区,由后端接口推送人工审核后再创建。这个设计很省钱——不用AI自己背责任,也避免误操作。
# 示例:工具定义的简化结构 tools = [ { "name": "search_tickets", "description": "查询客户的历史工单记录,用于判断是否为重复反馈", "parameters": { "customer_id": "string, 客户唯一标识", "keyword": "string, 工单内容关键词,可为空" } }, { "name": "create_ticket_draft", "description": "创建工单草稿(不直接落库),草稿需人工确认后转为正式工单", "parameters": { "title": "string, 工单标题", "description": "string, 简要描述问题", "priority": "high|medium|low" } } ]3.3 LangGraph的状态图编排
LangGraph的核心概念是“图”。每个节点是一个操作,每条边是状态转移。我的Agent图长这样:
classify_node:调用LLM,输入客户留言,输出分类与紧急度search_node:根据分类决定是否检索历史工单draft_node:生成回复话术和工单草稿end_node:汇总结果,返回前端
这个编排有几个好处:一是节点可以单独测试,比如单独测分类节点,看它在不同留言下的表现;二是状态是显式对象,节点A的输出就是节点B的输入,不会像自由Agent那样上下文失控。
关键代码如下(这是一个简化版的状态类,实际项目里字段会更多):
from typing import TypedDict, Annotated import operator class AgentState(TypedDict): message: str # 原始客户留言 category: str # 分类结果 urgency: str # 紧急度 related_tickets: list # 检索到的历史工单 created_draft_id: str # 工单草稿ID reply_text: str # 生成的回复话术 def classify_node(state: AgentState): result = llm.invoke(f"请将以下客户留言分类...\n留言:{state['message']}") return {"category": result.category, "urgency": result.urgency} def search_node(state: AgentState): if state["category"] in ["售后", "退款"]: tickets = search_tools.invoke(customer_id=..., keyword=...) return {"related_tickets": tickets} return {"related_tickets": []} def draft_node(state: AgentState): prompt = build_reply_prompt(state) reply = llm.invoke(prompt) draft_id = create_draft(reply) return {"reply_text": reply, "created_draft_id": draft_id}这里说一个实操细节:每个节点返回的字段,只会更新状态对象里对应的key,不是全量覆盖。所以节点之间是松耦合的。classify_node不知道search_node怎么实现,search_node不知道draft_node的回复风格。改一个节点不会牵连其他节点,工程上非常好维护。
3.4 异步并发与前端交互
按决策五的思路,这个Agent的前端不搞同步等待。用户提交留言后,API立刻返回一个任务ID,后端把任务推到Celery队列,Agent在Worker里异步跑。跑完后状态写入Redis,前端每秒轮询任务状态,拿到结果后展示。整个链路的RT从用户视角看是2~3秒,但API层的响应是同步的“已接收”,不会把请求卡死。
FastAPI这层用async接口,数据库用异步驱动,整体高并发下很稳。分享一个实测参数:8核16G的单个容器,跑这个Agent的HTTP接入层,单秒能扛住约5000个“提交任务”的请求(因为只做了入队操作,真正的Agent逻辑在Worker池里并发跑)。而Worker池的并发数要跟模型API限流对齐,我这里的配置是20个Worker并发,既不吃满模型配额,也能保障吞吐。
3.5 成本、Token与延迟的平衡
这个项目让我实打实理解了“Token就是钱”。一个典型的分类+回复任务,如果全部走强推理模型,总Token约4000左右,成本不算高,但如果日请求量上万,月成本就是一笔不小的数。
我做了一个很简单的分流:先用一个小模型做预筛,把明显的“闲聊”“无意义留言”挡掉,只有需要“深度理解”的留言才进入Agent完整链路。预筛模型的精度做到95%以上,成本是大模型的1/5。这个“前置过滤”设计,在大多数Agent场景都适用,强烈建议尝试。
延迟方面也分享一个心得:LLM生成回复时用流式输出(SSE),用户端可以边生成边看到文字,体感上比“憋好几秒一次性出来”好太多。Agent因为要多次调用模型,每次都用流式,配合前端打字机效果,整体体验完全不一样。
4. 常见问题与排查技巧实录
日志里攒了大半年,四个高频问题值得拿出来讲。
4.1 问题一:模型总生成无效工具参数
症状:Agent明明调用了工具,但参数是编的,比如customerId传了一个根本不存在的值,或者把日期传成非标准格式。排查发现根因往往是工具的参数约束没有写进描述里,模型只能“猜”。
解法:一是所有参数必须声明类型和枚举值;二是在Schema校验层直接拦,参数不过校验就抛一个“参数错误”给模型,让模型重新生成;三是在提示词里强调“如果不知道参数值,请先向用户询问,不要臆造”。实测改进后,错误调用率从约12%降到了2%以内。
4.2 问题二:上下文被撑爆,生成长度开始失控
症状:多轮任务跑一半,模型突然开始重复输出、答非所问。通常是上下文里积累了太多历史Action结果,噪声覆盖了有效信息。
解法:严格控制每次工具结果的注入格式。超过一定字符数就做摘要,保留关键字段,丢弃HTML样式、日志堆栈。另外,每轮消息里给模型的“历史聊天”只保留最近3轮完整对话,更早的压缩成摘要。这个方法能保证上下文长期稳定在可管理的大小,Agent决策质量也稳了很多。
4.3 问题三:Agent陷入循环,不停调用同一个无效工具
症状:Agent调用工具A,A返回失败;模型又调用A,又失败;来回十几次,Token哗哗烧,用户干等。
解法:两步走。第一步,给工具调用加“重试上限”,同一个工具连续失败超过3次,就把错误信息汇总返回给模型,并强制让它“尝试另一个策略或直接给用户说明”;第二步,在Agent状态里增加一个trial_count计数器,每轮循环加一,超过5次强制走结束节点。本质上是“循环熔断”,一定要在架构层面兜底。
4.4 问题四:并发上去之后,模型限流、响应排队
症状:并发用户一多,模型API开始报429限流,或每次请求排队好几秒。
解法:一是全局加令牌桶限流,把模型QPS控制在安全水位;二是加一个择优降级策略,当主力模型排队过长时,把请求切换到一个备用模型,宁可结果粗一点,也要保持服务可用;三是把用户能感的交互逻辑与Agent内部逻辑解耦,用户先看到“任务已接收”,而不是死等Agent内部推理。这部分跟前面决策五讲的异步编排是配套的。
4.5 测试与回归:Agent怎么测才靠谱
最后补一个测试的实战建议。Agent很难用传统单测覆盖,因为同一个输入可能有不同输出。我的实践是“双轨测试”:一轨是结构化断言,比如分类结果是否落在允许的枚举里、工具调用参数是否符合Schema、高影响动作是否走了审批;另一轨是样本集回归,提前准备50~100条代表性用户输入,每次改完代码就全量跑一遍,用“关键结论是否一致”来判断回归是否通过。
我在团队里立了一个规矩:任何Agent代码修改,如果要上线,必须跑一遍这两轨测试,结构化断言失败直接拦截,样本集回归里超过5%不一致就必须人工复查。这套机制让我的Agent项目上线后稳定了很多,很少出现“上周还能用,今天不知道怎么就废了”的情况。
我自己做Agent项目到现在最大的体会是:不要神化Agent,也不要低估它。它不是一个魔盒,你丢一段Prompt进去它就能替你干活;它是一个需要认真设计的工程系统。七要素帮你检查“该有的有没有”,七个决策点帮你决定“具体怎么做”,把这两套框架跑一遍,你会发现Agent突然变得可靠、可控、可维护了。哪怕后续你的技术栈换了,框架也用得上,因为Agent工程的核心从来不是某个框架,而是你在设计时想的有没有足够深、足够周全。