先别急着搜教程、下框架,搭建第一个AI Agent,核心不是“写代码”,而是搞懂一个问题:你到底想让Agent替你做什么?需求没想清楚,后面每一步都在给自己挖坑。
这篇文章基于我自己从零搭第一个Agent(一个模拟项目X的客户反馈分析助手)的完整经历写成。我会把从需求拆解、工具选型、Prompt设计到代码落地、排错优化的全过程都走一遍,所有踩过的坑、验证过的经验和可直接抄的配置都会放在里面。不管你之前有没有写过代码,只要具备一点点Python基础(知道什么是函数和循环就够),跟着这篇文章走,你就能拥一个真正能跑起来的AI Agent。
1. 搭Agent前必须先想明白的三件事
很多人把Agent想得太玄乎,觉得它像科幻电影里的数字生命。实际不是。一个生产环境能用的Agent,本质就是“一套可以被调度的AI服务”:接收输入、调用模型、触发工具、返回结果。所以在动手之前,你得先回答三个问题。
1.1 你的Agent职责边界在哪里
你希望它做什么,更重要的是,你希望它“不”做什么?边界不清晰,Agent就会变成脱缰野马。
我拿自己的客户反馈分析助手举例:它的任务是从一堆客服对话记录里提取客户痛点,并按严重程度分级。就这么简单。它不需要自动回复客户,不需要生成营销文案,更不能自行修改数据库。边界定好之后,我才知道该给它配什么工具:只需要一个读数据的接口和一份结构化的输出格式,其他的一概不要。
一个很常见的误区是刚上手就想做“全能助手”,恨不得让Agent帮你搞定炒股、写周报、订机票。结果就是Agent光理解任务就耗掉一半Token,输出质量还极其不稳定。我给你的建议是:第一个Agent永远只做一件小事,哪怕这个事小到只是“从一段文本里提取邮箱地址”。把一个闭环跑通,比做一个半吊子的大而全的东西有价值得多。
1.2 你手头有哪些数据和接口可以调用
Agent要干活,靠的是数据和工具。数据是它的原料,工具是它的四肢。如果你要做的Agent连最基本的输入源都没有确定,那后面全部是空中楼阁。
我在搭第一个Agent的时候,先把客户反馈数据整理成了统一的CSV格式:字段包括客户ID、反馈时间、反馈渠道、反馈原文。然后确认了我能调用的工具——当时我手里只有一个内部数据查询接口,通过HTTP请求能拿到脱敏后的反馈记录。数据和接口确定之后,整个Agent的交互路径就清晰了:用户输入问题,Agent判断是不是数据分析请求,是就调接口拿数据,然后交给模型分析,最后输出结构化结论。
如果你目前没有任何现成数据,也没有接口,那也不用担心。第一步可以先处理公开的、你自己生成的信息,比如一份手工整理的产品评论Excel,或者你手动抓取的几十条用户评价。关键在于把链路跑通,数据本身是不是海量不重要。
1.3 你用什么模型底座来承载Agent能力
模型选型是一个容易被忽视但极其影响体验的环节。我自己最早用的是当时很流行的全功能对话模型,确实聪明,但贵,而且响应慢。后来在跑Agent的过程中发现,很多任务根本用不上那么大参数的模型,换成轻量级模型反而更稳。
怎么选?我的建议是分场景:如果Agent主要做分类、提取、格式化输出这种“判别型”任务,选中等规模的专用模型就够了,便宜且快;如果要做的任务是开放式的创意生成、多轮复杂推理,那就得上能力更全面的旗舰模型。这里面没有绝对好坏,只有合适不合适。
在项目初期,我建议你先用一个容易拿到、接入成本低的模型把链路跑通,之后再逐步替换底座。模型更换对Agent整体架构的影响应该被控制在最低程度——好的Agent架构,模型只是其中一个可替换组件,而不是跟业务逻辑死死绑在一起。
2. 核心架构拆解:Agent的四个“器官”
市面上做Agent的框架非常多,有的提供完整编排能力,有的只有轻量级工具调用。但不管用哪个,底层逻辑都绕不开四个核心组件:模型、工具、编排器、记忆。理解这四个“器官”各管什么,你就理解了Agent的全部。
2.1 模型(大脑):所有判断的决策中心
模型承担的是“理解任务、拆分步骤、决定调用什么工具、解读工具返回结果”的职责。它就是Agent的大脑。
我实际用的是通过API调用的云端模型,只传文本、返回文本。很多代码框架喜欢把模型封装成对象,但如果你自己动手写,直接请求API接口也完全可行。关键是你要给模型设置清晰的“系统提示词”,告诉它你是谁、你能做什么、输出格式是什么。模型的发挥水平,很大程度取决于你提示词写得清不清楚。
一个容易踩的坑是让模型自由发挥输出格式。如果你没有限定“必须输出JSON”或“必须输出表格”,模型就会给你来一段花团锦簇的散文,下游解析程序直接崩掉。我第一次就是这么翻车的,后面才意识到,跟模型打交道,初期必须当“格式警察”。
2.2 工具(四肢):连接外部世界的桥梁
Agent没法只靠模型本身的知识库去回答所有问题,尤其是涉及实时数据、私有数据的时候。它必须能调用工具:读数据库、查天气、算数学题、发邮件……这些都是工具的范畴。
我在第一个Agent里只做了一个工具:一个用来查询客户反馈的“检索函数”。这个函数接收一个关键词,返回匹配的反馈记录列表。实现方式非常简单——就是拿关键词去CSV文件里做模糊匹配,然后把命中的行拼成文本返回。模型自己不会读CSV,但它知道自己有一个“检索工具”,在需要的时候会构造一个包含“工具名”和“参数”的结构化文本,编排器负责拦截这个文本并执行对应代码。
理解这个机制很重要:Agent的“工具调用”,本质上是一个“模型输出特定格式文本”的过程。模型不直接执行代码,它只是“请求”执行代码。真正执行的是你自己的程序。想通了这一点,你给Agent加任何工具都不会再慌。
2.3 编排器(躯干):控制决策循环与工具调度
编排器是Agent的灵魂循环:把模型输出解析出来,判断是“直接回复用户”还是“调用某个工具”,再根据工具返回结果决定是继续循环还是终止。
我给Agent设计的循环逻辑是:
- 把系统提示词、历史对话、当前用户问题,全部组装成发给模型的消息。
- 请求模型,拿到响应。
- 解析响应:如果响应是带工具调用的格式,取出工具名和参数,执行对应函数,把结果作为一条新消息追加到对话里,然后回到第2步。
- 如果响应是纯文本回复,就直接把这个回复抛给用户,结束流程。
这个循环写起来也就二三十行代码,但它是整个Agent的发动机。框架帮你做的也是这件事,了解底层原理之后,你就能在出问题时快速定位到哪个环节坏掉了。
2.4 记忆(履历):对话上下文的关键管理
记忆组件管两件事:短期记忆和长期记忆。短期记忆就是当前对话过程中产生的全部上下文,相当于记事本;长期记忆是跨会话保留的事实性信息,比如用户偏好、项目背景,相当于档案库。
我的第一个Agent只用了短期记忆:把所有消息都存在一个列表里,直接放进请求里发给模型。这种做法的代价是上下文越长,Token消耗越大、响应越慢。后面我做了简单优化:只保留最近5轮对话,更早的内容要么舍弃,要么提取摘要后压缩成一段历史概要放在系统提示词里。这个优化立竿见影,成本和延迟直接降了一截。
长期记忆则相对复杂一些,我当时用的是一个“记忆文件”:把用户的偏好写进一个JSON文档,在每次启动Agent的时候加载到系统提示词里。够用,而且实现起来非常简单。
3. 实操过程:从环境准备到第一个Agent跑起来
理论说完了,下面咱们动真格的。这一节我会带你从头搭一个“客户反馈分析助手”Agent。它只做一件事:给定一段客户反馈文本,调用本地“检索函数”找到相似历史反馈,结合模型判断这条反馈的严重等级和关键词。
3.1 环境准备与依赖安装
我假设你已经装了Python 3.10以上版本。如果没有,去官网下载装上就行,装的时候注意勾选“Add Python to PATH”。这一步卡住的人不少,我当时是在某台机器上忘了勾选,后面命令行里死活敲不出python。
接下来建一个项目目录,然后安装依赖。我在这个项目里用到的核心库只有两个:一个是调用模型的官方SDK,另一个是HTTP请求库。执行下面的命令:
mkdir my_first_agent cd my_first_agent python -m venv venv # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate pip install openai requests pandas解释一下为什么用虚拟环境:不同项目依赖的库版本可能互相冲突,虚拟环境把依赖隔离到项目自己的目录里,怎么折腾都不影响全局环境。这一点对于新手来说太重要了,我在一个旧项目里同时装了两个版本冲突的库,互相覆盖,排查了两天才发现是环境问题。
3.2 准备你的“工具函数”
这个Agent的核心工具是“检索历史反馈”。我准备了一份demo数据,就是一份包含几十条模拟客户评价的CSV文件。字段很简单:id、text、category、score。准备工具函数的代码如下:
import pandas as pd df = pd.read_csv("feedback.csv") def search_feedback(keyword: str, top_k: int = 5) -> str: """按关键词匹配反馈文本,返回匹配的前 top_k 条""" matched = df[df["text"].str.contains(keyword, case=False, na=False)] if matched.empty: return "未找到相关历史反馈。" results = [] for _, row in matched.head(top_k).iterrows(): results.append(f"[{row['id']}] (评分:{row['score']}) {row['text']}") return "\n".join(results)你可能会问:Agent怎么知道这个函数的存在?答案是通过“工具描述”告诉模型。在系统提示词或工具列表里,你要写清楚:
{ "name": "search_feedback", "description": "根据关键词检索历史客户反馈记录。当用户问题涉及具体产品名称、缺陷、情绪时使用。", "parameters": { "type": "object", "properties": { "keyword": {"type": "string", "description": "检索关键词"} }, "required": ["keyword"] } }看到没有,模型不依赖你的Python代码,它只看这份“说明书”。这也是为什么好的工具描述一定要写得详细、准确——描述越清晰,模型在需要的时候就越会正确调用它。
3.3 核心Agent循环代码
接下来是Agent的主循环。这里我用的是函数调用的API方式,但整个逻辑是通用的,你可以迁移到任何模型底座上。
from openai import OpenAI # 读取API密钥,建议使用环境变量,而不是硬编码到代码里 import os client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) TOOLS = [{ "type": "function", "function": { "name": "search_feedback", "description": "根据关键词检索历史客户反馈记录", "parameters": { "type": "object", "properties": { "keyword": {"type": "string", "description": "检索关键词"} }, "required": ["keyword"] } } }] system_prompt = """你是一个客户反馈分析助手。你的任务是: 1. 根据用户输入的反馈文本,使用search_feedback工具查找相似历史反馈; 2. 结合历史反馈,判断当前反馈的严重等级(高/中/低); 3. 提取反馈的3-5个核心关键词; 4. 输出格式为JSON,包含severity、keywords、summary三个字段。 注意:user_message是用户输入的反馈原文。""" def run_agent(user_message: str, max_turns: int = 3): messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ] for _ in range(max_turns): response = client.chat.completions.create( model="gpt-4o-mini", # 这里可以替换成任何你选定的模型 messages=messages, tools=TOOLS, tool_choice="auto", ) msg = response.choices[0].message if msg.tool_calls: # 如果有工具调用请求,就把工具调用结果拼回去 messages.append(msg) for tc in msg.tool_calls: result = search_feedback( keyword=eval(tc.function.arguments)["keyword"] ) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": result }) continue # 如果没有工具调用请求,说明模型已给出最终回复 return msg.content if __name__ == "__main__": test_input = "我买的智能手环三天就没电,客服还让我自己找说明书,体验太差了。" output = run_agent(test_input) print(output)这段代码就是Agent的全部核心。你把它跑起来,输入一条反馈,它就会自己判断需不需要检索历史数据,然后给出一个包含严重等级、关键词和总结的JSON结果。
3.4 让Agent按你的格式输出:一次“调教”实战
第一次跑的时候,我发现模型返回的JSON字段名跟预期不完全一致,有时候多了空格、有时候summary里带了多余引号。这其实不是模型不听话,是我没把规则讲清楚。
解决办法是在system prompt里进一步约束:
请严格输出JSON对象,格式如下: {"severity": "高|中|低", "keywords": ["关键词1", "关键词2"], "summary": "一句话总结"} 不要输出JSON以外的任何内容,不要使用代码块标记。加了这句之后,输出稳定度明显提升。如果你想更严谨,还可以在代码里写一个“JSON提取”函数:用正则从模型输出中截取第一个{到最后一个}之间的内容,然后用json.loads解析。解析失败时再重试一次,加上错误提示,让模型重新生成。这个兜底逻辑在真实场景里几乎是必备的——模型偶尔还是会抽风。
3.5 三种方式验证你的Agent没有“幻觉”
Agent最让人不放心的就是“幻觉”:一本正经地生成一个不存在的工具调用结果。我做了三层验证:
第一层是“工具结果强制注入”:工具的实际返回值直接来自search_feedback函数,模型无法凭空生成历史反馈内容,它只能是“引用”返回值。这一步从数据链路上杜绝了假数据。
第二层是“输出结构校验”:拿到模型返回的文本后,我用json.loads解析,如果解析失败直接抛异常。结构错了就重试,绝不放进下游。
第三层是“逻辑抽查”:用几条我预先知道答案的测试用例跑一遍,人工检查模型分析是否合理。比如“手环三天就没电”这条,应命中的是“续航短、质量差”方向,并判定为“高”严重等级。跑了三次之后,结果稳定在一个固定区间,我才敢说这个Agent能用了。
4. 常见问题与排查技巧实录
实操中几乎没有一个项目是一把过的。这里把我踩过的坑、以及很多同行问得最多的问题整理成速查表,供你对照排查。
4.1 模型就是不肯调用工具,怎么办
有时候模型压根不返回tool_calls,而是直接给一段文字回复。排查顺序如下:
- 工具描述写清楚了吗?“根据关键词检索历史反馈记录,当用户问题涉及具体产品名称、缺陷、情绪时使用”和“一个检索函数”,效果天差地别。
- 系统提示词里有没有明确引导:“根据用户输入的反馈文本,使用search_feedback工具查找相似历史反馈”。你不说,它就不会主动用。
- 模型选对了吗?有些轻量模型工具调用能力弱,你需要确保所选模型明确支持function calling。如果模型不支持,请换一个支持工具调用的型号。
- 多轮对话历史里,是否已经把工具调用结果正确注入?如果角色是tool的消息没有带上正确的tool_call_id,模型会陷入混乱。
4.2 Token费用超出预期怎么办
很多初学者发现跑几轮对话以后账单涨得飞快。我的经验是两个方向:压缩上下文和换模型。
压缩上下文就是前面说的“最近5轮”策略。超过5轮的历史,用一条摘要代替。比如“用户已经讨论过续航问题和客服态度问题,未涉及价格”。这条摘要由模型在每轮末尾生成,下一次请求放在system prompt里。
换模型就更直接了。工具调用为主的Agent,中等参数模型基本够用,价格便宜一个数量级。我自己实测下来,同类任务的准确率跟大模型差不了太多,但成本降了接近80%。做个人项目的话,别迷信“越大越好”。
4.3 工具返回数据太大,把上下文撑爆怎么办
如果你一次检索返回了100条反馈,每条text都很长,模型的处理效果反而会变差。我的做法是:把top_k限制在5条以内,同时每条只保留前200个字符。如果命中结果确实多于5条,可以在工具返回值末尾附一句“共命中N条,仅显示前5条”,让模型知道数据是截断过的。
更进一步的方案是“分层检索”:先用关键词捞一批候选,再做一次重排序,只把最相关的3条塞给模型。这个过程在代码里做,不要指望模型自己来排。
4.4 输出JSON偶尔断裂,下游程序报错
这是最常遇到的问题。我的解决思路是三次重试机制:第一次直接解析模型输出,失败后把原有消息再加一个系统提示“你刚才的输出不是合法JSON,请重新生成”,第二次还是失败就换一个更低的温度参数(比如从1降到0.2)重试,第三次仍失败就返回一个默认兜底结果。不会100%完美,但可以把失败率压到1%以下。
4.5 提示词调优的几个小偏方
关于提示词,最后分享几个我反复验证过的偏方:
- 在system prompt里写“你必须输出JSON”,比写“请输出JSON”管用得多。“必须”这个词对模型的约束力更强。
- 给模型提供示例:在system prompt的末尾放一个“用户输入示例和正确输出示例”。模型看到范例后跟随的准确率大幅提升。
- 角色设定也有作用:把模型设定成“资深客户体验分析师”之后,它更倾向于给出结构化、专业化的语言,而不是口语化的闲聊。
- 温度参数不要条条都设0。如果做分类,温度越低越稳定;如果做开放总结,稍微给一点温度(0.3到0.5)会让语言更自然。
5. 进阶方向与我的实操体会
如果你的Agent已经可以稳定跑通了,恭喜你,你已经跨过了“从0到1”最难的坎。接下来有几个很自然的进阶方向,你可以根据自己的需求去选。
第一个方向是给Agent增加“多工具编排”。现在的Agent只会检索客户反馈,你可以继续添加“发送邮件”“创建工单”“生成月报”等工具。注意每增加一个工具,都要把工具描述写清楚,并做多组交叉测试,因为工具越多,模型选错工具的概率也会变大。
第二个方向是把Agent从“单轮任务”升级为“多轮任务”。比如用户说了一句话,Agent先拆分成多个子任务,依次调用不同工具,最后汇总结果。这在框架里通常叫“规划”能力。我自己做这个的时候,是把系统提示词改成“先列出解决用户问题需要的步骤,再逐一步骤调用工具”,实测效果稳定不少。
第三个方向是加一点“记忆管理”。我之前用JSON文件存长期记忆的办法,在数据量小的时候很好用,但数据量上来以后维护成本变得很高。后来的方案是把记忆也通过向量检索来做:每条记忆存成向量,每次任务开始时先检索最相关的几条记忆注入上下文。这个方案更优雅,也更接近生产环境里的做法。
按照我个人的体会,搭好第一个Agent之后,最大的收获不是得到一个能用的工具,而是真正理解了大模型应用的核心逻辑:模型是决策者,不是执行者;工具是扩展能力的桥梁,不是模型自带的功能。以后无论你用多少框架、多少底层模型,这个思考方式都是通用的。
最后再分享一个我自己实际操作中总结的小技巧:调试Agent时,一定要把“模型收到的完整请求消息”(包括system prompt、历史记录、工具返回结果)打印出来看一遍。很多人排查问题只看最终输出,却不知道模型为什么这么输出——其实答案全部藏在请求里。这一步看似简单,却能帮你避免至少80%的排查弯路。希望这篇内容能帮你顺利迈出第一步。