共计 4389 个字符,预计需要花费 11 分钟才能阅读完成。
上个月在公司搭建内部多 Agent 自动化评审系统时,我踩了一个差点让整个服务雪崩的巨坑:业务线同时跑几十个 AutoGen 和 Dify 的工作流,每秒涌入上百个长上下文 prompt,原本跑得好好的 Ollama 实例直接由于排队等待导致 HTTP 连接成批超时,首字延迟(TTFT)飙到 15 秒以上,显存更是频繁抛出 CUDA OOM。很多团队在把本地大模型从小规模 Demo 推向多 Agent 生产协作时,最容易忽视的就是 推理引擎的吞吐架构与流式网关的连接池治理。
本文抛弃华而不实的玩具级方案,分享一套我反复调优并在生产环境稳定跑了几个月的极客组合拳:底层基于 vLLM 实现 PagedAttention 显存池化与 Continuous Batching,上层利用 LiteLLM 统一收敛 OpenAI 格式协议并实现高可用 Fallback 与连接限流。无论你手里是一张单卡 RTX 4090 还是企业级 A100/H100,这套方案都能榨干每一点显存与算力。
一、为什么 Ollama/llama.cpp 扛不住多 Agent 并发?
很多开发者习惯用 Ollama 本地起模型,跑单问答确实爽,但 Agent 场景有两个致命特征:
- 高频共享前缀(System Prompt 极长):多 Agent 往往带着长达 2k~4k tokens 的角色定义与 Tool Schema,每次请求都要重复计算 KV Cache。
- 并发突发性强、请求生存周期短:一个主任务可能分裂出 5 个子 Agent 并行检索,瞬间挤爆单工作线程队列。
传统轻量级方案在并发稍微上来时,显存碎片化严重,显存占用跟并发数几乎成线性硬绑定;而 vLLM 的核心优势在于将虚拟内存分页机制引入了 KV Cache(PagedAttention),结合 自动前缀缓存(Automatic Prefix Caching, APC),能让命中相同 System Prompt 的并发请求直接复用显存物理块,TTFT 瞬间砍掉 70% 以上。
二、底层搭建:vLLM 生产级启动与显存极限压榨
我们以 Qwen2.5-14B-Instruct(AWQ 4-bit 量化版)或 7B 原生 FP16 为例,单张 24G 显存(如 RTX 3090/4090)即可轻松打满高并发。以下是经过生产检验的 Docker 部署参数:
# 拉取最新官方优化镜像
docker run -d --gpus all \
--name vllm-engine \
--restart always \
-p 8000:8000 \
--ipc=host \
-v /data/models:/root/.cache/huggingface \
vllm/vllm-openai:latest \
--model Qwen/Qwen2.5-14B-Instruct-AWQ \
--quantization awq \
--max-model-len 8192 \
--gpu-memory-utilization 0.92 \
--max-num-seqs 64 \
--enable-prefix-caching \
--disable-log-requests
核心参数排障与避坑点:
--enable-prefix-caching:必须开启!这是 Agent 多轮交互与固定 System Prompt 的性能救星,开启后命中的 Token 几乎是零延迟预热。--gpu-memory-utilization 0.92:别留太多,但也不要设为 0.98。PyTorch 动态分配和 CUDA context 仍需少量缓冲,0.90~0.93 是防爆显存的黄金区间。--max-num-seqs 64:控制当前迭代步中最大的并发 Batch 数量。如果显存较小、上下文较长,可以适当降到 32,防止并发请求冲爆 KV Cache 触发 preempt(抢占置换)。--ipc=host:必须开启共享主机 IPC 命名空间,否则在多进程和 NCCL 通信时极容易莫名死锁报 Bus Error。
三、中间件治理:LiteLLM 接入、鉴权与高可用降级
有了底层的推理算力,千万不要让前端业务直接裸调 8000 端口。在多 Agent 场景中,网络抖动、模型超载降级、调用量分发是家常便饭。这里选用轻量且功能强悍的 LiteLLM Proxy 作为模型网关。
我们写一个统一编排的 docker-compose.yml 与路由规则文件 litellm_config.yaml:
# config/litellm_config.yaml
model_list:
# 本地主用模型(高并发)- model_name: qwen-agent
litellm_params:
model: openai/Qwen/Qwen2.5-14B-Instruct-AWQ
api_base: http://vllm-engine:8000/v1
api_key: "none"
tpm: 1000000
rpm: 1200
# 云端保底兜底模型(防本地 OOM 或宕机)- model_name: qwen-agent-fallback
litellm_params:
model: deepseek/deepseek-chat
api_key: os.environ/DEEPSEEK_API_KEY
router_settings:
routing_strategy: "latency-based-routing" # 按延迟路由
num_retries: 2
timeout: 45
fallbacks:
- qwen-agent: ["qwen-agent-fallback"] # 本地挂了自动走 API 保底
general_settings:
master_key: sk-nassky-master-secure-token
alerting: ["slack"] # 支持钉钉 /Slack 报警
随后编写极简的 docker-compose.yml 启动服务栈:
version: '3.8'
services:
vllm-engine:
image: vllm/vllm-openai:latest
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
ipc: host
restart: unless-stopped
volumes:
- /data/models:/root/.cache/huggingface
environment:
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}
command: >
--model Qwen/Qwen2.5-14B-Instruct-AWQ
--quantization awq
--max-model-len 8192
--gpu-memory-utilization 0.90
--max-num-seqs 64
--enable-prefix-caching
ports:
- "8000:8000"
litellm-proxy:
image: ghcr.io/berriai/litellm:main-latest
restart: unless-stopped
ports:
- "4000:4000"
volumes:
- ./config/litellm_config.yaml:/app/config.yaml
environment:
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
command: ["--config", "/app/config.yaml", "--port", "4000", "--num_workers", "8"]
depends_on:
- vllm-engine
四、实战避坑:解决 SSE 流式代理中的“断流”与超时
在多 Agent(比如 LangChain / CrewAI / AutoGen)联动时,客户端普遍依赖 Server-Sent Events (SSE) 持续接收 Token。网关层常会出现“流传输到一半突然中断”或“客户端提前取消后后端依然在盲目推理(Ghost Inference)”的问题。
1. 反向代理的 Buffer 缓冲踩坑
如果前面挂了 Nginx 作为反向代理,千万要关掉 proxy_buffering,否则 Nginx 会把流式 Token 攒够一块才吐出来,前端不仅感受不到打字机效果,还会因为 TTFT 超时触发重试:
location /v1/ {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
# 核心流式配置
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 180s;
chunked_transfer_encoding on;
}
2. 客户端主动中断的显存释放排查
在 Agent 的 ReAct 循环中,有时由于某个条件已满足,客户端会主动 abort 请求。早期的推理框架会继续把剩下的 max_tokens 生成完,浪费整整几百毫秒的显存。vLLM 内部已经对 HTTP 断开连接做了感知,但前提是 你的代理层不能屏蔽 client_disconnect 信号。在 LiteLLM 中启动参数加上 --num_workers 8 配合异步 FastAPI 事件循环,能确保断连时立即向 vLLM 发送 abort_request,避免显存幽灵占用。
五、真实压测实录:并发 50 场景下的数据表现
为了检验这套架构的可靠性,我使用 locust 模拟 50 个 Agent 进程持续向网关发送携带 2048 token System Prompt 的并发请求,测试不同方案的抗压表现(硬件为单卡 RTX 4090 24G,模型为 Qwen2.5-14B-AWQ):
| 方案架构 | 并发数 | 平均 TTFT (首字延迟) | 系统吞吐 (tokens/s) | 错误率 (5xx/Timeout) |
|---|---|---|---|---|
| 传统 Ollama (默认配置) | 50 并发 | 12.4s | 42.1 tps | 28.5% (显存挤爆 / 超时) |
| vLLM (无 Prefix Cache) | 50 并发 | 3.8s | 285.3 tps | 0.0% |
| vLLM + APC + LiteLLM | 50 并发 | 0.68s | 410.6 tps | 0.0% (极致平稳) |
可以看到,开启了 --enable-prefix-caching 的 vLLM 在处理多 Agent 的结构化长 Prompt 时具备降维打击级的优势,首字延迟直接从几秒压到了 680ms,整体系统吞吐暴涨近 10 倍!
总结与极客心得
本地大模型从单人玩具走向团队 Agent 基础设施,核心思路就是 专业的分层治理:让底层的 vLLM 专注于显存分页、算子融合与并行调度,把鉴权、负载均衡、熔断容灾交给轻量灵活的 LiteLLM。对于中小团队或独立开发者而言,不要盲目上昂贵的云端 API 集群,善用好手上的一张消费级显卡加上合理的架构调优,足以撑起每秒数十个并发 Agent 的稳健运转。动手试一下,把你的模型网关真正武装到牙齿吧!