
1. AI Agent 到底是什么从 LLM 到 Agent 的进化这两年AI 领域最热的关键词已经从“大模型”本身慢慢转移到了“AI Agent”。很多人第一次听到这个概念时会误以为 AI Agent 就是“接入了大模型的聊天机器人”或者“能自动回复消息的智能客服”。实际上这两者虽然有重叠但差异非常大。如果用一个通俗的比喻来理解大模型LLM本身像一个知识渊博但“呆在书房里”的顾问你问它问题它基于训练数据给你答案。它能写文章、写代码、做翻译但它无法替你执行任何现实世界中的操作。AI Agent 则像是一个“有手有脚、有目标感”的智能助理。它不只是回答你的问题而是把任务拆解成步骤主动调用工具、查询数据、执行操作并在过程中根据反馈调整策略直到完成一个最终目标。举个例子你让大模型“帮我查一下明天北京的天气并提醒我是否需要带伞”普通的大模型只能告诉你“我无法实时查询天气”。而一个 AI Agent 会先调用天气查询接口拿到明天下雨的概率再结合“下雨需要带伞”的规则最后输出一条完整的提醒。整个过程需要模型、工具、决策逻辑、结果验证四部分协同工作。从专业定义来看AI Agent 是“以大模型为推理大脑通过规划Planning、记忆Memory、工具调用Tool Use和行动Action四要素完成特定目标任务的智能体系统”。它不仅是调用 API而是把大模型嵌入到一个完整的工作循环中。理解这个区别是进入 Agent 开发的第一步。下面我们会从环境搭建开始逐步拆解 AI Agent 的底层原理然后手把手实现一个自定义智能体。2. 开发环境准备搭建第一套 Agent 工程2.1 环境依赖清单开发 AI Agent 本质上还是写代码所以一套干净、稳定的 Python 开发环境是必须的。本文的实战示例将以 Python 为主要语言核心依赖如下依赖库用途说明openai调用 LLM 接口支持 OpenAI 官方接口和兼容性 APIlangchain简化 Agent、工具、记忆的编排逻辑可选但推荐python-dotenv管理 API Key 等环境变量避免硬编码fastapi将 Agent 包装成 Web 服务进阶可选httpx / requests自定义工具函数中发起 HTTP 请求版本方面本文示例以 Python 3.9 及以上版本为基准。实际上不同大模型 SDK 的版本差异较大建议读者根据自己实际使用的模型服务商调整依赖版本重点理解整体开发流程。需要特别说明的是本文核心代码思路不绑定单一厂商。如果你使用的是国内大模型平台的 OpenAI 兼容接口只需要修改 base_url 和 api_key 即可复用。2.2 创建项目结构建议先按下面的目录结构组织工程后续所有代码示例都基于这个结构展开ai-agent-tutorial/ ├── .env # 存放 API Key 等敏感信息 ├── requirements.txt # 项目依赖清单 ├── agent/ │ ├── __init__.py │ ├── llm.py # 大模型调用封装 │ ├── tools.py # 工具函数定义 │ ├── memory.py # 记忆管理暂用简单列表 │ └── agent.py # Agent 核心逻辑 ├── main.py # 入口文件演示交互循环 └── README.md这样拆分的好处是模型接入、工具注册、记忆逻辑、流程控制各自独立后续想替换模型或新增工具时不需要大面积改动代码。2.3 安装依赖首先创建虚拟环境并激活cd ai-agent-tutorial python -m venv venv source venv/bin/activate # Windows 下为 venv\Scripts\activate然后创建requirements.txt并安装依赖openai1.0.0 langchain0.1.0 python-dotenv1.0.0 httpx0.24.0执行安装命令pip install -r requirements.txt以上版本号只是示例区间真实的版本兼容情况请以你的 Python 版本和模型服务商 SDK 文档为准。核心思路是尽量使用较新的 SDK因为早期版本在 Function Calling 和异步支持方面可能有差异。3. AI Agent 核心原理拆解在动手写代码之前我建议先把 Agent 的四个核心模块理解透。很多人照着教程能跑通 Demo但一旦要解决线上问题就束手无策根本原因是原理没搞懂。3.1 LLM 与 Agent 的分工关系简单说LLM 是 Agent 的“大脑”负责理解、推理和生成决策。但 LLM 本身没有“执行力”。Agent 的作用是在 LLM 外面包一层“执行循环”接收用户目标。把目标交给 LLM让模型判断需要调用什么工具、需要什么参数。代码侧执行工具函数拿到真实结果。把结果返回给 LLM让模型判断任务是否完成。如果未完成继续循环如果完成生成最终答案。这个循环就是我们常说的 ReActReasoning Acting模式即“推理-行动-观察”交替进行。ReAct 的核心价值在于它让模型不再只是“一次性输出答案”而是“边想边做做完再看看完再想”。3.2 工具调用Function Calling机制工具调用是 Agent 区别于普通聊天机器人的关键能力。大模型本身无法查天气、没法操作数据库、没法调用内部 API但我们可以通过“Function Calling”把外部能力暴露给模型。原理是这样的开发者在请求中声明一批 JSON 格式的工具描述包括函数名、参数说明、功能描述。模型的输出不再直接是最终答案而是“要不要调用某个函数、参数是什么”。代码侧负责真正执行这个函数并把执行结果作为“观察”喂回模型。这种设计最大的优势是安全可控。真正执行代码的是我们自己模型只负责“决定调用哪个函数、传什么参数”不会直接操作系统。3.3 记忆机制短期与长期Agent 要解决复杂任务离不开记忆。简单场景下记忆就是一个历史消息列表把之前的对话轮流塞进上下文让模型知道“前面发生过什么”。这种方法叫做短期记忆实现最简单但受限于模型的上下文窗口长度。当对话历史太长超出上下文窗口时就需要引入长期记忆方案。常见思路包括用向量数据库存储历史消息按相关性检索摘要后注入上下文。定期对历史记录做摘要压缩只保留关键信息。把结构化信息如用户偏好、任务状态单独存储用的时候再读取。在本文的实战部分我们先用列表实现短期记忆。工程化落地时再考虑接向量数据库。3.4 规划与任务分解一个真正好用的 Agent必须能把大任务拆成小步骤。比如用户说“帮我调研一下 RAG 技术的发展趋势并输出一份报告”Agent 不能一次性生成一份高质量报告而是应该拆解为搜索相关资料。整理核心观点。撰写报告大纲。分层填充报告内容。校验和输出。这个拆解过程可以由 LLM 自主完成也可以由开发者在 Prompt 中预定义“标准作业流程”。实际项目中我建议采用“预定义流程 模型动态调整”的混合模式这样稳定性更高不会让模型完全自由发挥导致跑偏。4. 从零实现自定义智能体完整实战案例下面进入本文的核心环节写一个可运行的自定义智能体。我们会实现一个支持天气查询和简单计算功能的 Agent代码保持精简但流程完整读者可以直接在此基础上扩展。4.1 配置环境变量在项目根目录创建.env文件# 模型服务商 API Key OPENAI_API_KEYyour_api_key_here # 如果使用 OpenAI 兼容接口修改为对应地址 OPENAI_BASE_URLhttps://api.openai.com/v1 # 模型名称 LLM_MODELgpt-3.5-turbo注意不同服务商的 API Key 获取方式不一样请根据你自己的账号体系配置。实际项目中不要把 Key 提交到 Git 仓库.env必须加入.gitignore。4.2 封装 LLM 调用模块文件路径agent/llm.pyimport os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) def chat_with_llm(messages, toolsNone, tool_choiceauto): 统一的 LLM 调用入口。 参数说明 - messages: 历史消息列表格式为 OpenAI Chat Completions 标准格式 - tools: 工具描述列表每个工具包含 type、function 等字段 - tool_choice: 控制模型是否必须调用工具默认 auto 表示由模型自行判断 params { model: os.getenv(LLM_MODEL, gpt-3.5-turbo), messages: messages, } if tools: params[tools] tools params[tool_choice] tool_choice response client.chat.completions.create(**params) return response.choices[0].message这个模块的核心价值是把模型调用统一封装。后续无论想增加重试机制、日志记录还是切换不同模型都只需改这一个文件。4.3 定义工具函数文件路径agent/tools.pyimport json import httpx def get_weather(city: str) - str: 查询城市天气。这里为演示目的使用一个公开的天气 API。 注意实际项目中请替换为你自己的天气服务地址。 url fhttps://api.open-meteo.com/v1/forecast?latitude39.9longitude116.4current_weathertrue # 真实场景中应该根据 city 查询经纬度。这里简化为调用固定城市。 try: resp httpx.get(url, timeout10) data resp.json() temp data[current_weather][temperature] wind_speed data[current_weather][windspeed] return json.dumps({city: city, temperature: temp, wind_speed: wind_speed}, ensure_asciiFalse) except Exception as e: return json.dumps({error: str(e)}, ensure_asciiFalse) def calculator(expression: str) - str: 计算数学表达式。注意这里使用 eval 仅作为教学示例 生产环境中必须使用安全的表达式计算库如 asteval。 # 安全校验只允许数字、运算符、括号、小数点 import re if not re.fullmatch(r[\d\-*/().\s], expression): return 非法表达式 try: result eval(expression) # 生产环境请替换为安全方案 return str(result) except Exception as e: return f计算错误: {e} # 工具注册表Agent 通过这个字典找到对应的执行函数 TOOL_FUNCTIONS { get_weather: get_weather, calculator: calculator, } def get_tool_schemas(): 返回大模型可识别的工具 JSON Schema 列表。 return [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气包括温度和风速, parameters: { type: object, properties: { city: { type: string, description: 城市名称如 北京、上海, } }, required: [city], }, }, }, { type: function, function: { name: calculator, description: 计算数学表达式如 1 2 * 3, parameters: { type: object, properties: { expression: { type: string, description: 合法的数学表达式, } }, required: [expression], }, }, }, ]这里需要特别说明一点工具函数的描述写得好不好直接影响模型判断的准确率。在 Function Calling 机制中模型是通过函数名和描述来理解“这个工具能干什么”的。描述写得模糊模型就会在多个工具之间犹豫甚至返回错误的参数。所以工具描述本身就是一种 Prompt Engineering。4.4 实现 Agent 核心循环文件路径agent/agent.pyfrom .llm import chat_with_llm from .tools import get_tool_schemas, TOOL_FUNCTIONS import json class SimpleAgent: 一个最简单的 ReAct 模式 Agent。 它只做三件事 1. 调用大模型判断下一步动作。 2. 如果需要工具就执行工具并回传结果。 3. 如果不需要工具就输出最终回答。 def __init__(self, system_prompt: str None): self.messages [] if system_prompt: self.messages.append({role: system, content: system_prompt}) self.tools get_tool_schemas() def run(self, user_input: str, max_steps: int 5) - str: 运行 Agent。 参数说明 - user_input: 用户输入 - max_steps: 最大循环步数防止 Agent 陷入死循环 # 1. 添加用户消息 self.messages.append({role: user, content: user_input}) current_step 0 while current_step max_steps: current_step 1 print(f[Step {current_step}] 调用大模型判断下一步...) # 2. 调用大模型 assistant_message chat_with_llm(self.messages, toolsself.tools) # 3. 模型判断是否需要调用工具 if assistant_message.tool_calls: # 4. 执行工具调用 self.messages.append(assistant_message) for tool_call in assistant_message.tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) print(f[Tool] 调用 {function_name}, 参数: {function_args}) # 从注册表获取并执行函数 if function_name in TOOL_FUNCTIONS: function_result TOOL_FUNCTIONS[function_name](**function_args) else: function_result f未知工具: {function_name} # 5. 把工具结果以 tool 角色消息回传 self.messages.append({ role: tool, tool_call_id: tool_call.id, content: function_result, }) # 继续循环让模型基于工具结果做下一步判断 continue else: # 没有工具调用说明模型认为任务已完成 final_answer assistant_message.content self.messages.append(assistant_message) return final_answer return 已达到最大步数任务停止。请尝试把任务拆得更细一些。这个 Agent 的核心逻辑就是前面说的 ReAct 循环。代码里有两个非常关键的设计第一max_steps的限制。真实的 Agent 开发中模型可能出现“反复调用同一个工具却得不到正确结果”的死循环。如果不加步数上限程序会一直打调用浪费 token 和时间。因此给循环加一个硬性上限是工程底线。第二工具结果的回传格式。OpenAI 的 Function Calling 要求工具结果必须包含tool_call_id并与模型的调用请求一一对应。如果你发现自己实现的 Agent“调用工具后模型理解不了结果”优先检查这个 ID 是否匹配。4.5 编写主程序文件路径main.pyfrom agent.agent import SimpleAgent # 给 Agent 一个系统提示词定义它的行为方式 SYSTEM_PROMPT 你是一个智能助手可以通过工具获取实时信息并回答问题。 当你需要查询天气时调用 get_weather 工具。 当你需要计算数学表达式时调用 calculator 工具。 如果工具结果不足以回答用户问题请继续调用工具直到获得足够信息。 回答时使用中文语言友好、简洁、准确。 def main(): agent SimpleAgent(system_promptSYSTEM_PROMPT) print(AI Agent 已启动。输入 exit 结束对话。) while True: user_input input(\n你: ) if user_input.lower() exit: print(再见) break try: response agent.run(user_input) print(f\nAgent: {response}) except Exception as e: print(f\nAgent 出错: {e}) if __name__ __main__: main()这一步把前面所有模块串了起来。用户输入一句话Agent 决定是直接回答、查天气、还是先算一个表达式的值。4.6 运行与验证在项目根目录执行python main.py尝试以下输入你: 北京今天天气怎么样预期流程模型判断需要查询北京天气。调用get_weather工具。拿到气温和风速后组织成自然语言回答。再试一个组合问题你: 帮我计算 (12 34) * 5 的结果另外上海今天冷吗预期流程模型判断需要调用calculator计算表达式。模型判断需要调用get_weather查询上海天气。模型综合两个工具的结果生成完整回答。注意一次循环中模型可以同时发起多个工具调用我们的代码用的是for tool_call in assistant_message.tool_calls来逐个处理这比逐个串行询问模型更高效。5. 常见报错与排查清单Agent 开发中坑点不少很多问题单独看文档很难定位。下面整理了一份高频问题排查清单基本覆盖了新手阶段的大部分困惑。问题现象常见原因解决思路模型返回“无法调用工具”或忽略工具工具 schema 格式错误或描述不清晰检查 tools 参数是否为标准 JSON Schema 格式完善工具的 description明确触发场景调用工具时报错provider rejected the request schema or tool payload工具参数格式与模型要求不一致或 SDK 版本过旧更新 openai SDK 到 1.0 以上用官方 API 调试工具打印完整的 tools 结构请求超时提示 model did not produce a response网络波动、模型推理时间过长、系统提示词引导模型出现死循环对请求设置超时与重试机制精简上下文检查是否因为工具无限循环导致单轮推理过长模型乱传参数工具执行失败工具参数缺少类型校验和默认值在函数入口增加参数校验把参数描述写得更具体例如“城市名称必须是中文全称”Agent 反复调用同一个工具但无法结束缺少任务完成条件或 max_steps 限制在系统提示词中明确“当获取足够信息后输出最终答案”代码中必须限制最大循环次数上下文越来越长导致 token 超限消息历史一直累积没有截断或摘要实现记忆管理删除最旧消息、做摘要压缩、或把历史写入向量数据库API Key 泄露到代码仓库直接把 Key 写在代码里忘记使用环境变量使用 .env 文件管理敏感信息加入 .gitignore必要时轮换 Key不同模型对工具调用的格式支持不一致切换模型后没有检查新模型的 Function Calling 兼容性先查阅目标模型的官方文档确认工具调用格式是否与 OpenAI 兼容6. 工程化最佳实践与安全边界上面的 Demo 跑通之后距离生产级 Agent 还有距离。下面这些经验是实际项目中最常踩的坑也是面试时的高频考点。6.1 Prompt 与工具描述的持续调优Agent 的效果好坏很大程度上取决于两个文本系统提示词和工具描述。它们共同决定了模型的“行为边界”。每次修改工具后都应该重新测试旧的用例确保没有回归。建议把测试用例沉淀成自动化回归集每次改动后统一跑一遍。具体来说系统提示词要明确“何时该调用工具、何时直接回答”。工具描述要写清“触发条件、参数含义、返回结果格式”。对核心场景可以在提示词中给出“标准输出格式”减少模型自由发挥的空间。6.2 安全边界工具执行必须可控Agent 能调用工具也就意味着攻击面扩大。日常开发中以下几个安全原则一定要遵守最小权限原则Agent 只能访问完成当前任务所必需的资源不要给它万能的管理员权限。输入校验所有传给工具函数的参数必须经过合法性校验。例如本文的calculator工具直接用eval只是教学演示生产环境必须使用asteval这类安全的表达式解析库。操作确认涉及删除、修改、资金操作等高风险动作必须有二次确认机制不能让 Agent 自动执行。日志留存每一次工具调用都要记录完整的输入与输出方便事后审计。6.3 可观测性与调试Agent 应用最大的难题是“不可控”。你很难定位一个错误回答到底是模型理解错了、工具数据错了、还是 Prompt 引导不对。因此可观测性设计必须从一开始就建立打印或记录每一步的完整请求与响应特别是 tool_calls 的内容。统计每一次调用的 token 消耗、耗时便于成本控制。为每次用户会话生成唯一的 trace_id串联整个 ReAct 循环。6.4 成本控制与性能优化Agent 比普通聊天多出多轮模型调用token 成本呈倍数增加。优化方向主要有缩短工具返回内容如果工具返回很长可以只提取关键字段回传。合并工具把多个功能合并成一个“综合查询”工具减少模型判断次数。缓存对高频、结果变化不大的查询如常见城市的天气做短时缓存。模型分级简单意图用便宜的小模型复杂推理才调用大模型。6.5 测试策略Agent 测试不同于传统软件测试重点在于“输出是否符合预期”而不仅仅是“程序是否报错”。常用手段包括单元测试单独测试每个工具函数确保输入输出正确。场景测试构造典型用户问题验证 Agent 是否能正确选择工具并完成任务。防御性测试给 Agent 发越权指令、模糊输入、恶意代码片段验证安全机制是否生效。回归测试修改 Prompt 或工具后跑一遍历史用例防止效果下降。7. 学习路线与进阶方向到这一步你已经从零实现了一个具备工具调用和基础记忆的 Agent。接下来的进阶路线建议按下面的层次逐步深入。第一层深入理解模型能力边界。熟练使用 Function Calling、JSON Mode、流式输出等基础能力理解不同模型在这些能力上的差异。多动手测试不要只看文档。第二层学习主流 Agent 框架。LangChain、LlamaIndex、Dify 等框架都封装了大量 Agent 基础能力。但建议先理解本文实现的手写循环再上手框架这样才能在框架出问题时知道底层是怎么回事。第三层掌握记忆与知识增强。把短期记忆升级为向量数据库 长期记忆结合 RAG 技术让 Agent 能访问企业私有知识库。这一块在真实项目中需求量最大尤其是知识库问答型 Agent。第四层多 Agent 协作。把一个大 Agent 拆成多个专职 Agent由调度 Agent 分配任务。这能解决单个 Agent 上下文过长、角色冲突的问题是 Agent 应用走向复杂化的必经之路。第五层生产级工程能力。围绕 Agent 建设完整的评估、监控、告警、灰度发布体系。目前业界公认“Agent 落地最大的瓶颈不是模型能力而是评测与稳定性”。学习过程中强烈建议多读一些开源项目的源码例如 LangChain 的 Agent Executor 实现看官方代码是怎么处理循环、异常和记忆协作的。也建议关注 Karpathy 在 LLM 学习和知识管理方面的方法论用工具化的方式管理自己沉淀的知识这一点对 Agent 开发者来说尤其重要。最后想说的是AI Agent 是一个实践性极强的领域光看教程不写代码永远停留在“懂了但不会做”的状态。强烈建议你按照本文的案例亲手敲一遍代码然后试着给 Agent 增加一个“查新闻”或“发送邮件”的工具你会真正找到感觉。如果本文对你有帮助欢迎收藏备用也欢迎在评论区交流你的 Agent 实战踩坑经历。