
1. 为什么“隔离内网”是 AI Agent 工程化的分水岭很多人做 AI Agent 的路径是这样的本地跑通一个 LangChain 或 Spring AI 的 Demo接上大模型 API调几个工具函数感觉“成了”。然后老板说把这套东西部署到公司内网环境里给业务部门用。这时候你才发现之前跑通的那套东西几乎要推倒重来。隔离内网意味着什么没有公网出口不能直接调用外部大模型 API不能从 PyPI 或 npm 拉包不能访问 GitHub甚至连 Docker Hub 都连不上。你手里只有一台或几台内网服务器一个内部镜像仓库如果有的话以及一堆需要离线安装的依赖。这不是“把代码拷过去跑起来”那么简单而是一次完整的工程重构。我经历过三次不同规模的内网 AI Agent 部署一次是十几台机器的私有集群一次是单机离线环境还有一次是半隔离环境有内部代理但无公网。每次踩的坑都不一样但核心矛盾是一致的——AI Agent 的工程化依赖链太长而内网环境的资源供给太短。这篇文章面向的是需要在隔离内网中落地 AI Agent 的工程师不管你是用 Python 生态的 LangChain/LangGraph还是 Java 生态的 Spring AI或者是基于 MCP 协议自建工具链这里面的思路和实操细节都能直接参考。我会从环境准备、模型部署、工具链集成、MCP 协议适配、并发处理、以及实际踩坑经验几个维度展开尽量把每个环节的“为什么”讲清楚。注意本文讨论的是完全合规的企业内网部署场景所有操作均在合法授权的内部环境中进行。2. 内网环境的依赖供应链从“缺什么装什么”到“提前备好一切”2.1 离线依赖的完整清单该怎么列在公网环境里pip install langchain一行命令解决的事到了内网就变成了一场供应链管理考试。我的做法是在公网环境用一台干净的机器完整模拟内网部署流程把所有依赖打成一个离线包。具体操作上Python 生态用pip download而不是pip install# 在公网机器上创建干净虚拟环境 python -m venv agent_env source agent_env/bin/activate # 下载所有依赖到本地目录不安装 pip download -r requirements.txt -d ./offline_packages --platform manylinux2014_x86_64 --python-version 311 --only-binary:all: # 如果有些包没有预编译wheel需要下载源码包 pip download -r requirements.txt -d ./offline_packages --no-binary:all:这里有个关键细节--platform和--python-version必须和内网目标机器完全一致。我踩过一次坑公网机器是 Ubuntu 22.04 Python 3.11内网是 CentOS 7 Python 3.9结果下载的 wheel 包全部不兼容到了内网一个都装不上。后来学乖了直接用 Docker 起一个和目标环境一致的基础镜像来下载依赖。Java 生态相对好一些Maven 可以用dependency:go-offline把依赖拉到本地仓库mvn dependency:go-offline -Dmaven.repo.local./offline_m2然后把整个offline_m2目录打包拷进内网配置settings.xml指向本地仓库路径。2.2 那些容易被忽略的“隐形依赖”除了代码层面的依赖还有几类东西经常被遗漏第一类是模型文件。如果你用的是开源模型比如 Qwen、ChatGLM、Baichuan 等模型权重动辄几个 GB 到几十个 GB。这些文件需要提前下载好通过物理介质或内部文件服务器传入内网。我建议在公网环境用huggingface-cli download把模型完整拉下来包括 tokenizer、config、generation_config 等所有配套文件少一个都可能导致加载失败。第二类是系统级依赖。比如某些 Python 包依赖libpq-dev、libssl-dev、gcc等系统库这些在内网机器上可能没有。我的做法是列一个系统依赖清单在内网部署前先检查# 检查关键系统库 ldconfig -p | grep -E libssl|libpq|libffi|libxml2第三类是证书和配置文件。内网环境如果有自签证书的 HTTPS 服务需要提前把 CA 证书导入到系统的信任链中否则 Python 的requests库会直接报 SSL 错误。2.3 内网镜像仓库的搭建与使用如果内网规模较大超过 5 台机器需要部署建议搭建一个内部 PyPI 镜像。用devpi或者简单的nginx静态文件服务都行# 用 nginx 做最简单的 PyPI 静态镜像 # 把 offline_packages 目录放到 /var/www/pypi/simple/ # nginx 配置 server { listen 8080; location /simple/ { autoindex on; root /var/www/pypi; } }然后在每台内网机器的pip.conf中配置[global] index-url http://internal-mirror:8080/simple/ trusted-host internal-mirror这样后续安装依赖就不需要每次从外部拷贝了。Maven 同理可以用 Nexus 或 Artifactory 搭建内部仓库但如果是小规模部署直接拷贝offline_m2目录更省事。3. 模型推理服务的内网部署从 API 调用到本地推理的切换3.1 内网环境下模型选型的核心考量公网环境用 GPT-4 或 Claude 的 API效果确实好但内网环境下这条路基本走不通。你需要考虑的是在内网服务器有限的算力条件下选哪个开源模型能在效果和性能之间取得平衡。我的经验是分场景选型场景推荐模型规模量化方式最低显存要求适用任务轻量工具调用7BINT46GB意图识别、参数提取中等复杂度推理14BINT410GB多步推理、工具编排高质量生成32BINT420GB报告生成、代码生成极致效果72BINT440GB复杂 Agent 决策如果内网服务器没有 GPU只能用 CPU 推理那 7B 模型 INT4 量化后大概需要 8GB 内存推理速度在可接受范围内每秒几个 token。但说实话CPU 推理做 Agent 的体验很差因为 Agent 往往需要多轮调用每轮都等几秒累积起来用户等待时间太长。3.2 用 vLLM 或 Ollama 搭建内网推理服务如果内网有 GPU我强烈推荐用 vLLM 部署推理服务它支持 OpenAI 兼容的 API 格式这样你的 Agent 代码几乎不需要改动只需要把base_url指向内网地址# vLLM 启动命令示例 python -m vllm.entrypoints.openai.api_server \ --model /path/to/Qwen2.5-14B-Instruct-AWQ \ --served-model-name qwen-14b \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --quantization awq启动后Agent 代码中这样配置from openai import OpenAI client OpenAI( base_urlhttp://internal-gpu-server:8000/v1, api_keynot-needed # 内网环境通常不需要鉴权 ) response client.chat.completions.create( modelqwen-14b, messages[{role: user, content: 帮我查询今天的服务器状态}], temperature0.1 )如果没有 GPU用 Ollama 更简单# 内网安装 Ollama需要提前下载好二进制包 ollama serve ollama pull qwen2.5:7b-instruct-q4_K_MOllama 同样提供 OpenAI 兼容接口默认监听11434端口。3.3 模型加载失败的常见原因排查在内网部署模型时最常见的失败原因有这么几个原因一模型文件不完整。从公网拷贝模型文件时如果用了scp或rsync但中途中断可能导致文件损坏。建议拷贝完成后做一次校验# 对比公网和内网的模型文件哈希 sha256sum /path/to/model/*.safetensors model_hashes.txt # 在内网执行同样的命令对比结果原因二CUDA 版本不匹配。vLLM 对 CUDA 版本有要求如果内网机器的 CUDA 版本太低vLLM 会直接报错。用nvidia-smi查看驱动支持的 CUDA 版本然后选择对应版本的 vLLM wheel 包。原因三显存不足。即使模型量化后理论显存够用实际加载时因为 KV Cache 和中间激活值可能还是会 OOM。这时候需要调整--gpu-memory-utilization参数或者减小--max-model-len。4. MCP 协议在内网 Agent 工具链中的落地实践4.1 MCP 到底解决了什么问题MCPModel Context Protocol本质上是一套标准化的工具调用协议。在没有 MCP 之前每个 Agent 框架都有自己的工具定义方式——LangChain 用tool装饰器Spring AI 用Tool注解各家互不兼容。MCP 的出现让工具的定义和调用有了统一标准一个 MCP Server 可以被任何支持 MCP 的 Agent 客户端调用。在内网环境中MCP 的价值更加明显你可以把内网的各种服务数据库查询、文件检索、内部 API 调用封装成标准化的 MCP Server然后让不同的 Agent 应用复用这些工具而不需要每个应用都重新写一遍工具集成代码。4.2 内网 MCP Server 的开发与部署一个典型的 MCP Server 用 Python 实现大概长这样from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app Server(internal-tools) app.list_tools() async def list_tools(): return [ Tool( namequery_database, description查询内网数据库, inputSchema{ type: object, properties: { sql: {type: string, description: SQL查询语句} }, required: [sql] } ), Tool( namesearch_docs, description搜索内部文档库, inputSchema{ type: object, properties: { keyword: {type: string, description: 搜索关键词} }, required: [keyword] } ) ] app.call_tool() async def call_tool(name: str, arguments: dict): if name query_database: result execute_sql(arguments[sql]) return [TextContent(typetext, textstr(result))] elif name search_docs: docs search_internal_docs(arguments[keyword]) return [TextContent(typetext, textdocs)] async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ __main__: import asyncio asyncio.run(main())这个 Server 通过 stdio 和 Agent 客户端通信不需要网络端口非常适合内网环境。部署时只需要把 Python 脚本和依赖打包好在内网机器上运行即可。4.3 MCP 工具调用的超时与重试策略内网环境中MCP Server 调用的后端服务比如数据库、内部 API可能响应较慢如果不设置合理的超时和重试Agent 很容易卡死。我的经验配置是import asyncio from tenacity import retry, stop_after_attempt, wait_exponential retry( stopstop_after_attempt(3), waitwait_exponential(multiplier1, min1, max10) ) async def call_mcp_tool_with_retry(tool_name, arguments, timeout30): try: result await asyncio.wait_for( mcp_client.call_tool(tool_name, arguments), timeouttimeout ) return result except asyncio.TimeoutError: raise Exception(f工具 {tool_name} 调用超时)这里的关键是超时时间要根据工具的实际响应时间来设置。数据库查询可能 5 秒够了但文件检索可能需要 30 秒。不要用一个统一的超时值而是针对每个工具单独配置。5. 并发场景下的 Agent 工程挑战从单用户到多用户5.1 Agent 并发和普通 Web 并发有什么不同普通 Web 服务的并发模型很成熟请求进来查数据库返回结果每个请求独立处理。但 Agent 的并发要复杂得多因为一个 Agent 请求可能包含多轮模型调用和多次工具调用整个链路可能持续几十秒甚至几分钟。这意味着你不能简单地用线程池或协程池来处理并发因为每个 Agent 会话是有状态的——它需要维护对话历史、工具调用上下文、中间结果等。如果多个用户共享一个 Agent 实例状态会串。我的做法是每个用户会话分配独立的 Agent 实例但共享底层的模型推理服务和工具连接池。这样既保证了会话隔离又避免了资源浪费。5.2 用消息队列做请求缓冲当并发请求超过模型推理服务的处理能力时需要一个缓冲机制。我用 Redis 做简单的请求队列import redis import json import uuid r redis.Redis(hostinternal-redis, port6379) def submit_agent_task(user_id, query): task_id str(uuid.uuid4()) task { task_id: task_id, user_id: user_id, query: query, status: pending } r.lpush(agent_task_queue, json.dumps(task)) r.hset(ftask:{task_id}, mapping{status: pending, result: }) return task_id def get_task_result(task_id): result r.hgetall(ftask:{task_id}) return result后台 Worker 从队列中取出任务调用 Agent 处理处理完成后把结果写回 Redis。前端通过轮询或 WebSocket 获取结果。5.3 模型推理服务的并发瓶颈与优化vLLM 本身支持并发推理但并发数不是越高越好。我实测下来单张 A100 80G 跑 14B 模型并发数设置在 8-16 之间比较合适。超过这个数虽然吞吐量还能提升但单个请求的延迟会显著增加用户体验反而下降。vLLM 的关键参数--max-num-seqs 16 # 最大并发序列数 --max-num-batched-tokens 4096 # 单批次最大 token 数 --enable-chunked-prefill # 启用分块预填充降低长请求的延迟如果并发需求更高可以考虑多实例部署 负载均衡。在内网环境中用 Nginx 做简单的轮询负载均衡就够了upstream vllm_backend { server gpu-server-1:8000; server gpu-server-2:8000; } server { listen 8080; location /v1/ { proxy_pass http://vllm_backend; proxy_read_timeout 300s; } }6. 内网 Agent 工程实战中的那些坑6.1 时间同步问题导致工具调用失败内网环境如果没有配置 NTP 时间同步不同服务器之间的时间可能相差几分钟甚至几小时。这在 Agent 场景下会导致严重问题工具调用返回的时间戳和 Agent 记录的时间不一致导致缓存失效、日志混乱、甚至逻辑判断错误。我踩过一次坑Agent 调用一个内部 API 获取数据API 返回的timestamp字段比 Agent 本地时间快了 5 分钟Agent 判断“这是未来数据”直接丢弃了。排查了半天才发现是时间不同步。解决方案很简单在内网搭建一个 NTP 服务所有机器定期同步# 在内网一台机器上启动 NTP 服务 yum install ntp systemctl start ntpd systemctl enable ntpd # 其他机器配置指向这台 NTP 服务器 echo server internal-ntp iburst /etc/ntp.conf systemctl restart ntpd6.2 日志和监控的离线方案公网环境可以用各种云监控服务内网环境只能自建。我的最小化方案是Prometheus Grafana Loki全部离线部署。Agent 的关键监控指标包括指标名称含义告警阈值agent_request_duration_seconds单次请求耗时P99 60sagent_tool_call_errors_total工具调用错误数5分钟内 10model_inference_latency_seconds模型推理延迟P95 10sagent_queue_length请求队列长度 50这些指标通过 Agent 代码中的埋点上报到 Prometheus Pushgateway再由 Prometheus 拉取。6.3 内网 Agent 的安全边界控制内网环境虽然相对封闭但 Agent 的工具调用权限仍然需要严格控制。我的做法是第一工具白名单。Agent 只能调用明确注册的工具不能动态执行任意代码或 SQL。对于数据库查询工具只允许 SELECT 语句禁止 INSERT/UPDATE/DELETE。第二参数校验。所有工具调用的参数都要做类型和范围校验防止注入攻击。比如 SQL 查询工具要对传入的 SQL 做语法解析确认只包含 SELECT 子句。第三调用频率限制。单个用户每分钟最多调用 20 次工具防止恶意刷接口。from ratelimit import limits, sleep_and_retry sleep_and_retry limits(calls20, period60) def call_tool_with_rate_limit(tool_name, arguments): return mcp_client.call_tool(tool_name, arguments)7. 从 Demo 到生产内网 Agent 工程的个人体会做了这么多次内网 Agent 部署我最大的体会是内网环境的限制反而倒逼出了更扎实的工程实践。在公网环境里你可以随意调用外部 API依赖随便装出了问题重启一下就行。但在内网里每一个依赖、每一个配置、每一个网络连接都需要提前规划这让你不得不把系统设计得更健壮。另一个深刻的感受是模型选型不要追求“最好”而要追求“最合适”。内网环境算力有限用一个 72B 模型跑出高质量结果但每次等 30 秒不如用一个 14B 模型跑出中等质量结果但 3 秒返回。Agent 的价值在于自动化和效率提升如果响应太慢用户宁愿手动操作。最后分享一个实用技巧在内网部署前一定要在公网环境做一次完整的“断网演练”。把公网机器的网络断开看看你的 Agent 还能不能跑起来。这个演练能帮你发现 90% 的离线依赖问题比到了内网再排查要高效得多。提示内网部署完成后建议保留一份完整的依赖清单和配置文档。下次迁移或扩容时这份文档能帮你节省大量时间。