1. 从“模型竞赛”到“智能体落地”:Agentic AI Infra 到底在解决什么问题
过去两年,大家的目光几乎都盯在模型本身——参数规模、榜单分数、推理能力。但真正把 AI 用起来的人会发现一个尴尬的现实:模型再强,如果没有一套能扛住真实业务流量的基础设施,它就只能待在演示视频里。Agentic AI Infra 这个词,说白了就是“让智能体真正跑起来、跑得稳、跑得久”的那层底座。它不负责训练模型,也不负责写提示词,它负责的是:当你的 Agent 要同时服务几千个用户、要调用十几个工具、要记住上下文、要在失败时自动重试、要在成本失控前刹车——这些事情由谁来做。
我自己的体会是,2024 年之前大家做 Agent 项目,基本是“手搓”状态:一个 Python 脚本起个 Flask 服务,前面挂个 Nginx,后面连个向量数据库,就算上线了。结果一到并发稍微高一点,Agent 就开始胡言乱语,工具调用超时,记忆丢失,账单爆炸。Agentic AI Infra 要解决的,就是把这些“手搓”的环节标准化、服务化、可观测化。它面向的读者很明确:正在或准备把 Agent 从 Demo 推向生产环境的开发者、架构师,以及需要评估 AI 落地成本的技术负责人。
这篇文章我会从整体设计思路、核心组件拆解、实操部署流程、常见问题排查四个维度展开,把 Agentic AI Infra 这层“看不见但离不开”的东西讲透。文中涉及的具体参数和配置,一部分来自公开的技术文档,一部分是我在实际项目中反复调优后的经验值,你可以直接参考,但建议根据自己的业务压力做调整。
2. 整体架构设计:为什么 Agentic AI Infra 不能照搬传统微服务
2.1 智能体流量的三个特殊性
传统 Web 服务的流量模型很清晰:请求进来,查数据库,返回结果,结束。但 Agent 的流量完全不是这个节奏。第一,长尾延迟极其严重。一个 Agent 请求可能涉及多次模型调用、多次工具调用、多次记忆检索,总耗时从几百毫秒到几十秒不等。第二,状态依赖复杂。Agent 需要记住对话历史、工具调用结果、中间推理步骤,这些状态不能简单丢给客户端。第三,成本敏感度极高。每一次模型调用都是真金白银,如果 Infra 层不做缓存、不做限流、不做降级,一个失控的 Agent 能在几分钟内烧掉你一个月的预算。
我见过一个典型的反面案例:某团队做客服 Agent,上线第一天因为一个死循环的工具调用,导致同一个问题被反复提交给模型,两个小时消耗了 3000 万 token。这不是模型的问题,是 Infra 层缺少熔断机制。所以 Agentic AI Infra 的第一个设计原则就是:假设 Agent 会犯错,Infra 要能兜住。
2.2 分层架构:接入层、编排层、执行层、观测层
基于上面的特殊性,我推荐的架构是四层分离。接入层负责协议转换、鉴权、限流、请求排队。这一层可以用传统的 API Gateway 来做,但需要针对流式响应做特殊处理,因为 Agent 的输出往往是逐字返回的。编排层是核心,负责 Agent 的生命周期管理、状态机推进、工具路由、记忆读写。这一层决定了你的 Agent 能做什么、不能做什么。执行层是真正干活的地方,包括模型推理服务、工具执行沙箱、向量检索服务。观测层贯穿所有层,负责日志、指标、链路追踪、成本核算。
为什么要分这么细?因为每一层的扩缩容策略完全不同。接入层是 IO 密集型,编排层是状态密集型,执行层是计算密集型。如果混在一起部署,你会在扩缩容时左右为难。我试过把编排和执行放在同一个 Pod 里,结果模型推理把 CPU 吃满,导致编排层的健康检查超时,整个 Agent 被 Kubernetes 杀掉重启。分开之后,这个问题自然消失。
2.3 关键选型:为什么我最终选择了事件驱动而非请求响应
在编排层的设计上,有两种主流思路:请求响应模式和事件驱动模式。请求响应模式就是传统的“来一个请求,处理完返回”,实现简单,但无法处理长时间运行的任务。事件驱动模式则是把每个 Agent 步骤抽象成事件,通过消息队列串联起来。我最终选择了事件驱动,原因有三个:第一,Agent 的步骤天然是异步的,模型调用可能几秒,工具调用可能几十秒,用同步等待会浪费大量连接资源。第二,事件驱动天然支持重试和补偿,某个步骤失败了,可以把事件重新入队,而不是让整个请求失败。第三,事件驱动更容易做可观测性,每个事件的流转都有记录,排查问题时有据可查。
当然,事件驱动也带来了复杂性。你需要一个可靠的消息队列,需要处理事件顺序问题,需要设计幂等机制。我的建议是,如果你的 Agent 步骤少于 3 步,且每步耗时都在 1 秒以内,用请求响应就够了。但如果你的 Agent 涉及多轮工具调用、需要人工审核介入、或者单次执行时间超过 10 秒,那就应该认真考虑事件驱动。
3. 核心组件拆解:编排、记忆、工具、观测四件套
3.1 编排引擎:状态机还是工作流
编排引擎是 Agentic AI Infra 的大脑。目前主流有两种实现方式:基于状态机的编排和基于工作流的编排。状态机适合步骤固定、分支明确的场景,比如“先查天气,如果下雨就推荐室内活动,否则推荐户外活动”。工作流适合步骤动态、需要循环和并行的场景,比如“根据用户问题,自主决定调用哪些工具,直到找到答案”。
我在实际项目中用的是混合模式:外层用状态机管理 Agent 的整体生命周期,内层用工作流处理具体的任务执行。这样既能保证整体流程可控,又能给 Agent 足够的自主权。具体实现上,我推荐用LangGraph或Temporal这类工具。LangGraph 更贴近 Agent 的开发习惯,Temporal 则在可靠性和可观测性上更胜一筹。如果你团队里没有专门的基础设施工程师,从 LangGraph 起步会更平滑。
这里有一个关键参数需要特别注意:最大步数限制。我建议默认设置为 15 步,超过就强制终止并返回当前结果。这个值不是拍脑袋定的,而是根据实际业务统计出来的。大部分正常的 Agent 任务在 5 到 10 步内完成,超过 15 步的基本都是陷入了循环或者遇到了无法解决的问题。设置这个上限,能有效防止 token 无限消耗。
3.2 记忆系统:短期、长期与工作记忆的分离
Agent 的记忆不是简单的“存对话历史”。我把记忆分为三类:短期记忆是当前会话的上下文,通常放在 Redis 里,设置 TTL 为 30 分钟。长期记忆是跨会话的知识,存在向量数据库里,比如 Milvus 或 Qdrant。工作记忆是 Agent 在执行任务过程中的中间状态,比如已经调用了哪些工具、得到了什么结果,这部分通常放在编排引擎的内存或本地存储里。
为什么要分这么细?因为它们的读写模式和生命周期完全不同。短期记忆读写频繁,但数据量小,适合内存数据库。长期记忆写入少、读取多,但数据量大,适合向量数据库。工作记忆只在单次任务执行期间存在,任务结束就可以丢弃。我见过有人把所有记忆都塞进向量数据库,结果每次对话都要做一次向量检索,延迟高得离谱,成本也下不来。
提示:短期记忆的 TTL 不要设置太长。我试过设置 24 小时,结果 Redis 内存暴涨,而且很多对话早就结束了,根本不需要保留那么久。30 分钟到 1 小时是比较合理的范围。
3.3 工具执行:沙箱隔离与超时控制
Agent 要调用外部工具,这是它区别于普通聊天机器人的核心能力。但工具调用也是风险最高的环节。一个恶意的或错误的工具调用,可能删库、可能泄露数据、可能产生高额费用。所以工具执行必须在沙箱里进行。我推荐用gVisor或Firecracker做轻量级虚拟化,每个工具调用在一个独立的微虚拟机里执行,执行完立即销毁。
超时控制同样关键。我建议给每个工具设置独立的超时时间,默认 10 秒,对于搜索类工具可以放宽到 30 秒,对于数据库查询类工具收紧到 5 秒。超时后不是简单报错,而是返回一个结构化的错误信息给 Agent,让 Agent 决定是重试、换工具还是放弃。这样 Agent 的行为更可控,不会因为一个工具卡住就整个任务失败。
3.4 观测体系:没有可观测性就没有生产级 Agent
Agent 的调试难度比传统服务高一个数量级,因为它的行为是不确定的。同一个输入,两次执行可能走不同的路径。所以观测体系必须能记录每一步的决策依据、输入输出、耗时和成本。我用的方案是OpenTelemetry做链路追踪,Prometheus做指标采集,Loki做日志聚合。每个 Agent 请求生成一个 Trace ID,贯穿所有步骤,排查问题时可以完整回放。
这里有一个容易被忽略的指标:Token 消耗速率。我建议设置一个告警阈值,比如每分钟消耗超过 10 万 token 就触发告警。这个指标能帮你及时发现失控的 Agent。另外,工具调用失败率也要重点监控,如果某个工具的失败率突然升高,可能是外部服务出了问题,需要及时降级。
4. 实操部署:从零搭建一套可用的 Agentic AI Infra
4.1 环境准备与依赖安装
假设你已经有了一台 Linux 服务器,配置不低于 8 核 16G,并且安装了 Docker 和 Docker Compose。我们先用 Docker Compose 搭建一套最小可用的环境,包括 Redis、Qdrant、RabbitMQ 和编排服务。以下是docker-compose.yml的关键部分:
version: '3.8' services: redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru qdrant: image: qdrant/qdrant:latest ports: - "6333:6333" volumes: - qdrant_data:/qdrant/storage rabbitmq: image: rabbitmq:3-management-alpine ports: - "5672:5672" - "15672:15672" environment: RABBITMQ_DEFAULT_USER: agent RABBITMQ_DEFAULT_PASS: agent123 volumes: redis_data: qdrant_data:Redis 的maxmemory-policy设置为allkeys-lru,这样当内存满了会自动淘汰最久未使用的键,避免 OOM。Qdrant 用来存长期记忆,RabbitMQ 用来做事件队列。这三个组件加起来,内存占用大概在 4G 左右,剩下的资源留给编排服务和模型调用。
4.2 编排服务的核心代码结构
编排服务我用 Python 写,基于 FastAPI 和 LangGraph。核心目录结构如下:
agent-infra/ ├── app/ │ ├── main.py # FastAPI 入口 │ ├── graph/ │ │ ├── state.py # 状态定义 │ │ ├── nodes.py # 节点函数 │ │ └── builder.py # 图构建 │ ├── memory/ │ │ ├── short_term.py # Redis 短期记忆 │ │ └── long_term.py # Qdrant 长期记忆 │ ├── tools/ │ │ ├── registry.py # 工具注册 │ │ └── executor.py # 工具执行沙箱 │ └── observability/ │ ├── tracer.py # OpenTelemetry 配置 │ └── metrics.py # Prometheus 指标 ├── requirements.txt └── Dockerfile状态定义是关键。我用 TypedDict 定义了一个AgentState,包含messages、current_step、tool_calls、final_answer等字段。每个节点函数接收状态,返回更新后的状态。LangGraph 会自动处理状态流转和条件分支。
4.3 工具注册与沙箱执行的具体实现
工具注册我用装饰器模式,每个工具函数加上@tool装饰器,自动注册到工具注册表。工具定义包括名称、描述、参数 schema 和超时时间。Agent 根据描述来决定调用哪个工具,所以描述要写得清晰准确。我见过有人把工具描述写成“查询数据”,结果 Agent 根本不知道什么时候该用。改成“根据用户 ID 查询订单历史,返回订单列表和状态”之后,调用准确率大幅提升。
沙箱执行我用的是subprocess加资源限制。每个工具调用在一个独立的子进程里执行,通过resource模块限制 CPU 时间和内存。超时用signal.alarm实现。虽然不如 gVisor 那么彻底,但对于内部工具来说已经够用了。如果工具涉及外部网络调用,我会额外加一层 HTTP 代理,记录所有出站请求。
import signal import resource def execute_tool(tool_func, args, timeout=10): def handler(signum, frame): raise TimeoutError("Tool execution timed out") signal.signal(signal.SIGALRM, handler) signal.alarm(timeout) try: result = tool_func(**args) return {"status": "success", "result": result} except TimeoutError: return {"status": "timeout", "result": None} except Exception as e: return {"status": "error", "result": str(e)} finally: signal.alarm(0)4.4 记忆读写的性能优化
短期记忆用 Redis 的 Hash 结构存储,每个会话一个 key,字段包括messages、summary、last_active。每次读写只操作需要的字段,避免全量加载。长期记忆用 Qdrant 的向量检索,写入时把对话摘要和关键信息向量化,检索时用相似度搜索。我建议长期记忆的写入不要太频繁,每 5 轮对话写入一次就够了,否则向量数据库的压力会很大。
注意:向量化模型的选择很重要。我试过用大模型做向量化,效果确实好,但成本和延迟都太高。后来换成专门的嵌入模型,比如
bge-small-zh,效果差距不大,但速度快了 10 倍,成本几乎可以忽略。
5. 常见问题与排查技巧实录
5.1 Agent 陷入循环怎么办
这是最常见的问题。Agent 反复调用同一个工具,或者反复输出同样的内容。排查思路是:先看日志,确认循环发生在哪一步。如果是工具调用循环,检查工具返回的结果是否包含足够的信息让 Agent 做出判断。很多时候是因为工具返回了空结果,Agent 以为没查到,就反复查。解决方法是在工具返回空结果时,明确告诉 Agent“没有找到相关数据,请尝试其他方法”。
如果是推理循环,检查提示词是否过于模糊。我遇到过一个案例,提示词里写了“尽可能详细地回答”,结果 Agent 为了追求详细,反复扩展同一段内容。改成“用不超过 200 字回答”之后,问题解决。另外,前面提到的最大步数限制是最后的兜底手段,一定要设置。
5.2 并发高了之后响应变慢
Agent 的并发瓶颈通常不在模型调用,而在编排层的状态管理。如果每个请求都要读写 Redis,并发一高,Redis 就成了瓶颈。我的优化经验是:第一,用 Redis Pipeline 批量读写,减少网络往返。第二,对状态做分片,不同会话的状态存在不同的 Redis 实例上。第三,对于只读的状态,加本地缓存,减少 Redis 访问。
还有一个容易被忽略的点:连接池。FastAPI 默认的 HTTP 连接池大小是 10,对于 Agent 这种长耗时请求来说远远不够。我把它调到 100 之后,并发能力提升了 5 倍。具体配置在httpx.AsyncClient的limits参数里设置。
5.3 成本失控的预防与止损
成本失控通常有三个原因:Token 消耗过多、工具调用过于频繁、模型选型不当。预防措施包括:设置每个会话的 Token 上限,超过就强制结束;设置工具调用的频率限制,比如每分钟最多 20 次;根据任务复杂度动态选择模型,简单任务用便宜的小模型,复杂任务才用大模型。
止损措施也很重要。我建议在 Infra 层加一个“熔断开关”,当检测到某个会话的 Token 消耗速率异常时,自动暂停该会话并通知管理员。这个开关用 Redis 的计数器实现,每消耗 1000 token 就递增一次,超过阈值就设置一个标志位,后续请求直接返回错误。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent 反复调用同一工具 | 工具返回空结果或错误信息不明确 | 查看工具调用日志 | 优化工具返回信息,增加空结果提示 |
| 响应时间突然变长 | Redis 连接池耗尽或网络抖动 | 检查 Redis 监控指标 | 增大连接池,增加 Redis 实例 |
| Token 消耗异常升高 | Agent 陷入循环或提示词过于宽泛 | 查看 Token 消耗速率指标 | 设置最大步数,优化提示词 |
| 工具调用超时频繁 | 外部服务不稳定或超时设置过短 | 查看工具调用失败率 | 调整超时时间,增加重试机制 |
| 记忆丢失或错乱 | 短期记忆 TTL 过期或键冲突 | 检查 Redis 键的命名规则 | 使用会话 ID 作为键前缀,延长 TTL |
6. 一些踩坑之后的经验之谈
Agentic AI Infra 这个领域,文档里不会写的坑太多了。我挑几个印象最深的说说。第一个是关于流式响应的。Agent 的输出往往是流式的,但如果你在编排层做了缓冲,流式就变成了批量,用户体验会差很多。我的做法是在接入层直接透传流式响应,编排层只负责生成事件,不负责组装最终文本。这样虽然实现复杂一点,但延迟能降低 50% 以上。
第二个是关于工具版本管理的。Agent 调用的工具会不断迭代,如果工具接口变了但 Agent 的提示词没更新,就会出现调用失败。我的做法是给每个工具加版本号,提示词里引用具体版本。工具升级时,先发布新版本,观察一段时间,确认 Agent 能正确调用后再下线旧版本。这样虽然麻烦,但能避免很多线上事故。
第三个是关于多租户隔离的。如果你的 Agent 平台要服务多个团队,资源隔离必须做好。我见过一个团队因为没做隔离,一个租户的 Agent 把消息队列塞满了,导致其他租户的请求全部超时。后来我们给每个租户分配独立的队列和 Redis 数据库,虽然成本高了一点,但稳定性提升了一个档次。
最后分享一个小技巧:给 Agent 加一个“思考时间”。在 Agent 决定调用工具之前,强制它先输出一段简短的推理,说明为什么要调用这个工具。这个简单的改动,能让工具调用的准确率提升 20% 以上,因为 Agent 在“说出来”的过程中会自我纠正。这个技巧在多个项目里都验证过,成本几乎为零,效果立竿见影。