
1. AI Agent 的七要素先搞清楚 Agent 到底由什么组成过去一年我几乎每周都会被问到同一个问题Agent 和普通的 ChatBot 到底有什么区别我的回答通常很直接——ChatBot 是你问我答Agent 是你交代任务它自己想办法完成。但这个想办法三个字背后藏着一整套工程结构。业内讨论 Agent 架构时最常引用的是一个七要素框架。我做了这几年 Agent 项目认为这套划分在工程落地层面确实够用——它不是学术定义而是可以直接对着它做技术选型的需求清单。这七个要素分别是模型底座、提示词、工具、记忆、规划、执行反馈以及安全边界。下面一个个拆开说。1.1 模型底座一切能力的源头模型底座是 Agent 的大脑所有推理、决策、语言生成都从这里出发。选型时大家最先关心的通常是参数量和效果排名但真正做工程后你会发现比模型效果更重要的几个指标是上下文窗口长度、工具调用Function Calling / Tool Calling的稳定性、以及推理延迟。上下文长度直接决定了 Agent 单轮能记住多少信息。比如你要让 Agent 读一份 50 页的合同再做摘要窗口太小就得做切片和分段处理这会引入额外的工程复杂度。工具调用的稳定性则更关键——Agent 能不能准确把查询天气这个意图映射到search_weather(city北京)这个函数调用上决定了你的工具链是否真的能用。我实测下来不同模型在复杂工具选择场景下的成功率差距可以超过 30%这个数字在线上是致命的。推理延迟则是被很多人忽略的点。Agent 不是一次推理就结束它可能要经历思考-调用工具-观察结果-再思考的多轮循环一次任务可能消耗 5 到 15 次模型调用。单次 2 秒的延迟乘上 10 轮用户就要等 20 秒。所以做 Agent 项目我一般把单次推理延迟预算控制在 1 秒以内否则交互体验会崩。1.2 提示词给 Agent 立规矩提示词在 Agent 项目里不是写一段你是一个助手那么简单它承担的是角色设定、任务边界、工具使用规范、输出格式约束四重职责。我见过太多人把提示词当作文案来写充满请务必一定要这种主观色彩强烈的词实际效果反而不好——模型对过度请求式的措辞敏感度并不高它更吃结构化的指令。我自己常用的提示词框架是四段式角色与目标、执行流程、工具使用规则、输出格式模板。角色与目标解决你是谁、要干什么执行流程解决遇到什么情况先做什么工具使用规则解决什么时候调用哪个工具、参数怎么填输出格式模板解决最终怎么把结果交还给用户。这里有个踩坑经验提示词里的工具说明要和代码里的函数描述保持一致。很多时候 Agent 调错工具不是模型笨而是提示词里写的是查询天气函数 docstring 写的却是获取气象信息模型在语义匹配上出现了偏差。把工具描述统一成一套话术工具调用的准确率能立竿见影地提升。1.3 工具Agent 的手脚没有工具的 Agent 只是高级聊天机器人有了工具它才真正下地干活。工具的本质是把模型无法直接完成的操作——查数据库、调接口、执行代码、发消息——封装成可以被模型调用、且有明确输入输出契约的函数。工程上工具设计有三个原则。第一是输入参数要简单尽量用字符串和数字这种基础类型不要用复杂的嵌套对象否则模型生成参数时很容易出错。第二是输出结果要结构化最好统一返回 JSON让后续的观察环节能够稳定解析。第三是必须有明确的错误返回工具执行失败时要返回人能看懂的报错信息而不是抛一个堆栈异常——因为模型的观察能力只停留在读文本层面它在看到一堆英文 Traceback 时往往不知道该怎么处理。我在实际项目里还会给工具加一层超时保护。有些工具调用外部接口遇到网络抖动可能卡住几分钟这会让整个 Agent 的循环被拖死。给 ToolNode 加一个统一的超时中间件比如 30 秒没返回就强制结束并返回工具执行超时比让 Agent 无限等待要可靠得多。1.4 记忆让 Agent 有记性记忆是 Agent 区别于无状态 API 调用的核心能力之一。工程上记忆至少分成两层短期记忆和长期记忆。短期记忆对应当前会话的上下文窗口就是模型能直接看到的那部分对话历史长期记忆则指跨会话存储的用户偏好、历史事实、业务数据通常要落到外部存储里。很多 Agent 工程翻车都翻在记忆设计上。最常见的问题是全量塞入——把用户所有的历史对话一股脑拼进 Prompt结果上下文窗口爆掉Token 费用飙升模型反而被无关信息干扰。我的做法是分级管理短期记忆只保留最近 10 到 20 轮的核心对话长期记忆用向量数据库按语义检索只把与当前问题相关的历史片段取出来注入上下文。这里要特别提醒一个坑记忆不是越多越好。模型在上下文中处理无关信息时会显著降低注意力质量这叫上下文干扰。所以我做记忆管理时遵循一个原则——能不放进去的就不放进去宁可少忆也不乱忆。1.5 规划Agent 的思考路径规划能力决定了 Agent 面对一个复杂任务时是无头苍蝇还是步步为营。通俗理解规划就是让模型把大任务拆成子任务再决定执行顺序。比如帮我订一张下周去上海的机票Agent 需要先确认出发时间、再搜索航班、再比价、最后下单——每一步都是一个子任务。工程实现规划的常见方案有两种一种是 Prompt 驱动的隐式规划让模型在每轮推理时自行决定下一步动作另一种是显式规划用结构化框架如 Plan-and-Execute、ReAct或专门的规划节点让模型先输出一份完整计划再逐步执行。LangGraph 这类图编排框架能很好地支持后者因为它允许你在图里显式定义规划节点和执行节点。我对规划环节的建议是不要让模型自由发挥太多。在 Prompt 里明确限定每一步最多拆出几个子任务、每个子任务的产出物是什么能在很大程度上避免模型陷入反复横跳。一个真实案例是我们的 Agent 在执行数据分析任务时模型经常在清洗数据和统计数据两个动作之间来回切换后来加了规划约束必须一次性完成清洗再进入统计问题立刻消失。1.6 执行与反馈闭环的关键Agent 的本质是一个循环系统模型做出决策调用工具观察工具返回的结果根据结果再做下一步决策直到任务完成。这个观察-决策-执行-再观察的闭环就是 Agent 和普通程序最大的区别。程序是线性的Agent 是循环的。工程上执行与反馈环节最考验框架的稳定性。这个循环谁来驱动状态怎么传递工具返回结果怎么回到模型上下文里手动实现这套循环很容易出 bug——比如状态对象在循环中丢失、消息列表被重复追加、工具调用的中间结果没有正确封装成工具消息回到模型手里。我强烈建议直接用成熟的编排框架LangGraph、LlamaIndex Workflows 等而不是自己造轮子。它们把循环的骨架搭好了你只需要关注业务逻辑。实际操作中我最常用的控制结构是条件边模型判断任务已完成就走结束分支判断需要再调工具就走工具分支这个条件判断本身也由模型来完成但判断的逻辑会被收敛到一个专门函数里方便做日志和监控。1.7 安全边界与合规闸门最后一个要素是做生产级 Agent 时必须考虑的安全边界。模型可以调用工具就意味着它有了行动能力而这个行动能力如果不加约束在生产环境里会变成灾难。我见过不止一个团队把删除数据库记录的工具直接暴露给 Agent结果测试时模型在错误场景下真的执行了删除操作。安全边界在工程上至少要做三层。第一层是工具准入白名单——能暴露给 Agent 的工具必须经过人工审核敏感的写操作单独走审批流程。第二层是参数校验——工具函数内部必须对模型传入的参数做类型检查和取值范围校验不能盲目信任模型的输出。第三层是操作审计——所有工具调用都要记录日志包括调用者、调用时间、传入参数、返回结果出问题可以回溯。我常用的一个实践经验是给危险操作加二次确认机制当 Agent 要执行删除、修改、转账这类敏感动作时先返回一个确认请求给用户用户确认后再真正执行。这个机制虽然在技术上多了一步但换来的安全收益远远大于交互成本。毕竟 Agent 的不可控性本来就高能在架构层面把它管住比事后补救强太多。2. 工程实现的七个决策点从能跑到能扛的进阶之路理解了七要素你只是理解了 Agent 的零件清单。真正动手做项目时你会发现自己在每个技术选型路口都要做抉择。我把这些抉择归纳为七个决策点几乎覆盖了从零搭建一个可用 Agent 服务的全部关键岔路。这七个决策点没有绝对正确的答案但每个决策背后的权衡逻辑是共通的。2.1 决策点一技术栈怎么选第一个决策点通常也是最纠结的用哪个框架、哪种语言、哪套生态。市面上的选择大致分三类以 LangChain/LangGraph 为代表的 Python 生态以 Spring AI 为代表的 Java 生态以及以 Rust 为核心的高性能自研路线。Python 生态最成熟资料最多LangGraph 对复杂状态流编排的支持尤其到位Spring AI 适合已经在 Java 技术栈扎根的传统企业团队不需要跨语言转型Rust 路线强在并发和资源占用但开发效率会打折扣生态也还在早期。我的建议是除非你的团队有特殊理由比如必须融入现有 Java 中台、或者并发要求极高且预算有限否则优先选 Python LangGraph 这套组合。原因很实在——Agent 领域的迭代速度极快新的模型能力、新的工具协议、新的编排模式通常先在 Python 生态落地等 Java 或 Rust 跟进往往已经落后半年以上。对大多数业务场景来说跟上迭代速度比省一点资源更重要。另外要明确一个边界框架只是辅助核心的编排逻辑还是在你手里。LangGraph 给你的是状态管理、图执行、断点续跑这些基础设施但怎么设计状态结构怎么划分节点粒度怎么控制循环终止这些仍然是要靠你自己的工程判断。框架不是银弹它能做的是让你少写一些低水平的胶水代码。2.2 决策点二单 Agent 还是多 Agent第二个决策点直接决定系统架构的复杂度是搞一个全能的 Agent还是拆成多个各司其职的 Agent让它们协作完成复杂任务单 Agent 架构简单、调试方便、Token 开销相对可控适合任务边界清晰的中等复杂度场景。但当任务涉及多个差异很大的子域时——比如一个 Agent 既要管客服对话又要管订单查询还要管售后工单——单 Agent 的 Prompt 会膨胀到难以维护模型也容易在意图切换中出岔子。多 Agent 架构的思路是让每个 Agent 只干一件事。比如客服场景拆成接待 Agent、订单 Agent、售后 Agent由路由器 Agent 根据用户意图分派。这种模式的好处是每个 Agent 的 Prompt 短而专注模型行为更稳定代价是引入了 Agent 之间的通信协议、任务编排逻辑和额外的 Token 开销。我做项目时的判断标准很简单子域之间的耦合度。如果不同任务共享大量上下文和状态用单 Agent如果任务之间基本是分诊-转交的关系用多 Agent。最怕的是为了技术感强行拆多 Agent结果大部分 Token 都耗在 Agent 之间的相互解释上得不偿失。2.3 决策点三记忆方案放哪里记忆方案的选型是第三个决策点。前面讲过记忆分短期和长期工程上真正决定架构的是长期记忆的落地方式。常见的方案有三种第一种是全量存文本文件或数据库适合对检索精度要求不高的场景第二种是向量数据库 语义检索这是目前的主流适合需要按语义召回历史信息的场景第三种是混合方案——结构化数据放关系库、非结构化数据放向量库用一层统一的记忆访问接口去屏蔽底层差异。我测试过多个向量数据库选型时重点看四个指标检索延迟、召回准确率、运维成本和生态完整度。对中小项目来说开箱即用的向量库往往比需要独立部署集群的方案更划算。但有一点要注意向量检索的效果高度依赖 Embedding 模型和切片策略同一个库换一个 Embedding 模型召回效果可能天差地别。所以选记忆方案时花在切片和 Embedding 调优上的时间通常比花在选数据库上的时间更值得。另外一个容易踩的坑是记忆的脏数据问题。用户的历史输入质量参差不齐包含大量无效信息、情绪化表达甚至错误假设如果不做筛选直接入库检索到的记忆可能反过来误导 Agent。我在实践中会对写入记忆的内容做一层过滤——只保留包含明确实体、明确偏好或明确事实的片段——这比事后清洗要省力得多。2.4 决策点四并发怎么扛AI Agent 怎么扛并发是近期搜索热词中出现频率很高的问题可见大家做 demo 容易上生产就被并发问题卡住。Agent 的并发和传统 Web 服务的并发完全不同——一个 Agent 任务要串行调用多次大模型接口每次调用可能耗时数秒如果每个请求都同步阻塞在那里等结果线程池很快就会耗尽。扛 Agent 并发我总结三条路。第一条是异步化用 FastAPI 的 async 接口 asyncio 管理并发任务让 IO 等待期间释放线程资源。第二条是队列化把耗时的 Agent 任务放入消息队列Redis Stream、RabbitMQ、Celery 都行由 Worker 异步消费客户端轮询或通过 WebSocket 接收结果。第三条是池化给大模型 API 调用做连接池和信号量限流防止突发流量把上游 API 打爆。具体选哪条路取决于实时性要求。如果用户需要即时拿到结果走异步 长连接如果用户能接受几秒到几十秒的等待走队列 轮询会更稳。我的经验是对外提供 HTTP 接口时永远不要让请求直接在请求线程里跑完整个 Agent 循环——这是最快被打垮的方式。哪怕是单机部署也要把任务提交到内部队列再通过回调或轮询返回结果。2.5 决策点五工具调用的协议与安全第五个决策点是工具层怎么设计。前面七要素里讲过工具要有明确的输入输出契约这里要补充的是协议层的选择。目前主流的工具调用协议是各大模型厂商提供的 Function Calling 原生格式模型会以结构化 JSON 输出要调用的函数名和参数由你的代码去执行。另一种方案是用 MCPModel Context Protocol这类标准化协议把工具的发现、调用、鉴权统一起来。我对 MCP 的态度是方向是对的但目前生态还处于早期不同实现之间的兼容性参差不齐。如果你的工具集不大比如 10 个以内直接用原生 Function Calling 更省事如果工具数量膨胀到几十上百或者需要跨团队共享工具清单MCP 的标准化价值就体现出来了。工具安全方面除了前面讲的白名单和参数校验还有一个实战细节工具返回结果要给模型设置信息上限。有些工具会返回超大结果比如一次查询返回几千行数据全部塞进模型上下文会迅速烧光 Token。我通常在工具内部做结果截断或摘要比如只返回前 20 条 共 300 条是否继续查询让模型自己决定下一步。2.6 决策点六可观测性与调试体系Agent 的调试难度远超普通程序因为它的执行路径不是固定的——同一个问题模型这次走三步完成下次可能走了十步还绕了远路。这种不确定性让传统的日志排查方式失效你对着日志根本不知道模型当时为什么选择了那条路。所以可观测性是做 Agent 生产级应用必须补的一课。我在项目中搭建的观测体系分三层。第一层是 Trace 追踪记录每次请求完整走过了哪些节点、调用了哪些工具、每个节点耗时多少这层我用 LangSmith 或者自研的埋点方案都能做。第二层是状态快照在每个节点前后记录 state 的完整内容尤其是消息列表和模型输出这样出问题可以回放模型当时看到了什么。第三层是指标聚合统计工具调用成功率、平均轮次、Token 消耗量、超时率这些核心指标。调试 Agent 时我最大的心得是一定要让模型思维链可见。很多框架默认把中间推理过程隐藏掉只返回最终结果这在开发调试阶段等于蒙着眼睛开车。开发环境务必开启详细日志把模型每轮的思考内容和工具调用决策打印出来。我甚至会在调试时用一次只改一个变量的原则——同时改 Prompt 和换模型出问题你根本分不清是哪个改动引起的回归。2.7 决策点七评测指标与上线标准最后一个决策点往往是团队最容易拖延的怎么评测 Agent 好不好用。传统软件有明确的单元测试和功能用例Agent 的输出却是开放式的同一个需求可能有多种可接受的答案。但这不代表没法评测只是评测方法要设计得更细致。我的评测体系分三层准确性、稳定性和成本效率。准确性用关键要素命中率来评估——比如客服场景用户问退货运费谁出Agent 回答里是否包含买家承担时效说明例外条款这多个关键要素命中几个算几分。稳定性则用同样的测试集跑多轮看输出波动有多大——我遇到过同一个 Prompt 连续跑 10 次答案质量忽高忽低这种 Agent 直接上生产就是砸招牌。成本效率则统计单任务平均 Token 消耗和平均轮次轮次越多说明模型越绕需要优化 Prompt 或工具设计。上线标准我一般定三条硬指标关键场景测试集的要素命中率不低于 85%、工具调用成功率不低于 95%、平均任务轮次不超过 8 轮。三条都过了才准上生产。没有评测就上 Agent等于闭眼开车——用户迟早会用差评教会你什么叫翻车。3. 实操落地基于 FastAPI LangGraph 的 Agent 完整实现理论说了一堆最终还是要落到代码上。这一节我用一个天气查询 日程查询的混合 Agent 做示例完整走一遍基于 FastAPI LangGraph 的实现流程。选择 LangGraph 而不是单纯用 LangChain是因为它把循环、状态、条件分支这些 Agent 特有的编排逻辑封装成了图结构代码读起来清晰也方便后续扩展多 Agent 架构。3.1 目录结构与环境准备先看目录结构。我习惯把 Agent 核心逻辑和服务层分开这样以后换框架或加一层缓存都比较方便agent-service/ ├── main.py # FastAPI 入口 ├── agent/ │ ├── graph.py # LangGraph 图定义 │ ├── state.py # 状态结构 │ ├── tools.py # 工具注册 │ ├── prompts.py # 提示词模板 │ └── config.py # 模型与运行时配置 ├── requirements.txt └── .env依赖方面核心需要四个包langgraph、langchain-openai或你用的模型厂商对应的包、fastapi、uvicorn。如果要做长期记忆再加一个向量库客户端。安装命令很简单pip install langgraph langchain-openai fastapi uvicorn这里提醒一句LangGraph 的版本迭代很快不同版本的 API 偶有变动安装时最好固定版本号。我的requirements.txt里会把主版本锁住避免过一段时间拉新依赖后代码悄悄跑不起来。3.2 核心 Agent 逻辑实现先定义状态结构。LangGraph 的核心概念是状态所有的信息传递都通过状态对象完成。这里我用消息列表作为状态主体LangGraph 的add_messages归约器会自动把新消息追加到已有列表里# agent/state.py from typing import Annotated, TypedDict from langgraph.graph.message import add_messages class AgentState(TypedDict): messages: Annotated[list, add_messages]接着定义工具。我用两个工具做示例一个是查询天气一个是查询日程。注意每个工具都要有清晰的 docstring因为 LangChain 会把函数名和 docstring 一并注入给模型做工具描述# agent/tools.py import json def search_weather(city: str) - str: 根据城市名查询当前天气情况入参为城市名称字符串返回 JSON 字符串。 # 实际项目里这里会调外部天气 API mock { city: city, weather: 晴, temp: 26, humidity: 40, } return json.dumps(mock, ensure_asciiFalse) def query_schedule(date: str) - str: 按日期查询日程安排入参为 YYYY-MM-DD 格式日期返回 JSON 字符串。 schedules { 2025-07-01: [10:00 产品评审, 14:00 技术周会], 2025-07-02: [09:30 客户拜访], } result schedules.get(date, []) return json.dumps({date: date, events: result}, ensure_asciiFalse)然后是 Prompt。我用前面说的四段式结构把角色、执行流程、工具规则和输出格式都写清楚# agent/prompts.py SYSTEM_PROMPT 你是一个个人助理 Agent负责帮助用户查询天气和日程信息。 执行流程 1. 分析用户意图判断需要调用哪个工具。 2. 调用工具获取信息。 3. 根据工具返回结果用自然语言回复用户。 工具使用规则 - 查询天气时调用 search_weather 工具入参必须是城市名称。 - 查询日程时调用 query_schedule 工具入参必须是 YYYY-MM-DD 格式日期。 - 如果用户提供的信息不完整先向用户确认不要猜测参数。 输出要求 - 回复简洁、准确直接给出查询结果。 - 如果工具调用失败如实说明并提供下一步建议。核心部分来了构建 LangGraph 图。图上有两个节点——模型节点负责推理决策工具节点负责执行工具。模型节点把 LLM 绑定工具后接入工具节点用 LangGraph 的ToolNode封装。条件边决定下一步是继续调工具还是结束# agent/graph.py from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, END from langgraph.prebuilt import ToolNode from agent.state import AgentState from agent.tools import search_weather, query_schedule from agent.prompts import SYSTEM_PROMPT def build_agent(): # 初始化模型并绑定工具 llm ChatOpenAI(modelgpt-4o, temperature0) tools [search_weather, query_schedule] llm_with_tools llm.bind_tools(tools) # 模型节点 def agent_node(state): messages [ {role: system, content: SYSTEM_PROMPT} ] state[messages] result llm_with_tools.invoke(messages) return {messages: [result]} # 条件边判定 def should_continue(state): last state[messages][-1] # 模型消息带 tool_calls 就继续走工具节点否则结束 if getattr(last, tool_calls, None): return tools return end # 构建图 graph StateGraph(AgentState) graph.add_node(agent, agent_node) graph.add_node(tools, ToolNode(tools)) graph.set_entry_point(agent) graph.add_conditional_edges( agent, should_continue, {tools: tools, end: END}, ) graph.add_edge(tools, agent) return graph.compile()这段代码的核心逻辑是模型节点收到用户消息后如果模型决定调用工具就会在返回消息里携带tool_calls字段条件边检测到这个字段就跳到工具节点执行对应的工具工具执行结果以ToolMessage的形式追加到状态消息列表里图再回到模型节点让模型基于工具结果继续推理。这个循环会一直持续到模型不再要求调用工具为止。3.3 服务化与并发处理有了 Agent 核心逻辑接下来要把它包成一个 HTTP 服务。这里的关键决策是不能直接在 FastAPI 请求协程里同步跑完整个 Agent 循环因为慢请求会拖垮所有并发。我用asyncio 线程池的方式把 Agent 执行丢到后台再配合一个简单的任务队列# main.py import asyncio from fastapi import FastAPI, BackgroundTasks, HTTPException from pydantic import BaseModel from agent.graph import build_agent from agent.state import AgentState app FastAPI() agent_app build_agent() class ChatRequest(BaseModel): message: str session_id: str default class ChatResponse(BaseModel): session_id: str reply: str # 内存版会话管理生产环境请替换为 Redis 等外部存储 sessions {} app.post(/agent/chat, response_modelChatResponse) async def chat(req: ChatRequest): # 每个会话独立维护消息列表避免串话 if req.session_id not in sessions: sessions[req.session_id] [] sessions[req.session_id].append({role: user, content: req.message}) state AgentState(messagessessions[req.session_id]) # 用线程池执行同步的 Agent 循环避免阻塞事件循环 loop asyncio.get_running_loop() result await loop.run_in_executor(None, lambda: agent_app.invoke(state)) # 提取最终回复 reply_msg result[messages][-1] reply reply_msg.content if hasattr(reply_msg, content) else str(reply_msg) sessions[req.session_id].append({role: assistant, content: reply}) # 避免会话无限膨胀只保留最近 20 条 sessions[req.session_id] sessions[req.session_id][-20:] return ChatResponse(session_idreq.session_id, replyreply)注意到几个细节第一会话消息按session_id隔离这是多用户场景的底线要求否则 A 用户的对话会被 B 用户看到第二消息列表做了长度上限控制截断到最近 20 条防止上下文窗口被慢速对话塞满第三用run_in_executor把同步的 Agent 循环丢到线程池释放事件循环给其他请求。如果你要支撑更高的并发请务必引入真正的任务队列Redis Stream / Celery把 HTTP 请求变成提交任务-返回任务号再用独立的 Worker 去消费。Worker 数量和模型 API 的速率限制要匹配一般是 Worker 数和上游 API 并发上限 1:1不要盲目开几百个 Worker 然后被上游限流打到崩溃。3.4 参数调优与成本控制Agent 服务的成本控制和传统 API 完全不同你没法精确预知一次请求消耗多少 Token因为 Agent 可能会多轮循环。我控制成本的几个实操手段第一是限制最大迭代轮次。LangGraph 里用recursion_limit参数控制图的最大执行步数比如设置为 15超过就强制终止result agent_app.invoke(state, config{recursion_limit: 15})第二是控制注入模型的上下文长度。工具返回结果要截断、对话历史要裁剪前面已经反复强调过。第三是模型分级——简单的意图识别用便宜的小模型复杂的推理才用旗舰模型。LangGraph 支持在节点级别指定不同的模型这比全程用一个贵模型能省下可观的成本。第四是缓存重复问题。用户问今天天气怎么样短时间内的答案基本没差别。在工具层做结果缓存用城市名 日期做 key缓存 30 分钟能把重复查询的 Token 消耗直接砍掉。我见过不少项目把缓存做在 HTTP 层但对 Agent 来说工具层的缓存覆盖面更广——因为同样的工具结果可能被多个不同的用户问题命中。4. 常见问题与排查技巧实录这部分是我在多个 Agent 项目里积累的真实踩坑记录。每一条都是线上环境实际碰到过的对应的排查思路和修复方案也经过了验证。如果你正在做 Agent 开发这些内容应该能帮你少走不少弯路。4.1 工具反复调用、陷入死循环症状Agent 在某个工具上反复调用多次每次结果都差不多但它就是不结束Token 哗哗地烧。排查时打开日志看到的是同一个工具被调用了七八次。这类问题十个里有八个是因为工具返回结果的信息量不足以让模型做出结束判断。比如你的工具每次只返回查询成功没有把真正有价值的结果放进去模型就会觉得还没拿到答案于是再调一次。修复方法是让工具返回包含明确结论的结果文本并且在 Prompt 里加一条硬规则如果工具已返回有效结果直接根据结果组织最终回复不得重复调用同一工具。另一个原因是模型自身的决策缺陷。有些模型在复杂场景下会手滑连续输出相同的工具调用。应对办法是加调用去重——在工具节点记录最近一次调用的工具名和参数 hash如果重复就直接把上次结果返回给模型不再真正执行。这个兜底逻辑在线上非常管用。4.2 模型返回格式不稳定症状大多数时候模型输出规范的 JSON偶尔输出一段带自然语言解释的混合内容导致下游解析报错。我见过一个团队为了兼容这种偶发异常写了四层 try-catch最后还是崩。核心修复思路是降低对模型完全守规矩的依赖。具体做法有三步第一在 Prompt 里给出精确的输出模板并配上正例和反例第二在解析层用容错解析器先从文本里提取 JSON 片段再解析而不是直接整体json.loads第三也是最重要的——解析失败时不要让程序崩溃而是构造一个模型输出格式异常请重新输出的修正消息丢回给模型让它自己纠错。我在 LangGraph 里实现这个逻辑是在工具节点前加了一个格式看门人节点,专门检查模型的工具调用参数是否合法。不合法就返回一条修正提示再走一次模型节点。注意这个修正最多重试两次防止模型一直输出错误格式导致死循环。4.3 会话记忆串台症状用户 A 在对话里问了帮我订上海到北京的机票用户 B 紧接着问我的票订好了吗结果 B 收到了 A 的订票信息。这类问题极其严重属于安全事故。原因几乎都是会话隔离没做好。常见的坑有两个一是用全局单例存消息列表请求直接覆盖二是 session_id 在前端生成并随请求传入但前端逻辑有缺陷导致多个用户共用一个 ID。我的排查思路是先在网关层打印每个请求的session_id和用户身份映射确认是不是 ID 分配环节出了问题然后再查后端存储的 key 设计确认是否真的按session_id隔离。解决方案就是前面代码里演示的内存或 Redis 里用session_id作为 key 存独立的会话列表并且在读写时加锁。生产环境我建议直接使用 Redis 的 Hash 结构天然支持按会话隔离还能设置过期时间自动清理僵尸会话。4.4 并发场景下的超时与限流症状线上刚开始跑没问题一旦做活动引流并发上来后大量请求超时甚至引发上游模型 API 的限流报错。这类问题的根因往往是没有流量整形。我处理的思路是分三层第一层入口限流——用 FastAPI 的依赖注入或中间件限制每个用户的 QPS防止单用户刷爆第二层队列削峰——把所有 Agent 任务丢进队列Worker 按固定速率消费宁可让用户等久一点也不要雪崩第三层链路超时——给整个 Agent 调用链路设置总超时比如 60 秒给单次模型调用设置 30 秒超时超时就返回友好错误而不是无限挂起。这里有个容易被忽略的常识大模型 API 的限流是按分复用每分钟请求数和 Token 速率双重计算的。你做并发规划时不能只看 QPS还要估算平均每请求的 Token 消耗——如果每个请求平均消耗 5000 Token100 个并发就是 50 万 Token很多 API 套餐的分钟级 Token 上限根本扛不住。合理做法是给每个会话设每日/每小时的 Token 预算超了就降级到仅文本回复、不调工具的兜底模式。4.5 实测避坑清单最后把最有价值的经验浓缩成一张速查表每个场景都是真实碰过的问题表现根因解法工具调用准确率低工具 docstring 和提示词描述不一致统一工具描述文案模型经常绕远路Prompt 中缺少流程约束限定子任务数量和执行顺序上下文 Token 飙涨全量对话塞入模型只保留最近 N 轮 语义召回多用户串话session_id 隔离缺失按会话 key 存储Redis 落地并发一上来就超时同步阻塞事件循环异步化 队列化 限流模型偶发格式错误对格式约束过度自信容错解析 修正消息重试Agent 反复调用工具工具结果信息量不足返回含结论的结果 调用去重这张表背后有一个统一的思路Agent 的工程化本质上是把模型的不可控性关进制度的笼子里。工具准入、参数校验、格式校验、会话隔离、限流熔断每一条都是在给模型的自由度做约束。约束越多系统越稳但也不能把所有路都堵死——关键是要找到业务安全性和模型自由度的平衡点。我见过最成功的 Agent 项目无一例外都是模型大胆想、系统兜住底的架构而不是让模型完全放飞自我。从七要素到七个决策点再到这一套可以直接抄作业的代码实现Agent 的工程实现其实没有想象中那么神秘。它就是一个带有循环、状态和行动能力的软件系统——难点在于你既要理解模型的脾气又要有扎实的工程功底去驯服它。希望这篇文章能把你在 Agent 路上最关键的为什么和怎么做都讲透。如果看完你直接动手把第一个 Agent 服务搭起来那这篇文章的价值就算真正落地了。