news 2026/10/7 6:28:13

大模型Agent开发实战:从零搭建规划、记忆与工具系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大模型Agent开发实战:从零搭建规划、记忆与工具系统

1. 大模型Agent到底是个什么东西

1.1 从“会聊天的模型”到“会干活的模型”

很多人第一次接触大模型,都是从对话框开始的:问一句,答一句,像个知识渊博但只会动嘴的顾问。而Agent(智能体)要解决的,恰恰是“动嘴”到“动手”这一步。你可以把它理解成给大模型装上了手脚和记事本——它不仅能理解你的意图,还能自己拆解任务、调用工具、记住中间结果、根据反馈调整下一步动作,直到把一件事真正办完。

举个最直观的例子。你让一个纯聊天模型“帮我查一下明天北京的天气,如果下雨就提醒我带伞”,它大概率会告诉你“我无法获取实时天气”。但一个Agent可以做到:先调用天气查询接口拿到数据,判断降水概率,再决定是否触发提醒逻辑,最后把结论告诉你。这中间的“调用接口—判断—决策—输出”链条,就是Agent的核心价值。

从技术定义上讲,大模型Agent是以大语言模型(LLM)作为推理内核,配合规划(Planning)、记忆(Memory)、工具使用(Tool Use)三大能力模块,能够在给定目标下自主完成多步任务的系统。它和普通Prompt调用的区别在于:普通调用是“一问一答”,Agent是“给定目标,自主循环”。

1.2 为什么现在值得学Agent开发

过去一年,Agent从概念验证快速走向工程落地。原因不复杂:模型本身的能力在提升,函数调用(Function Calling)协议逐渐标准化,工具生态越来越丰富,而企业和个人对“让AI真正干活”的需求越来越迫切。无论是自动处理工单、批量分析文档、还是驱动一个自动化流程,Agent都是当前最直接的实现路径。

对开发者来说,这意味着一个新的技术栈正在形成。你不需要从头训练模型,但需要理解如何设计Agent的架构、如何编写工具接口、如何管理上下文和记忆、如何处理失败重试。这些能力,和传统的后端开发、脚本编写有交集,但又有自己独特的坑和技巧。

1.3 这篇文章适合谁看

如果你有基本的编程经验(Python或JavaScript即可),用过大模型API,想从“调Prompt”进阶到“搭系统”,那这篇内容就是为你准备的。我会从零开始,把Agent开发的环境搭建、核心模块设计、工具接入、记忆管理、调试排查全部走一遍,附上可直接复现的代码和参数说明。不需要你有机器学习背景,但需要你愿意动手跑代码。

提示:本文所有代码基于Python生态,使用OpenAI兼容接口。如果你用的是其他模型服务,只要支持Function Calling,替换base_url和api_key即可。

2. 动手之前:环境搭建与核心依赖选型

2.1 开发环境的最小化配置

Agent开发对本地环境的要求其实不高,核心就是Python运行环境和几个关键库。我建议用Python 3.10或以上版本,因为很多Agent框架对类型注解和异步支持有要求。虚拟环境用venv或conda都行,我个人习惯venv,轻量且够用。

python -m venv agent-env source agent-env/bin/activate # Windows用 agent-env\Scripts\activate pip install openai httpx pydantic python-dotenv

这几个包的分工很明确:openai是模型调用SDK,httpx用于异步HTTP请求(调外部工具接口时用),pydantic做数据校验和结构化输出,python-dotenv管理密钥。如果你打算用LangChain或LlamaIndex这类框架,可以额外安装,但我建议第一版手写,把底层逻辑摸清楚再上框架。

2.2 模型选型:不是越贵越好

Agent开发对模型的要求和普通对话不同。它需要模型具备稳定的Function Calling能力、较强的指令遵循能力,以及合理的上下文长度。我实测下来,几个选型维度值得关注:

