免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

零基础手写最小AI Agent:从工具调用到ReAct循环的完整实践

零基础手写最小AI Agent:从工具调用到ReAct循环的完整实践 先说句实在话这两年被 AI Agent 刷屏的人不在少数但大部分人卡在同一个地方看了无数概念帖、产品盘点、框架对比到了自己动手的时候连个能跑的最小demo都搞不出来。这篇教程就是冲着零基础可跑去的我不跟你扯太多玄乎的理论直接带你从环境搭建开始一步一步写出一个真正能自主调用工具、完成多步任务的 AI Agent。这套内容我踩过不少坑才整理出来适合三种人看一是对 AI Agent 只有模糊概念、想快速入门的开发者二是想在公司内部做技术预研或 demo 验证的工程师三是打算用 Agent 做个人效率工具但不知道从哪下手的爱好者。你不需要有深度学习背景只要会一点 Python 基础语法跟着下面的步骤走完就能拥有一个属于自己的最小 Agent。1. AI Agent 到底是什么先搞清楚你在搭什么1.1 一句话说清 Agent 与普通对话助手的核心区别很多人以为接了大模型 API、能聊天就是 Agent这是个很常见的误区。普通的对话机器人比如你写个 prompt 调一下 GPT 或国产大模型接口它只能输入文本-输出文本。它没有手、没有脚没法帮你查数据库、发邮件、订机票。而 Agent 的本质是让模型具备使用工具的能力并且能自己规划行动步骤。你可以把它理解成一个有手有脑的员工——大脑是大模型负责理解任务、拆解计划手是各种工具比如代码执行器、搜索接口、数据库查询、文件读写。举个例子你让普通聊天机器人帮我查一下本周天气并生成穿衣建议它只能凭训练数据编一个答案。但 Agent 会这样工作先调用天气查询工具拿到实时数据再根据温度数据分析穿衣建议最后把结果整理成文字回复你。整个过程不是一次性生成的而是观察-思考-行动-再观察的循环。1.2 Agent 的三个核心循环感知、决策、行动任何 Agent不管用 LangChain、MetaGPT 还是自己纯手写底层都是围绕一个循环在转感知PerceptionAgent 接收用户请求之后需要理解当前的状态。这个状态可能是用户输入的一句话也可能是它调完工具之后拿到的返回值。决策Decision大模型根据当前状态决定下一步该做什么。是调用工具还是直接给出最终答案这是由模型推理出来的不是代码写死的。行动Action执行具体的动作比如调用一个函数、发起一次 HTTP 请求、执行一段 Python 代码。行动之后会产生新的观察结果再喂给模型做下一轮决策。这个过程跟人类做事的逻辑很接近。你做饭的时候先看看冰箱里有什么感知决定做番茄炒蛋还是红烧肉决策然后开火下锅行动尝一口发现咸了新的感知于是加水补救又一次决策和行动。Agent 的 ReAct 模式就是把这个人类的思维过程搬到了代码里。1.3 常见 Agent 形态与练手项目方向2026 年这个时间点上国内外的 Agent 产品已经相当丰富了。CrewAI 管多角色协作、Dify 做可视化编排、Coze 面向业务人员、AutoGPT 走全自动路线再加上各大云厂商的 Agent 中台产品确实让人眼花缭乱。但我的建议很直接练手别一上来就上重型框架。用纯代码手写一个最小 Agent你才能真正理解工具调用和循环决策的机制。等你搞明白了内部逻辑再去用那些框架就像学过手动挡再去开自动挡一样心里特别有底。适合零基础练手的项目方向很多个人日程助手查日历、定提醒、代码生成与执行工具、文档问答机器人、周报总结 Agent。后面我会挑一个自动查询信息并汇总的典型场景带你把完整代码跑通。2. 工具选型三条路线按你的基础挑一条2.1 路线一纯 Python 大模型 API适合想搞懂原理的人这条路线是我最推荐的入门方式。核心依赖只有两个一个 Python 环境一个大模型的 API 接口。不需要任何第三方 Agent 框架逻辑全自己写。优点非常明显第一没有框架的黑魔法每一步做了什么你都清清楚楚第二调试方便出问题你能精确定位是模型的问题还是工具的问题第三代码量少核心逻辑加起来不到 200 行。缺点也很直接所有底层细节都要自己处理比如对话历史怎么管理、工具返回结果怎么塞给模型这些在框架里是现成的。对于零基础的人来说我反而觉得这些麻烦是好事。因为 Agent 的本质就是一个循环你自己写一遍这个循环比看十篇框架源码分析都管用。2.2 路线二LangChain LangGraph适合有 Python 基础的人如果你想跳过底层实现直接做比较复杂的 Agent 应用LangChain 和 LangGraph 是目前社区最成熟的方案。LangChain 提供了封装好的 Tool 抽象、模型接入、Prompt 模板LangGraph 则在图结构上做 Agent 的状态管理和流程控制。用框架的好处是开发效率高很多边缘情况框架已经帮你处理了。比如 OpenAI Function Calling 的回调格式、工具参数的 JSON Schema 生成框架里封装得都很完善。坏处是你需要花时间理解框架的概念而且框架版本迭代快API 变动频繁在搜索引擎里找到的教程很可能已经过时了。我的建议是先用路线一跑通一个最小 Agent把核心概念理解透了再决定要不要进框架的坑。顺序反了的话你会陷入报错-查文档-改代码-再报错的循环非常打击信心。2.3 路线三可视化智能体平台适合完全不写代码的人如果你完全不会编程又确实需要用 Agent 解决实际问题那 Dify、Coze 这类的可视化平台是可以考虑的。这些平台把 Agent 的构建过程做成了拖拽式的工作流你只需要配置节点、填写 Prompt 模板平台会自动帮你处理模型调度和工具集成。不过说实话这类平台对零基础搭建来说虽然门槛最低但天花板也明显。你只能在平台提供的工具范围内做组合一旦遇到定制化需求就会束手束脚。而且平台封装的逻辑太多你在里面拖出来的 Agent 到底是怎么工作的可能并不清楚。所以这条路线适合做产品验证或者业务侧自助使用不适合想深入学习 Agent 原理的人。这篇教程的主角是路线一后面所有代码都是纯手写实现。3. 动手前必看环境准备与关键概念3.1 Python 环境与项目结构工欲善其事必先利其器。我建议用 Python 3.10 以上版本避免一些新版库的兼容问题。环境管理用 venv 就够了不需要上 Conda 那么重的工具。python3 -m venv agent_env source agent_env/bin/activate pip install openai项目结构非常简单粗暴agent-demo/ ├── main.py # 主程序 ├── tools.py # 自定义工具函数 └── .env # 存放 API Key别提交到 Git不要一上来就搞复杂的包结构。我们练手的目的是把 Agent 跑起来工程化的事情以后再考虑。等你理解了核心逻辑再拆分模块、加日志、做配置管理完全来得及。3.2 API Key 管理与成本意识这个点我必须单独拿出来说因为太多新手在这里翻车。第一API Key 绝对不能硬编码在代码里尤其不能提交到公开仓库。建议用环境变量或者 .env 文件管理并在 .gitignore 里把 .env 排除掉。第二如果你调用的是国内大模型服务通常会自动有免费额度或者比较低的计费标准。但不管用哪家我都建议在代码里加上最大轮次限制防止 Agent 陷入无限循环疯狂调 API一晚上把你预算烧光。我见过不止一个人因为没设上限一觉醒来账单几十块的。第三关于远程访问这些 API 服务的网络问题不同服务商的配置方式不一样遇到连接超时的情况优先检查你的 API 地址配置是否正确、网络环境是否满足服务商的连接要求。3.3 理解 Prompt 与 Function Calling在写代码之前有两个概念你得先明白。第一个是 Prompt 的角色设定Agent 的系统提示词直接决定了它的行为风格和决策逻辑。比如你给 Agent 设定成一个严谨的科研助手它会更多调用数据查询工具设定成一个活泼的聊天伙伴它可能就不太想调工具。第二个是 Function Calling这是 OpenAI 兼容接口提供的一种结构化能力。你把自己的工具函数用 JSON Schema 描述给模型模型在需要调用工具的时候不会直接输出一段文字让你去解析而是输出一个结构化的 JSON其中包含函数名和参数。你的代码收到这个 JSON 之后执行对应的函数再把结果作为新的消息喂回模型。这里有个很关键的思路转变Function Calling 的本质不是让模型执行代码而是让模型决定该调用哪个函数、参数是什么。真正执行函数的是你的本地代码。模型输出的 JSON 只是它的决策结果。理解了这个后面代码看起来就顺了。4. 从 0 到 1 手写一个最小 Agent完整代码跑通4.1 ReAct 模式的核心逻辑先不要急着看代码我们把核心逻辑捋一遍。ReAct 是 Reasoning Acting 的缩写翻译成大白话就是边想边做。一个最小 Agent 的主循环长这样把用户问题加入消息队列。把整个消息队列发给大模型带上所有工具的定义。模型返回结果分两种情况结果里有工具调用的请求那就解析出函数名和参数执行本地函数把结果作为新消息追加进队列回到第 2 步。结果里没有工具调用请求说明模型认为任务完成了那这就是最终答案退出循环。这里每一步都值得细看。比如为什么要用消息队列而不是只发当前那一轮因为 Agent 需要上下文记忆它得知道之前调过什么工具、得到过什么结果才能做出下一步决策。再比如工具返回的结果为什么是一条消息而不是直接改代码逻辑为了把工具结果放进对话上下文里模型才能看见自己行动的结果。这个模式别看简单它是几乎所有主流 Agent 框架的基石。LangChain 的 AgentExecutor、老版 AutoGPT 的核心循环本质都在做这件事。4.2 核心代码60 行跑通最小闭环下面这段代码是最小可运行版本你复制到 main.py 就能跑。import json from openai import OpenAI client OpenAI( api_key你的API_KEY, base_url你的API服务地址, ) # 第一步定义工具函数真正执行的代码 def get_city_weather(city: str) - str: 模拟天气查询实际项目可替换成真实API调用 weather_data { 北京: 晴25度, 上海: 多云28度, 广州: 阵雨30度, } return weather_data.get(city, f暂无{city}的天气数据) # 第二步用 JSON Schema 描述工具让模型知道有这个函数可用 tools [ { type: function, function: { name: get_city_weather, description: 查询指定城市的实时天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名比如北京、上海, } }, required: [city], }, }, } ] def run_agent(user_input: str, max_rounds: int 5): # messages 就是 Agent 的记忆 messages [ {role: system, content: 你是一个有用的助手需要查询天气的时候调用工具其他情况直接回答。}, {role: user, content: user_input}, ] for round_idx in range(max_rounds): # 把整个消息队列含全部历史发给模型 response client.chat.completions.create( modelgpt-4o-mini, # 换成你实际使用的模型名 messagesmessages, toolstools, tool_choiceauto, ) msg response.choices[0].message # 模型没有要求调用工具说明任务完成直接返回 if not msg.tool_calls: print(f最终答案{msg.content}) return msg.content # 模型要求调用工具把模型的消息加入队列 messages.append(msg) # 逐个执行工具调用 for tool_call in msg.tool_calls: func_name tool_call.function.name arguments json.loads(tool_call.function.arguments) print(f第{round_idx1}轮调用工具 {func_name}参数 {arguments}) # 在本地函数表里查找并执行 if func_name get_city_weather: result get_city_weather(**arguments) else: result f未知工具: {func_name} # 把工具执行结果作为消息追加到队列给模型看到 messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse), }) return 达到最大轮次任务未完成 if __name__ __main__: run_agent(北京今天天气怎么样适合出门跑步吗)这个代码你直接跑就能看到效果。过程会打印出每一轮的工具调用情况。注意看第二问适合出门跑步吗模型拿到天气数据之后会结合晴、25度推理出适合然后给出最终答案。这就是 Agent 比普通聊天机器人强的地方它真的拿到了数据而不是编了一个答案。4.3 关键代码逐行拆解很多新手第一次看这段代码会有点懵我逐块拆一下。tools 列表是给模型看的工具说明书里面每一项描述了函数名、函数作用、参数类型和必填项。模型看了这个列表才知道你有什么工具可用、参数怎么传。这里有个细节description 字段非常重要写得不清楚模型就不会在正确的时候调用。比如你写查询天气和查询指定城市的实时天气并返回温度和天气现象后者明显更容易被模型理解和触发。tool_choiceauto表示让模型自己决定要不要调工具。你也可以设成 none 强制不调用或者 required 强制必须调用但日常用 auto 最合理。这里我建议新手可以调一下参数观察行为差异——把 tool_choice 改成 none 再跑一次你会发现模型完全把天气数据当幻想了直接开始自由发挥。messages.append(msg)这一步很多人会漏。必须把模型返回的这条含 tool_calls 的消息追加进上下文否则模型会忘记自己刚刚决定要调工具。后面再把工具执行结果也 append 进去这样一个完整的观察-思考-行动闭环才建立起来。有个很容易踩的坑是 role 字段。工具执行结果必须以 role: tool 加入消息队列并且要带上对应的 tool_call_id这样模型才知道这个结果是为了响应哪一次工具调用。如果你写错 role 或者漏掉 tool_call_idAPI 会直接报错。4.4 实战进阶增加代码执行工具和搜索工具天气查询只是热身。真正让 Agent 变得强大的是给它接上代码执行和搜索能力。下面我们扩展 tools.py添加两个更实用的工具。# tools.py import json import subprocess import urllib.parse import urllib.request def run_python_code(code: str) - str: 执行一段Python代码返回标准输出 try: result subprocess.run( [python3, -c, code], capture_outputTrue, textTrue, timeout5, ) if result.returncode 0: return result.stdout.strip() else: return f执行报错{result.stderr.strip()} except subprocess.TimeoutExpired: return 执行超时5秒限制 def search_web(query: str) - str: 调用公开搜索接口返回前几条结果摘要 try: base_url https://api.duckduckgo.com/ params urllib.parse.urlencode({ q: query, format: json, no_html: 1, skip_disambig: 1, }) req urllib.request.Request(f{base_url}?{params}, headers{ User-Agent: Mozilla/5.0 }) with urllib.request.urlopen(req, timeout10) as resp: data json.loads(resp.read().decode(utf-8)) abstract data.get(AbstractText, ) related data.get(RelatedTopics, [])[:3] lines [f摘要{abstract}] if abstract else [] for item in related: if Text in item: lines.append(f- {item[Text]}) return \n.join(lines) if lines else 没有搜索到相关结果 except Exception as e: return f搜索失败{str(e)}接入这两个工具之后你的 Agent 能力边界一下子扩大了很多。比如你问它帮我算一下 15 的阶乘它不会直接编一个数字而是生成一段 Python 代码调用 run_python_code 去执行再把真实结果返回给你。如果你问它最近 AI Agent 领域有什么新动态它会调用 search_web 去搜索然后把结果整理成一段摘要给你。这就是 Agent 从聊天到干活的质变。不过要特别提醒给 Agent 开放代码执行能力等于给了它一把刀。如果你只在本地跑、处理自己的数据风险可控但如果做成了线上服务必须加沙箱隔离、执行超时、资源限制。我这个 demo 里的 subprocess 实现只是教学用途生产环境千万别直接照搬。5. 实操中的高频问题与避坑手册5.1 模型卡在循环里出不来这是新手遇到最多的问题Agent 调完工具拿到结果又继续调同一个工具或者来回调两个工具就是不输出最终答案。出现这个问题的原因主要有三个。一是系统提示词没写清楚什么时候该结束比如你没告诉模型任务完成后直接回复最终答案不要再调用工具模型就会倾向于一直调。解决办法是在 system prompt 里显式加一句如果工具结果已经足够回答用户问题请直接给出最终答案。二是工具返回的结果本身质量差模型拿到的信息不足以做出决策于是反复尝试。这时候要检查工具返回的文本是不是足够清晰。比如天气工具返回晴25度就比返回0更容易让模型做决策。三是最大轮次限制设得太小模型还没推理完就断了但这种情况不会死循环只是任务未完成。我在项目里会同时设置最大轮次比如 5~10 轮和相同工具连续调用次数限制比如同一个工具连续调用超过 3 次就强制停止双重保险。5.2 Token 成本比预期高很多很多零基础同学第一次跑 Agent 的时候会被 token 消耗吓了一跳。原因其实很简单每一次工具调用之后你要把整个历史消息队列重新发给模型而且每轮还会追加模型回复和工具结果两段内容。轮次越多上下文越长成本呈线性甚至超线性增长。控制成本的办法有几种。第一能精简的上下文就精简工具返回结果不用全量塞给模型截取关键部分就行。比如搜索结果返回了 5000 字你可以只保留前 500 字。第二控制工具调用的粒度和频率同一个 Agent 任务里让模型先做规划再分批查询而不是每问一句就调一次工具。第三在开发调试阶段用便宜的小模型比如 mini 版本跑通逻辑之后再换强模型。还有一个容易被忽略的点模型的输出 token 也很贵。如果你发现 Agent 回复特别长可以在 API 请求参数里设置 max_tokens 上限既控制成本也防止模型话痨。5.3 工具参数频繁传错或格式不对Function Calling 虽然解决了解析问题但模型仍然会传错参数。最典型的情况是你定义的参数是 integer 类型模型传了一个字符串 3或者参数是必填的模型直接没传。这个问题的根源在于你的 JSON Schema 写得不够严格。我调试过很多次之后总结出几个经验。一是描述的措辞要具体要告诉模型这里必须是整数、如果用户没有明确指定城市询问后再调用工具不要猜。二是如果工具参数有默认值就把 optional 的定义写清楚。三是要有容错逻辑函数执行之前先做类型校验不合法就返回一个明确的错误信息比如参数 city 缺失或格式不正确请确认后再调用。千万不要假设模型每次都会完美地传参代码里一定要做健壮性检查。5.4 国内大模型服务的兼容性问题汇总如果你的 API 服务是国内大模型厂商提供的大概率走的是 OpenAI 兼容接口但细节上会有差异。我实测下来最常见的有三类。第一有些国产模型对 Function Calling 的支持是通过一个额外的工具描述解析层实现的实际 API 的请求格式会有微调最稳妥的做法是找到你所用服务商的文档看它要求的 tools 字段格式。第二部分模型的 tool 调用结果需要放在特定的消息角色里跟 OpenAI 标准格式不完全一样比如有的需要 role: function 而不是 role: tool。第三部分国产模型对 system prompt 的优先级处理跟 OpenAI 不同如果你的 Agent 行为不符合预期优先排查这一块。所以我建议你写代码的时候把模型接入逻辑稍微抽象一下不要把所有细节都写死在 main.py 里。这样以后换模型或者换服务商只改配置就好了。5.5 高频提问速查表现象最可能的原因解决办法Agent 不调用工具直接编答案tool_choice 设置不对 / 工具描述不清晰确认 tool_choice 为 auto优化工具 description工具被调用了但报错参数格式不符合函数签名检查 JSON Schema 参数类型函数入口增加校验循环调工具不停缺少停止条件 / 工具结果不充分在 system prompt 明确停止条件增加最大轮次限制模型回复特别长输出 token 没限制设置 max_tokens精简 system prompt上下文太长导致费用高历史消息累积过多截断工具返回结果减少不必要的工具调用轮次API 返回格式解析不了不同服务商兼容性差异查对应文档调整消息角色或 tools 格式6. 从最小 Demo 走向真实项目我的扩展建议6.1 增加长期记忆层让 Agent 记住之前的交互简单 Agent 的上下文只在当前会话里有效一重启就全忘了。真实项目里你需要让 Agent 具备记忆。这个记忆不需要很复杂最简单的方案是引入一个记忆文件或者数据库表每次对话结束后把关键信息提取出来存进去下次对话开始的时候把相关记忆注入 system prompt。比如我的日程 Agent会把用户说过的我每周三上午有例会提取成结构化记录。下次用户说帮我把会议推迟一小时Agent 就能从记忆里找到周三例会这个日程然后调用日历工具做修改。没有记忆层的 Agent 根本做不到这件事。6.2 用 Agent 中台或者框架做工程化一旦你的 Agent 从能跑走向能用就要考虑工程化问题了。这时候再用纯手写的方式就有点吃力了你需要引入框架或者公司内部的 Agent 中台。框架层面LangGraph 的状态图很适合管理复杂流程CrewAI 适合做多 Agent 协作平台层面Dify 之类的工具可以做可视化管理。但无论用哪个前面手写循环打下的基础都会让你理解得更透彻遇到框架的报错你能一眼看出是状态管理问题还是工具调度问题。6.3 我的个人经验学习路径上的三个不要最后说几个我实战下来的体会。第一个是不要贪多别一上来就搞多 Agent 协作、知识库增强、流式输出这些花活先把一个最小 Agent 跑得非常熟练再逐步加功能。第二个是不要只抄代码把核心循环的逻辑讲给别人听讲不明白就说明没掌握。第三个是不要怕报错Agent 的调试过程本身就是学习过程我第一次跑通这个循环的时候前前后后改了两个多小时但理解深度远超看十小时视频教程。学习 AI Agent 这件事最关键的从来不在于你看了多少资料而在于你有没有亲手把那个循环跑通。哪怕你只实现了天气查询这一个工具跑通的那一瞬间你脑子里对 Agent 的理解就会发生质变。剩下的路就顺了。
返回列表