隔离内网AI Agent落地实战:架构选型、离线部署与并发调优
发布时间:2026/10/7 6:25:48 作者:尧图编辑部 阅读量:1,286

接手这个项目的时候甲方的一句话让我印象特别深模型可以弱一点但数据绝对不能出这个机房。这就是典型的隔离内网 AI Agent 工程场景——业务系统部署在安全隔离的企业内网里不能调用公网大模型 API不能有任何业务数据经过外部链路所有的大模型推理、知识检索、Agent 编排都得在自己机房的算力池里完成。这篇文章我想把隔离内网下 AI Agent 从零到落地的完整链路拆给大家看包括整体架构怎么定、模型推理层怎么选、LangGraph 编排怎么做、离线依赖怎么分发、并发扛压怎么调还有我在过程中踩过的一堆坑。不管你是正准备在企业内部落地智能助手、工单处理、知识问答这类 Agent 应用还是单纯想了解不连外网怎么做大模型应用这篇都值得你花十分钟看完。1. 隔离内网 AI Agent 项目概述与核心挑战1.1 隔离内网里做 AI Agent 到底在做什么先把这个场景说清楚。隔离内网简单理解就是一套与公共互联网物理隔离或逻辑隔离的企业内部网络环境金融、能源、政务、制造这些行业里非常常见。在这类环境里部署 AI Agent目标通常不是陪人聊天而是让 Agent 真正去干活解析内部工单、查询 ERP 系统库存数据、调用 OA 审批接口、检索内部规范文档之后给出结论。它的本质是把一个原本需要人完成的理解-规划-调用-执行完整流程变成一套计算机系统自主跑完的自动化能力。为什么不能直接用公网的闭源大模型 API原因很现实数据出网合规风险、接口稳定性不可控、调用成本随量上涨以及关键业务链路对第三方服务的不可依赖。更直接的是很多企业的安全策略就一句话数据不许出网。所以这个场景下做的所有工程决策都绕不开一个前提模型自己带依赖自己备链路自己扛。说白了隔离内网不是不能做 AI Agent而是它把所有开箱即用的便利都收走了逼着你用工程手段把整条链路自己拼起来。1.2 和公网部署相比多出来的硬约束在公网做一个 Agent demo 很简单注册一个 API key、调 LangChain、写个 ReAct 循环、上线完事。但在隔离内网里每个环节都变成了一道独立的工程题模型从哪来不能在线下载权重需要提前把模型文件打包、过审批、逐级分发到内网服务器。Python 依赖从哪来pip 在线安装不存在所有第三方库都要提前下载好再推到内网私有源。基础组件怎么办向量数据库、消息队列、对象存储这些中间件必须以离线镜像或离线安装包的形式准备。算力是固定的内网机房的 GPU 数量写在采购单上不像云上可以随时扩容并发能力必须在有限的卡数和显存里算清楚。模型能力有上限内网能部署的通常是开源模型规划能力、工具调用成功率都需要靠提示词设计和编排层的重试机制去兜底。公网部署考验的是你怎么用好现成的服务隔离内网部署考验的是你怎么在有限资源下自建一套完整服务。这也是我把工程两个字看得很重的原因——不是写个 Python 脚本调通就完事而是要把它当成一个长期运行、可运维、可排查的系统来做。2. 隔离内网 Agent 整体架构设计与选型思路2.1 主框架为什么选 FastAPI LangGraph 组合先看整个系统长什么样。我们最终落地的架构从请求入口到最终返回一共五层接入层是 FastAPI 提供 HTTP/SSE 接口处理前端对话、工单系统回调、内部 IM 机器人消息编排层用 LangGraph 定义 Agent 图结构负责任务拆解、状态流转、工具调用重试工具层是一组内部 API 适配器和数据库查询器Agent 通过函数调用方式按需触发模型推理层用 vLLM 加载本地开源模型提供兼容 OpenAI 协议的推理服务数据层用 Chroma 存知识库切片、PostgreSQL 存会话与运行记录、Redis 存任务队列。为什么选 FastAPI 而不是别的理由很直接Python 生态对接 LangGraph 最顺畅FastAPI 自带异步能力对长连接和流式输出支持好OpenAPI 文档在内网调试时非常方便配合接口测试工具可以快速定位联调问题。有人用 Django 做过这类网关我也见过但 FastAPI 在异步调用模型推理这种 IO 密集场景下更省心代码量也少一个量级。LangGraph 相比直接手写 ReAct 循环的优势在于它把 Agent 流程变成了一个有状态、可中断、可回放的图。这个特性在隔离内网场景里特别值钱因为业务方会反复要求你把这步中间状态记录下来审计要用某一步出错了要从断点恢复。图结构的每个节点都有明确的输入输出方便持久化到 PostgreSQL也方便在运行出错时定位是规划错了还是工具调用错了。这些在 demo 阶段无所谓进了生产环境全是刚需。2.2 模型推理层选型与量化方案内网部署大模型第一件事是选模型底座。当时我们评估过几个方向模型参数规模工具调用能力显存需求评估结论Qwen2.5-14B-Instruct14B较好约 40GBFP16最终选用Qwen2.5-32B-Instruct32B强约 64GBINT8机房硬件不够ChatGLM3-6B6B中等约 12GBFP16中文效果有限Llama3.1-8B-Instruct8B中等约 16GBFP16中文场景一般最终选的是 Qwen2.5-14B-Instruct配 2 张 A100-80G。选它的核心原因有两个一是千问系列在中文工具调用和指令遵循上的表现在同量级开源模型里确实稳定二是它对函数调用格式的兼容性好LangGraph 的 openai 工具协议可以直接适配不需要自己写很复杂的中间层。单卡 80G 用 FP16 部署 14B 大约吃掉 40G 显存剩下显存留给 KV cache推理吞吐比权重塞满的情况舒服很多。推理服务用的 vLLM。相比直接用 Ollama 或者 Transformers 起推理vLLM 的 PagedAttention 和连续批处理在并发场景下的吞吐差距是数量级的。实测同样的 14B 模型Ollama 单机默认配置压到 8 个并发就开始明显排队vLLM 把 batch size 调起来之后单请求延迟保持稳定吞吐能多出三到四倍。你如果只是内网自己调试Ollama 完全够用一旦要做生产系统直接上 vLLM别犹豫。2.3 知识检索与工具调度层设计Agent 要落地到具体业务光有对话能力不够必须能查到企业自己的数据。我们用两层检索第一层是向量检索知识库文档规章制度、设备手册、历史工单切块后用 embedding 模型转成向量存到 Chroma。选 Chroma 而不是 Milvus是考虑到数据规模没到百万级单机向量库足够还省掉一套分布式组件的运维成本。embedding 模型用的 bge-large-zh离线部署权重约 1.3GB中文切块做余弦相似度召回的效果比较稳。第二层是结构化数据查询通过工具函数走内网数据库。比如用户问上个月华东区的故障工单有多少条Agent 先从意图里识别出需要查库然后调用 query_workorder_stats 这个工具SQL 在工具内部写死Agent 只负责传参数。这样从源头上避免模型自己生成 SQL 带来的注入风险和语法错误。这一点非常关键模型再聪明也不能让它直接操作数据库工具层做好权限控制和安全兜底是底线。工具调度是整个 Agent 能不能下地干活的核心。每个工具都必须有严格定义的入参 schema、超时时间和错误返回格式。Agent 在工具调用环节经常出现参数填错返回结果没解析出来这类情况所以编排层要留重试逻辑和兜底文案。我们在 LangGraph 里对工具节点单独定义了重试策略首次失败重试一次二次失败就把错误信息作为文本反馈给模型让它重新规划。3. 离线环境下的 Agent 落地实操与并发调优3.1 离线环境初始化与依赖包分发这是隔离内网项目最磨人、也最容易被低估的一块。我把踩过的路整理成一套相对标准化的流程照着做能少折腾一周。第一步准备一台可以访问正常软件源的构建机这台机器只负责打包不碰业务数据。在构建机上创建虚拟环境把 requirements.txt 完整装好然后用 pip download 把依赖全部拉下来pip download -r requirements.txt -d ./offline_packages \ --platform manylinux2014_x86_64 \ --python-version 3.11 \ --implementation cp \ --abi cp311 \ --only-binary:all:这里要特别注意 --platform 和 --python-version 必须和目标服务器一致否则拉回来的包根本装不上。如果构建机和目标机 CPU 架构不一致比如构建机是 x86、目标机是 ARMbinary 包基本全废只能走源码包编译工程量直接翻倍。所以先确认目标机的架构和 Python 版本再决定怎么下载这个顺序不能反。第二步把离线包推到内网。内网环境通常有制品仓库Nexus 或 Artifactory把离线包上传成内网 PyPI 源。目标服务器上配置 pip 指向内网源[global] index-url http://nexus.internal/pypi/simple/ trusted-host nexus.internal第三步镜像用 docker save / docker load 传递。当时把 vLLM、Chroma、Redis、PostgreSQL 全部容器化在构建机上把镜像打包成 tar分发给内网服务器后统一 load再用 docker compose 编排起来。有个经验打包前先执行 docker prune 清理构建过程中的中间层tar 包体积能小不少尤其 vLLM 这种基础镜像动辄几个 G能省则省。模型权重文件的分发也是一样的思路提前从模型仓库把 safetensors 文件完整下载打包通过内部流程传到内网 NAS服务器再从 NAS 拉取。我强烈建议做一份 model.lock 文件把模型名称、版本、文件数量、SHA256 校验值全部记录在案。内网环境拷来拷去最容易出现文件不完整导致加载失败有个校验清单能救你很多次。3.2 Agent 核心链路实现与代码骨架整个 Agent 的编排我们用了 LangGraph 的状态图模型。核心代码结构给大家看一下业务细节做了简化保留骨架from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated import operator class AgentState(TypedDict): messages: Annotated[list, operator.add] tool_name: str tool_args: dict tool_result: str final_answer: str def plan_node(state: AgentState): response llm_client.chat( messagesstate[messages], toolsTOOL_SCHEMAS, temperature0.1, ) if response.tool_calls: return { tool_name: response.tool_calls[0].function.name, tool_args: json.loads(response.tool_calls[0].function.arguments), } return {final_answer: response.content} def execute_tool_node(state: AgentState): result, error run_tool(state[tool_name], state[tool_args]) if error: return {messages: [{ role: tool, content: f调用失败: {error} }]} return {tool_result: result, messages: [{ role: tool, content: result }]} def final_node(state: AgentState): answer llm_client.chat(messagesstate[messages]) return {final_answer: answer.content} graph StateGraph(AgentState) graph.add_node(plan, plan_node) graph.add_node(execute_tool, execute_tool_node) graph.add_node(final, final_node) graph.set_entry_point(plan) graph.add_edge(plan, execute_tool) graph.add_edge(execute_tool, final) graph.add_edge(final, END) app graph.compile()这里头的细节比骨架看起来要多得多。第一个细节是系统提示词内网模型的工具调用格式高度依赖提示词里的明确说明必须把你是一个企业智能助手你可以使用以下工具工具参数必须符合 schema不知道就说不知道这些约束写进去。第二个细节是工具调用容忍度本地模型输出的 function arguments 经常不是合法 JSON比如多一个逗号、中英文引号混用。我们在 run_tool 之前加一个解析层先 json.loads失败后用正则做简单修复再失败就让模型重新输出。再补一个我觉得特别有效的设计把规划和执行拆开不要一步到位。小参数模型在做复合任务时让它一口气输出思维链工具调用最终答案很容易崩。拆成 plan 节点只负责输出工具调用执行完再走 final 节点生成回复成功率会明显上升。实测 14B 模型这样拆之后工具调用成功率从 70% 提到了 90% 以上。3.3 并发扛压的估算方法与调优过程并发是大家问得最多的点。隔离内网下的并发问题本质上是在固定算力下做资源分配的问题。先给一个估算方法再讲实际调优过程。假设一个请求平均要经过两轮模型调用一轮规划加一轮生成每轮生成约 500 token。单并发时14B 模型在 A100 上生成速度大约 30 token/s所以单请求耗时约 500 × 2 / 30 ≈ 33 秒。vLLM 支持连续批处理当并发从 1 升到 16 时吞吐可以提升到 120 token/s 左右单请求耗时反而能压到 8 到 10 秒。这是因为 GPU 计算在 batch 内是复用的越多请求共享显存和算力单位成本越低。但并发不是无限涨的当 batch 过大导致单请求延迟超过用户可接受范围时就要限流。我们实际做了四件事推理层用 vLLM 开启连续批处理通过 --max-num-seqs 限制最大并发序列数避免极端情况把显存撑爆我们设的是 32。接入层加并发闸门超过阈值的请求直接进 Redis 排队前端拿到 202 后轮询任务状态。用户不会因为模型推理慢而一直挂着一个 HTTP 连接。模型调用设 60 秒超时超时后取消请求并返回系统繁忙请稍后重试。长时间不返回的请求会占着批处理槽位必须主动掐掉。Agent 整体加熔断如果最近一分钟模型服务错误率超过 30%网关直接降级成知识库检索兜底停掉 Agent 流程返回人工客服提示。压测数据供参考2 张 A100-80G、Qwen2.5-14B-InstructFP16、vLLM 连续批处理16 并发下 P95 响应时间约 12 秒单卡吞吐约 110 token/s。如果业务要求 P95 在 5 秒内就得考虑把模型降到 7B/8B 档位或者增加卡数。这个账必须在项目初期就算清楚否则后期扩容很被动。3.4 内网发布流程与监控体系发布流程我们用的是 GitLab Runner 构建、内网制品库分发、docker compose 滚动更新。隔离内网没有公网 Registry镜像统一推到内网 Harbor服务器拉取指定 tag。这个流程的好处是回滚方便一个 docker compose down 再指定旧 tag 就能恢复。监控这块容易被忽视但内网环境不能依赖任何云监控必须自己搭。我们用 Prometheus 收集指标、Grafana 做看板重点盯四类指标GPU 利用率与显存占用、vLLM 推理队列长度、Agent 各节点执行耗时分布、工具调用失败率。特别是工具调用失败率它直接反映模型在真实业务上的表现建议把它做成看板上第一个图。日志方面LangGraph 每个节点执行都会产出结构化日志统一打到 Loki按 request_id 串联整条链路。排查问题时直接搜 request_id 就能看到一次 Agent 完整执行中每一步的输入输出这个能力在联调阶段省了我大量时间。没有这套链路追踪出了问题基本只能靠猜。4. 内网 AI Agent 常见问题与排查技巧实录4.1 显存加载与推理异常排查最典型的问题是 vLLM 启动时 OOM。明明模型权重只占 40G 显存启动却报显存不足。原因通常是没设置 gpu_memory_utilizationvLLM 默认会给 KV cache 预留显存但如果同时开了多个模型进程显存就被瓜分光了。解决办法是启动参数显式控制vllm serve Qwen/Qwen2.5-14B-Instruct \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --max-num-seqs 32 \ --served-model-name local-qwen还有个坑vLLM 默认按模型配置里的 max_position_embeddings 分配 KV cache如果业务请求经常超过 8192 的上下文窗口会直接报输入超长。我们当时给所有 Agent 的上下文控制逻辑都加了截断策略对话历史超长就做窗口裁剪保底只保留最近 10 轮加知识检索结果。4.2 工具调用与输出解析失败的处理工具调用出错我把原因分成三类对应三种修法第一类是模型压根不触发工具调用直接生成了文本。这种情况通常是提示词里没把工具说清楚或者模型对需求理解有偏差。修法是在系统提示词里加遇到以下场景必须调用工具查库存、查工单、查审批进度并给一两个 few-shot 示例。第二类是触发了工具但参数填错比如日期格式传成了昨天而不是具体日期。修法是工具 schema 里把参数格式写死描述里给合法示例同时在工具层做参数校验非法参数直接返回明确错误让模型重新输入。第三类是工具结果解析失败模型输出的 JSON 不可解析。修法就是前面说的加容错解析层先标准解析再正则修复再不行给模型喂错误信息让它重来一次。实测重来一次成功率很高因为第二次模型已经知道第一次错在哪了。4.3 RAG 检索与幻觉问题治理内网知识问答最常见的翻车场景模型一本正经地胡编。原因很简单本地 14B 模型在开放生成任务上容易自由发挥。我们的对策是给 RAG 环节加强约束一是检索结果必须有引用来源生成回答时强制要求模型只依据提供的检索片段作答知识库里没有的信息直接回答未在现有资料中找到。二是对检索相关性设阈值bge-large-zh 召回结果里 cosine 相似度低于 0.65 的切片直接丢弃宁可不答也不能瞎答。三是答案生成后单独过一道校验节点检查最终回答里是否包含检索片段中的关键实体如果完全没有就判定为疑似幻觉触发一次重新检索。效果很直观上线前内部测试里知识类问题准确率从 62% 提升到 88%幻觉率大幅下降。这里想强调一个观点RAG 系统的效果上限由召回质量决定生成模型只是把你给它的材料组织成话。所以与其花时间调提示词压制幻觉不如先把切块粒度、检索阈值、引用格式这几个基础参数打扎实。4.4 离线依赖兼容性问题的避坑离线环境最崩溃的时刻往往是装依赖装到一半发现某个包需要编译而内网机器没有编译工具链。比如 pydantic-core、uvloop 这些带 C 扩展的包必须有对应平台的 wheel 才能装上。我们的解法是一是在构建机上用 pip download --only-binary:all: 提前校验所有依赖都有对应平台的 wheel二是把目标机的 Python 小版本也固定死很多 C 扩展包对 Python 小版本敏感cp311 和 cp312 的 wheel 不通用三是在内网准备一个 gcc 工具链镜像作为后备万一真有源码包要编译至少不会当场傻眼。还有一个容易踩的点内网 PyPI 源如果配置了 trusted-host 但证书有问题pip 会一直报 SSL 错误。这种时候优先检查内网源的 HTTPS 证书是否被内网 CA 正确签名而不是直接关掉校验。后者虽然能临时解决问题但在安全审计时会留下不好的记录得不偿失。5. 隔离内网 Agent 项目的个人心得隔离内网下的 AI Agent 工程说实话比在公网上做同样的系统要累得多但做完之后你对整个链路的理解会深得多。公网上有各种托管服务和无缝体验把你保护得太好很多底层问题根本轮不到你操心到了内网镜像、模型、依赖、算力、监控每一项都要自己从零搞定反而逼着你把 Agent 系统的每一层都摸透。我个人感受最深的一句话是隔离内网并不是 Agent 落地的阻碍它只是把工程的复杂度提前暴露了出来让你必须用更扎实的方案去应对。如果你接下来也要做类似项目我建议从三件事开始先花一周时间把离线依赖和模型分发这套流程跑通这是后面所有工作的地基再拿一个具体的、很小但真实的业务场景做 Agent 闭环比如查工单状态别一上来就铺太大面最后把工具调用的失败率和延迟做成每日必看的看板这两项指标能最真实地反映系统在业务里的健康度。最后再分享一个小技巧内网模型服务的服务名、模型名、端口最好在项目第一天就统一约定并写进配置文档。很多后端同事联调时被坑就是因为每个人配置文件里写的模型名都不一样报错信息牛头不对马嘴。把这些基础约定打牢后面的协作会顺畅很多。