维度说明建议
Function Calling能否稳定输出结构化工具调用必须支持,否则Agent无法驱动工具
上下文长度影响记忆和任务链长度至少32K,推荐128K以上
推理能力多步任务拆解的准确性中等以上即可,不必追求最强
响应速度影响Agent循环效率流式输出优先
成本多轮循环消耗大开发阶段用便宜模型,上线再换

我个人的做法是:开发调试阶段用一个中等能力的模型,把逻辑跑通;上线前再用更强的模型做一轮回归测试,对比任务完成率。不要一上来就用最贵的模型,因为Agent会反复调用,成本会成倍放大。

2.3 项目目录结构设计

一个清晰的目录结构能让后续调试省很多事。我通常这样组织:

agent-project/ ├── .env # 密钥配置 ├── main.py # 入口 ├── agent/ │ ├── core.py # Agent主循环 │ ├── tools.py # 工具定义与注册 │ ├── memory.py # 记忆管理 │ └── prompts.py # 提示词模板 ├── utils/ │ └── logger.py # 日志 └── tests/ └── test_tools.py # 工具单测

这样分的好处是:工具、记忆、主循环各自独立,出问题时能快速定位是哪一层的问题。我踩过的坑是早期把所有逻辑塞在一个文件里,结果工具调用出错时根本分不清是提示词问题还是参数解析问题。

3. Agent核心架构拆解:规划、记忆、工具

3.1 规划模块:让模型学会“分步走”

规划是Agent的大脑。最简单的规划方式是ReAct模式:模型先输出思考(Thought),再决定行动(Action),然后观察结果(Observation),循环直到任务完成。这个模式的好处是逻辑透明,每一步都能看到模型在想什么。

但ReAct有个明显问题:对于复杂任务,模型容易在中间步骤跑偏。我的改进做法是在系统提示词里强制加入任务分解要求,让模型先输出一个步骤列表,再逐步执行。提示词大概长这样:

你是一个任务执行Agent。面对用户请求时,请按以下流程工作: 1. 先分析任务,列出需要完成的步骤(不超过5步) 2. 逐步执行每个步骤,每步说明你在做什么 3. 如果需要调用工具,明确说明调用哪个工具、传什么参数 4. 每完成一步,检查结果是否符合预期 5. 全部完成后,汇总结果给用户

这个提示词看起来简单,但实测能把任务完成率提升不少。原因是它给了模型一个明确的工作框架,减少了“想到哪做到哪”的随机性。

3.2 记忆模块:短期记忆与长期记忆的分工

Agent的记忆分两层。短期记忆就是当前对话的上下文,直接放在消息列表里。长期记忆则需要持久化存储,通常用向量数据库或简单的键值存储。

短期记忆的管理核心是上下文窗口控制。Agent循环多轮后,消息列表会越来越长,最终超出模型上下文限制。我的处理策略是:

  • 保留系统提示词和最近N轮对话
  • 对更早的对话做摘要压缩
  • 工具调用的原始返回结果如果太长,只保留关键字段
def manage_context(messages, max_tokens=8000): """简单的上下文管理:保留系统消息和最近对话""" system_msgs = [m for m in messages if m["role"] == "system"] other_msgs = [m for m in messages if m["role"] != "system"] # 从后往前保留,直到接近token上限 kept = [] token_count = sum(len(m.get("content", "")) for m in system_msgs) for msg in reversed(other_msgs): msg_tokens = len(msg.get("content", "")) if token_count + msg_tokens > max_tokens: break kept.insert(0, msg) token_count += msg_tokens return system_msgs + kept

长期记忆我一般用两种方案:轻量场景直接存JSON文件,按key检索;复杂场景用向量库做语义检索。对于入门阶段,JSON文件完全够用,不要过早引入复杂依赖。

3.3 工具模块:Agent的手和脚

工具是Agent与外部世界交互的接口。一个工具本质上就是一个函数,加上一份描述(告诉模型这个工具是干什么的、需要什么参数)。模型根据描述决定是否调用、怎么传参。

工具定义的标准格式(OpenAI Function Calling):

tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的当前天气", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如北京、上海" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位" } }, "required": ["city"] } } } ]

这里有个关键经验:工具描述的质量直接决定调用准确率。描述要写清楚“什么时候用这个工具”,而不只是“这个工具是什么”。比如不要写“查询天气”,要写“当用户询问某地天气、温度、是否下雨时使用此工具”。我做过对比测试,优化描述后工具调用的准确率能从70%左右提升到90%以上。

4. 从零实现一个可运行的Agent

4.1 主循环的完整实现

Agent的核心就是一个while循环:调用模型→检查是否有工具调用→执行工具→把结果塞回消息列表→再次调用模型。直到模型不再请求工具调用,输出最终答案。

import json from openai import OpenAI from dotenv import load_dotenv import os load_dotenv() client = OpenAI( api_key=os.getenv("API_KEY"), base_url=os.getenv("BASE_URL") ) def run_agent(user_input, tools, tool_map, max_iterations=10): messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ] for i in range(max_iterations): response = client.chat.completions.create( model="your-model-name", messages=messages, tools=tools, tool_choice="auto" ) msg = response.choices[0].message messages.append(msg) # 没有工具调用,任务结束 if not msg.tool_calls: return msg.content # 执行所有工具调用 for tool_call in msg.tool_calls: func_name = tool_call.function.name func_args = json.loads(tool_call.function.arguments) if func_name in tool_map: try: result = tool_map[func_name](**func_args) except Exception as e: result = f"工具执行失败: {str(e)}" else: result = f"未知工具: {func_name}" messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(result) }) return "达到最大迭代次数,任务未完成"

这段代码有几个细节值得说。max_iterations是必须的保险丝,防止Agent陷入死循环。工具执行要用try-except包住,因为外部接口随时可能失败,不能让一个工具报错就整个Agent崩掉。工具返回结果统一转成字符串,因为消息列表里content必须是字符串。

4.2 工具注册与参数校验

工具注册我推荐用一个装饰器模式,把函数和它的schema绑定在一起:

TOOL_REGISTRY = {} def register_tool(name, description, parameters): def decorator(func): TOOL_REGISTRY[name] = { "function": func, "schema": { "type": "function", "function": { "name": name, "description": description, "parameters": parameters } } } return func return decorator @register_tool( name="calculate", description="执行数学计算,当用户需要做算术运算时使用", parameters={ "type": "object", "properties": { "expression": { "type": "string", "description": "数学表达式,如 '2 + 3 * 4'" } }, "required": ["expression"] } ) def calculate(expression): # 安全起见,限制可用字符 allowed = set("0123456789+-*/(). ") if not all(c in allowed for c in expression): return "表达式包含不允许的字符" return str(eval(expression))

参数校验这块,pydantic能帮大忙。如果工具参数复杂,建议用pydantic模型定义,然后在调用前做一次校验,避免模型传了错误类型导致工具内部报错。

4.3 一个完整的实战案例:文档分析Agent

假设我们要做一个Agent,能读取本地文档、提取关键信息、做简单统计。工具集包括:读文件、统计词频、提取关键词。

@register_tool( name="read_file", description="读取指定路径的文本文件内容,当需要分析文件时使用", parameters={ "type": "object", "properties": { "path": {"type": "string", "description": "文件路径"} }, "required": ["path"] } ) def read_file(path): with open(path, "r", encoding="utf-8") as f: return f.read()[:5000] # 限制长度 @register_tool( name="count_words", description="统计文本中的词频,返回出现次数最多的词", parameters={ "type": "object", "properties": { "text": {"type": "string", "description": "待统计的文本"}, "top_n": {"type": "integer", "description": "返回前N个高频词"} }, "required": ["text"] } ) def count_words(text, top_n=10): from collections import Counter words = text.split() counter = Counter(words) return str(counter.most_common(top_n))

跑起来之后,用户只需要说“帮我分析一下report.txt,看看主要讲了什么,高频词有哪些”,Agent就会自动完成:读文件→统计词频→汇总结果。整个过程不需要用户指定调用哪个工具,模型自己规划。

