
先别急着搜教程、下框架搭建第一个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, caseFalse, naFalse)] 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_keyos.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( modelgpt-4o-mini, # 这里可以替换成任何你选定的模型 messagesmessages, toolsTOOLS, tool_choiceauto, ) msg response.choices[0].message if msg.tool_calls: # 如果有工具调用请求就把工具调用结果拼回去 messages.append(msg) for tc in msg.tool_calls: result search_feedback( keywordeval(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%的排查弯路。希望这篇内容能帮你顺利迈出第一步。