ARTICLE DETAIL

资讯详情

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

AI Agent工程落地:从七要素到高并发架构实战

AI Agent工程落地:从七要素到高并发架构实战 聊到 AI Agent大家最先想到的往往是能自己规划、自己调用工具、还会自我纠错的智能体概念。但真到了工程实现阶段问题很快就从Agent 能做什么变成Agent 到底怎么搭、怎么扛并发、怎么保证稳定。我接触过不少项目团队对 Agent 的理解停留在给大模型套个 Prompt 再挂几个 API 调用结果一上线就崩会话状态乱、工具调用超时、多用户一并发整个服务直接卡死。这篇文章不聊玄学我会从 Agent 的七要素讲起再拆到工程落地必须拍板的七个决策点最后结合 FastAPI LangChain LangGraph 这套主流组合带着你把一个能扛并发的 Agent 服务真正搭建起来。先说清楚这篇文章适合谁读如果你正在用 LangChain、LangGraph、Spring AI 这类框架搭 Agent或者你打算从零选型想知道 Rust、Python、Java 这些技术栈在 Agent 场景下各自的优劣势又或者你已经被AI Agent 怎么扛并发这个热搜词折磨过那这篇内容就是为你准备的。我会尽量用工程实践的视角讲透不堆概念。1. 从七要素看 Agent 的本质想搞懂 Agent 的工程实现第一步不是选框架而是把 Agent 拆成一套稳定的结构。我习惯把 Agent 的运行时拆成七个要素指令、上下文、工具、模型、推理框架、记忆、可观测性。这不是教科书式的分类而是我在实际开发和排查问题过程中总结出来的最小组成单元。任何一个 Agent 项目你都能拿这七个要素去对照缺哪块补哪块出问题也能快速定位到底坏了哪个环节。1.1 指令System Prompt 不是作文是系统边界指令也就是 System Prompt是整个 Agent 的顶层约束。很多人把 System Prompt 当成写作文恨不得把大模型的角色设定、说话风格、知识背景全写进去。但在工程实现里System Prompt 的第一职责不是让它更像一个人而是告诉它哪些事情绝对不能做以及当一个任务来临时它应当按什么顺序处理哪些信息。我见过一个实际的客服 Agent团队把 System Prompt 写了两千多字结果模型在复杂会话里频繁跑偏回答带着浓郁的开场白腔调。排查之后发现问题出在指令里混入了大量跟决策无关的内容真正关键的必选动作路径反而被淹没了。一个可靠的 System Prompt 应该具备三层结构角色和限制、任务处理流程、输出格式要求。角色不要超过两句限制必须用否定句写得绝对明确比如不得编造工具返回值中不存在的数据任务处理流程要按步骤编号输出格式要求最好直接给 JSON 示例。真实经验是System Prompt 写得越短模型遵循得越好。还有个细节容易被忽略System Prompt 里不要写你可以使用搜索工具查询实时信息这种话而是要在工具层把能力锁死。指令只负责决策工具的可用性由运行时控制。否则模型会在一轮对话中反复调同一个工具甚至在不需要查询的时候也去查白白增加延迟和费用。1.2 上下文模型看到什么比模型是什么更重要上下文决定了 Agent 在某一轮决策时眼前看到的东西。早期我见过最典型的错误是把所有历史对话都塞给模型甚至把几百轮消息全部拼进 Prompt。这样做有两个后果一是 token 成本爆炸二是模型在长上下文里注意力被稀释关键信息反而抓不住。工程上处理上下文的核心思路是分层压缩。会话历史里距离当前时间近的消息保留完整距离远的用摘要替代工具调用结果里只保留结构化的返回值和必要字段不保留完整 JSON每个 Agent 节点所见的上下文应该是经过筛选和格式化的任务相关切片而不是把所有信息一股脑倒给它。这里有个非常实用的操作给每一轮上下文打标签。比如用户当前意图上一轮工具执行结果摘要本轮需要完成的核心动作然后把标签本身也作为 Prompt 的一部分。模型在生成时看到的是有语义分层的输入而不是一段平铺直叙的文本。实测下来这个改动对回复准确率的提升比换一个更大的模型还要明显。1.3 工具让模型学会用更要防止模型乱用工具是 Agent 与外部世界交互的接口它的工程本质是一份 JSON Schema 描述再加上一个可执行函数。我在设计工具层的时候关心三件事模型的意图是否能准确映射到工具上工具调用的参数校验是否在运行时完成工具返回结果是否对模型友好。第一件和第二件可以通过强制使用 Function Call 或者 Tool Call 协议解决。不管用 OpenAI、Claude 还是本地模型都要优先选择原生工具调用格式而不是让模型自己输出一段我想调用某工具的文本来解析。原生协议的好处是模型输出结构化参数你不需要写正则去猜它要传什么参数。但这不意味着不需要校验。模型经常把参数类型传错比如数字字段传了字符串、枚举值超出范围。这些必须在进入真正函数前做一层硬校验否则你在业务逻辑里排查半天最后发现是入参的不严谨。第三件经常被忽略工具返回值也要设计成模型方便消化的形式。比如数据库查询工具你不应该把整张表的原始记录返回给模型而应该返回聚合后的统计摘要。模型对数字表格的理解远不如对一句话摘要的理解而这句话摘要你可以用一个小模型或者模板先生成好。工具返回结果做得足够干净Agent 的下一轮决策质量会大幅提升。1.4 模型选型不是越大越好是越合适越好模型选型是整个 Agent 系统里最容易被先入为主的决策。很多团队上来就选参数最大的模型但真实场景里不同的 Agent 节点对模型能力的需求完全不同。工具调用、意图识别、信息抽取这类任务用小模型完全够用最终的回复生成、复杂推理、长文本重写才需要大模型。我建议把模型分成决策模型和生成模型两层。决策模型负责在每个 Agent 步骤中选择走哪条路径要求响应快、结构化输出稳定生成模型负责用户可见的最终回答要求语言质量和逻辑性好。这样做的好处不仅在于省钱和省延迟更在于你能独立地为每一层做降级方案。决策模型挂了生成还能靠兜底策略继续生成模型如果偶尔不稳定决策模型也不受影响。另外模型供应商切换要做抽象。你不确定今天选的模型明天还撑不撑得住。团队里只要有条件就把模型访问封装成统一接口底层支持多家供应商热切换。这件事非常现实我在项目里经历过某家模型服务连续一天状态异常靠另一家的兼容模型撑了过去。1.5 推理框架ReAct 之外还有更可控的路线推理框架解决的是Agent 怎么决定下一步做什么。最常见的 ReAct 模式就是让模型在思考-行动-观察循环里自己摸索。这个模式实现简单但也有明显问题链条越长失败率越高且过程不可控。一旦模型在一个循环里反复走错路你很难从外部去干预。我更推荐在工程中用 Plan-and-Execute 或者基于图结构的编排。LangGraph 是这种思路的典型代表你把整个任务拆成节点和边节点是具体的动作生成、调用工具、判断条件边是流转规则。模型只在节点内部做局部决策而下一步去哪个节点由你在图上预先定义或由条件判断函数控制。这样做的本质是不让模型独占整个流程的控制权而是把控制权分散到代码逻辑里。对于复杂度可控的 Agent纯 ReAct 可以用一旦任务开始涉及多轮工具调用、条件分支、人工确认就得上图编排。这个决策会在后面影响你的并发、状态管理、可观测性设计所以早做比晚做好。1.6 记忆长期记忆是战略短期记忆是战术记忆是 Agent 工程里最容易被做虚的环节。很多人说我的 Agent 有记忆其实就是把聊天记录存在了 MySQL 里每次全量读出来。这样做是短期记忆不是长期记忆。长期记忆的关键是提炼而不是存储。用户和 Agent 的每一次交互结束后都需要把对话内容做结构化提炼例如提取用户偏好待办事项项目中多次提到的关键实体。提炼后的信息进向量数据库或者结构化存储在后续会话开始前检索出来作为上下文的一部分注入。这个提炼过程可以由大模型完成也可以用小模型加规则完成关键是提炼字段的设计要贴合业务。从工程上我习惯把短期记忆放在 Redis 这类高速缓存里设置过期时间长期记忆放在数据库里通过用户标识和会话标识关联。你今天在对话里提到的项目背景我建议放进长期记忆但上午聊的某条零食推荐就让它过期算了。记忆如果做太满Agent 的每一步响应都要背着一个巨大的背景包袱速度和成本都会拖垮。1.7 可观测性与护栏上线之后你靠它活着最后这个要素很多人拖到上线才补但实际上应该从第一天就进入设计。可观测性包含三层日志、指标、追踪。Agent 的每一步开销包括模型响应、工具调用耗时、token 消耗、重试次数都需要有指标记录。链路追踪尤其重要因为 Agent 的一次请求常常跨好几个节点没有 trace你根本说不清楚是哪个环节拖慢了整体响应。护栏则是安全网。比如输出内容过滤、工具调用权限校验、敏感信息脱敏、超时熔断。护栏的位置应该在模型输出之后、用户可见之前。很多项目既没做可观测性也没做护栏Agent 一旦出现幻觉或者工具乱调就只能等用户来骂了。2. 七个决策点Agent 工程实现的十字路口要素拆完你会发现自己面对的是无数条可以走的路。每个方向都意味着一种工程取舍。这里我把最常见也最关键的七个决策点整理出来。如果你正在搭一个生产级 Agent这七个问题每一个都得有明确答案。2.1 决策点一单体 Agent 还是多 Agent 架构第一个十字路口就是一套流程里到底是一个 Agent 干完所有事还是拆成多个各司其职的子 Agent。很多团队看到多方案对比多 Agent 协作的炫酷 Demo 就上头了一上来就拆五个 Agent 各分管一个模块结果状态流转、通信协议、上下文共享全成了灾难。我自己的判断标准是如果整个任务只需要一两轮工具调用单体 Agent 就够了如果任务件里包含明确的专业分工比如数据分析 Agent报告撰写 Agent质量审核 Agent每个角色需要完全不同的上下文和工具权限那就拆。拆的时候要想清楚子 Agent 之间的通信协议最稳妥的通信方式是结构化消息加共享状态绝不要让一个 Agent 直接读另一个的一长篇对话记录。多 Agent 架构绝对不是默认选项。它带来的并发压力、一致性问题和调试复杂度都会成倍增加。先单体打底等真的遇到瓶颈了再拆是绝大多数项目最务实的路线。LangGraph 的好处在于你可以在同一个图上先把所有节点放在一个 Agent 里做再逐步把部分节点独立成子 Agent变更成本相对可控。2.2 决策点二状态管理Agent 的账本记在哪Agent 执行过程中产生的中间状态包括当前任务进度、已收集的信息、已执行过的工具调用、待处理的分支这些都是状态。状态管理有两大方向存内存还是存外部存储。存内存实现简单但不支持多副本横向扩展服务一重启就全丢。存储外部队列或数据库能支撑多实例但要考虑状态序列化和一致性问题。我的建议是第一步可以用内存状态加 Redis 兜底把关键状态在每次节点切换后持久化一次。后来演变到用数据库保存会话级状态。对于 LangGraph它的 State 机制天生支持跨节点传递结构化数据。你要注意一个节奏不要让状态在上千轮里无限膨胀每个节点结束后做一次状态瘦身把已经用完的临时大字段清掉。单实例 Agent 还面临另一个问题同一个用户的两次请求可能并发到达如果状态没有加锁两个请求会在同一个状态副本上互相踩踏轻则数据错乱重则 Agent 流程直接卡死在某个分支。我在下文讲并发时会细说这件事。2.3 决策点三流程编排讲逻辑还是讲自由Agent 的任务流程编排方式本质上是在可控和灵活之间做取舍。纯 ReAct 流程足够自由模型想跳哪步就跳哪步但不可控也难观测基于图结构的编排流程稍显死板但每一步都可预期出现问题可以被精确回放。我个人的工程倾向是80% 的常规流程先画一张剧本图先做什么再做什么哪种情况走哪里遇到异常回到哪个节点。然后给模型留出自由动作的空间只在节点内部。比如数据检索节点里模型可以自由选择调用哪些检索工具但它不能直接跳到报告生成节点。这个决策直接影响项目排期。纯 ReAct 一两天就能跑通但生产可用至少再多花两三周去处理模型乱跑的问题。图编排静态搭建会慢一些但稳定性和可维护性都大幅提升。我宁可前期多花点时间把图设计好也不愿意上线后每天追着模型的行为修 BUG。2.4 决策点四并发模型Agent 怎么扛住多用户AI Agent 怎么扛并发是最近大家问得最多的问题。要理解这个问题先要搞清楚 Agent 服务的大量耗时本质上不是 CPU 计算而是等待模型 API 响应和工具 API 响应。这意味着并发优化核心是 IO 密集型优化而不是死磕 CPU 处理速度。在 Python 技术栈里如果用的是 FastAPI 加异步函数每个请求在网络 IO 等待时都会让出事件循环从而支撑大量并发请求同时处于等模型返回的状态。但你得注意LangChain 和 LangGraph 的内部节点如果用的是同步函数调用时会阻塞事件循环直接拖垮整体吞吐。解决方式是给每个可能耗时的节点都用异步版本async函数编写或者在需要调用同步库时改用线程池执行。另一个关键因素是排队机制。模型供应商有速率限制你的应用层如果不做流量控制发出去一百个并发请求九十九个会被限流打回来。所以要在网关层做令牌桶控制单位时间内的请求速率同时为不同级别用户设置优先级队列保证 VIP 用户的请求不被海量免费用户挤到后面。这是生产级 Agent 和 Demo 级 Agent 的分水岭。2.5 决策点五记忆的持久化层级哪些该存、哪些该忘记忆怎么持久化前面已经提过原则这里讲讲落地。短期记忆建议存在 Redis 里键设计成user:{userId}:session:{sessionId}:history设置 30 分钟到一个小时过期对话结束后做一次提炼把结构化结果写入 MySQL 或 PostgreSQL。向量数据库在这种场景里保存的是长期记忆和知识库的检索片段不是对话原文。要特别注意记忆的权限隔离。如果多个用户共用一套知识库你的向量检索必须加租户过滤条件否则 A 用户检索到的内容可能混入 B 用户的私有信息。我在实际排查中见过不止一次问题出在向量检索没带过滤条件导致不同租户的数据在 Agent 回答里混到了一起。这是严重事故级的问题一定要在系统设计的初始阶段就考虑清楚。2.6 决策点六工具协议选型模型怎么握住你的工具模型调用工具依赖的是工具描述与参数 Schema。OpenAI 的 Function CallingClaude 的 Tool UseGoogle 的 Function Calling它们虽然名称不同但本质上都是把一组 JSON Schema 定义传给模型模型返回一个结构化的调用请求。工程实现时你要设计一个统一的工具注册中心每个工具包含名称、描述、输入参数 JSON Schema、执行函数、超时时间、权限标识。注册中心对外暴露统一的调用协议模型端用供应商原生格式描述工具内部执行时转成统一的工具调用上下文。工具描述写得是否清晰直接影响模型能否正确选择工具。我踩过最大的坑是描述模糊。比如一个发短信的工具描述写成发送消息模型在一堆工具里就经常选错改成向指定用户手机号发送营销短信内容不能超过 140 字需先获取用户同意之后选错率几乎降为零。工具描述里的细节比如参数范围、前置条件、副作用都应该明确写进描述里而不是写到参数说明里。2.7 决策点七可观测性基建排查问题靠流水线不靠猜Agent 排障比传统 Web 服务排障难得多因为一个错误可能来自模型幻觉、Prompt 设计、工具返回、状态时序、供应商限流等多个环节。没有一套完整的可观测性基建你上线后只能靠猜。我建议每个 Agent 请求都生成一个 trace_id贯穿整个请求生命周期。每个节点执行前和执行后都记录关键状态摘要包括模型的输入输出、token 开销、耗时、调用的工具和参数。还要有独立的评测集不只是测答得对不对而是拆开测意图识别是否准确工具选择是否正确流程是否走了预设路径回答是否有幻觉。有了这些数据每次出问题你都能快速回溯到具体环节而不是在日志海里捞碎片。3. 实操基于 FastAPI LangChain LangGraph 搭建一个能扛并发的 Agent 服务理论聊完了我们直接进入实操。我选 FastAPI LangChain LangGraph 这套组合是因为它贴近 Python 生态主线同时 LangGraph 的状态图和异步支持能覆盖前面讲的大部分决策点。下面按步骤拆解。3.1 工程目录与依赖先规划目录。我的习惯是按接口层-服务层-图层-工具层四层组织代码。agent-service/ ├── app/ │ ├── api/ # FastAPI 路由 │ ├── core/ # 配置、日志、速率限制 │ ├── graph/ # LangGraph 图定义与节点 │ ├── tools/ # 工具注册表与具体实现 │ ├── memory/ # 短期记忆、长期记忆 │ ├── llm/ # 模型抽象层 │ └── schemas/ # Pydantic 模型 ├── tests/ └── pyproject.toml依赖方面核心包我用fastapi、uvicorn、langchain、langgraph、redis、sqlalchemy、pydantic-settings。这里有个容易被忽略的点要把langgraph和langchain的版本锁定LangGraph 迭代很快不同版本的 API 差异很大。3.2 用 LangGraph 组织 Agent 执行图LangGraph 的核心概念是 StateGraph。State 是你定义的共享状态类型节点是函数边决定流转关系。我搭一个典型的 Agent 图包含五个节点意图识别、工具调用、结果评估、回复生成、结束。from typing import TypedDict, Literal from langgraph.graph import StateGraph, END class AgentState(TypedDict): query: str intent: str tool_calls: list tool_results: list final_answer: str error: str | None async def handle_goal(state: AgentState) - dict: # 判断用户意图决定走工具分支还是直接回复 return {intent: tool_call} async def execute_tool(state: AgentState) - dict: # 调用工具注册中心执行 return {tool_results: [{name: search, result: ...}]} async def evaluate_result(state: AgentState) - dict: # 判断是否满足用户需求不满足则允许再次调用工具 return {error: None} async def generate_answer(state: AgentState) - dict: return {final_answer: ...} graph StateGraph(AgentState) graph.add_node(intent, handle_goal) graph.add_node(tool, execute_tool) graph.add_node(evaluate, evaluate_result) graph.add_node(answer, generate_answer) graph.add_node(end, lambda state: state) graph.set_entry_point(intent) graph.add_edge(intent, tool) graph.add_edge(tool, evaluate) graph.add_conditional_edges( evaluate, lambda state: answer if not state.get(error) else tool, {answer: answer, tool: tool}, ) graph.add_edge(answer, end) app_graph graph.compile()这里有一个细节add_conditional_edges是 LangGraph 的杀手级能力它让节点是否继续循环由代码控制而不是由模型自由发挥。在我这个例子里如果工具结果评估未通过流程会回到工具节点再试一次但我会在evaluate节点里加一个重试计数器超过两次就强制走向 answer 节点防止无限循环。3.3 FastAPI 接入与异步并发处理FastAPI 天然支持异步这意味着你的接口函数只要定义成async def内部遇到await时会把控制权交还给事件循环其他用户的请求同时可以继续执行。这里最关键的是LangGraph 的图节点执行函数必须全部写成异步函数否则会阻塞整个事件循环。我还为每个用户的会话创建了一份独立的图实例上下文。LangGraph 的compile对象本身是纯逻辑状态通过调用时的invoke或stream传入。并发安全的关键点是同一个图实例可以被多个请求同时调用只要你的输入状态各自独立。from fastapi import FastAPI, HTTPException from langgraph.checkpoint.sqlite import SqliteSaver app FastAPI() def get_session_state(session_id: str): # 从 Redis/DB 恢复状态 ... app.post(/api/agent/chat) async def chat(payload: ChatRequest): session_id payload.session_id state await get_session_state(session_id) async for chunk in app_graph.astream(state): if final_answer in chunk: await save_session_state(session_id, chunk[final_answer]) return {answer: chunk[final_answer]} raise HTTPException(status_code500, detailAgent execution failed)此外LangGraph 提供了一个 checkpoint 机制配合SqliteSaver或PostgresSaver可以做状态持久化。session 的中间状态每隔一个节点就会被存下来服务重启后可以从断点继续执行。这个能力对长耗时任务非常重要。我建议所有生产环境的 Agent 服务都开启 checkpoint因为没有任何模型是永远稳定的断点续跑能极大增强容错性。3.4 使用 Redis 做短期记忆与请求队列短期记忆放在 Redis每次接口进来时先读取最近若干轮对话的压缩摘要拼入上下文。Redis 的另一个作用是实现请求队列和速率限制。我实现了一个非常简单但有效的令牌桶import asyncio from redis.asyncio import Redis class TokenBucket: def __init__(self, redis: Redis, key: str, capacity: int, rate: float): self.redis redis self.key key self.capacity capacity self.rate rate async def acquire(self): # Lua 脚本实现原子操作 script local current tonumber(redis.call(get, KEYS[1]) or 0) local last_time tonumber(redis.call(get, KEYS[1] .. :ts) or 0) local now tonumber(ARGV[1]) local rate tonumber(ARGV[2]) local capacity tonumber(ARGV[3]) current math.max(0, current - (now - last_time) * rate) if current capacity then redis.call(set, KEYS[1], current 1) redis.call(set, KEYS[1] .. :ts, now) return 1 else return 0 end # 注意 Lua 脚本里需要引入 math 库在 FastAPI 的依赖注入里使用这个令牌桶每个用户每个会话都有独立的 key。当令牌耗尽接口直接返回 429 状态码客户端可以根据Retry-After头重试。我还把队列分了大客户和普通用户两个桶保证重要流量不会被免费流量击穿。3.5 用队列化改造 LangChain 的同步工具调用LangChain 里大量工具类比如某些搜索 API 的官方封装经常是同步的。如果你在异步节点里直接调用同步函数事件循环会被卡死。我的解决办法是给工具执行器加一个 Prometheus 指标的线程池把同步工具调用丢进线程池去执行然后通过asyncio.wrap_future转成协程等待结果。import asyncio from concurrent.futures import ThreadPoolExecutor executor ThreadPoolExecutor(max_workers16) async def call_sync_tool(tool_func, *args, **kwargs): loop asyncio.get_running_loop() return await loop.run_in_executor(executor, lambda: tool_func(*args, **kwargs))线程池的容量要根据工具的 IO 等待时间和单实例 CPU 核数来调。一般经验值是核数的两倍到四倍池子太小会排队池子太大会造成线程切开销。这个参数建议压测时逐步调整。3.6 挂载长期记忆模块长期记忆我用 PostgreSQL 加 pgvector 做向量检索。每个会话结束时后台任务会把本次会话的关键结论、用户偏好、未尽事宜提取出来写入记忆表。下次会话开始时程序检索出与当前 Query 相关的记忆片段注入 System Prompt 的已知背景区域。长期记忆写入也需要过滤和脱敏。模型生成的记忆条目不一定都可信我会让质量评估节点判断这条记忆是否有价值、是否符合事实再决定是否入库。这个过滤步骤虽然消耗一次模型调用但它能阻止垃圾记忆污染长期记忆库长期看是省钱省心的事。3.7 部署形态与横向扩展部署上FastAPI 服务本身无状态搞个容器镜像扔到 Kubernetes 或者容器平台里按副本数拉起。因为会话状态已经在 Redis 和数据库里不依赖本地内存所以任意一个副本挂掉请求都可以被其他副本接管。真正需要小心的反而是模型 API 的速率限制而不是你自己的服务。你把应用横向扩到十个副本结果每个副本都往同一个模型 API 发请求照样会被限流。所以速率限制要放在共享的 Redis 层面做全局控制。这不是可选优化而是并发架构里的必选项否则扩副本反而会加剧稳定性问题。4. 常见问题与排查技巧实录这里整理一些我在 Agent 项目里真实踩过并解决了的问题。很多坑如果不亲身经历根本不会想到。4.1 模型陷入工具调用死循环症状是Agent 不停地调用同一个工具拿到结果后又调用一次循环五六次都不出结果。表面上看是 Prompt 写得不够明确但实际原因往往是工具返回的结果不够决策友好。如果搜索结果里没有提炼出是否满足用户需求的信号模型就很难决定何时停止。我的排查套路是三步走。第一步打开 trace确认每一次工具调用的输入输出第二步看模型在循环中的推理内容确认是认为结果不充分还是逻辑已经迷失第三步针对性地在工具返回里加一段结果摘要并在系统提示里写明当所有结果均表明无法满足用户需求时停止工具调用并如实告知用户。同一类问题还有变种模型反复用同一个工具查询已经查过的信息。我的办法是把本次会话内的工具结果缓存起来如果下一次请求的参数完全一样直接复用之前的返回不再发起真实调用。4.2 多用户并发时状态串线这个问题的典型表现是用户 A 的对话里出现了用户 B 的数据。排查后发现问题出在状态对象的封装上。如果用了类级别的变量保存会话状态或者图实例是一个单例而且节点函数直接修改了共用状态那并发一旦上来就必然串线。正确的做法是所有状态都从请求级别的输入参数构建绝不使用模块级可变变量StateGraph的节点函数签名里状态永远作为参数传入并返回新状态的增量不要从外部闭包读写状态。我自己痛定思痛之后给项目加了一条硬性规定任何节点函数都不允许修改外部变量违反者代码审查不通过。4.3 模型供应商限流导致大量 429这是生产环境最常见的稳定性问题。表现是白天用户一多模型 API 就开始持续报错Agent 服务整体响应时间迅速拉长。解决思路前面已经提过这里补充一个细节要给不同的模型调用设置不同的限流 key。比如意图识别用小模型回复生成用大模型两者的速率限额是不共享的限流控制要分开统计。否则一个模型被限流会把另一个模型的配额也误伤。5. 最后再聊几句Agent 的工程实现确实比传统后端服务复杂因为它引入了一个不可完全预测的决策者。但也正是这种不可预测性要求工程侧的约束更加严格。从我个人的实践体会来看与其追求让模型更聪明不如多花点心思把流程边界、状态管理、并发控制和可观测性做扎实。框架选择上Python 加 FastAPI 加 LangGraph 依然是我目前的主力组合至于 Rust 在 Agent 场景下的高并发和性能表现我也关注了很久它的确在资源效率上有明显的技术优势未来在高吞吐 Agent 服务里会有一席之地但现阶段生态成熟度还是 Python 更友好。感兴趣的团队可以持续关注 Rust 生态在这个方向的进展。最后再分享一个做 Agent 项目最有用的习惯从第一天就把每个环节的耗时和 token 开销记下来。Agent 的成本和延迟不是上线之后才考虑的事而是架构设计阶段就要想明白的约束。你把这篇文章里的七要素和七个决策点逐一过一遍再对着自己的业务场景做出选择我相信在生产环境里踩的坑会少很多。
返回列表