5. 调试与排查:Agent开发中最容易踩的坑

5.1 工具调用不触发或触发错误

这是最常见的问题。模型要么不调用工具,要么调用了错误的工具,要么参数传错。排查思路按优先级来:

现象可能原因排查方法
完全不调用工具工具描述不清晰检查description是否说明了使用场景
调用错误工具多个工具描述重叠让每个工具的适用场景互斥
参数缺失required字段没标全检查parameters定义
参数类型错误模型理解偏差在description里给示例
循环调用同一工具结果不符合模型预期检查工具返回内容是否清晰

我的经验是,90%的工具调用问题都能通过优化描述解决。描述里要包含:什么时候用、参数怎么填、返回什么格式。最好给一个具体示例。

5.2 上下文爆炸与token超限

Agent跑多轮之后,消息列表会迅速膨胀。一个工具返回的长文本可能就占几千token。我的处理原则是:工具返回结果只保留模型决策需要的信息。比如查询数据库返回100条记录,不要全塞回去,只返回前几条加总数。

另外,可以在系统提示词里加一句:“工具返回结果如果过长,请自行提取关键信息后再继续。”这样模型会主动做压缩。

5.3 死循环与任务跑偏

Agent陷入死循环通常有两种原因:一是工具一直返回错误,模型反复重试;二是任务目标不明确,模型来回打转。解决办法:

  • 设置max_iterations硬限制
  • 在提示词里加入“如果连续两次工具调用结果相同,请停止并汇报”
  • 对工具错误做分类,不可恢复的错误直接终止

注意:不要指望模型自己发现死循环,它没有“循环计数”的概念。这个保险必须由代码层来加。

5.4 常见问题速查表

问题快速定位解决
Agent不响应检查API密钥和网络打印原始response
工具执行报错看工具内部日志加try-except返回错误信息
结果不准确检查提示词和工具描述优化描述,加示例
响应太慢模型太大或循环太多换小模型,限制迭代
成本过高循环次数多压缩上下文,缓存结果

6. 进阶方向:让Agent更可靠、更实用

6.1 多Agent协作的思路

单个Agent能力有限,复杂任务可以拆给多个Agent。比如一个“研究员Agent”负责搜集信息,一个“写作Agent”负责整理输出,一个“审核Agent”负责检查质量。它们之间通过消息传递协作。这种模式适合任务边界清晰的场景,但要注意通信开销和协调逻辑,不要为了多Agent而多Agent。

6.2 记忆持久化的工程实践

长期记忆我推荐从简单方案起步:用SQLite存对话历史,用关键词检索。等数据量大了再上向量库。关键是要设计好记忆的写入和读取策略——不是所有对话都值得记,也不是所有记忆都需要每次读取。我的做法是:只存任务完成后的摘要,读取时按时间衰减加权。

6.3 安全与边界控制

Agent能调工具,就意味着它能产生实际影响。必须做权限控制:哪些工具只读、哪些可写、哪些需要人工确认。我的原则是:涉及外部副作用的操作(发邮件、改数据、下单)一律加确认环节。可以在工具执行前插入一个检查函数,或者让Agent先输出计划,人工确认后再执行。

DANGEROUS_TOOLS = {"send_email", "delete_record", "place_order"} def execute_with_guard(tool_name, args): if tool_name in DANGEROUS_TOOLS: print(f"即将执行敏感操作: {tool_name}") print(f"参数: {args}") confirm = input("确认执行? (y/n): ") if confirm.lower() != "y": return "用户取消操作" return TOOL_REGISTRY[tool_name]["function"](**args)

这个简单的守卫机制,在实际项目中能避免很多误操作。尤其是调试阶段,Agent可能会做出意料之外的调用,有人工确认兜底会安心很多。

6.4 性能优化的几个实用技巧

