1. 隔离内网下 AI Agent 工程实战:从零搭建到落地的完整复盘
隔离内网这四个字,做过企业级交付的人看到都会头皮一紧。没有公网、没有外部镜像源、没有在线大模型接口,连 pip install 都得先想想包从哪来。偏偏这两年 AI Agent 又成了刚需,业务方不管你的网络环境,只看你能不能把活干出来。我在过去大半年里,前后在三个完全物理隔离的内网环境里落地过 AI Agent 工程,踩的坑从依赖打包一直延伸到模型推理服务的并发瓶颈,今天把整套思路和实操细节完整拆一遍。
先说清楚这篇文章适合谁看。如果你所在的环境是金融、能源、制造、政务这类内网隔离场景,需要把 AI Agent 真正跑起来而不是停留在 demo 阶段,那这篇内容基本可以当施工手册用。如果你只是在自己笔记本上玩 Agent,网络畅通无阻,那这篇里关于离线依赖、内网模型服务、MCP 协议适配的部分可能对你参考价值有限,但工程化思路依然值得一看。核心关键词就几个:AI Agent、MCP、Skills、内网、工程实战,全文围绕这五个词展开,不跑题。
我先把结论性的判断放在前面:隔离内网下做 AI Agent,难点从来不是 Agent 本身,而是依赖供应链、模型推理服务、协议适配层这三座大山。Agent 框架的代码逻辑反而是最简单的部分。很多人一上来就研究 LangChain、LangGraph 怎么编排,结果卡在第一步——内网装不上包。这个顺序搞反了,会浪费大量时间。
2. 内网 AI Agent 的整体架构设计与选型逻辑
2.1 为什么不能照搬公网那套方案
公网环境下搭 AI Agent,大家习惯的路径是:pip 装框架、调云端大模型 API、用现成的 MCP 服务、Skills 直接从市场拉。这套流程在内网里每一步都会断。pip 装不上是因为没有 PyPI 源,云端 API 调不通是因为没有出口,MCP 服务拉不下来是因为没有外部网络,Skills 市场更是想都别想。
所以内网方案的核心思路必须转变:把所有外部依赖变成内部资产。具体来说就是三件事——依赖包全部离线化、模型推理全部本地化、协议服务全部自建化。这三件事听起来简单,但每一件都有大量细节。
我见过太多团队在内网里硬套公网架构,结果搭了两周还在解决依赖冲突。正确的做法是先盘点内网已有的资源:有没有 GPU 服务器、有没有内部 PyPI 镜像、有没有容器仓库、有没有内部文件服务。这些资源决定了你的架构上限。
2.2 三层架构的划分方式
我最终稳定下来的架构是三层:基础设施层、能力层、编排层。
基础设施层负责提供算力和依赖,包括 GPU 推理服务器、内部包管理服务、容器镜像仓库、对象存储。这一层是地基,地基不稳后面全白搭。能力层是 AI Agent 的“手脚”,包括本地大模型推理服务、MCP 协议服务、Skills 执行引擎、工具调用网关。编排层是“大脑”,负责 Agent 的决策逻辑、任务规划、上下文管理。
这么分层的好处是每层可以独立演进。比如模型换了,只动能力层的推理服务;Agent 逻辑改了,只动编排层。内网环境下变更成本高,分层能大幅降低维护难度。
2.3 模型选型的现实考量
内网里选模型,不能只看 benchmark 分数。我总结下来要看四个维度:显存占用、推理速度、中文能力、工具调用稳定性。
显存占用直接决定你能跑多大的模型。一张 24G 显存的卡,7B 模型量化后能跑,14B 勉强,32B 就得量化到 4bit 甚至更低。推理速度决定用户体验,内网里没有云端那种弹性扩容,速度不够就是不够。中文能力对国内业务场景是硬指标,很多英文强的模型中文一塌糊涂。工具调用稳定性是 Agent 场景特有的,模型能不能稳定输出结构化的工具调用请求,直接决定 Agent 能不能用。
我实测下来,在内网 24G 显存的环境里,7B 到 14B 的量化模型是比较务实的选择。再大就得考虑多卡或者更激进的量化,但量化太狠会显著影响工具调用的准确率,这个 trade-off 要自己权衡。
2.4 MCP 协议在内网里的定位
MCP 这两年热度很高,但很多人没搞清楚它在内网里的实际价值。MCP 本质是一套标准化的工具调用协议,让 Agent 能用统一的方式访问各种外部能力。在内网里,MCP 的价值在于解耦——Agent 不需要知道每个工具的具体实现,只需要按 MCP 协议调用就行。
但内网里用 MCP 有个前提:你得自己实现 MCP Server。公网上那些现成的 MCP 服务在内网里一个都用不了。所以实际工作量是:定义工具接口、实现 MCP Server、部署到内网、让 Agent 通过 MCP Client 连接。这套流程走下来,比直接硬编码工具调用要重,但长期维护性更好。
3. 离线依赖供应链的搭建与避坑
3.1 依赖打包的正确姿势
内网装包这件事,我踩过的坑能写一本书。最开始的笨办法是在公网机器上 pip download 一堆 whl 文件,拷进内网 pip install。这个方法在依赖简单的时候能用,一旦遇到有 C 扩展的包就崩——因为 pip download 默认只下当前平台的 whl,内网机器架构不一样就装不上。
正确的做法是用pip download加上平台参数,把所有可能的平台都下下来。命令大概是这样:
pip download -r requirements.txt \ --platform manylinux2014_x86_64 \ --python-version 310 \ --only-binary=:all: \ -d ./offline_packages但这里有个坑,--platform和--only-binary一起用的时候,如果某个包没有对应平台的 whl,会直接报错。这时候要么找替代包,要么就得下源码包在内网编译。源码编译在内网里是噩梦,因为编译依赖往往又是一堆包。
我的经验是:优先选纯 Python 实现的包,避开有 C 扩展的。比如向量库,能用纯 Python 的就别用需要编译的。实在避不开的,提前在公网把编译好的 whl 准备好。
3.2 内部 PyPI 镜像的搭建
依赖包多了以后,靠拷贝 whl 文件管理会失控。这时候需要搭一个内部 PyPI 镜像。可选方案有 devpi、pypiserver、Nexus 等。我推荐 devpi,因为它支持缓存代理和本地包上传,功能比较全。
搭好之后,内网机器的 pip 配置指向内部镜像,装包就跟公网一样顺畅。但要注意,devpi 本身也需要在内网部署,它的依赖也得提前准备好。这是个鸡生蛋的问题,第一次部署会麻烦一点,之后就一劳永逸。
提示:内部镜像一定要做好权限控制,别让所有人都能往上传包。我见过有人误传了一个同名但版本不同的包,导致整个团队的构建全部失败,排查了大半天。
3.3 容器镜像的离线搬运
如果 Agent 用容器部署,镜像搬运是另一个大坑。公网拉镜像、save 成 tar、拷进内网、load 进去,这套流程本身没问题,但镜像层数多、体积大的时候,tar 文件能到几个 G,拷贝和加载都很慢。
优化思路是减小镜像体积。用多阶段构建,把编译依赖和运行时依赖分开;用 alpine 或者 slim 基础镜像;清理不必要的缓存文件。我做过一个对比,优化前后镜像从 3.2G 降到 800M,加载时间从几分钟降到几十秒。
另外,内网如果有 Harbor 之类的镜像仓库,把镜像 push 进去,其他机器直接 pull,比每台机器都 load tar 文件高效得多。
3.4 依赖版本锁定的重要性
内网环境最怕的就是“在我机器上能跑”。因为内网装包麻烦,大家往往装完就不动了,结果不同机器上的依赖版本不一致,同一个 Agent 在不同机器上行为不同。
解决办法是严格锁定版本。用 pip freeze 生成精确的 requirements.txt,所有版本号都写死。更进一步,可以用 pip-tools 或者 poetry 管理依赖,生成 lock 文件。内网部署时严格按照 lock 文件安装,确保环境一致。
这个习惯在公网可能觉得无所谓,内网里是刚需。我吃过亏,一个 Agent 在测试环境好好的,上到生产环境就报错,最后发现是某个依赖的小版本差异导致的。
4. 本地模型推理服务的部署与调优
4.1 推理框架的选择
内网部署本地模型,推理框架的选择直接影响性能和稳定性。主流的有 vLLM、TGI、Ollama、llama.cpp 等。选哪个要看场景。
vLLM 吞吐量高,适合并发请求多的场景,但显存占用相对大。TGI 是 HuggingFace 出的,生态好,部署简单。Ollama 最省心,一条命令就能跑,但性能和并发能力一般。llama.cpp 最省资源,CPU 也能跑,但速度慢。
我的建议是:有 GPU 且并发要求高,用 vLLM;快速验证和单机使用,用 Ollama;资源极度受限,用 llama.cpp。内网环境往往资源紧张,Ollama 的性价比其实很高。
4.2 模型量化的取舍
量化是内网部署绕不开的话题。FP16 精度最高但显存占用大,INT8 折中,INT4 最省显存但精度损失明显。
我做过一组对比测试,同一个 7B 模型,FP16 需要约 14G 显存,INT8 约 7G,INT4 约 4G。推理速度上,INT4 最快,FP16 最慢。但工具调用的准确率,FP16 和 INT8 差距不大,INT4 明显下降。
对于 Agent 场景,工具调用的准确率是生命线。所以我一般推荐INT8 量化,在显存和精度之间取得平衡。如果显存实在不够,INT4 也能用,但要接受一定的准确率损失,并且在 prompt 设计上做补偿。
4.3 并发能力的实测与瓶颈
“AI Agent 怎么扛并发”是热词里高频出现的问题。内网里并发瓶颈往往不在模型本身,而在推理服务的配置。
vLLM 有个关键参数--max-num-seqs,控制同时处理的请求数。设太小并发上不去,设太大显存爆掉。这个值要根据显存和模型大小算。经验公式是:可用显存除以单个请求的 KV Cache 占用。7B 模型 INT8 量化,24G 显存,--max-num-seqs设 16 到 32 比较稳。
另一个瓶颈是 Agent 本身的编排逻辑。如果 Agent 串行调用工具,那并发再高也没用。要把能并行的工具调用并行化,这需要在编排层做设计。
我实测过一个场景,单张 24G 卡跑 7B INT8 模型,--max-num-seqs设 24,QPS 能到 8 到 10 左右。再往上加并发,延迟会明显上升。这个数据供参考,具体要看模型和硬件。
4.4 推理服务的健康检查与降级
内网环境没有云端的自动运维,推理服务挂了得自己发现。所以健康检查机制必须做。
我的做法是:推理服务暴露一个/health接口,Agent 调用前先检查。如果服务不可用,走降级逻辑——要么返回缓存结果,要么提示用户稍后重试,要么切换到备用模型。
备用模型这个策略在内网里很实用。主模型用大的,备用模型用小的,主模型挂了自动切到小的,虽然效果差一点但至少能用。这个切换逻辑要在 Agent 编排层实现。
5. MCP 协议与 Skills 体系的内网落地
5.1 MCP Server 的自建流程
内网里用 MCP,第一步是自建 MCP Server。MCP 协议本身不复杂,核心就是定义工具的描述和调用接口。一个最小的 MCP Server 大概长这样:
from mcp.server import Server from mcp.types import Tool, TextContent server = Server("internal-tools") @server.list_tools() async def list_tools(): return [ Tool( name="query_database", description="查询内部数据库", inputSchema={ "type": "object", "properties": { "sql": {"type": "string", "description": "SQL 查询语句"} }, "required": ["sql"] } ) ] @server.call_tool() async def call_tool(name: str, arguments: dict): if name == "query_database": result = execute_sql(arguments["sql"]) return [TextContent(type="text", text=str(result))]这个 Server 部署在内网,Agent 通过 MCP Client 连接。关键点是工具的描述要写清楚,模型靠这个描述来决定调不调用、怎么调用。描述写得含糊,模型就会乱调。
5.2 Skills 的设计原则
Skills 这个概念这两年很火,但内网里落地 Skills 要务实。Skills 本质是封装好的能力单元,Agent 按需加载。设计 Skills 有几个原则。
粒度要适中。太细了 Agent 要加载一堆,太粗了复用性差。我的经验是按业务动作划分,比如“查询订单”“生成报表”“发送通知”各是一个 Skill。
接口要稳定。Skills 一旦被 Agent 依赖,改动成本很高。所以接口设计要预留扩展空间,参数用对象而不是散列,方便后续加字段。
要有降级方案。内网里某个 Skill 依赖的服务挂了,不能让整个 Agent 崩掉。每个 Skill 要有超时和降级逻辑。
5.3 Skills 的测试方法
Skills 测试在内网里容易被忽视,但很重要。我一般分三层测:单元测试测 Skill 本身的逻辑,集成测试测 Skill 和依赖服务的交互,端到端测试测 Agent 调用 Skill 的完整链路。
端到端测试最容易被跳过,但恰恰最能发现问题。我遇到过一个 case,Skill 单独测都正常,但 Agent 调用时因为上下文太长导致模型输出格式错乱,Skill 解析失败。这种问题只有端到端测才能发现。
测试数据要覆盖边界情况:空输入、超长输入、特殊字符、并发调用。内网环境数据敏感,测试数据要脱敏,但结构要真实。
5.4 MCP 与 Skills 的协同
MCP 和 Skills 不是二选一,而是协同关系。MCP 解决的是“怎么调用”的问题,Skills 解决的是“调用什么”的问题。一个 Skill 可以封装成 MCP Server 暴露出去,Agent 通过 MCP 协议调用。
这种分层的好处是,Skill 的实现可以独立演进,只要 MCP 接口不变,Agent 就不用改。内网里变更成本高,这种解耦能省很多事。
6. 常见问题与排查技巧实录
6.1 依赖相关问题的排查
内网依赖问题排查,核心是定位是哪个包出的问题。pip 报错信息往往很长,关键信息在前面几行。我一般先看报错类型:是找不到包,还是版本冲突,还是编译失败。
找不到包,检查内部镜像里有没有这个包,版本对不对。版本冲突,用pip check看冲突详情,然后手动调整版本。编译失败,看缺什么系统库,在内网里找对应的 rpm 或 deb 包装上。
有个技巧是用虚拟环境隔离。不同 Agent 用不同的 venv,避免依赖互相污染。内网里重建环境成本高,隔离能减少很多麻烦。
6.2 模型推理异常的排查
模型推理异常,表现可能是输出乱码、格式错误、速度极慢、服务崩溃。排查思路是从输入到输出逐段检查。
先看输入,prompt 是不是太长,是不是有特殊字符。再看模型加载,显存够不够,量化配置对不对。然后看推理过程,日志里有没有报错。最后看输出,格式符不符合预期。
速度慢的问题,先看是不是首次加载慢(正常),再看是不是并发太高(降--max-num-seqs),最后看是不是显存不足导致频繁 swap(减模型大小或量化)。
6.3 MCP 连接问题的排查
MCP 连接问题,常见的是连不上、超时、返回格式错误。连不上先检查网络和端口,内网防火墙规则要确认。超时看服务端处理时间,可能是工具执行太慢。返回格式错误,看 MCP Server 的实现是不是符合协议。
我遇到过一个坑,MCP Server 返回的 JSON 里有 NaN 值,Agent 解析直接崩。这种问题要在 Server 端做数据清洗,确保返回的都是合法 JSON。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决思路 |
|---|---|---|---|
| pip 装包失败 | 内部镜像无此包 | 检查镜像包列表 | 上传缺失的包 |
| 依赖版本冲突 | 版本锁定不严 | pip check | 严格锁定版本 |
| 模型输出乱码 | 量化过度或 prompt 问题 | 检查量化配置和 prompt | 提高量化精度或优化 prompt |
| 推理速度慢 | 并发过高或显存不足 | 看 GPU 利用率和 swap | 降并发或减模型 |
| MCP 连不上 | 网络或端口问题 | 检查防火墙和端口 | 开放端口或换端口 |
| MCP 返回格式错 | Server 实现问题 | 看 Server 日志 | 修复 Server 数据清洗 |
| Agent 工具调用失败 | 模型工具调用能力弱 | 看模型输出格式 | 换模型或优化 prompt |
| 并发上不去 | 编排层串行 | 看 Agent 调用链路 | 并行化工具调用 |
6.5 独家避坑经验
最后分享几个我踩过的坑,都是文档里不会写的。
坑一:内网时间不同步。内网机器时间如果不同步,会导致 token 过期、日志时间错乱、缓存失效。部署前一定要配好 NTP,哪怕内网没有外部 NTP,也要有个内部时间源。
坑二:磁盘空间不足。模型文件、日志、缓存都很占空间。内网扩容麻烦,部署前要算好磁盘需求,留足余量。我见过模型跑到一半磁盘满了,服务直接挂掉。
坑三:GPU 驱动版本不匹配。推理框架对 GPU 驱动版本有要求,内网升级驱动很麻烦。部署前确认驱动版本,选兼容的推理框架版本。
坑四:日志级别设太高。内网排查问题全靠日志,日志级别设太高会丢关键信息。生产环境用 INFO,排查问题时临时调 DEBUG。
坑五:没有监控。内网里服务挂了没人知道。至少要有个简单的监控,看服务存活、GPU 利用率、请求延迟。Prometheus 加 Grafana 是标配,内网部署也不复杂。
7. 工程化落地的几点个人体会
内网 AI Agent 工程化,说到底是个平衡的艺术。效果和资源要平衡,开发效率和维护成本要平衡,功能完整和稳定可靠要平衡。公网环境下可以堆资源解决问题,内网里资源有限,必须精打细算。
我个人的经验是,先把最小可用链路跑通,再逐步加能力。不要一上来就追求大而全,内网里每加一个组件都是成本。先让 Agent 能调用一两个核心工具,跑通端到端,然后再扩展 Skills 和 MCP 服务。
另外,文档和自动化脚本要跟上。内网环境重建成本高,好的文档和脚本能让重建变得简单。我每个项目都会写一份部署文档和一套自动化脚本,虽然前期费时间,但后期省的事更多。
最后说个实际的,内网里做 AI Agent,别追求最新最炫的技术。稳定压倒一切。选成熟的框架、成熟的模型、成熟的方案,把精力放在业务适配和工程优化上。新技术在内网里试错成本太高,等公网验证成熟了再引入也不迟。