大模型Agent开发实战:从架构设计到工具调用与记忆管理

发布时间:2026/9/6 3:25:41
大模型Agent开发实战:从架构设计到工具调用与记忆管理 今年年初开始身边讨论 Agent 开发的同事明显变多了。不管是内部工具、自动化脚本还是对外服务都在往“让大模型自己决定下一步做什么”的方向走。但真正动手做的时候很多人会发现一个问题跑通一个 Demo 很容易做一个能稳定完成任务的 Agent 很难。这次借字节周会技术分享的机会我把自己在 Agent 开发上踩过的坑、总结出来的套路整理成了一篇偏实战的笔记。内容不绕弯核心围绕几个问题展开Agent 到底是什么架构、选什么框架、怎么写工具调用、怎么做记忆、怎么调试、怎么上线、怎么观察效果。如果你正准备开始做 Agent 开发或者已经在做但经常被效果不稳定、工具调用失败、上下文混乱这些问题卡住这篇文章可以直接收藏。1. Agent 开发核心能力速览能力项说明核心概念LLM 规划 工具调用 记忆 执行循环主流框架LangChain、LangGraph、Dify、Coze、AutoGen、自研编排关键机制ReAct、Plan-and-Execute、工具注册、记忆窗口、状态机开发语言Python 为主Node.js / TypeScript 也可模型需求GPT 系列、Claude、Qwen、DeepSeek 等支持 Function Calling 的模型最低环境调用云端 API 不需要 GPU本地部署需按模型规模配置启动方式代码启动 / Docker 启动 / WebUI 工作流编排是否支持 API支持可封装为 HTTP 服务批量任务支持推荐队列 任务表 回调设计调试难度中等偏上核心难点在链路追踪和状态恢复从材料看Agent 开发并不是某个单一开源项目的使用教程而是一套工程方法。所以这篇文章会更侧重通用的开发闭环环境准备、框架选型、架构拆解、实战代码、接口封装、部署观测和问题排查。2. 适用场景与使用边界2.1 适合的场景Agent 开发比较适合以下几类需求需要大模型自主决策的任务比如根据用户目标自动选择工具、拆分步骤。需要串联多个内部系统的操作比如查数据库、调内部 API、操作文件、发送通知。需要处理长流程、多轮交互的场景比如智能客服、数据分析助手、代码生成助手。需要批量处理结构化任务的场景比如批量生成报告、批量审核内容、批量提取信息。2.2 不适合的场景固定流水线任务用传统代码或者工作流引擎更稳定。对延迟极度敏感的场景多轮推理会放大耗时。完全不能接受模型犯错的场景需要人工审核兜底。工具调用失败率较高的场景如果工具本身不稳定Agent 再聪明也没用。2.3 合规与安全边界Agent 开发涉及自动执行操作必须注意几个底线调用外部 API 或内部系统时先确认权限边界不能让 Agent 自主执行高危操作。处理用户数据时遵循最小化原则脱敏后再进入模型上下文。涉及人脸、声音、版权素材或个人信息时必须确保已获得合法授权。上线前要做安全测试防止提示词注入导致 Agent 执行非预期操作。这些不是套话。Agent 的能力越强越需要约束它的操作范围。3. Agent 开发的核心概念与架构拆解在写代码之前先明确 Agent 到底在工程上长什么样。从周会分享整理出的结论是Agent 的本质是一个“循环”不是一次性的模型调用。一个基础 Agent 循环包含 4 个步骤接收用户目标。将目标分解为计划或下一步动作。调用一个或多个工具获取观察结果。将观察结果反馈给模型决定继续执行还是结束。这个循环一直重复直到模型认为任务完成。从更细的架构角度看Agent 系统通常由以下模块组成模块职责说明意图识别理解用户目标可能直接由 LLM 完成也可以先走分类模型规划器制定执行步骤ReAct 逐轮规划或 Plan-and-Execute 先生成完整计划工具层封装外部能力搜索、计算、数据库查询、API 调用、文件操作执行器驱动循环循环调用模型和工具管理状态记忆模块保存上下文短期记忆、长期记忆、向量检索记忆观测模块记录运行过程Token 消耗、工具调用耗时、失败原因、完整轨迹4. 主流 Agent 开发框架对比周会分享里把当前主流方案分成了三层应用框架层、平台编排层、自研核心层。框架类型优点不足适合场景LangChain应用框架生态丰富工具多上手快抽象层多调试链路较长快速原型、工具调用、文档处理LangGraph应用框架支持图状态、循环、分支可控性强学习曲线高需要理解状态机复杂流程、状态化 Agent、生产落地Dify平台编排可视化编排内置知识库和工具自定义逻辑受限非深度开发团队、快速搭建应用Coze平台编排免部署插件丰富发布渠道多国产平台有自己的规则迁移成本高快速验证产品、面向 C 端助手AutoGen多 Agent 框架支持多 Agent 对话协作多 Agent 调度本身有稳定性成本多角色协作、研究实验自研编排核心层完全可控能针对场景优化开发量大需要维护核心循环对效果、成本、稳定性要求高的生产系统如果从开发效率角度排序我的建议是第一次做概念验证先用 Dify 或 Coze不写代码。确认产品形态后用 LangChain 或 LangGraph 做工程化。如果要做高并发、强管控的生产系统自研编排是更稳的方向。5. 环境准备与前置检查Agent 开发的环境准备并不复杂但要提前把几件事确认好。5.1 基础环境清单项目建议配置操作系统Windows 10/11、macOS、Linux 均可Python3.10 或 3.11Node.js18 及以上如果涉及前端或 Node 框架模型 APIOpenAI / Claude / 通义 / DeepSeek 等兼容 API包管理pip、poetry 或 uv容器化Docker部署阶段需要5.2 环境检查命令# 检查 Python 版本 python --version # 检查 pip 版本 pip --version # 检查 Node 版本 node -v如果没有满足版本要求建议先升级 Python 或安装 pyenv 做多版本管理。5.3 依赖安装以 Python 为例创建虚拟环境并安装 LangChain 相关依赖# 创建虚拟环境 python -m venv venv # 激活虚拟环境Windows venv\Scripts\activate # 激活虚拟环境macOS / Linux source venv/bin/activate # 安装核心依赖 pip install langchain langchain-openai这里建议在项目内用虚拟环境隔离依赖避免污染全局 Python。5.4 模型服务配置大多数 Agent 框架都兼容 OpenAI 格式的接口。如果使用国内模型通常只需要修改 base_url 和 api_key。from langchain_openai import ChatOpenAI llm ChatOpenAI( modelqwen-plus, api_keyyour-api-key, base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1 )需要说明具体模型名称和接口地址以模型服务商文档为准这里只是演示兼容 OpenAI 格式的接入方式。6. 从零实现一个可运行的 Agent框架再多也要落到代码。周会分享里最有价值的部分是现场手写了一个不依赖框架的最小 Agent 循环。这个循环逻辑是所有 Agent 框架的底层模型。6.1 最小 Agent 循环先定义一个工具函数import json from datetime import datetime def get_current_time() - str: 获取当前时间 return datetime.now().strftime(%Y-%m-%d %H:%M:%S) def calculate(expression: str) - str: 计算数学表达式 try: return str(eval(expression)) except Exception as e: return f计算失败: {str(e)} TOOLS { get_current_time: { description: 获取当前时间, function: get_current_time }, calculate: { description: 计算数学表达式例如 12, function: calculate } }再实现主循环from openai import OpenAI client OpenAI( api_keyyour-api-key, base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1 ) def run_agent(user_input: str, max_iterations: int 5): messages [ {role: system, content: 你是一个助手可以使用工具完成任务。}, {role: user, content: user_input} ] tool_list [ {type: function, function: {name: get_current_time, description: 获取当前时间}}, {type: function, function: {name: calculate, description: 计算数学表达式}} ] for i in range(max_iterations): response client.chat.completions.create( modelqwen-plus, messagesmessages, toolstool_list ) message response.choices[0].message if message.tool_calls: messages.append(message) for tool_call in message.tool_calls: tool_name tool_call.function.name args json.loads(tool_call.function.arguments) if tool_name in TOOLS: result TOOLS[tool_name][function](**args) else: result f未知工具: {tool_name} messages.append({ role: tool, tool_call_id: tool_call.id, content: str(result) }) else: return message.content return 达到最大迭代次数任务结束。调用效果print(run_agent(现在是几点)) print(run_agent(计算 12345 * 6789 的结果))这个小循环已经具备 Agent 的最核心能力模型决定调用哪个工具、生成参数、执行工具、把结果返回给模型继续推理。框架做的事情本质上是把这个循环做得更健壮、更好调试。6.2 ReAct 模式的代码实现ReAct 是 Reasoning Acting 的组合让模型先思考再行动。上面 tool_calls 的流程其实就是 ReAct 的一种框架化实现。如果不想依赖模型的 Function Calling 能力也可以用文本方式让模型输出 JSON 格式的动作指令。import openai import json prompt_template 你是一个任务规划助手。根据用户输入选择要执行的工具。 工具列表 1. get_current_time - 获取当前时间 2. calculate - 计算数学表达式 请以 JSON 格式输出你的决定 {tool: 工具名, arguments: {参数名: 参数值}} 用户输入{user_input} def react_agent(user_input: str): prompt prompt_template.format(user_inputuser_input) response openai.chat.completions.create( modelyour-model, messages[{role: user, content: prompt}] ) content response.choices[0].message.content return json.loads(content)这种方式的优点是兼容不支持 Function Calling 的老模型缺点是需要自己处理输出格式错误。生产系统建议优先使用 Function Calling 或 Structured Output。7. Agent 记忆管理的实现Agent 开发最常见的问题之一是模型在长对话或多轮任务中丢失关键信息。记忆模块是工程化的重点。7.1 短期记忆与上下文窗口短期记忆本质上就是 messages 数组。随着对话变长Token 消耗会快速增长。常用策略是只保留最近 N 轮消息。把历史对话做摘要压缩成一段 summary。设置 Token 上限超过后自动裁剪。def trim_messages(messages, max_messages20): if len(messages) max_messages: return messages return messages[:1] messages[-(max_messages - 1):]这里的messages[:1]是保留 system 提示词后面只保留最近的对话。7.2 长期记忆用向量检索短期记忆足够应付简单对话但要做知识积累需要长期记忆。长期记忆的常用实现是向量数据库 Embedding。流程把历史知识、用户偏好、任务结果写入向量库。每次新对话先从向量库检索相关片段。把检索结果拼入本次上下文。from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings # 初始化向量库 embeddings OpenAIEmbeddings() vectorstore FAISS.from_texts( [用户偏好喜欢简洁的回复, 上一次任务是数据分析], embeddings ) # 检索相关内容 docs vectorstore.similarity_search(用户偏好, k2) context \n.join([doc.page_content for doc in docs])长期记忆的核心不是存得多而是检索得准。要根据业务场景设计记忆写入策略什么信息值得写入、编码成什么格式、多久更新一次。7.3 结构化记忆更工程化的做法是维护一个记忆内存表比如用户画像字段、任务状态字段每次 Agent 循环更新。memory { user_name: 张三, task_progress: 已获取原始数据, 正在清洗, last_query: 统计 Q3 销售额 }这样记忆不会占模型上下文只在需要时作为工具数据注入。8. 工具调用的设计与稳定性工具调用是 Agent 与外部系统交互的桥梁。工具定义质量直接决定 Agent 的成功率。8.1 工具定义规范从实际经验看工具定义要满足名称使用英文小写加下划线。描述尽可能详细说明适合用于什么场景。参数尽量少最好控制在 5 个以内。参数描述要给出枚举值和示例。{ type: function, function: { name: query_sales_data, description: 查询指定日期范围的销售数据。当用户询问销售额、订单量、增长率时使用。, parameters: { type: object, properties: { start_date: { type: string, description: 开始日期格式 YYYY-MM-DD }, end_date: { type: string, description: 结束日期格式 YYYY-MM-DD }, region: { type: string, description: 地区可选华东、华南、华北, enum: [华东, 华南, 华北] } }, required: [start_date, end_date] } } }8.2 工具调用的失败处理任何外部系统都可能失败。Agent 循环里必须对工具调用做异常捕获把错误信息返回给模型让模型决定是换一种方式还是向用户说明。def safe_call_tool(name, args): try: tool TOOLS.get(name) if not tool: return {success: False, error: f未知工具 {name}} result tool[function](**args) return {success: True, result: result} except Exception as e: return {success: False, error: str(e)}将错误信息作为 tool 消息返回给模型Agent 就能自动调整策略。8.3 工具权限要收敛周会分享里特别强调了一点不要让 Agent 直接拿到全部工具。建议做工具分级。级别说明示例只读工具查询、检索、计算查数据库、搜索、计算写入工具需要确认后执行发邮件、写文件、提交订单高危工具必须人工审批删除数据、转账、修改权限在业务系统中通常先将低风险工具全量开放高风险工具走二次确认流程。9. Agent 编排工作流从单步到多步骤真实业务很少只有一个工具调用。很多时候 Agent 需要按顺序执行好几个步骤先查数据再分析再生成报告最后发送到指定位置。9.1 用 LangGraph 实现可控流程LangGraph 的核心思想是把 Agent 定义为一个状态图每个节点是一个处理步骤每条边是状态转换条件。from langgraph.graph import StateGraph, END from typing import TypedDict class AgentState(TypedDict): user_input: str plan: list result: str def plan_step(state: AgentState): # 生成计划 return {plan: [查数据, 分析, 生成报告]} def execute_step(state: AgentState): # 执行下一步 return {result: 执行完成} graph StateGraph(AgentState) graph.add_node(plan, plan_step) graph.add_node(execute, execute_step) graph.set_entry_point(plan) graph.add_edge(plan, execute) graph.add_edge(execute, END)LangGraph 相比纯手写循环的优势是状态可序列化、支持分支和循环、方便接入人工审批节点。9.2 编排实战一个带审核的 Agent如果任务包含“发送对外邮件”这种操作可以在流程里插入一个确认节点def review_step(state: AgentState): # 模拟人工审核 approved input(是否批准执行y/n: ) if approved y: return {approved: True} return {approved: False} graph.add_node(review, review_step) graph.add_edge(execute, review) # 根据状态决定下一个节点 def should_send(state): if state[approved]: return send return END这样即使 Agent 自动跑了大部分流程关键节点仍然由人来控制这个设计在生产环境里非常管用。10. 将 Agent 封装为 API 服务纯函数运行只能自己测试。真正要接入产品必须把 Agent 封装成 HTTP 服务。10.1 FastAPI 封装示例from fastapi import FastAPI, HTTPException from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): session_id: str user_input: str class ChatResponse(BaseModel): result: str session_id: str app.post(/agent/chat, response_modelChatResponse) async def chat(request: ChatRequest): try: result run_agent(request.user_input) return ChatResponse(resultresult, session_idrequest.session_id) except Exception as e: raise HTTPException(status_code500, detailstr(e)) # 启动: uvicorn main:app --host 0.0.0.0 --port 8000这里的0.0.0.0表示允许外部访问。生产环境要按安全策略设置绑定的 IP不用的端口不要开。10.2 通用 API 调用示例请求接口curl -X POST http://127.0.0.1:8000/agent/chat \ -H Content-Type: application/json \ -d {session_id: test-001, user_input: 计算 100 * 200}Python 调用import requests url http://127.0.0.1:8000/agent/chat payload { session_id: test-001, user_input: 计算 100 * 200 } response requests.post(url, jsonpayload, timeout120) print(response.json())注意Agent 服务通常推理耗时较长要合理设置超时时间。如果任务长建议改成异步任务模式先返回 task_id再轮询结果。10.3 异步任务模式比较成熟的方案是引入任务表tasks {} app.post(/agent/async) async def async_chat(request: ChatRequest): import uuid task_id str(uuid.uuid4()) tasks[task_id] {status: running, result: None} return {task_id: task_id} app.get(/agent/async/{task_id}) async def get_result(task_id: str): return tasks.get(task_id)实际生产环境中任务表要放在 Redis 或数据库中不能让任务状态存在进程内存里。11. 批量任务与队列设计Agent 跑单条任务没问题跑一百条任务就很容易暴露稳定性问题。批量任务的核心不是并发而是可控。11.1 批量任务流程推荐流程任务入队。多个 worker 消费队列。每个任务执行独立的 Agent 循环。结果统一写入任务表。失败任务自动重试。# 批量任务伪代码 import queue import threading task_queue queue.Queue() completed {} def worker(): while True: task_id, user_input task_queue.get() try: result run_agent(user_input) completed[task_id] {status: success, result: result} except Exception as e: completed[task_id] {status: failed, error: str(e)} finally: task_queue.task_done() for i in range(3): threading.Thread(targetworker, daemonTrue).start()11.2 批量任务的注意事项控制并发数避免模型 API 限流。设置单任务超时防止卡死。记录每个任务的输入、输出、Token 消耗。对失败任务先重试重试仍失败再人工处理。真正生产环境建议用 Celery、Arq 或消息队列中间件进程内线程池只适合小规模测试。12. 资源占用与性能观察Agent 开发相比传统接口性能问题更值得关注。主要瓶颈有三个模型推理耗时、上下文长度、工具调用往返次数。12.1 资源观察维度指标说明Token 消耗每次任务消耗多少输入/输出 Token延迟从请求到返回总耗时以及每次模型调用的耗时工具调用次数任务平均调用几次工具越多延迟越高失败率工具调用失败率模型解析失败率成本Token 成本 API 调用成本12.2 性能优化方向减少不必要的工具调用模型要会判断什么时候停止。压缩上下文系统提示词精简历史对话做摘要。模型分级简单任务用小模型复杂任务用大模型。增加缓存相同问题或相同检索结果直接命中缓存。并行化多个不相关的工具调用并行执行。12.3 可观测性方案调试 Agent 最痛苦的是不知道模型内部在想什么。所以一定要做轨迹日志。import json import logging logger logging.getLogger(agent_trace) def log_trace(step, input_data, output_data): trace_entry { step: step, input: input_data, output: output_data } logger.info(json.dumps(trace_entry, ensure_asciiFalse))每次模型调用、工具调用、错误处理都记录一条轨迹。这样出了问题可以回放整个 Agent 的决策过程而不是只看最终结果。13. 常见问题与排查方法从周会分享的实战情况看Agent 开发最容易出问题的环节集中在下面这些地方。问题现象可能原因排查方式解决方案模型一直重复调用同一个工具工具返回结果没有让模型理解进展查看工具返回内容和完整轨迹日志优化工具返回格式增加状态描述模型调用不存在的工具工具函数没有正确注册检查 tools 参数是否传入确保每个工具都在 tools 列表中工具参数格式错误模型生成的参数和工具定义不一致检查工具 schema 定义增加参数校验和自动纠正逻辑上下文越来越长Token 超限没有做消息裁剪查看请求消息长度定期摘要和裁剪历史消息Agent 答非所问系统提示词不清晰或目标不明确逐轮查看日志重写 system prompt明确输出格式批量任务卡住某次工具调用超时查看 worker 日志增加超时和失败重试机制API 返回 429触发模型服务限流查看服务商限流策略控制并发数增加退避重试结果不稳定时好时坏模型随机性或上下文扰动固定 temperature0 测试降低 temperature用结构化输出多轮对话丢上下文记忆模块没有正确保存检查 messages 和记忆逻辑使用摘要记忆或向量记忆13.1 Agent 卡在循环里的处理Agent 最常见的死法是陷入死循环。可以在循环中增加最大迭代次数同时检测重复行为seen_actions set() for i in range(max_iterations): action get_next_action(messages) action_key json.dumps(action) if action_key in seen_actions: break seen_actions.add(action_key)这个方法虽然简单但能有效避免 Agent 卡在同一个工具调用上。14. 最佳实践与工程建议14.1 开发流程建议第一阶段先用提示词跑通目标不要急着写工具。第二阶段把能用文本完成的事情尽量用文本完成减少工具调用。第三阶段加入必要的工具一次只加一个。第四阶段做完整轨迹回放测试覆盖关键业务场景。第五阶段逐步放量观察失败率再优化。14.2 发布前检查清单所有工具都有权限和审计。高危操作有人工确认。上下文裁剪和记忆策略已生效。批量任务有超时和重试机制。接口服务有鉴权和限流。轨迹日志完整可回溯。关键场景有自动化回归测试。涉及真实用户数据时已确认脱敏和授权。14.3 关于安全合规Agent 自动执行操作必须有边界意识。不要让 Agent 直接执行删除、修改权限、转账等高危操作。对工具返回的外部内容做必要的过滤。提示词注入是真实存在的风险当 Agent 读取了外部文档或网页内容可能被其中隐藏的指令控制。必要时对工具结果做脱敏处理或将外部内容限制在普通数据角色而非系统指令角色。15. 总结与下一步这次周会分享最核心的收获不是某个框架的用法而是把 Agent 开发从“调模型”变成“做系统”的思维转变。一个能跑的 Agent 需要具备四个基本能力循环控制、工具调用、记忆管理和状态观测。框架只是加速开发的工具真正决定上限的是工程设计的完整度。建议优先验证的内容从最小循环开始。不要一上来就套业务场景先把一个带模型和工具的测试跑通再逐步加编排和记忆。最容易踩的坑主要有三个工具定义不清晰、没有做轨迹日志、批量任务没有重试机制。这三个坑在开发初期就要规划好后面改起来成本很高。下一步可以继续扩展的方向有几个用 LangGraph 做更复杂的可视化工流程接入向量数据库做长期记忆设计多 Agent 协作机制或者把 Agent 服务部署成异步任务系统。核心思路不变先让模型能安全地调用工具再逐步增加编排复杂度和业务能力。Agent 开发没有玄学就是把循环、工具、记忆、观测这几个模块打磨到位。这篇分享里的代码和流程可以直接作为团队内部技术方案讨论的起点。建议收藏备用后面做 Agent 项目时再回来对照检查。