ARTICLE DETAIL

资讯详情

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

隔离内网部署AI Agent实战:从离线架构到并发优化

隔离内网部署AI Agent实战:从离线架构到并发优化 最近我们团队接了一个挺特殊的任务在隔离内网环境里落地一套 AI Agent 应用。所谓“隔离内网”说人话就是这台服务器既上不了公网也不允许随便对外开端口所有数据必须在内部封闭流转。这个前提下过去那种“一键 pip install 调云端大模型 API”的快乐开发方式全部失效整个技术栈从模型部署到依赖安装都要换思路。这篇文章我就把自己从零到一跑通这套环境的过程整理出来包括架构选型、离线部署、并发扛量和内网环境下独有的排坑经验。无论你是要给内部系统接一个智能助手还是想把 Agent 部署到涉密或合规要求高的生产环境这篇内容应该能让你少走不少弯路。1. 项目概述隔离内网里部署 AI Agent 到底难在哪1.1 先搞清楚“隔离内网”的三个层次很多人一听到“内网部署”第一反应是“不就是没外网嘛多拷贝几个安装包就行”。实际上隔离环境是分层级的不同层级的处理方式完全不一样。第一类是物理隔离。服务器在一个独立机房物理上不连接互联网没有网闸也没有任何出网通道。这种最彻底但也最麻烦因为所有软件依赖都得人工搬运进去。第二类是逻辑隔离。机器本身有外网网卡但防火墙策略限制出站流量只开放特定端口给特定目标。很多企业内部生产网属于这一类日常开发机能上网生产机默认断外网。第三类是单向导入。允许外部数据通过特定通道比如光盘、审批后的U盘导入内网但内网数据不允许流出。这类环境常见于保密等级较高的单位。我这次遇到的属于第二类偏严的形态服务器能联内部办公网但出站流量全部被防火墙拦截只有通过审批的特定端口才能访问外部资源。这种“半隔离”状态比纯物理隔离更阴险因为它经常让你误以为能用外网结果一执行命令就超时。1.2 隔离环境给 Agent 工程带来的四大约束在隔离内网里跑 AI Agent核心难点不是模型本身而是整个开发生态被切断了。我梳理了一下主要卡在四个方面。依赖安装是最先炸的坑。Python 包、Node 模块、系统库平时一条命令搞定的事情在这里需要先在外网机器上用pip download把所有 wheel 包拉全连同传递依赖一起打包再用离线方式安装。这个过程中最难受的是版本依赖冲突经常出现装到一半发现缺了某个间接依赖必须重新回外网环境补包。模型下载同样困难。Hugging Face 上的开源模型动辄几十 GB如果机器没有外网权限就得先下载到移动介质或中转服务器再传输进内网。而且模型文件不像普通压缩包一个模型拆成几十个 shard 文件校验和、目录结构一点都不能错。外部 API 调用全部失效。Agent 要真正干活通常需要调用各种工具——搜索、天气、地图、数据库查询。隔离内网里这些外部服务统统调不了Agent 只能调用内部系统提供的接口这意味着工具生态必须围绕企业内网自建。最后是推理资源受限。内网不可能像云上那样弹性扩 GPU你手上就那几张卡显存就那么大所有模型的选型、量化、并发方案都必须围绕着固定资源来规划。1.3 为什么非要在这个环境里跑 Agent既然隔离内网这么麻烦为什么还要做这件事我接触到的实际需求主要有三类。第一类是数据合规要求。某些行业金融、政务、医疗明确规定客户数据、业务数据不能出域任何第三方 API 都不可用AI 能力必须本地化部署。在这个前提下Agent 是唯一能把手头大模型能力和内部业务系统串起来的方案。第二类是业务实时性要求。有些场景对延迟极其敏感比如内部工单自动分拣、交易风控辅助判断、客服工单的实时摘要这些任务如果走公网 API一来一回的网络延迟和不可控性就是硬伤。第三类是成本可控。云端 API 按 token 收费高频调用场景下成本累积非常快。自建开源模型一次性投入硬件成本后续边际成本趋近于零对于调用量大的内部场景反而更划算。2. 整体架构设计与技术选型2.1 三层架构模型层、编排层、工具层隔离内网环境下的 Agent 架构我把它拆成三层底层是模型推理层中间是 Agent 编排层上层是对接内网业务系统的工具层。模型推理层负责加载开源大模型并提供推理服务。这一层的核心选型是推理框架。目前主流的有三个方向Ollama、vLLM、llama.cpp。Ollama 胜在简单一条命令就能跑起来适合快速验证和原型开发vLLM 主打高吞吐和并发通过 PagedAttention 和 Continuous Batching 技术在并发场景下表现远优于原生推理适合正式上线llama.cpp 则强在 CPU 推理和低资源环境GPU 不够的时候能顶上。Agent 编排层负责 Agent 的状态管理、任务规划和工具调用逻辑。我这次选的是 LangGraph因为它把 Agent 的工作流定义成一张图节点和边清晰可控方便调试也方便加状态记忆。如果团队没有太强的开发能力也可以选 Dify 这类可视化平台或者直接基于 FastAPI 自己写一个轻量编排器。工具层是 Agent 能不能真正解决业务问题的关键。在隔离内网里Agent 能调用的工具必须来自内部系统统一认证接口、内部知识库 API、工单系统、数据库查询服务等。这一层要做的是把内部 API 封装成 Agent 能理解的工具描述通常用 JSON Schema 描述参数和返回结构让模型能在规划阶段准确地选择并调用这些工具。2.2 推理框架对比Ollama、vLLM 怎么选选推理框架之前要先回答一个问题Agent 场景下的推理负载是什么特征Agent 和普通的单轮对话不一样一个任务要跑很多轮模型推理。比如让 Agent 查一下本月销售数据并生成分析报告它可能要经历理解意图、生成查询参数、调用数据库工具、阅读返回结果、生成中间结论、继续追问、最终汇总。一个完整任务在模型侧产生的 token 数量是所有中间步骤的总和远大于一个普通问答请求。这意味着 Agent 场景天然是多轮、长上下文、高并发的负载。如果只是拿 Ollama 本地跑着玩或者内部只有个位数并发问题不大。但要支撑几十上百个用户同时使用vLLM 的连续批处理能力就拉开差距了。对比项OllamavLLMllama.cpp部署难度极低适合原型验证中等需配置较低性能依赖编译参数并发能力较弱单请求占用显存强连续批处理高吞吐一般适合低资源显存控制自动但不够精细可精细配置支持 CPU/GPU 混合生产稳定性一般偶发服务重启高社区生态成熟依赖自行封装适用场景原型、个人生产上线无 GPU 或低配环境我最终的方案是验证阶段用 Ollama 快速跑通链路上线阶段切换 vLLM 支撑并发。两个框架都支持 OpenAI 兼容的 API 格式切换成本很低Agent 编排层不需要改代码。2.3 模型选型纯内网环境下的现实选择模型选型在隔离内网里比在公网环境下更关键因为换模型意味着要重新搬运模型文件、重新跑评测成本不低。我基于“显存预算内选最强”的原则做了一轮筛选。如果只有一张 24GB 显存的显卡比如 RTX 3090 / 4090首选量化后的 7B~14B 参数模型。这个量级在中文任务上已经能覆盖大部分内部场景。目前国内开发者用得比较多的开源模型有 Qwen 系列和 DeepSeek 系列其中 DeepSeek-R1-Distill-Qwen-14B 这类蒸馏模型在推理和工具调用上有不错的表现适合做 Agent 底座。模型文件需要提前在公网机器上用镜像站或官方源下载完整包括模型权重、分词器、配置文件缺一不可。如果有多卡或单卡 48GB 以上的 A6000/A800可以上 32B 级别的模型推理能力和指令遵循能力会明显提升但对 Agent 场景而言收益边际递减需要结合业务复杂度平衡。我个人建议是不要一上来就追求大参数量的模型。Agent 的效率瓶颈往往是编排逻辑和工具质量而不是模型聪明程度。先用一个中等量级模型跑通闭环再根据实际效果决定要不要换更大的模型这个路径最稳妥。3. 实操过程从零到一部署成功的关键环节3.1 离线环境准备把“搬包”这一步做扎实离线准备的核心工作是三件事软件依赖打包、模型文件搬运、镜像传递。Python 依赖的离线打包在外网机器上用pip download可以一次拉全。比如我需要安装 FastAPI、LangGraph、vLLM 等依赖先在外网环境里执行pip download fastapi langgraph vllm -d /offline_packages \ --platform manylinux2014_x86_64 \ --python-version 3.11 \ --only-binary:all: --no-deps不过这里有个坑--no-deps只拉顶层包不做依赖解析手动补依赖会非常痛苦。更省事的做法是先用pip install正常装一遍再通过pip freeze拿到全部间接依赖列表然后把列表交给pip download统一拉取pip freeze requirements.txt pip download -r requirements.txt -d /offline_packages到了内网机器上直接用pip install --no-index --find-links/offline_packages -r requirements.txt安装全程不需要外网。容器镜像的传递用docker save和docker load。把镜像打包成 tar 文件拷贝进内网再导入docker save vllm-image:latest | gzip vllm-image.tar.gz # 内网环境 gunzip -c vllm-image.tar.gz | docker load如果你在内网要用 docker compose 编排服务记得提前把 compose 文件里引用到的所有镜像都导出别问我是怎么知道这个坑的——启动时发现拉不了镜像整个环境只能重新搬。3.2 模型推理服务启动与验证推理服务我用 vLLM 做生产环境。启动时的关键参数有几个先看最基础的一条命令python -m vllm.entrypoints.openai.api_server \ --model /models/deepseek_qwen_14b_instruct \ --served-model-name internal-agent-14b \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 32768 \ --port 8000参数的含义逐个说一下。--model指定模型路径内网环境直接用本地目录不要走 Hugging Face 缓存。--served-model-name是客户端调用时使用的模型名内网里自己定义就好。--tensor-parallel-size是张量并行卡数单卡设 1。--gpu-memory-utilization控制在 0.85~0.92太高容易 OOM太低浪费显存。--max-model-len要根据业务最大可能输入的上下文长度来设置设大了会显著增加显存占用设小了长文本会被截断。启动后用 curl 验证服务是否正常curl -X POST http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: internal-agent-14b, messages: [{role: user, content: 请用一句话解释什么是AI Agent}], max_tokens: 100 }能正常返回内容说明模型服务已经通了接下来才轮到 Agent 编排层接入。3.3 Agent 编排服务开发FastAPI LangGraph 的最小实现编排层我采用 FastAPI 提供 HTTP 接口LangGraph 负责 Agent 的工作流管理。一个最基础的 Agent 服务需要具备三个能力接收用户请求、调用模型规划、执行工具调用并返回结果。先定义一个工具让 Agent 有能力查询内部数据。这里以查内部工单状态为例from pydantic import BaseModel, Field from langchain_core.tools import tool class OrderQueryInput(BaseModel): order_id: str Field(description工单编号) tool(args_schemaOrderQueryInput) def query_internal_order(order_id: str) - str: 查询内部工单系统状态 # 实际环境里这里调用内网 API return f工单 {order_id} 当前状态为处理中预计完成时间 24小时内然后定义 Agent 的图结构让模型可以自主决策是否调用工具from langgraph.graph import StateGraph, END from langchain_openai import ChatOpenAI llm ChatOpenAI( modelinternal-agent-14b, api_basehttp://localhost:8000/v1, api_keyEMPTY, temperature0.1, ) tools [query_internal_order] llm_with_tools llm.bind_tools(tools) def agent_node(state): response llm_with_tools.invoke(state[messages]) return {messages: [response]} def tools_node(state): # 实际执行工具调用并返回结果给模型 return {messages: [state[messages][-1]]} graph StateGraph(...) # 定义节点和边构建 Agent 循环FastAPI 侧只需要暴露一个对话接口接收用户输入调用 LangGraph 的工作流最后把结果返回给前端。这是最简骨架真实项目里还要加多轮会话管理和任务状态持久化。有一点特别提醒模型推理和 Agent 编排最好拆成两个独立服务。推理服务专注于模型计算编排服务负责业务逻辑。这样做的好处是模型推理服务的重启不会影响 Agent 服务的稳定性而且两者可以独立扩缩容。3.4 开发联调阶段的端口映射做法开发过程中有个很现实的问题Agent 服务跑在内网服务器上但参与联调的前端、测试同事可能不在同一个网段。这时候就需要通过内网端口映射工具把开发调试端口临时映射到测试网统一入口让联调方访问。我在这个项目里使用了 frp 作为内网端口映射工具。需要特别说明的是这里的使用场景是开发阶段的受控联调映射的端口仅限内部测试网段访问涉及的是同一个隔离内网体系之内的联调协作而不是突破任何安全边界。配置大致如下# frpc.ini运行在内网开发服务器上 [common] server_addr 10.10.x.x # 测试网统一入口服务器 server_port 7000 [agent-debug] type tcp local_ip 127.0.0.1 local_port 8000 remote_port 18000启动frpc后联调方通过http://10.10.x.x:18000访问内网服务器上的 Agent 服务。联调完成后立即停止映射并回收端口。这里我必须强调三条边界第一frp 这类工具只用于开发调试和受控联调严禁在生产环境或未经授权的场景下使用第二映射端口必须限定在内部测试网段可访问第三所有映射操作必须记录日志用完即关不允许长期挂机。合理使用这些工具能显著提升内网开发效率但绝不意味着可以绕过安全策略这一点希望大家务必把握好。4. 并发扛量Agent 场景下的性能优化三板斧4.1 先搞清楚瓶颈到底在哪里“AI Agent 怎么扛并发”是搜索热度很高的一个话题但很多人一上来就想着加服务器、调网关实际上没抓住问题的核心。Agent 场景和普通 Web 请求有本质区别一个 Agent 任务不是一次性生成的它内部会循环执行多次模型推理和工具调用。举个例子一个查询销售数据并生成报表的 Agent 任务可能一个请求在模型侧要产生一万甚至几万 token。如果推理服务的 token 生成速度是每秒 50 个单这一个请求就要花三四分钟。此时就算 Web 层随便扛几千 QPS模型侧也早就堵死了。所以并发优化的第一原则是先数清楚模型服务的吞吐上限再反推 Web 层的承载设计。4.2 第一板斧异步化与任务队列Agent 任务耗时动辄几十秒甚至几分钟不能再像普通接口那样同步阻塞等待结果。我们把接口改成“提交任务 获取状态”模式。用户在请求里提交任务接口立刻返回一个task_id后台 Worker 异步执行 Agent 逻辑执行结果写入 Redis。前端通过轮询或 SSE 推送获取最终结果。核心伪代码如下app.post(/agent/run) async def run_agent(request: AgentRequest, background_tasks: BackgroundTasks): task_id str(uuid.uuid4()) background_tasks.add_task(run_agent_task, task_id, request) return {task_id: task_id, status: queued} app.get(/agent/task/{task_id}) async def get_task_status(task_id: str): result redis_client.get(fagent_result:{task_id}) if not result: return {status: running} return {status: done, result: json.loads(result)}这样 Web 层只负责收发请求资源占用极小真正的压力全在模型推理和后台 Worker。如果任务量继续上涨可以把 Worker 独立进程部署用 Celery 或简单的 Redis 队列做任务分发。4.3 第二板斧推理服务的并发参数调优模型服务是 Agent 系统吞吐量最大的瓶预vLLM 调得好了能成倍提升并发能力。核心要调的是两个参数max_num_seqs和gpu_memory_utilization。max_num_seqs控制同时参与连续批处理的序列数量。这个值太小GPU 算力喂不饱太大可能超出显存导致 OOM。一般建议从 64 开始配合监控逐步上调。在启动时还要留足 KV Cache 的空间。KV Cache 是推理过程中缓存历史 token 的中间结果Agent 长上下文场景下 KV Cache 吃显存很凶。如果max_model_len设置过大KV Cache 会挤占模型权重空间导致可并发数反降。一个实践中比较稳的参数组合--gpu-memory-utilization 0.9 \ --max-num-seqs 128 \ --max-model-len 32768 \ --enforce-eager其中--enforce-eager可以关闭 CUDA Graph虽然第一次推理会慢一点但能减少显存碎片适合在显存紧张的机器上使用。4.4 第三板斧限流、超时与熔断没有限流的内网服务一样会崩。Agent 服务的限流不能只看每秒请求数因为不同任务消耗的 token 差异很大。简单做法是限并发任务数比如同一时刻最多允许 N 个 Agent 任务在执行超过的排到队列里等待。from fastapi import HTTPException import asyncio semaphore asyncio.Semaphore(10) # 最多10并发任务 app.post(/agent/run) async def run_agent(request: AgentRequest): if semaphore.locked() and len(task_queue) 100: raise HTTPException(status_code503, detail系统繁忙请稍后重试) async with semaphore: return await execute_agent(request)超时控制同样重要。Agent 任务根据模型规模和任务复杂度设置合理超时建议取 P95 耗时的 1.5~2 倍。超时后任务写入失败状态前端能明确看到失败原因。熔断机制则是在连续出现超时或推理服务返回 5xx 时自动拒绝新任务进入等待服务恢复后再放量。5. 内网环境下最容易踩的坑与排查实录5.1 依赖安装与模型加载类问题这类问题是内网环境的“第一道坎”我在过程中遇到过几个特别典型的情况。离线 pip 安装时版本冲突。pip download时如果外网环境的 Python 版本和系统依赖与内网不一致拉回的 wheel 包可能根本装不上。解决方法是下载时严格指定--python-version和--platform参数最好用 pyenv 在外网模拟一个同类环境再拉包。模型文件传输不完整。Hugging Face 的模型文件动辄几十个分片传输中断导致某个 shard 文件缺失时vLLM 启动不会立刻报错而是加载到一半才提示safeensors file not found。排查了一下午才发现是传输中断漏了文件。后续我用sha256校验和脚本对完整模型目录做了逐文件核对再没出过问题。模型首次加载超时。Agent 服务启动后会调用模型推理如果推理服务还没完成权重加载HTTP 请求会一直挂起直到超时。内网环境的模型加载经常要几分钟Web 侧超时时间需要放宽。我用了一个最简单直接的方案编排服务启动时先请求推理服务的/v1/models接口做健康检查确认模型就绪后才正式开始接收业务流量。5.2 网络超时与长连接断裂Agent 任务跑得久前端通过 HTTP 请求等结果的方案容易踩超时。内网网关和中间代理设备经常有idle timeout一个请求几十秒内没有数据传输就会被切断。解决办法前面已经说过核心是改成轮询模式。SSE 方案也可以但要注意代理设备对长时间流式连接同样有超时限制实测下来轮询反而是最稳的。如果必须用流式输出逐字返回模型结果建议前端通过 WebSocket 建立连接规避普通 HTTP 代理的超时限制。同时后端要加心跳机制每 15 秒发一个 ping 帧保持连接活跃。5.3 GPU 显存管理与推理性能排查内网 GPU 资源有限显存管理直接影响服务稳定性。常见报错和解决办法我整理成了表格报错现象可能原因解决方案CUDA out of memory并发数过高或 KV Cache 占用过大调低max_num_seqs降低gpu_memory_utilization推理速度突然变慢显存碎片化严重重启推理服务开启--enforce-eager多卡张量并行效率低卡间通信带宽不足检查 PCIe 拓扑优先用 NVLink 连接的卡加载模型时 shared memory 不足/dev/shm空间太小容器启动加--shm-size10g排查显存问题最有效的手段是开 watch 监控watch -n 1 nvidia-smi连续观察多个请求落进批处理后显存的变化曲线就能直观看到是 KV Cache 吃满还是权重占用过大。5.4 可观测性日志、追踪与审计内网系统要过合规审计Agent 的日志和追踪体系比普通应用更重要。Agent 调用过哪些工具、读取过哪些数据、生成过哪些结论全部要留痕。我在每条 Agent 执行记录里保留了完整链路任务 ID、用户标识、输入内容摘要、模型推理 token 数、工具调用列表、每轮耗时、最终结果。日志存储到独立的审计日志文件定期归档。这样出了问题可以精准回溯。6. 实战心得与后续扩展6.1 我在这套项目中总结的几条经验整个项目跑下来最大的体会是先跑通闭环再谈优化。一开始我把精力花在构架设计上模型选了又选编排方式改了又改结果连一条最简单的 Agent 流程都没跑通。后来推倒重来先用 Ollama 跑一个 7B 量化模型配合一个最简单的 FastAPI 接口和最基础的编排逻辑半天时间就让 Agent 能回答内部系统相关的问题了。链路通了后面的优化才有的放矢。第二个体会是Agent 的体验上限由模型决定但可用下限由工具决定。模型再聪明如果工具层的接口不稳定、返回结构不清晰Agent 一样会反复调用失败。我后来把内网 API 的返回结构统一成 JSON Schema模型工具调用的成功率肉眼可见地提高了。还有一个特别容易被忽视的点内网环境的时间同步。Agent 日志、任务队列、模型服务的健康检查都依赖准确时间戳。如果内网服务器没有配置 NTP 时间同步多台机器之间的时间偏差会导致任务状态错乱排查起来非常痛苦。6.2 这套能力后续还能怎么扩展初版 Agent 跑通之后我接下来的扩展方向有三个。第一是接入 RAG 知识库让 Agent 能基于内部文档回答员工问题。内网环境正好适合做私有化知识库文档数据不需要出域索引和检索全部本地化。第二是升级为多 Agent 协作模式。把单一 Agent 拆成“主管 Agent 执行 Agent”主管负责拆解任务执行 Agent 分别负责查询数据、分析数据、生成报告。每一步都有专门的 Agent 把关复杂度上去了但单任务的完成质量也能显著提升。第三是模型的热更新流程。内网环境下模型不能自动拉新版本需要建立一套“外网下载 → 校验哈希 → 审核 → 导入内网 → 灰度验证 → 全量切换”的上线流程把模型迭代也纳入规范的 DevOps 管道。内网部署 AI Agent 这件事说难也难说简单也简单。难在生态切断后一切都要自己手工搬运简单在架构思路和公网并没有本质区别。只要把模型推理、Agent 编排、工具接入这三层拆清楚把离线依赖搬运做扎实把并发瓶颈定位准确它就是一个普通但需要耐心的工程问题。希望这篇实战笔记能给你带来一些参考。最后分享一个小技巧内网环境里办公往往离不开各种内网端口映射工具。我这里再明确一次个人建议——这类工具在开发联调时确实提高效率但务必严格限定在受控环境、审批流程之内使用生产环境千万别碰。任何时候安全合规都应该排在对效率的追求之前。
返回列表