
1. 为什么要在隔离内网里折腾 AI Agent先把场景说清楚。所谓隔离内网就是那种物理上跟公网断开、或者只允许极少数白名单流量出入的办公网络常见于金融、制造、科研院所、涉密单位。这类环境有个共同特点你能用的东西基本都得自己搬进去。外网那些一键部署、在线调用 API、云端托管的路子在这里统统走不通。我最近刚在一个完全离线的内网环境里从零把一套 AI Agent 工程跑了起来。整个过程踩的坑比我想象的多得多但也正因为踩过才值得写下来。这篇文章面向的是那些手上有内网服务器、有算力、但被网络隔离卡住、不知道怎么把 Agent 这套东西落地的工程师。不管你是刚听说 AI Agent 想上手还是已经在公网玩过 Dify、LangChain 想往内网迁移这篇都能给你一条能直接抄的路径。核心关键词先摆出来AI Agent、MCP、Skills、内网、工程实战。这五个词基本概括了整件事的全貌——你要在内网里搭一个能调用工具、能加载技能、能自主完成任务的智能体系统。听起来玄乎拆开看其实就是三件事模型怎么跑起来、工具怎么接进来、技能怎么管起来。先说清楚一个前提内网环境里模型推理、工具调用、技能加载这三层必须全部本地化。任何一层依赖外部服务整个链路就断了。这也是为什么很多人公网玩得溜一进内网就懵——因为公网方案默认你随时能联网。提示本文所有方案均基于完全离线的内网环境设计不涉及任何形式的网络穿透或外部代理。所有组件均需提前准备好离线安装包通过物理介质导入。2. 整体架构设计与选型思路2.1 三层架构模型层、编排层、工具层内网 AI Agent 的架构我最终收敛成了三层。这个分层不是拍脑袋定的是被内网环境逼出来的。模型层负责推理也就是大模型本身。内网里你不可能调云端 API所以必须本地部署。可选方案有 Ollama、vLLM、llama.cpp 这几种。我最后选了 vLLM原因是它对并发请求的处理明显更好Agent 场景下工具调用频繁吞吐量是硬指标。模型本身选的是 Qwen 系列的开源权重中文能力强工具调用格式支持也成熟。编排层是 Agent 的大脑负责决定下一步干什么。这一层我用了 LangChain 加自研的一层轻量调度。为什么不用现成的 Agent 框架一把梭因为内网环境里很多框架的默认行为会去联网拉取配置或者校验版本跑着跑着就卡住了。自己包一层把所有外部依赖掐断反而更稳。工具层就是 MCP 和 Skills 发挥作用的地方。MCP 负责把外部能力数据库、文件系统、内部 API标准化成 Agent 能调用的工具Skills 负责把一套固定的操作流程封装成可复用的技能包。这两者配合Agent 才真正有了干活的能力。2.2 为什么是 MCP 而不是自己写函数调用很多人会问工具调用我自己写个函数注册进去不就行了为什么要用 MCP这个问题我在项目初期也纠结过。自己写函数调用短期看确实简单一个字典映射就搞定了。但问题在于扩展性和复用性。当你接了十几个工具之后每个工具的入参格式、返回格式、错误处理都不一样维护成本会指数级上升。MCP 的价值就在于它定义了一套标准协议工具的描述、参数、调用方式全部统一Agent 侧只需要一套解析逻辑就能对接所有工具。更关键的是MCP 让工具和 Agent 解耦了。工具可以独立开发、独立测试、独立部署Agent 只管调用。在内网这种多人协作的环境里这个解耦带来的收益非常大——做数据库的同事只管写数据库的 MCP Server做文件系统的只管写文件系统的互不干扰。2.3 Skills 的定位把经验固化成可复用资产Skills 这个概念容易被误解。它不是工具工具是能做什么Skills 是怎么做。举个例子工具层面你有一个查询数据库的能力但先查订单表、再关联用户表、最后按地区聚合这一整套流程就是一个 Skill。在内网工程实战里Skills 的价值在于把老员工的经验沉淀下来。以前这些流程都藏在人脑子里新人来了得手把手教。现在把它写成 SkillAgent 直接就能按这个流程干活而且每次执行都一致不会因为人的状态波动。我自己的做法是每个 Skill 用一个独立的目录管理里面包含流程描述、依赖的工具列表、参数模板、以及几个典型的输入输出样例。这样 Agent 加载 Skill 的时候能通过样例快速理解这个技能该怎么用。3. 内网环境的前置准备与依赖处理3.1 离线包的准备与导入内网部署最烦的就是依赖。公网一句pip install搞定的事内网得折腾半天。我的做法是在外网准备一台同架构、同系统的机器把所有依赖装好然后整体打包。具体操作上Python 环境用pip download把依赖的 wheel 包全部下下来注意要指定平台和 Python 版本否则下下来的包在内网装不上。命令大概是这样pip download -r requirements.txt -d ./offline_packages --platform manylinux2014_x86_64 --python-version 310 --only-binary:all:模型权重文件动辄几十个 G用移动硬盘拷贝是最实在的办法。拷进去之后校验一下哈希值我吃过一次亏硬盘中途出问题导致模型文件损坏排查了大半天才发现是文件本身的问题。注意所有离线包导入前务必做完整性校验模型文件尤其重要。一个字节的损坏可能导致推理结果完全错乱而且这种错误很难定位。3.2 内网 DNS 与服务发现内网里没有公网 DNS服务之间怎么找到对方是个问题。我的方案是在内网搭一个轻量的 DNS 服务或者更简单点直接在每台机器的 hosts 文件里写死映射。如果 Agent 和工具服务部署在不同机器上建议用固定的内网 IP 加端口的方式通信别用主机名。主机名解析在内网环境里经常出幺蛾子尤其是跨网段的时候。我现在的做法是维护一张服务清单表所有服务的 IP 和端口都记录在案配置里直接写 IP。服务内网 IP端口说明模型推理服务192.168.10.218000vLLM 提供 OpenAI 兼容接口Agent 编排服务192.168.10.225000自研调度层MCP 工具网关192.168.10.239000统一工具入口向量数据库192.168.10.246333用于 Skill 检索3.3 权限与安全边界内网不等于没有安全要求恰恰相反内网的安全边界往往更严格。Agent 能调用的工具必须做权限控制不能让它随便访问任何资源。我的做法是给每个 MCP Server 配置一个能力清单明确它能访问哪些路径、哪些表、哪些接口。Agent 侧再叠一层权限校验双重保险。这样即使 Agent 被诱导去调用不该调的工具也会在工具层被拦下来。4. MCP 工具层的落地实操4.1 MCP Server 的最小实现MCP 的核心是协议实现一个 MCP Server 其实不复杂。我用 Python 写了一个最简版本核心就是暴露几个标准方法列出可用工具、描述工具参数、执行工具调用。from mcp.server import Server from mcp.types import Tool, TextContent app Server(internal-tools) app.list_tools() async def list_tools(): return [ Tool( namequery_database, description查询内网业务数据库支持标准 SQL, inputSchema{ type: object, properties: { sql: {type: string, description: 要执行的 SQL 语句} }, required: [sql] } ) ] 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))]这段代码看着简单但有几个细节值得说。description字段非常关键Agent 就是靠这个描述来判断什么时候该调用这个工具。描述写得含糊Agent 就会乱调或者不调。我一般会把使用场景、限制条件都写进去比如仅支持 SELECT 查询不支持写操作。4.2 工具描述怎么写才让 Agent 会用这是我在实战中花时间最多的地方。工具描述写得好不好直接决定 Agent 的调用准确率。我的经验是描述里必须包含三样东西这个工具干什么、什么时候用、有什么限制。举个例子同样是查询工具写成查询数据和写成根据用户 ID 查询订单历史适用于需要了解用户购买行为的场景单次最多返回 100 条效果天差地别。另外参数描述也要具体。别写sql: SQL 语句要写sql: 标准 SQL 查询语句表名必须是 orders、users、products 之一。把约束条件写清楚Agent 就不会瞎猜。4.3 多工具编排的坑当工具数量超过十个之后Agent 的选择困难症就来了。它经常在几个相似的工具之间反复横跳或者该用 A 工具的时候用了 B。我的解决办法是给工具分组。把功能相近的工具归到一个命名空间下Agent 先选组再选具体工具。这样选择空间从十几个降到三四个准确率明显提升。另一个坑是工具调用的超时处理。内网环境里某些工具比如查大表可能跑很久如果不设超时Agent 会一直等整个流程就卡死了。我给每个工具都配了独立的超时时间超时后返回一个明确的错误信息Agent 收到错误会自己决定重试还是换方案。5. Skills 技能体系的设计与实现5.1 Skill 的目录结构规范Skills 要能复用目录结构必须规范。我定的规范是这样的skills/ order_analysis/ skill.yaml # 技能元信息 prompt.md # 技能提示词 tools.json # 依赖的工具列表 examples/ # 输入输出样例 case1_input.json case1_output.jsonskill.yaml里记录技能名称、描述、版本、作者。prompt.md是这个技能的核心写清楚执行步骤。tools.json声明这个技能需要哪些工具Agent 加载技能时会自动检查这些工具是否可用。5.2 用提示词固化操作流程Skill 的本质是一段结构化的提示词。我写 Skill 提示词的时候遵循一个固定模板先说明目标再列步骤最后给约束。比如一个月度销售报告生成的 Skill提示词大概是这样目标生成指定月份的销售报告 步骤 1. 调用 query_database 工具查询该月所有订单 2. 按地区分组统计销售额 3. 找出销售额前三的地区 4. 调用 generate_chart 工具生成柱状图 5. 汇总成 Markdown 格式报告 约束 - 金额单位统一为万元 - 如果某地区无数据标注为无销售 - 报告必须包含环比数据这种写法比自然语言描述靠谱得多Agent 执行起来步骤清晰不容易漏步骤。5.3 Skill 的版本管理与灰度Skills 是会迭代的。今天写的流程明天业务变了就得改。所以版本管理很重要。我的做法是每个 Skill 目录下保留历史版本用版本号区分。Agent 加载时默认用最新版但可以通过配置指定用某个特定版本。这样新版本出问题的时候能快速回滚。灰度发布这块我是在 Agent 侧做的。新版本 Skill 先只对部分请求生效观察一段时间没问题再全量。内网环境里没有现成的灰度工具都是自己写逻辑控制。6. 模型层的内网部署要点6.1 模型选型能力与资源的平衡内网部署模型第一个要面对的就是资源约束。你不可能像公网那样随便调 70B 的模型得看手上有多少卡。我的经验是Agent 场景对模型的要求和纯对话不一样。Agent 更看重指令遵循能力和工具调用格式的准确性而不是知识广度。所以一个 14B 到 32B 的模型只要工具调用训练得好完全够用。我最后用的是 Qwen2.5-32B 的量化版本在两张卡上跑得挺稳。如果资源实在紧张7B 级别的模型也能用但工具调用的准确率会下降需要靠更严格的提示词和更多的校验来补。6.2 推理服务的参数调优vLLM 部署起来之后有几个参数必须调。max_model_len决定了上下文长度Agent 场景下上下文会很长工具描述、历史对话、技能提示词都占地方我设的是 8192。gpu_memory_utilization控制显存占用比例设太高容易 OOM设太低浪费资源我一般从 0.85 开始试。还有一个容易被忽略的参数是enable_prefix_caching。Agent 场景下系统提示词是固定的开启前缀缓存能显著降低重复计算吞吐量能提升不少。6.3 工具调用格式的适配不同模型的工具调用格式不一样。有的用特定的 token 标记有的用 JSON 格式。内网部署时必须确保模型的输出格式和 Agent 的解析逻辑对得上。我的做法是在 Agent 侧做一层格式适配不管模型输出什么格式都先归一化成标准结构再处理。这样换模型的时候只需要改适配层不用动上层逻辑。7. 常见问题与排查实录7.1 工具调用失败排查表现象可能原因排查方法解决Agent 不调用工具工具描述不清检查 description 字段补充使用场景和限制调用参数错误参数 schema 不明确查看 inputSchema细化参数类型和约束调用超时工具执行慢看工具侧日志加超时和异步处理返回结果解析失败格式不匹配对比返回和预期加格式适配层工具选择错误工具太多太像统计调用分布分组或合并工具7.2 模型输出不稳定的处理内网模型有时候会抽风同样的输入两次输出不一样。这在 Agent 场景下很要命因为工具调用需要确定性。我的处理办法是降低温度参数。Agent 场景下温度设 0 到 0.1 就够了不需要创造性。另外关键的工具调用步骤加校验如果模型输出的参数不符合 schema直接打回重试而不是硬着头皮执行。7.3 内网环境特有的坑内网有几个坑是公网遇不到的。一个是时间同步内网机器时间不一致会导致日志混乱、缓存失效。我现在的做法是内网搭一个 NTP 服务所有机器定期同步。另一个是磁盘空间。模型文件、日志、缓存加起来很占地方内网扩容又麻烦。我养成了定期清理的习惯日志按天切割超过七天的自动删。还有一个是依赖版本锁定。内网装包不方便所以一旦装好就别乱动。我把所有依赖的版本号都锁死在配置文件里避免有人手贱升级导致环境崩掉。8. 实操心得与经验沉淀8.1 先跑通最小闭环再扩展我见过太多人一上来就想搭个大而全的系统结果卡在某个环节动弹不得。我的建议是先跑通最小闭环一个模型、一个工具、一个技能能完成一个最简单的任务就行。这个最小闭环跑通之后你会对整个链路的瓶颈有清晰的认识。是模型推理慢还是工具调用不稳还是技能加载有问题一目了然。然后再针对性地扩展效率高得多。8.2 日志要打全但别打太多内网排查问题全靠日志。我的做法是每个环节都打日志但要分级。INFO 级别记录关键流程节点DEBUG 级别记录详细参数默认只开 INFO出问题的时候临时开 DEBUG。日志格式要统一带上时间戳、服务名、请求 ID。请求 ID 特别重要一个请求跨多个服务的时候靠它才能把链路串起来。8.3 技能库要持续维护Skills 不是写完就完事了得持续维护。业务变了技能就得跟着改。我现在的做法是每个月review一次技能库把没人用的技能归档把常用的技能优化。另外鼓励一线同事贡献技能。他们最清楚实际业务怎么跑写出来的技能最接地气。我搭了个简单的技能提交和审核流程大家提交我审核后合并进主库。8.4 性能优化的几个方向内网 Agent 的性能瓶颈通常在三个地方模型推理、工具调用、技能检索。模型推理这块量化、批处理、前缀缓存是三个最有效的手段。工具调用这块异步化和连接池能显著降低延迟。技能检索这块如果技能多了得用向量检索而不是关键词匹配否则找技能本身就慢。我实测下来优化前后整体响应时间能差三到五倍。所以别急着堆硬件先把这几个软件层面的优化做扎实。最后分享一个我踩过的坑内网环境里千万别在高峰期做模型热更新。有一次我图省事业务跑着的时候换了模型结果显存没释放干净新模型加载失败整个服务挂了半小时。后来我学乖了所有更新都安排在低峰期而且更新前先做好回滚预案。这个教训值不少钱希望你别再踩一遍。