Agent的响应速度直接影响体验。我常用的优化手段:一是并行工具调用,如果多个工具之间没有依赖,让模型一次性返回多个tool_calls,然后并发执行;二是结果缓存,相同参数的查询直接返回缓存;三是流式输出,让用户尽早看到Agent的思考过程,感知上更快。

并行执行的代码框架:

import asyncio async def execute_tools_parallel(tool_calls, tool_map): async def run_one(tc): func = tool_map.get(tc.function.name) if not func: return tc.id, f"未知工具: {tc.function.name}" try: args = json.loads(tc.function.arguments) result = await asyncio.to_thread(func, **args) return tc.id, str(result) except Exception as e: return tc.id, f"执行失败: {str(e)}" tasks = [run_one(tc) for tc in tool_calls] return await asyncio.gather(*tasks)

这套东西跑通之后,Agent的吞吐能力会有明显提升。不过要注意,并行执行的前提是工具之间没有状态依赖,否则会出现竞态问题。

我在实际项目里最大的体会是:Agent开发的门槛不在模型,而在工程细节。提示词怎么写、工具怎么设计、错误怎么处理、上下文怎么管理,这些才是决定一个Agent能不能真正用起来的关键。模型能力每年都在涨,但这些工程经验是实打实需要自己踩出来的。建议你从最简单的单工具Agent开始,跑通一个完整闭环,再逐步加复杂度。别一上来就追求多Agent、复杂记忆、全自动,那样很容易在调试阶段就放弃。先把一个能用的东西做出来,比什么都重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 6:28:10

Python-CNN车牌识别实战:从源码拆解到推理部署

简介:基于Python与卷积神经网络的车牌识别完整项目资源,面向计算机视觉、深度学习的入门与进阶学习者,可用于智能交通、自动车辆等场景中的车牌检测与识别任务。包内聚焦CNN建模全流程,涵盖数据预处理、Keras/TensorFlow模型构建、…

作者头像 李华
网站建设 2026/10/7 6:27:52

AI智能陪练功能逻辑与门店销售剧本框架详解

做培训管理系统这几年,我遇到过最多的一个需求,不是排课,也不是考试,而是怎么让一线销售真正开口练。尤其是家电门店这种强对话场景,很多新人背熟了参数,站到真实顾客面前却只会报参数;老销售知…

作者头像 李华
网站建设 2026/10/7 6:27:37

YOLOv7跌倒检测实战:Python端到端部署避坑指南

简介:本资源是一套面向人工智能初学者与计算机视觉开发者的YOLOv7跌倒检测实战项目,聚焦公共安全、养老监护等现实场景中对异常行为的实时识别需求。资源包含完整可运行的Python源码、分步式图文教程(含环境配置、数据预处理、模型训练与部署…

作者头像 李华
网站建设 2026/10/7 6:27:21

FPGA上实现稳定10G UDP的工程实践指南

1. 项目概述:为什么10G UDP在FPGA上不是“玄学”,而是可复现的工程实践别再为10G UDP发愁了——这句话不是营销话术,而是我踩过三块开发板、重写四版MAC层状态机、抓包分析超过2700个异常帧之后的真实体会。过去三年,我在高速网络…

作者头像 李华
网站建设 2026/10/7 6:27:21

AI Agent工程落地:七要素拆解与七个关键决策指南

最近两年,AI Agent 从一个概念词变成了实打实的工程项目。我和不少团队聊过,大家普遍的心态是:调用大模型 API 很简单,但把一个 Agent 放进生产环境,让它稳定干活、扛得住并发、还能被监控和评估,完全是另一…

作者头像 李华
网站建设 2026/10/7 6:26:07

QFN封装芯片手工焊接全流程与避坑指南

1. QFN封装芯片手工焊接的核心难点与整体思路QFN封装,全称Quad Flat No-leads,中文叫方形扁平无引脚封装。这东西在现代电子产品里太常见了,电源管理芯片、射频前端、微控制器、传感器,到处都是它的身影。但凡是手工焊过QFN的人都…

作者头像 李华