ARTICLE DETAIL

资讯详情

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

Agentic AI Infra 生产级落地:架构设计、核心组件与部署实践

Agentic AI Infra 生产级落地:架构设计、核心组件与部署实践 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 观测体系没有可观测性就没有生产级 AgentAgent 的调试难度比传统服务高一个数量级因为它的行为是不确定的。同一个输入两次执行可能走不同的路径。所以观测体系必须能记录每一步的决策依据、输入输出、耗时和成本。我用的方案是OpenTelemetry做链路追踪Prometheus做指标采集Loki做日志聚合。每个 Agent 请求生成一个 Trace ID贯穿所有步骤排查问题时可以完整回放。这里有一个容易被忽略的指标Token 消耗速率。我建议设置一个告警阈值比如每分钟消耗超过 10 万 token 就触发告警。这个指标能帮你及时发现失控的 Agent。另外工具调用失败率也要重点监控如果某个工具的失败率突然升高可能是外部服务出了问题需要及时降级。4. 实操部署从零搭建一套可用的 Agentic AI Infra4.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, timeout10): 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 作为键前缀延长 TTL6. 一些踩坑之后的经验之谈Agentic AI Infra 这个领域文档里不会写的坑太多了。我挑几个印象最深的说说。第一个是关于流式响应的。Agent 的输出往往是流式的但如果你在编排层做了缓冲流式就变成了批量用户体验会差很多。我的做法是在接入层直接透传流式响应编排层只负责生成事件不负责组装最终文本。这样虽然实现复杂一点但延迟能降低 50% 以上。第二个是关于工具版本管理的。Agent 调用的工具会不断迭代如果工具接口变了但 Agent 的提示词没更新就会出现调用失败。我的做法是给每个工具加版本号提示词里引用具体版本。工具升级时先发布新版本观察一段时间确认 Agent 能正确调用后再下线旧版本。这样虽然麻烦但能避免很多线上事故。第三个是关于多租户隔离的。如果你的 Agent 平台要服务多个团队资源隔离必须做好。我见过一个团队因为没做隔离一个租户的 Agent 把消息队列塞满了导致其他租户的请求全部超时。后来我们给每个租户分配独立的队列和 Redis 数据库虽然成本高了一点但稳定性提升了一个档次。最后分享一个小技巧给 Agent 加一个“思考时间”。在 Agent 决定调用工具之前强制它先输出一段简短的推理说明为什么要调用这个工具。这个简单的改动能让工具调用的准确率提升 20% 以上因为 Agent 在“说出来”的过程中会自我纠正。这个技巧在多个项目里都验证过成本几乎为零效果立竿见影。
返回列表