ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从0到1手写最小AI Agent:ReAct循环与工具调用实战

从0到1手写最小AI Agent:ReAct循环与工具调用实战 如果你正准备学 AI Agent但搜了一圈资料发现要么是“三分钟看懂 Agent”的概念科普要么是“教你用框架调接口”的 API 拼装那就说明你看的内容还不够落地。真正的 Agent 开发最核心的并不是某个框架、某个模型而是你能不能把一个“会思考的模型”接进你自己的业务工具里让它真的帮你干活。本篇文章就按照“应用解读 项目实战”两条线从 0 到 1 带你拆一个最小可运行的 AI Agent 项目看懂运行逻辑也拿得出手。先说我对 2026 年 AI Agent 学习路线的判断越早上手做“最小闭环”成长越快。现在很多岗位描述里写“熟悉 Agent 开发”“了解 ReAct 原理”本质上都是在问一件事——你是否理解模型、工具、编排这三个层次的关系。本文不会让你上来就啃 LangChain 源码而是先用一个不到 300 行的 Python 项目把 Agent 的完整运行链路跑通然后再讲清楚它背后的原理以及工程化落地的坑。你不需要先成为提示词专家也不需要会微调模型只需要有一点 Python 基础能打开命令行就能跟上这篇文章。读完以后你会得到一套可复制的 Agent 项目代码一套工具调用的安全设计思路以及一份能用来准备面试或实际项目开发的排错清单。1. 这篇文章真正要解决的问题如果你去搜“AI Agent”看到的一定是一堆名词ReAct、Function Calling、多 Agent 协作、记忆机制、规划能力。看的时候觉得懂了合上文章还是不会写代码。这是目前 AI Agent 学习最普遍的问题——信息密度高但项目闭环少。这篇文章要解决的痛点有三个第一个痛点只会用 ChatGPT 聊天不知道怎么让模型主动调用工具。很多人以为 Agent 就是“模型更聪明了什么都能答”实际上它是“模型在推理过程中能决定去调用某个外部函数”。这个动作听起来简单但真正写代码时会遇到一连串问题工具怎么定义参数怎么传模型返回的结果怎么解析一旦出错怎么恢复第二个痛点会用 LangChain 但只停留在复制粘贴。LangChain 这类框架确实封装了大量能力但也把 Agent 的运行逻辑藏在了黑盒里。一旦出现 bug你很难判断是模型问题、工具问题还是编排问题。我见过不少同学简历上写“熟悉 LangChain”面试官问“Agent 的循环依赖怎么处理”就卡住了。所以本文故意不用框架而是基于大模型的 Function Calling 接口手写一个最小执行器。第三个痛点缺少从“代码跑通”到“项目落地”的工程意识。写一个 demo 和做一个可部署的服务中间还隔着很多东西环境隔离、密钥管理、超时控制、工具权限边界、日志追踪。文章后面会专门用一章来交代最佳实践。什么背景的读者最该看这篇文章后端开发想把 Agent 能力嵌入自己的 Spring Boot、FastAPI 服务测试开发想用 Agent 做自动化测试用例生成、缺陷分析前端开发想给自己的应用增加“自然语言操作数据”的交互层产品和运营不需要写核心算法但需要理解 Agent 能力边界能和技术有效沟通正在准备 Agent 相关面试的求职者需要把原理讲清楚也要能现场写一个最小实现。说到底2026 年判断一个开发者“懂不懂 Agent”看的不是他收藏了多少资料而是他能不能从一个空目录开始把一个带工具的 Agent 服务搭起来。2. AI Agent 的核心概念它和聊天机器人到底差在哪要理解 Agent先要破除一个直观误解ChatGPT 本身不是一个 Agent它只是一个对话模型。你问它“帮我查一下服务器日志”它只能告诉你“你可以用 grep 命令”但它不会真的去执行 grep。Agent 则不同它可以在推理后决定调用一个工具把工具执行的结果拿回来继续推理最终给你一个答案。用一句话概括Agent 大模型 工具 循环决策。这里拆开看四个核心要素模型Model负责理解意图、生成推理、决定下一步行动。2026 年几乎所有主流模型都支持 Function Calling这是 Agent 能落地的关键能力。工具Tool模型与外部世界交互的“手脚”。可以是查数据库、调 API、执行搜索、读写文件、发消息。模型的训练数据是固定的工具让它可以获取实时信息并产生真实影响。记忆Memory短期记忆就是当前对话上下文长期记忆一般靠向量数据库保存历史知识。最小实现里短期记忆已经足够。规划Planning把一个复杂任务拆成多个步骤。简单 Agent 靠 ReAct 循环自动规划复杂 Agent 会显式生成计划再逐步执行。再看几个容易混淆的词概念常见误解更准确的理解ChatBot能回答问题就是 Agent只输出文本不产生外部动作Function Calling模型“会”调用函数模型只是输出结构化的调用意图实际执行靠你的代码ReAct一种框架一种“推理-行动-观察”的循环模式Agent 框架等于 LangChainLangChain 只是工具Agent 的核心是编排逻辑Tool由模型内置工具由开发者实现模型只会描述“想调哪个”这里真正容易踩坑的是 Function Calling 的边界。模型并不会真的去连接数据库、发送 HTTP 请求它只是在生成的内容里多了一种结构化字段。例如模型可能返回{ name: safe_calc, arguments: {\expression\: \23*4\} }你的代码解析这个字段执行真正的加法逻辑再把结果作为消息回传给模型。这个“发起调用 - 执行工具 - 回传结果 - 继续推理”的过程就是 Agent 最核心的循环。ReAct 思想也值得单独说。ReAct 是 Reasoning Acting它强调模型不能只“想”不“做”也不能只“做”不“想”。每一轮循环里模型先根据当前信息判断下一步该干什么然后行动观察行动结果再继续推理。你会在下面的代码里实际看到这个模式。3. 从 0 到 1 的落地思路为什么先手写一个最小 Agent既然框架很多为什么还要手写因为手写一遍你会被迫搞清楚三个问题工具如何被描述、模型输出如何被解析、多轮上下文如何被组织。这三个问题恰恰是任何一个 Agent 框架都要替你解决的核心。先不用框架跑通之后再切换到 LangChain、LangGraph 或者 Spring AI你会发现自己是在“降维使用”而不是被框架牵着走。本文的项目叫minimal_agent_demo整体结构如下minimal_agent_demo/ ├── tools.py # 工具实现与注册 ├── agent_core.py # 最小 ReAct 执行器 ├── main.py # FastAPI 封装把 Agent 暴露成 HTTP 接口 └── docs/ # 本地知识库目录用于演示文档检索工具演示场景选了一个贴近日常的组合Agent 需要具备三个能力——获取当前时间、做数学计算、检索本地文档。这三个能力分别代表了 Agent 的常见工具类型系统类工具、计算类工具、检索类工具。在技术选型上我采用 OpenAI 兼容的 Chat Completions 接口。这样做的原因是兼容接口非常普遍无论是官方模型服务还是各种私有化部署的模型网关大都支持这一协议。项目代码不绑定某个具体厂商你只需要配置模型服务地址和 API Key 就能运行。关于“为什么不用 LangChain”还要多说一句。并不是 LangChain 不好而是对学习者来说先用最少的依赖理解原理更符合“从 0 到 1”的目标。等你看懂了手写版本再去看 LangChain 的 AgentExecutor 源码思路会非常流畅。4. 环境准备与项目初始化项目依赖很少只有三个openai官方 Python SDK、fastapi、uvicorn。其中 fastapi 和 uvicorn 只在最后封装 HTTP 服务时用到如果你只想跑命令行版本可以先不装。操作系统不限macOS、Linux、Windows 都可以。建议使用 Python 3.10 及以上版本并且创建独立的虚拟环境。mkdir minimal_agent_demo cd minimal_agent_demo python -m venv .venv # Windows .venv\Scripts\activate # macOS / Linux source .venv/bin/activate pip install openai1.0 fastapi uvicorn安装完成之后需要配置模型服务的访问变量。强烈建议不要直接在代码里写死 API Key而是通过环境变量注入避免不小心提交到 Git 仓库。# macOS / Linux export OPENAI_API_KEY你的API-KEY export OPENAI_BASE_URL你的兼容模型服务地址例如 https://your-model-service.example.com/v1 # Windows PowerShell # $env:OPENAI_API_KEY 你的API-KEY # $env:OPENAI_BASE_URL 你的兼容模型服务地址如果你的模型服务就是官方默认地址OPENAI_BASE_URL可以不设置代码里会按默认地址处理。但要注意本项目的 Agent 依赖 Function Calling 能力所以模型服务需要支持 tools 参数这一点在选型时要提前确认。启动时建议先做一个连通性测试用下列命令确认 SDK 能正常访问模型python -c from openai import OpenAI; import os; client OpenAI(api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL) or None); print(client.chat.completions.create(model你的模型名, messages[{role:user,content:hi}]).choices[0].message.content)如果这里能返回结果说明基础环境没问题可以直接进入下一步。否则先排查地址、Key 和网络连通性不用急着往下写代码。5. 工具设计Agent 的“手脚”从哪来工具是 Agent 价值和风险并存的来源。一方面没有工具模型只能凭训练知识回答无法触达你的业务系统另一方面如果给 Agent 挂载了危险工具比如任意命令执行、删库接口一旦模型误调用后果会很严重。下面这段代码定义三个工具函数。注意安全计算器的实现方式我没有直接用eval而是用 Python 的ast模块解析表达式只允许白名单内的运算节点。这是一个非常值得保留的工程习惯——凡是给 Agent 用的工具都要尽量缩小能力边界。# 文件路径minimal_agent_demo/tools.py import ast import operator from datetime import datetime from pathlib import Path def get_current_time() - str: 返回当前的日期和时间格式为 YYYY-MM-DD HH:MM:SS。 return datetime.now().strftime(%Y-%m-%d %H:%M:%S) # 安全计算器只允许四则运算、取模、整除和幂运算避免 eval 注入风险 _ALLOWED_OPS { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.Mod: operator.mod, ast.FloorDiv: operator.floordiv, ast.Pow: operator.pow, } def safe_calc(expression: str) - float: 计算一个仅包含数字和四则运算的数学表达式。 tree ast.parse(expression, modeeval) for node in ast.walk(tree): if isinstance(node, ast.Expression): continue if isinstance(node, ast.Constant): continue if type(node) not in _ALLOWED_OPS: raise ValueError(f表达式包含不允许的语法: {type(node).__name__}) result eval(compile(tree, filename, modeeval), {__builtins__: {}}, {}) return float(result) def search_docs(keyword: str, base_dir: str ./docs) - str: 在 docs 目录下的 .md / .txt 文件中搜索包含关键词的片段。 results [] for path in Path(base_dir).rglob(*): if path.suffix not in (.md, .txt): continue try: lines path.read_text(encodingutf-8).splitlines() except Exception: continue for idx, line in enumerate(lines, 1): if keyword in line: results.append(f{path}:{idx}: {line.strip()}) if not results: return 没有找到相关文档。 return \n.join(results[:20])tools.py里的函数还只是普通 Python 函数模型并不知道它们的存在。要让模型知道“可以调用哪些工具”需要把工具描述成结构化的 schema这就是 Function Calling 里最关键的一步。# 文件路径minimal_agent_demo/tools.py继续追加 TOOLS_META [ { type: function, function: { name: get_current_time, description: 获取当前日期和时间。适合回答现在几点、今天是几号等问题。, parameters: { type: object, properties: {}, required: [] } } }, { type: function, function: { name: safe_calc, description: 计算数学表达式例如 23*4。适合需要精确计算的场景。, parameters: { type: object, properties: { expression: {type: string, description: 要计算的数学表达式} }, required: [expression] } } }, { type: function, function: { name: search_docs, description: 在本地知识库 docs 目录中搜索包含关键词的文档片段。, parameters: { type: object, properties: { keyword: {type: string, description: 要搜索的关键词} }, required: [keyword] } } }, ] TOOLS_DISPATCH { get_current_time: lambda args: get_current_time(), safe_calc: lambda args: str(safe_calc(args[expression])), search_docs: lambda args: search_docs(keywordargs[keyword]), }TOOLS_META发给模型告诉它“环境里有哪些工具可用”。TOOLS_DISPATCH是开发者自己的函数路由表等模型真的喊出“我要调用 safe_calc”时程序从这个字典里找到对应的函数并执行。这里有三个值得注意的设计和单纯调 API 不同第一description很重要。模型不会看你的函数源码它只知道这些文字描述。描述写得越清楚模型选对工具的概率越高。第二TOOLS_DISPATCH采用字典映射而不是一堆 if-else方便以后动态注册新工具。第三search_docs的base_dir没有暴露在 schema 里这样即使模型“想”去扫描其他目录也没有参数入口。这是权限收敛的典型做法。6. Agent 核心循环手写一个最小 ReAct 执行器有了工具下面实现 Agent 的核心类。这个类的任务很简单接收用户问题进入一个最多执行 N 轮的循环每一轮把当前消息列表发给模型如果模型要求调用工具就执行工具并把结果加入消息列表如果模型直接给出最终回答就返回结果。# 文件路径minimal_agent_demo/agent_core.py import json import os from openai import OpenAI from tools import TOOLS_DISPATCH, TOOLS_META SYSTEM_PROMPT 你是一个运行在本地的最小 AI Agent 助手可以帮助用户查询时间、计算数学表达式以及检索本地知识库。 请根据用户的问题决定是否需要调用工具 - 如果需要工具返回对应的 function_call - 如果已有工具结果请根据工具结果组织自然语言回答 - 如果不需要工具直接回答用户。 回答请保持简洁不要重复工具输出中不必要的信息。 class MinimalAgent: def __init__(self, model: str 你的模型名, max_iters: int 5): api_key os.getenv(OPENAI_API_KEY) base_url os.getenv(OPENAI_BASE_URL) or None if not api_key: raise ValueError(请先设置环境变量 OPENAI_API_KEY) self.client OpenAI(api_keyapi_key, base_urlbase_url) self.model model self.max_iters max_iters def run(self, user_input: str) - str: messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input}, ] step 0 while step self.max_iters: step 1 print(f[Agent] 第 {step} 轮推理 ...) response self.client.chat.completions.create( modelself.model, messagesmessages, toolsTOOLS_META, ) message response.choices[0].message # 1. 不需要调用工具直接返回最终回答 if not message.tool_calls: return message.content or # 2. 模型请求调用工具先把这条 assistant 消息追加进上下文 messages.append({ role: assistant, content: message.content, tool_calls: [ { id: tc.id, type: function, function: { name: tc.function.name, arguments: tc.function.arguments, }, } for tc in message.tool_calls ], }) # 3. 依次执行工具并把执行结果作为 tool 消息回传 for tc in message.tool_calls: print(f[Agent] 调用工具: {tc.function.name}, 参数: {tc.function.arguments}) try: tool_output TOOLS_DISPATCH[tc.function.name]( json.loads(tc.function.arguments) ) except Exception as e: tool_output f工具执行出错: {e} messages.append({ role: tool, tool_call_id: tc.id, content: tool_output, }) # 4. 达到最大轮数之后让模型基于已有上下文做一次最终总结 final_response self.client.chat.completions.create( modelself.model, messagesmessages, ) return final_response.choices[0].message.content or 达到最大推理轮数任务未完成。 if __name__ __main__: agent MinimalAgent() while True: question input(请输入问题输入 q 退出) if question.lower() q: break print(回答:, agent.run(question))这段代码就是 ReAct 循环的骨架。它的核心逻辑可以抽象成四步第一步把系统提示词、用户问题和所有历史消息一起发送给模型并带上工具 schema。第二步检查模型返回结果里是否有tool_calls。没有说明模型已经可以根据现有信息回答直接返回。第三步如果模型决定调用工具先把 assistant 消息连同 tool_calls 完整存入消息列表再逐个执行工具。执行结果以role: tool的消息回传并携带对应的tool_call_id让模型知道这个结果属于哪一次调用。第四步回到循环顶部继续推理。如果达到最大轮数额外让模型做一次最终总结避免返回空内容。有个细节值得强调连续多轮工具调用时消息列表的顺序非常重要。大模型并不是“记住”了工具结果而是每次从你传的 messages 里重新理解上下文。如果你在追加 assistant 消息时遗漏了tool_calls字段或者 tool 消息没有对应上tool_call_id推理就会中断或者报错。这是手写 Agent 时最常见的技术债务。另一个细节是工具异常处理。我在执行工具时用 try-except 包裹并把错误信息作为 tool 结果返回。这样做的好处是即使某个工具运行失败Agent 也能继续推理而不是整个流程崩溃。真正生产中错误信息甚至会被模型学习帮它调整下一步策略。7. 用 FastAPI 把 Agent 包装成可调用的服务到了这里命令行版 Agent 已经能跑了。但如果要接入真实业务通常需要把它包装成 API 服务。下面用 FastAPI 写一个最小接口只暴露一个POST /agent/ask。# 文件路径minimal_agent_demo/main.py from fastapi import FastAPI from pydantic import BaseModel from agent_core import MinimalAgent app FastAPI(titleMinimal Agent API) try: agent MinimalAgent() except ValueError as e: agent None print(f警告: {e}) class QueryRequest(BaseModel): question: str class QueryResponse(BaseModel): answer: str app.post(/agent/ask, response_modelQueryResponse) def ask(req: QueryRequest): if agent is None: return QueryResponse(answerAgent 未初始化请先检查环境变量 OPENAI_API_KEY。) answer agent.run(req.question) return QueryResponse(answeranswer)这里我把MinimalAgent()初始化放进了 try-except。因为main.py一旦被 uvicorn 加载如果环境变量缺失整个服务会启动失败。对工程来说与其让服务崩溃不如把它降级为一个可读的错误提示。这种“防御式初始化”在微服务场景里很有参考价值。启动服务uvicorn main:app --reload --port 8000看到类似下面的日志说明服务已经就绪INFO: Uvicorn running on http://127.0.0.1:8000 INFO: Application startup complete.然后打开另一个终端用 curl 调用接口curl -X POST http://127.0.0.1:8000/agent/ask \ -H Content-Type: application/json \ -d {question: 现在几点了}也可以借助 FastAPI 自带的交互文档在浏览器打开http://127.0.0.1:8000/docs直接点击接口调试对新手更友好。到这里你已经从“一个会对话的模型”走到了“一个能对外提供能力的 Agent 服务”。这个服务的前景是无限的你可以把入参改成业务字段把工具换成订单查询接口就变成了一个能处理业务问题的“数字员工”。8. 运行结果与效果验证按前面的步骤先把 docs 目录建好写一个测试文档mkdir docs echo AI Agent 的核心是模型、工具与循环决策。 docs/agent_notes.txt然后运行命令行版python agent_core.py在交互界面输入“现在几点了”正常输出类似[Agent] 第 1 轮推理 ... [Agent] 调用工具: get_current_time, 参数: {} 回答: 现在是 2026-06-14 10:23:45。再输入“计算 23*4 的结果”Agent 会先调用 safe_calc再结合计算结果回答而不是自己硬算。这说明函数调用链路是通的。最后输入“搜索文档中关于 Agent 的内容”Agent 会调用 search_docs从agent_notes.txt中找到匹配片段并组织回答。如果用 FastAPI 方式验证curl 的返回格式类似{answer:现在是 2026-06-14 10:23:45。}如何判断 Agent 是否真的工作正常不是只看返回值而是看两件事第一模型是否在需要时主动发起了工具调用。如果输入“现在几点了”模型没有调用get_current_time而是直接给出一个编造的时间说明 Function Calling 没有生效或者模型不支持 tools 参数。第二工具结果是否正确回传到了上下文。如果模型明明调用了safe_calc但没有基于工具结果回答而是自己又算了一遍多半是消息列表组织有误tool 结果没有真正被模型感知。如果启动失败先按顺序排查三件事环境变量是否生效、模型服务地址是否能访问、模型是否支持 Function Calling。不要一上来就怀疑代码逻辑绝大多数初学问题都出在前两层。9. 常见问题与排查思路手写 Agent 的过程中有几个问题出现频率极高。下面用表格整理出来方便你对照排查。问题现象可能原因排查方式解决方案报错Please set OPENAI_API_KEY环境变量未设置或未在当前终端生效在终端执行echo $OPENAI_API_KEY重新 export 环境变量或把配置写入.env文件并加载模型返回内容为空模型不支持 Function Calling或 tools 参数被忽略查看返回的原始 message确认tool_calls字段是否存在换用支持 function calling 的模型或检查模型服务兼容性工具被调用但报错参数格式不匹配或函数内部抛异常看异常信息打印json.loads(tc.function.arguments)的结构在工具函数内部加 try-except按实际参数调整解析逻辑Agent 陷入死循环工具结果不符合模型预期模型反复尝试给循环设置 max_iters打印每一轮消息优化工具返回格式让结果更结构化便于模型判断多轮工具调用后上下文错乱assistant 消息缺少 tool_calls 字段或 tool 消息未关联 tool_call_id检查发送给模型的 messages 结构严格按照 OpenAI 的协议要求组织消息API 请求超时模型推理时间较长或网络不稳定查看请求日志统计耗时提高 timeout 参数或者把模型换成推理速度更快的版本搜索文档找不到内容目录路径不对或文件编码不是 UTF-8在 search_docs 里打印实际搜索的目录确认当前工作目录或使用绝对路径看到这些现象时记忆一个原则先定位是模型层、工具层还是编排层的问题。模型层看返回内容质量工具层看函数执行结果编排层看 messages 结构。三个层面分开排查定位会快很多。10. 最佳实践与工程化建议代码跑通只是起点真正进入生产环境还需要考虑可维护性、权限安全和可观测性。下面这些建议来自实际 Agent 项目里常见的教训。10.1 安全边界要设得足够小Agent 的工具调用一旦失控影响面远超普通代码异常。一个读文件工具如果路径由模型任意指定就可能变成任意文件读取一个命令执行工具如果没做白名单就相当于把服务器权限交给了外部输入。建议遵循这些原则不要给 Agent 开放任意 shell如果必须执行命令把命令列表限制在白名单内。文件工具只允许访问指定目录并用路径标准化校验绕过../之类的跳转。涉及数据库、支付、删除等高风险操作不要直接让 Agent 执行而是让 Agent 生成“待确认指令”由人工审核后执行。API Key 一律通过环境变量或密钥管理服务注入禁止写在代码和日志里。10.2 工具描述就是你的接口文档模型对工具的理解完全来自description字段。同一个函数如果描述写“搜索文档”模型可能不知道什么时候该用如果描述写成“当用户提供关键词时在本地知识库 docs 目录中检索并返回相关行文本”模型就会更准确地触发它。写工具描述时尽量包含三个信息工具解决什么问题、什么场景下使用、输入参数的具体含义。不要惜字如金也不要写模型用不上的内部实现细节。10.3 引入超时、轮数与并发控制Agent 的循环比普通 HTTP 请求更难以预估耗时。一个复杂任务可能触发 3 到 5 次模型调用单次 10 秒总时长可能超过 30 秒。对外提供服务时一定要设置超时上限最好用异步任务或者流式返回避免请求长时间占用连接。max_iters是防止死循环的最后防线建议按业务复杂度调整。简单问答 3 轮足够复杂任务可以放宽到 8 轮。另外要考虑并发当多个用户同时调用 Agent 时工具执行是否会相互影响那些带全局状态的工具特别容易踩这个问题。10.4 日志与可观测性Agent 的调试难度远高于普通代码因为它多了一层“模型为什么这样决策”的不确定性。建议在代码里把每一轮的 assistant 回复、工具调用、工具结果都记录下来至少包含用户请求 ID模型名称推理轮次工具调用名称和参数工具执行耗时和结果有了这些日志当线上回答表现异常时你才能判断是模型决策错误、工具结果错误还是上游数据问题。简单项目可以用 print 或 logging 输出生产环境建议接入可观测平台。10.5 用一组黄金测试集做回归模型不是确定性代码同一个问题在不同时间可能得到不同回答。为了保证业务稳定可以准备一组“黄金测试用例”覆盖每个工具的核心场景和边界场景。每次修改工具描述、替换模型或调整 Prompt 时都用同一组用例跑一遍回归。例如本文项目至少应该覆盖这些测试python agent_core.py EOF 现在几点了 计算 100/8 的结果 搜索文档中关于模型的内容 EOF把输出人工确认一遍再考虑发布。这种方式成本很低但能拦住大部分明显的劣化。11. 总结与学习路线图现在回到标题里的问题AI Agent 到底该怎么学怎么实战这篇文章给出的答案是先别急着追新框架先理解 Agent 的底层运行逻辑。你通过tools.py学会了工具设计和注册通过agent_core.py学会了 ReAct 循环和 Function Calling 的消息组织通过main.py学会了把 Agent 变成可调用的服务。这三件事本质上就是 2026 年大多数 Agent 岗位要求的核心能力。下一步可以按这个路线继续深入第一步把本文的项目扩展到自己熟悉的领域。如果你是后端开发可以把 safe_calc 换成订单查询接口如果你是测试开发可以把 search_docs 换成缺陷库检索。替换工具的过程就是对 Agent 理解加深的过程。第二步复用框架但读懂源码。当你跑通手写版本后再去看 LangChain 的 AgentExecutor、LangGraph 的 StateGraph、Spring AI 的 Tool Calling就不会被包装层迷惑。它们解决的核心问题和本文的循环本质上是一致的只是把可复用性、可观测性和复杂状态管理做得更完善。第三步补上记忆与规划能力。手写版只用了短期对话上下文真实项目往往需要向量库存储历史知识需要任务分解流程处理复杂问题。你可以从例如向量检索入门再尝试多 Agent 协作模式。第四步准备面试时把“运行逻辑”讲透。面试官问到 Agent 时你可以直接画出这段循环模型通过 tools 描述感知可用工具在推理中决定是否调用工具代码执行工具并把结果回填上下文模型再基于新上下文继续推理或生成最终答案。这条链路讲清楚了比背十个名词都管用。最后提醒一件事Agent 工具不是越多越好而是越收敛越好。真正可用的 Agent背后一定有一套清晰的工具边界、完善的日志链路和严格的人工审核机制。这也是从“能跑 demo”到“敢上生产”之间最需要补的课。
返回列表