AI Agent 生产环境部署实战
先说一个普遍现象:Agent 在本地 demo 跑得飞起,一问一答顺滑得像真人,但一到生产环境就开始花式翻车——超时、幻觉、工具调用失败、并发一上来直接把后端打挂。这篇文章想聊的不是“怎么搭一个 Agent demo”,而是把 AI Agent 真正放到生产环境里那一整套避坑流程,包括模型推理层怎么部署、Agent 编排层怎么选型、Redis 这类基础设施怎么做生产配置,以及上线之后怎么监控和排查问题。我自己前后折腾过不止一个 Agent 项目,代码层面其实没多复杂,真正消耗精力的全是生产化这部分,踩过的坑都在下面,适合正准备把 Agent 从原型推到线上的人。
我默认你已经有基本概念:知道什么是 Agent、见过 LangChain 或 Dify 之类的框架,也了解怎么调用大模型 API。接下来的内容不会重复讲概念,只讲生产环境下那些绕不开的决策和实操细节。
1. 部署前必须想清楚的事:先定义“生产”二字
1.1 “能跑”和“扛得住”是两个量级
很多团队第一步就栽了跟头。本地起一个 Ollama,加一个 Python 脚本,Agent 能调用工具能回答问题,大家就以为项目差不多了。但生产环境要回答的是另外几个问题:
- 服务挂了怎么办?模型推理服务崩了,用户请求是排队还是直接失败?
- 并发从 10 涨到 1000,现有架构会不会被打穿?
- Agent 执行过程中出了问题,能不能在日志里还原它当时的决策步骤?
- 一次 Agent 调用要烧掉多少 token?成本超了谁来管?
- 如果 Agent 调用了外部工具,结果错了,这个锅算谁的?
这些问题在 demo 阶段完全可以不关心,但上了生产就是避不开的硬指标。我见过不止一个项目,Agent 的逻辑写得挺漂亮,结果 Redis 用的是默认配置、模型推理服务单点部署、没有任何超时控制,一上线就被真实流量教做人。
所以第一步的决策不是“选什么框架”,而是“你要做到哪个级别”。如果是内部工具、容忍一定失败率,那最小可用架构就行;如果是面向用户的核心业务,高可用、可观测、限流熔断一样都不能少。这个定位直接决定后面所有技术选型。
1.2 架构选型:代码编排、低代码平台还是混合?
Agent 编排层现在主流的路径有三条:纯代码编排(LangGraph、自研状态机)、低代码/可视化编排(Dify)、以及两者混合。选哪条不是看哪个技术更酷,而是看你的场景复杂度、团队构成和迭代速度。
| 对比维度 | LangGraph 代码编排 | Dify 低代码编排 | 混合模式 |
|---|---|---|---|
| 上手门槛 | 高,需要理解图结构 | 低,拖拽即可 | 中等 |
| 复杂流程控制 | 强,状态转移可精细控制 | 中等,长尾逻辑会受限 | 强 |
| 与现有系统集成 | 完全可控 | 通过 API/插件,稍绕 | 部分逻辑外置 |
| 可测试性 | 好,可单元测试 | 一般 | 好 |
| 适合场景 | 核心业务、复杂 Agent | 快速验证、运营配置 | 大规模复杂项目 |
我自己实践下来的建议是:如果你的 Agent 核心逻辑会被反复迭代、需要跑大量自动化测试,用代码编排;如果主要靠运营同学调整流程、且逻辑不复杂,用 Dify;团队大了以后,往往演变成混合——Dify 管知识库和简单工作流,LangGraph 管核心交易链路,中间用 API 通信。
我见过一个踩坑案例:团队为了快速上线选了低代码平台,结果业务越搞越复杂,几百个节点挂在画布上,调试一次要半小时,最后全部推翻重写。生产环境不是“快速出效果”的地方,选型必须在前期想清楚未来半年的业务走向。
1.3 模型推理层:Ollama、vLLM 还是商业 API?
模型这一层的部署选项大概分成三类:本地推理框架、云上推理服务、商业闭源 API。现在热门的 DeepSeek、Qwen 等开源权重都能自己部署,关键是怎么选推理引擎。
- Ollama:胜在简单,一条命令起服务,适合本地开发和个人项目。但生产环境要面对高并发时,Ollama 的吞吐量优化不如专门为生产设计的推理引擎。
- vLLM:业界用得最多的生产级推理框架,核心优势是 PagedAttention 和 continuous batching,同样的显存能扛更多的并发请求,吞吐量明显优于 naive 方案。
- 商业 API:偷懒方案,把模型调用托管出去,省去 GPU 运维,代价是数据出域、单次成本高、有被限流的风险。
选型不是非黑即白。很多团队的实际做法是:开发环境用 Ollama,生产环境用 vLLM 拉起同一份权重,配置好之后 API 格式都是 OpenAI 兼容格式,代码切换成本几乎为零。这也是我先讲推理层的原因——先把模型服务跑稳,Agent 骨架才有意义。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础设施层部署细节:Redis、Docker Compose 与推理服务
2.1 Docker Compose 编排一套最小生产环境
生产环境部署大模型服务,Docker 几乎是标配。你不用一开始就上 Kubernetes,Docker Compose 能把服务编排清楚,后面真要上容器编排平台,迁移成本也可控。我习惯的最小生产拓扑是四个服务:
- 模型推理服务:vLLM 或 Ollama,暴露 /v1/chat/completions
- Agent 应用服务:跑 LangGraph/Dify 的运行时,核心业务逻辑
- Redis:会话状态、限流、分布式锁、任务队列
- 关系型数据库:PostgreSQL,存业务数据、审计日志、向量
一个可以起步的 compose 文件长这样:
yaml复制version: "3.8"
services:
vllm:
image: vllm/vllm-openai:latest
command: >
--model /models/Qwen2.5-7B-Instruct
--tensor-parallel-size 1
--gpu-memory-utilization 0.85
--max-model-len 8192
--served-model-name qwen
volumes:
- /data/models:/models
- shm:/dev/shm
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
retries: 3
agent-api:
build: ./agent
ports:
- "8080:8080"
environment:
- LLM_BASE_URL=http://vllm:8000/v1
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
depends_on:
- vllm
- redis
redis:
image: redis:7-alpine
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
- redis-data:/data
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD=${PG_PASSWORD}
volumes:
- pg-data:/var/lib/postgresql/data
volumes:
shm:
redis-data:
pg-data:
几个容易翻车的点:
第一个是 /dev/shm。vLLM 在容器里跑,默认的共享内存只有 64MB,并发一上来直接报 shared memory 相关的错。必须把它挂成卷,大小给到容器内存的一半以上。
第二个是健康检查。Agent API 的 depends_on 只保证容器启动了,不保证里面进程 ready。不加健康检查的话,Agent 服务一启动就去连 vLLM,大概率连不上然后缓存了连接失败的异常,后面也不自动重试。要配合 healthcheck 或等依赖就绪的脚本。
第三个是配置项不要写死在镜像里。密码、模型名、并发上限这些全部用环境变量,后面改配置不用重新构建镜像。我见过有人把 Redis 密码直接写进 Dockerfile,密码一漏整个环境都是裸奔状态。
2.2 Redis 生产配置:ACL、持久化和连接策略
Redis 在 Agent 架构里承担的角色比传统 Web 应用更重——它不光做缓存,还存 Agent 的会话状态、分布式锁、限流计数、异步任务队列。生产配置不能直接用默认参数。“redis docker compose 生产环境部署”在每个搜索平台上都是热门词,说明踩坑的人是真多。
生产 Redis 配置我一般至少调这几项:
conf复制# 开启密码和 ACL
requirepass ${REDIS_PASSWORD}
aclfile /usr/local/etc/redis/users.acl
# 持久化:RDB + AOF 双开
save 900 1
save 300 10
appendonly yes
appendfsync everysec
# 内存上限与淘汰策略
maxmemory 2gb
maxmemory-policy allkeys-lru
ACL 这块很多人容易忽略。默认的 requirepass 只是给一个超级密码,一旦泄露,整个 Redis 数据都能被刷掉。ACL 可以做到细粒度控制:给 Agent 应用创建一个专用用户,只允许访问业务前缀的 key,只给需要的命令权限。
code复制user agent on >${AGENT_REDIS_PASSWORD} ~agent:* +@read +@write +@del +@expire
这样即便 Agent 侧被入侵,攻击者也只能操作 agent: 前缀的数据,影响范围被限制住了。如果是多环境共用一台 Redis,ACL 更是必需品,按环境拆用户、拆前缀,隔离清晰。
key 前缀设计也值得提前规划。同一个 Redis 里可能有 agent:state(会话状态)、agent:lock(分布式锁)、agent:ratelimit(限流)、agent:queue(任务队列)。一锅乱炖没有前缀,后面做清理、做监控、做迁移全都痛苦。原则很简单:环境:业务:类型:ID,比如 prod:agent:state:8f3a...。
内存策略要讲一下。如果不设置 maxmemory,Redis 会一直吃内存直到 OOM;设置了但策略不对,比如用了默认的 noeviction,内存满了以后写入直接报错。Agent 状态是典型的热数据,允许淘汰旧会话没问题,所以 allkeys-lru 合理。要是某些 key 不能丢,需要把它们标记为 no-evict 并单独配置持久化。
2.3 向量库选型:从 pgvector 到 Milvus
Agent 生产环境大概率要接入 RAG,向量存储就是躲不开的环节。选型上我按数据量和查询复杂度分了三档:
- 数据量在几百万以内、业务逻辑不复杂:直接用 pgvector。建一张带 vector 字段的表,跟业务数据放同一个数据库,事务一致性和运维成本都解决了。不用额外引入新系统,这是大多数中小项目最务实的选择。
- 数据量大、向量检索是关键路径:上 Milvus 或 Qdrant。Milvus 功能全,支持标量过滤、混合检索,但部署偏重;Qdrant 轻一些,Rust 写的,性能和易用性平衡得不错。
- 需要全文检索+向量联合查询:Elasticsearch 的 dense vector 也能做,但性能一般,非必要不推荐。
嵌入模型这一层,生产环境推荐用本地 embedding 服务,把文本转成向量,避免频繁调用外部接口造成的延迟和成本。常用的是 BGE 系列、M3E 这类开源模型,跑在 GPU 或 CPU 上都行,几千维度向量做批量入库,性能足够。
2.4 推理服务的启动参数与显存规划
vLLM 启动时的参数直接决定服务稳定性。这里给一组我常用的参数并解释每个的作用:
bash复制vllm serve /data/models/Qwen2.5-7B-Instruct \
--served-model-name qwen \
--tensor-parallel-size 1 \
--max-model-len 8192 \
--gpu-memory-utilization 0.85 \
--max-num-seqs 64 \
--enforce-eager
tensor-parallel-size:模型跨卡并行数。单卡能装下就设为 1,多卡才需要加大。设大了会引入卡间通信开销,反而变慢。gpu-memory-utilization:控制显存预留比例,0.85 意味着 15% 留给 KV cache 之外的开销。设成 0.95 能多一点并发吞吐,但容易在长上下文场景下 OOM,我建议从 0.85 开始调。max-model-len:上下文窗口长度。设大了 KV cache 占显存更凶,设小了用户多轮对话会被截断。7B 模型配 8192 是多数场景的平衡点。max-num-seqs:单个 batch 里最多同时处理的序列数。它直接限制并发效果,不是越大越好,要结合显存实测调。enforce-eager:关闭 CUDA graph 捕获,牺牲一点性能换更快的启动速度。要是首 token 延迟敏感,还是去掉这个参数。
部署完了必须做个压测,别等服务上线才知道扛不住。本地起一个 wrk 或 hey,模拟并发请求,观察 P95 延迟和显存占用,一般要求 P95 在 3 秒以内、显存占用不超过 85%。如果发现 P95 飙升,优先调低 max-num-seqs 或降低 gpu-memory-utilization。
3. Agent 编排核心落地:状态、工具与多 Agent
3.1 LangGraph 状态图生产化:从画图到能扛流量
LangGraph 的核心是状态图:把 Agent 的决策过程建模成节点和边,每个节点执行一个操作,边决定流程走向。demo 阶段画个图很简单,生产化以后要额外处理四件事:
第一是状态持久化。LangGraph 的 Checkpointer 可以把执行状态存到外部存储,进程重启后能从上次的断点继续。生产环境不能把状态存在内存里,要接 Redis 或 Postgres。接上之后有个直接好处:用户下次请求可以带同一个 thread_id,Agent 能记住之前的会话进度。
第二是控制递归深度。图结构一旦有循环,就必须设置 recursion_limit,否则 Agent 在某些场景下会无限循环,烧光 token。我在生产环境把默认限制设为 25 步,超过直接终止并返回“执行步骤超限”的提示。
第三是超时控制。每个节点都可能调用模型、调用外部 API,单个节点的执行必须设超时。LangGraph 里可以在节点内部用 asyncio.wait_for 包一层,也可以在调用工具层统一处理。生产环境我一般设置模型调用超时 60 秒、工具调用超时 30 秒,超时走重试或降级逻辑。
第四是图定义的版本管理。Agent 的逻辑改一次,图的结构就变了。把这个图结构当成代码一样走分支、走评审、打 tag,上线以后还要支持按版本回滚。很多生产事故就是“昨天跑得好好的,今天怎么突然傻了”,查下来是图逻辑被悄悄改过。
3.2 MCP 协议:打通 Agent 与工具链的标准通道
MCP(Model Context Protocol)现在已经成为 Agent 接入外部工具/知识库的主流方式,思路和 USB 接口类似——定义一套统一的协议,模型通过标准接口读写外部数据源。它把“工具服务”和“Agent 运行时”解耦了。
生产部署 MCP 有两个关键选择。一是传输方式:进程内用的 stdio 模式只适合本地调试,生产环境要走 SSE 或 streamable HTTP,这样工具服务才能独立部署、独立扩缩容。二是认证与审计:MCP Server 暴露在公司内网,Agent 调用工具前必须做身份校验;同时所有工具调用记录要有日志,谁在什么时间调了什么工具、传了什么参数、返回了什么结果,全部存下来。
MCP 对生产环境的实际价值是工具能力标准化。以前每接一个工具就要写一套适配代码,有了 MCP 之后,工具提供方只要实现 MCP Server,Agent 侧零代码接入。做过系统集成的读者应该能体会到这个价值——生产环境的接入成本是决定生态丰富度的关键。
MCP 也不是银弹。工具调用失败的场景依然要处理:MCP Server 挂了怎么办?返回结果格式不对怎么办?工具执行时间太长怎么办?这些都要在 Agent 编排层做兜底,不能直接抛给用户。我的做法是:每个工具调用都包一层重试和错误降级,调一次失败就再试一次,再失败就返回一个明确的错误信息给用户,而不是让 Agent 自己瞎猜。
3.3 Dify 部署与低代码路线的边界
如果你选了 Dify 路线,部署本身不算难,Docker Compose 直接拉起来就行,但生产化配置有几处要单独处理:
- 替换默认密钥:Dify 的
.env文件里有一堆 SECRET_KEY,不上生产前必须全部换掉,否则等于把管理后台开着门。 - 外置数据库:默认会拉起 postgres 和 redis,最好改成连你自己管控的实例,方便备份和监控。
- API 发布与密钥管理:Dify 工作流编排好后,发布为 API 服务,外部系统通过 API 密钥调用。这个密钥要用独立配置管理,定期轮换。
低代码平台的问题在于它封装了太多细节,出问题以后不好排查。Dify 的 Agent 内部逻辑对开发者是个黑盒,模型输出不达预期时,你只能反复改 prompt,调试体验比代码编排差不少。所以我的建议是:低代码适合快速搭建和验证,长期要沉淀的核心能力,最终还是往代码方向收敛。
3.4 多 Agent 协作:Spring AI multi-agent 与消息总线
业务复杂度上来以后,单个 Agent 处理不了所有请求,得引入多 Agent 协作。常见的模式:
- 规划者-执行者:一个 Planning Agent 拆解任务,多个 Worker Agent 并行执行。
- 角色分工:比如一个写代码、一个做评审、一个跑测试。
- 流水线:上一轮的输出作为下一轮的输入,每个 Agent 只干一段具体的事。
多 Agent 生产化的核心不是怎么编排,而是怎么通信。进程内共享内存只在单机有效,分布式环境下要用外部消息通道。轻量方案是 Redis Stream 或 Redis Pub/Sub,重一点的上消息队列(RabbitMQ、Kafka)。Spring AI 提供了 multi-agent 支持,如果你团队以 Java 为主,这套东西集成起来比硬上 Python 的 LangGraph 要顺手得多。选择依据很简单:团队技术栈是什么就用什么,别为了框架选型把团队拆成两半。
共享状态依然用 Redis 存,注意要做好 key 隔离和过期时间,不然 Agent 之间互相覆盖状态,排查问题会非常痛苦。我给每个 Agent 分配独立的 key 前缀,同时用 trace_id 串联一次完整任务的日志,出问题顺着 trace 就能拉出全链路。
4. 上生产前必须做的加固:超时、限流、可观测性
4.1 超时、重试、限流与熔断
Agent 系统最危险的地方在于:一次用户请求可能触发几十次模型调用和工具调用,任何一环出问题,整体响应时间都会被放大。所以每一层都要有防护。
超时的设置逻辑是“逐层递减”。用户侧 API 网关超时设置为 120 秒;Agent 编排层单步执行超时 30 秒;模型调用超时 60 秒(Agent 内部多轮调用要算总量);外部工具调用超时 15 秒。超时链路的设置原则是外层大于内层,给内层重试留出空间。
重试必须带指数退避。模型调用返回 429(限流)或 503(过载)时,重试是合理的;但如果是 400(参数错误)或 401(认证失败),重试多少次都没意义。重试次数一般 2-3 次,退避间隔从 200ms 开始指数增长。
限流这块,很多人以为只限用户维度,其实要分两层。用户维度限流防止单个用户把系统打爆;模型调用维度限流防止推理服务过载。模型服务的并发是有上限的,vLLM 的 max-num-seqs 就是硬限制,超出以后请求排队、延迟飙升。生产环境我习惯在 Agent API 层做一个令牌桶限流,同时在模型服务前面再加一层队列或负载保护,双保险。
还有一个容易被忽略的成本控制:任何一次 Agent 执行都必须有 token 预算。在请求进来时估算这次任务允许的最大 token 消耗,执行过程中实时累加,超过阈值直接终止。没有这层保护,一个死循环 Agent 可以一夜之间烧掉你一个月的推理预算。我在生产环境的标准做法是单次任务 token 上限设为 20 万,超过自动熔断并告警。
4.2 链路追踪:把 Agent 的“思考过程”变成可审计日志
Agent 系统的排查难度比传统 Web 应用高一个量级。传统请求是线性路径,Agent 请求是图状路径,同一个问题可能走完全不同的分支。手工翻日志定位问题基本是不可行的,必须做结构化追踪。
每个请求进来时生成一个 trace_id,Agent 的每步决策都记一条结构化日志,包含:节点名称、输入、输出、耗时、模型调用的 token 消耗、工具调用参数与结果。日志格式用 JSON,统一输出到 stdout,采集到 Loki 或 Elasticsearch 里。有一次排障,我发现 Agent 在某些场景下会重复调用同一个工具十几次,靠的就是按 trace_id 聚合日志,一眼看出循环路径。
OpenTelemetry 是标准方案,各家云厂商都支持。但我的经验是:Agent 独有的需求不能只靠通用链路追踪解决,还要加一层“语义追踪”。也就是把 Agent 的决策点作为 span 记录下来——它看到什么、选择了哪条路、为什么选这条路。这些信息对优化 prompt、定位幻觉问题至关重要,在告警和排障时能省一半时间。
另外,Agent 的每一次工具调用和模型输出都应该入库做审计。一方面是为了出问题时能追溯;另一方面是以后要训练模型或者做评测,这些数据是最宝贵的资产。我在 Postgres 里放了一张 agent_execution_log 表,记录 session_id、trace_id、节点、输入输出、token 数、耗时,定期做数据分析,能发现很多测试阶段发现不了的问题。
4.3 监控指标:不只盯 CPU,要盯这些关键值
传统运维监控盯 CPU、内存、磁盘就差不多了,Agent 系统还得盯一批特有指标。我自己搭了一套 Prometheus + Grafana 监控,核心指标分成三类:
| 类别 | 关键指标 | 告警阈值参考 |
|---|---|---|
| 推理服务 | GPU 显存使用率、in-flight 请求数、首 token 延迟、每 token 延迟 | 显存超 90% 持续 5 分钟、in-flight 接近 max-num-seqs |
| Agent 应用 | QPS、按节点聚合的耗时、失败率、工具调用成功率、token 消耗速率 | 工具调用成功率低于 95%、单步 P95 超过 30 秒 |
| 基础设施 | Redis 内存使用率、命令延迟、连接数、Postgres 慢查询 | Redis 内存超 75%、连接数超 80% 上限 |
这些指标里最有 Agent 特色的是 token 消耗速率。它直接反映成本,一旦异常飙升,通常意味着有 Agent 在循环里空转。我给它设了两级告警:日消耗超过预算的 80% 触发警告,超过 100% 直接暂停非核心场景的 Agent 调用。
Arthas 能不能在生产环境用,这是很多人纠结的问题。我的结论是:可以,但只能作为临时诊断工具,不能常驻。Arthas 适合在生产环境在线排查类加载问题、方法调用参数和耗时,操作起来不用重启服务,很强大。但常驻会带来不小的性能开销,而且它的字节码增强功能有风险,用完之后必须立刻退出并确认没有遗留的增强。我的一般流程是:先看监控指标和日志定位到可疑方法,再用 Arthas 做一次短时在线观测,拿到必要信息以后马上退出。
4.4 模型与 Prompt 的版本管理、灰度发布和回滚
Agent 系统的发布和传统应用发布有一个关键区别:除了代码,还有模型权重和 Prompt 这两个“运行时变量”。它们都会导致行为变化,也必须纳入版本管理。
模型权重切换必须做灰度。先在影子环境跑几天,对比新旧模型的输出质量;再用 5% 流量切到新模型,观察效果和错误率;没问题再逐步放大。切模型前最好用一批回归测试用例跑一遍,覆盖边界场景。配置中心里放一个 model_version 字段,切换时改配置即可,不用重新发版。
Prompt 的修改也是发布行为,不是直接编辑完就生效。我的做法是:Prompt 模板存到配置中心或专门的数据库表,带版本号和生效时间;修改后先在测试环境验证,再对低流量用户灰度,最后全量。每次 Prompt 变更都留下记录,出问题可以秒级回滚到上一版。
Agent 编排逻辑(比如 LangGraph 的图结构)也要跟代码一起走 CI/CD,做成可回滚的发布单元。发布脚本里同时记录代码版本、模型版本、Prompt 版本,出了问题可以一键切回上次稳定的组合。这个组合快照机制救过我很多次。
5. 常见问题与排查技巧实录
5.1 推理服务 OOM/显存溢出
症状:vLLM 进程崩溃重启,或者 GPU 显存突然飙到 95% 以上。日志里出现 CUDA out of memory。
排查步骤:先看是不是长时间运行后的显存碎片问题,可以周期性重启推理服务;再看请求的上下文长度,很多 OOM 是长上下文请求导致的,调低 max-model-len 或 gpu-memory-utilization 能缓解;最后看并发,把 max-num-seqs 降下来。
这个问题的常见诱因是测试阶段没人用长上下文,一上生产用户聊了几十轮,KV cache 暴涨,显存直接打爆。结合实际经验,gpu-memory-utilization 调到 0.8,max-model-len 设置一个业务场景够用就行的值,远比一味拉满要稳定。
5.2 单核 CPU 拉满却不见回报:Agent 死循环排查
症状:某个请求一直不返回,对应容器 CPU 跑满,token 消耗疯狂上涨。
先确认是不是代码死循环。用 top 找到进程,配合 Arthas 看线程栈,看是不是卡在某个 while 循环里。如果是 LangGraph 图里的循环,检查 recursion_limit 是否配置生效,以及条件边是否真的会走向终止节点。生产环境一定给每个 Agent 任务加硬性最大步数,别依赖模型“自己知道什么时候该停”。
还有一类情况是模型陷入了自我对话:Agent 反复调用“思考”工具,不做任何实际动作。这种时候要从语义追踪日志里看它的输入输出,找出触发循环的模式,针对性改 prompt 或加规则终止。
5.3 模型返回的 JSON 格式不合法,工具调用失败
症状:工具调用频繁报错,查看日志发现 model 返回的 tool call 参数是截断的 JSON,或者字段顺序错乱。
大模型是有“犯懒”倾向的,尤其上下文变长以后,偶尔就会给你返回一段不完整或者格式错误的 JSON。应对方案有三层:第一层,解析时用容错解析器,自动修复常见的格式问题,比如缺引号、缺括号;第二层,请求参数里尽量用 function calling 的结构化输出,比纯 prompt 要求稳定;第三层,解析失败就自动重试一次,但重试前把上一轮的输出作为反例反馈给模型,告诉它“这就是错误格式,请按正确格式输出”。
5.4 Redis 连接池被耗尽
症状:Agent 服务频繁超时,日志里出现 Redis 连接超时或连接池空等异常。
排查方向:先看连接池配置,默认连接数往往不够高并发场景;再看是否有连接泄漏,一些 Agent 框架的长连接没有正确释放;还要确认阻塞命令,比如 BRPOPLPUSH、BLPOP 这类命令在高并发下会占住连接不释放。
用 Redis 6/7 以后,可以在客户端开启连接池监控,低频打印连接池使用情况。出现耗尽问题后,优先检查代码里的异常分支是否有 finally 释放连接。生产环境我一般把连接池最大连接数调成应用并发数的两倍,同时加空闲回收策略,避免连接被服务端断开后客户端还在用。
5.5 常见问题速查表
| 症状 | 可能原因 | 快速排查命令 | 解决建议 |
|---|---|---|---|
| 首 token 延迟高 | KV cache 命中低、模型并行配置不当 | curl 看首包耗时、nvidia-smi 看显存 |
调低 max-model-len、检查 tensor-parallel-size |
| Agent 答非所问 | 检索结果质量差、prompt 被污染 | 查看 RAG 检索日志、查最近 prompt 变更 | 优化 embedding 切分、回滚 prompt 版本 |
| 并发一高就报错 | 推理服务过载、限流未生效 | 观察 in-flight 请求数、检查限流日志 | 增加推理副本、调整 max-num-seqs、启用限流 |
| 工具调用结果异常 | MCP Server 版本不一致、参数被截断 | 查看工具调用日志、核对参数长度 | 统一 MCP 版本、加大参数传输限制 |
| 会话状态丢失 | Redis 数据被清或过期策略问题 | redis-cli 查 key 的 TTL |
调整过期时间、检查持久化策略 |
写在最后:我踩过几次坑之后的部署节奏
说了这么多,最后分享一个我认为最重要的经验:Agent 生产环境部署最忌一步到位。我踩过几次坑之后的实践节奏是:第一周先单 Agent 单场景跑通内部流程,不做高可用,只求把链路跑顺,同时把监控、日志、审计这三件事做好;第二周才上真实流量,限制并发上限,观察推理服务表现;第三周开始做容量规划和高可用,逐步放开限流。任何一步发现不对劲,先回滚再排查,不要带着问题继续推。
把模型权重、Prompt、Agent 编排代码当成同一个发布单元对待,每次变更都留版本记录,是我在生产环境反复验证过最有效的做法。这样做的好处是:出了问题你能快速找回之前稳定运行的组合,而不是面对着几十个变量猜测是哪一步改坏了。如果你的 Agent 项目也准备上生产,希望这篇运维视角的实战记录能帮你少走一些弯路。
