
1. 为什么要在隔离内网里折腾 AI Agent先把场景说清楚。所谓隔离内网就是那种物理上跟公网断开、或者只允许极少数白名单流量进出的网络环境。银行核心机房、政务专网、军工单位的研发网、大型制造企业的工控网基本都是这个形态。这类环境里跑 AI Agent跟你在自己笔记本上连个 API 就能玩起来完全是两码事——没有外网、没有在线模型服务、没有现成的 SaaS 工具链连 pip install 都得先想办法把包搬进去。我在这种环境里前后落地过三个 Agent 项目踩的坑足够写一本小册子。最核心的体会是隔离内网做 AI Agent难点从来不在模型本身而在于怎么把一整套依赖链、工具链、审批链在断网条件下重新拼装起来并且让它稳定运行。这跟公网环境下的玩法是两套逻辑。这篇文章要聊的就是这套逻辑。我会从整体架构设计讲起然后拆解 MCP Tools 和 Skills 这两个关键概念在内网里怎么落地接着讲审批机制怎么设计才不会被业务方骂最后给一份完整的实操流程和排查清单。适合正在或者准备在内网环境里做 AI Agent 的工程师、架构师也适合想搞清楚 Agent 工程化到底难在哪的技术管理者。不需要你事先精通某个框架但得对 Python、容器、基本的网络概念有认知。先给一个结论性的判断内网 Agent 的成败70% 取决于工程架构30% 才取决于模型能力。很多人一上来就纠结用哪个模型、参数怎么调方向就偏了。2. 整体架构设计与方案选型思路2.1 内网 Agent 的三种典型部署形态在隔离环境里Agent 的部署形态基本逃不出这三种选哪种取决于你的算力资源和运维能力。第一种是全本地单机形态。模型、Agent 运行时、工具服务全部塞在一台高性能服务器上用容器编排起来。优点是简单、可控、没有网络依赖缺点是算力天花板明显跑个 7B 到 14B 的量化模型还行想上更大的模型就得堆硬件。适合中小规模、并发不高的场景。第二种是内网集群形态。模型推理服务单独部署在 GPU 集群上Agent 运行时和工具服务部署在 CPU 集群通过内网的服务发现和负载均衡串起来。这是中大型项目的标配扩展性好但运维复杂度陡增你得自己搞一套内网的模型服务网关。第三种是混合边界形态。核心数据和工具在内网模型推理走一个受控的边界节点。这种形态对安全要求极高边界节点的审计和流量控制必须做到极致一般只在有明确合规依据时才用。我个人的建议是如果并发在 50 以内、模型不超过 14B优先选第一种把复杂度压到最低。别一上来就追求集群内网环境的运维成本比你想象的高得多。2.2 为什么选 MCP 作为工具接入标准MCPModel Context Protocol这两年被讨论得很多它的核心价值在于把模型怎么调用外部工具这件事标准化了。在没有 MCP 之前每个 Agent 框架都有自己的工具定义方式你换个框架就得重写一遍工具层。MCP 把这层抽象出来工具提供方实现一个 MCP ServerAgent 侧实现一个 MCP Client两边通过标准协议通信。在内网环境里这个标准化的价值被放大了。原因是内网的工具往往五花八门——有老旧的 SOAP 接口、有直接读数据库的、有调用内部 RPC 的、有操作文件系统的。如果每个工具都要为 Agent 单独适配一遍工作量爆炸。用 MCP 统一封装之后Agent 侧只需要维护一套 Client 逻辑新增工具就是新增一个 Server解耦得很干净。选型上要注意MCP 的传输层支持 stdio 和 HTTP/SSE 两种。内网环境我强烈建议用 stdio 模式因为它是进程内通信不涉及网络端口暴露安全审计简单也不受内网防火墙策略影响。HTTP 模式虽然灵活但你得额外处理认证、限流、端口管理在内网里纯属给自己找事。2.3 Skills 机制的定位与边界Skills 这个概念容易和 Tools 混淆得先掰扯清楚。Tools 是 Agent 能调用的原子能力Skills 是面向具体任务的能力封装。打个比方Tools 像是厨房里的刀、锅、铲Skills 像是做一道番茄炒蛋这个完整流程——它内部会编排多个 Tools还带着自己的提示词、参数约定和输出格式。在内网 Agent 里Skills 的价值在于把高频、复杂的任务固化成可复用的模块。比如生成月度运维报告这个 Skill内部可能要调用数据库查询 Tool、日志分析 Tool、图表生成 Tool还要套一套固定的报告模板。如果每次都让模型自由发挥输出质量极不稳定封装成 Skill 之后行为就收敛了。但 Skills 不能滥用。我的经验是只有当一个任务被重复执行超过 20 次、且流程相对固定时才值得封装成 Skill。过早封装会让系统僵化模型失去了灵活应对的能力。这个度得把握好。2.4 审批机制为什么是内网 Agent 的命门公网环境里Agent 干错事大不了重来。内网环境里Agent 可能直接操作生产数据库、下发工单、修改配置一旦出错就是事故。所以审批机制不是可选项是必选项。审批机制的设计核心是风险分级。不是所有操作都需要人工审批那样效率太低没人用。我的做法是把 Agent 的动作分成四级风险等级典型操作审批策略L0 只读查询、检索、统计自动放行仅记录日志L1 低危写生成草稿、写临时文件自动放行事后抽检L2 中危写修改业务数据、发通知需人工确认可批量审批L3 高危删除、下发生产变更、资金操作强制双人审批不可绕过这套分级得跟业务方一起定不能技术团队自己拍脑袋。定完之后写进 Agent 的策略配置里由运行时强制执行模型无权绕过。3. 核心细节解析与实操要点3.1 模型选型内网能跑什么该跑什么内网模型选型的第一约束是硬件。假设你手头有一台 2 张 A100 80G 的服务器能跑什么粗略估算FP16 精度下模型参数量乘以 2 就是显存占用单位 GB再加上 KV Cache 和框架开销。所以 2 张 A100 大概能跑 70B 的 FP16 模型或者 140B 的 INT8 量化模型。但能跑不等于该跑。Agent 场景对模型的要求跟纯对话不一样它更看重指令遵循能力和工具调用格式的稳定性。一个 14B 但工具调用训练充分的模型实际表现可能比 70B 的通用模型还好。我实测下来在内网 Agent 场景里14B 到 32B 这个区间的模型性价比最高。选型时重点看三个指标一是工具调用格式的准确率模型能不能稳定输出符合 MCP 规范的 JSON二是长上下文下的指令保持能力Agent 的上下文经常上万 token模型不能中途忘事三是推理速度Agent 一次任务可能调用十几次模型单次延迟超过 3 秒体验就很差了。提示内网模型部署优先考虑 vLLM 或 SGLang 这类高吞吐推理框架它们对并发和批处理的支持比原生 transformers 好太多。别用 Ollama 跑生产那是给个人玩票用的。3.2 MCP Server 的内网封装实操写一个内网 MCP Server核心是把内部系统的能力包装成标准接口。以封装一个数据库查询工具为例关键点有这么几个。第一连接池必须做。Agent 调用工具是高频的每次新建数据库连接会直接把连接数打满。用连接池池大小根据并发量定一般 10 到 20 就够。第二SQL 必须做白名单或参数化。绝对不能让模型直接拼 SQL 字符串那是灾难。我的做法是工具只暴露预定义的查询模板模型传参数工具内部做参数绑定。# MCP Server 中数据库查询工具的核心逻辑示意 from mcp.server import Server from mcp.types import Tool, TextContent import asyncpg app Server(internal-db-tool) pool None app.list_tools() async def list_tools(): return [ Tool( namequery_metrics, description按时间范围查询系统指标仅支持预定义指标名, inputSchema{ type: object, properties: { metric_name: {type: string, enum: [cpu, mem, disk_io]}, start_time: {type: string}, end_time: {type: string} }, required: [metric_name, start_time, end_time] } ) ] app.call_tool() async def call_tool(name, arguments): if name query_metrics: # 参数化查询杜绝注入 rows await pool.fetch( SELECT ts, value FROM metrics WHERE name$1 AND ts BETWEEN $2 AND $3, arguments[metric_name], arguments[start_time], arguments[end_time] ) return [TextContent(typetext, textformat_rows(rows))]第三超时必须设。内网系统响应慢是常态但 Agent 不能无限等。每个工具调用设 30 秒超时超时返回明确的错误信息让模型知道该重试还是该放弃。第四日志要全。每次工具调用记录谁调的、调了什么、参数是什么、返回什么、耗时多少。这是事后审计的唯一依据。3.3 Skills 的封装粒度与提示词设计Skills 的封装粒度是个技术活。太粗一个 Skill 干太多事复用性差太细跟 Tools 没区别失去了封装意义。我的经验法则是一个 Skill 对应一个完整的业务动作输入输出都是业务语义而不是技术语义。比如生成周报这个 Skill输入是周次和团队名输出是一份格式化的周报文本。它内部可能调用了五六个 Tools但这些细节对调用方是透明的。Skills 的提示词设计有几个要点。一是角色和边界要写死明确告诉模型你是一个周报生成助手只处理周报相关请求。二是输出格式要约束用 JSON Schema 或者明确的模板别让模型自由发挥。三是异常处理要预设告诉模型当某个 Tool 失败时该怎么办是重试、降级还是直接报错。# Skill 定义示意 name: weekly_report description: 根据指定周次和团队生成运维周报 input_schema: week: string # 格式 2024-W23 team: string prompt: | 你是运维周报生成助手。请按以下步骤执行 1. 调用 query_metrics 获取本周指标数据 2. 调用 query_incidents 获取本周故障记录 3. 调用 query_changes 获取本周变更记录 4. 按模板生成周报包含概览、指标趋势、故障复盘、变更汇总、下周计划 若任一数据源查询失败在报告中标注数据缺失并继续不要中断。 output_format: markdown3.4 审批机制的工程实现审批机制落地时最容易出问题的是状态管理。一个 Agent 任务可能触发多次审批每次审批的状态、上下文、超时都得管好。我的实现方案是引入一个审批中间件Agent 的每个动作先过中间件中间件根据策略判断是否需要审批。需要的话把动作挂起生成审批单推给审批人审批人处理后中间件恢复动作继续执行。关键设计点审批单必须带完整上下文。审批人看到的不能只是Agent 想执行 DELETE 语句而应该是Agent 在执行 XX 任务时因为 YY 原因需要执行 ZZ 操作影响范围是 AA。上下文不全审批就是走过场。超时策略也得想清楚。审批单挂起后如果审批人一直不处理怎么办我的做法是设一个默认超时比如 30 分钟超时后动作自动取消Agent 收到取消信号后走降级路径。绝不能无限等待那会把整个任务链卡死。4. 完整实操流程与关键环节实现4.1 环境准备断网条件下的依赖搬运内网部署第一步就是解决依赖问题。公网环境下 pip install 一行命令的事内网里得走离线流程。标准做法是在公网机器上准备一个完整的离线包仓库。具体步骤先在一台跟内网环境操作系统、Python 版本完全一致的公网机器上把项目所有依赖下载到本地目录。# 在公网机器上执行下载所有依赖到 offline_packages 目录 pip download -r requirements.txt -d ./offline_packages \ --platform manylinux2014_x86_64 \ --python-version 310 \ --only-binary:all:这里有个坑--platform和--python-version必须跟目标环境严格一致否则下载的 wheel 包装不上。如果有些包没有预编译 wheel就得下源码包到内网再编译那还得把编译工具链也搬进去。搬进去之后在内网机器上从本地目录安装pip install --no-index --find-links./offline_packages -r requirements.txt--no-index是关键它强制 pip 不去访问任何在线源避免在内网里卡在超时上。模型文件同理得提前下载好权重用移动介质搬进去。模型文件动辄几十 GB搬运前算好校验和搬完必须校验介质损坏是常有的事。4.2 Agent 运行时的容器化部署内网环境我强烈建议全容器化部署原因是环境一致性。内网机器往往不能随便改系统环境容器能把依赖全部封在里面干净。一个典型的 docker-compose 编排长这样version: 3.8 services: model-server: image: local-registry/vllm-server:latest runtime: nvidia ports: - 8000:8000 volumes: - /data/models:/models command: --model /models/qwen-14b --tensor-parallel-size 2 --max-model-len 32768 --gpu-memory-utilization 0.9 deploy: resources: reservations: devices: - driver: nvidia count: 2 capabilities: [gpu] agent-runtime: image: local-registry/agent-runtime:latest depends_on: - model-server environment: - MODEL_ENDPOINThttp://model-server:8000/v1 - MCP_CONFIG/app/config/mcp.json volumes: - ./config:/app/config - ./logs:/app/logs mcp-db-tool: image: local-registry/mcp-db-tool:latest environment: - DB_DSNpostgresql://user:passinternal-db:5432/metrics # stdio 模式下不需要暴露端口注意model-server的--gpu-memory-utilization 0.9这个参数控制显存占用比例。设太高容易 OOM设太低浪费显存。0.85 到 0.9 是比较稳的区间。--max-model-len根据你的 Agent 上下文长度需求定Agent 场景一般 32K 够用设太大反而拖慢推理。4.3 MCP 工具链的注册与联调工具链联调是内网部署里最耗时的环节。我的建议是先单测每个 MCP Server再联调 Agent。单测 MCP Server 可以用官方提供的 inspector 工具或者自己写个简单的 client 脚本# 测试 MCP Server 是否正常响应 import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def test_server(): params StdioServerParameters( commandpython, args[-m, mcp_db_tool], env{DB_DSN: postgresql://...} ) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools() print(可用工具:, [t.name for t in tools.tools]) # 逐个测试工具调用 result await session.call_tool(query_metrics, { metric_name: cpu, start_time: 2024-06-01T00:00:00, end_time: 2024-06-01T01:00:00 }) print(调用结果:, result) asyncio.run(test_server())每个 Server 单测通过后再在 Agent 的 MCP 配置里注册。配置文件一般长这样{ mcpServers: { db-tool: { command: python, args: [-m, mcp_db_tool], env: {DB_DSN: postgresql://...} }, log-tool: { command: python, args: [-m, mcp_log_tool], env: {LOG_PATH: /var/log/app} } } }联调时重点观察Agent 能不能正确选择工具、参数格式对不对、工具返回结果 Agent 能不能理解。这一步出问题八成是工具的 description 写得不够清楚模型看不懂该什么时候用。4.4 审批流的端到端打通审批流打通需要三方配合Agent 运行时、审批中间件、审批人界面。我一般用消息队列把三者解耦。流程是这样的Agent 要执行一个 L2 动作运行时把动作序列化成审批请求发到消息队列审批中间件消费请求生成审批单存库同时推送给审批人界面审批人处理后结果写回队列Agent 运行时消费结果决定继续还是取消。# Agent 运行时侧的审批等待逻辑 async def execute_with_approval(action, context): risk assess_risk(action) if risk RiskLevel.L1: return await execute(action) # 需要审批 approval_id await submit_approval(action, context) result await wait_for_approval(approval_id, timeout1800) if result ApprovalResult.APPROVED: return await execute(action) elif result ApprovalResult.REJECTED: raise ActionRejected(f动作被拒绝: {action}) else: # 超时 raise ApprovalTimeout(f审批超时: {action})这里有个细节审批等待期间Agent 的上下文要保存好。因为审批可能耗时几分钟期间 Agent 进程可能被调度走恢复时得能接着之前的上下文继续。我一般把上下文序列化存到 Redis 或本地文件恢复时反序列化。4.5 并发压力下的稳定性调优内网 Agent 扛并发瓶颈通常不在模型而在工具调用和审批环节。模型推理有批处理工具调用是同步阻塞的审批更是人工环节。优化思路有这么几条。一是工具调用异步化能并行的工具调用并行发起别串行等。二是审批批量化把同一任务内的多个 L2 动作打包成一个审批单减少审批次数。三是模型推理批处理vLLM 的 continuous batching 默认开着但要注意--max-num-seqs参数设太小并发上不去设太大显存扛不住。实测数据供参考2 张 A100 跑 14B 模型--max-num-seqs 64的情况下单次推理延迟约 800msQPS 能到 30 左右。如果 Agent 每个任务平均调用 8 次模型那单机大概能支撑 4 个任务并发。要支撑更多要么加卡要么优化任务流程减少模型调用次数。注意别迷信压测数字。内网环境的实际负载往往有突发性压测时留 50% 的余量比较稳妥。5. 常见问题与排查技巧实录5.1 工具调用失败排查速查表内网 Agent 最常见的故障就是工具调用失败。我把踩过的坑整理成一张表遇到问题按表排查能省不少时间。现象可能原因排查方法解决工具完全不被调用description 太模糊看 Agent 日志里模型输出重写 description加使用场景说明调用参数格式错inputSchema 定义不清对比模型输出和 schema加 examples 字段给参数示例调用超时后端系统慢或连接池满看工具侧日志和连接数加连接池、设超时、加缓存返回结果模型看不懂返回格式太技术化看模型后续输出工具侧做结果格式化转成自然语言间歇性失败并发竞争或资源泄漏看错误率随时间变化加锁、修资源释放逻辑5.2 模型输出不稳定的处理经验模型输出不稳定是 Agent 工程的老大难。同样的输入这次对下次错很让人抓狂。我的处理经验有这么几条。第一降低 temperature。Agent 场景不需要创造性temperature 设 0.1 到 0.3 之间输出稳定性大幅提升。有些框架默认 0.7那是给聊天用的Agent 场景必须调低。第二用结构化输出约束。如果框架支持 JSON mode 或 function calling 的强制模式一定打开。让模型在受限的格式空间里输出比自由生成稳定得多。第三关键步骤加校验和重试。模型输出的工具调用参数执行前先校验格式不对就让它重新生成最多重试 3 次。这个简单的机制能挡掉大部分偶发错误。第四上下文管理要克制。Agent 的上下文不是越长越好无关的历史消息会干扰模型判断。我一般只保留最近 10 轮对话加系统提示更早的做摘要压缩。5.3 审批环节的典型堵点审批机制上线后业务方最常见的抱怨是太慢。排查下来堵点通常在这几个地方。一是审批人不在线。内网环境审批人可能就是某个特定岗位的同事他开会去了审批就卡住。解决办法是设审批代理人机制或者对 L2 动作设自动通过的超时比如 10 分钟无人处理则放行但记录在案。二是审批单信息不全。审批人看不懂 Agent 要干什么不敢批来回问。解决办法是审批单必须带自然语言的说明把技术动作翻译成业务语言。三是审批粒度太细。一个任务触发十几个审批单审批人烦了就开始无脑点通过审批就失去意义了。解决办法是合并审批一个任务一个审批单列出所有待批动作。5.4 内网特有的坑内网环境有些坑是公网遇不到的单独拎出来说。时间同步问题。内网机器可能没有 NTP 服务各机器时间不一致。Agent 的日志、审批单、工具调用记录时间对不上排查问题时能把人逼疯。部署前务必确认所有机器时间同步没有 NTP 就手动对时。DNS 解析问题。内网的服务发现可能靠 hosts 文件或者内部 DNS容器里的 DNS 配置容易出问题。容器间通信用服务名解析失败是常见故障排查时先nslookup一下。磁盘空间问题。模型文件、日志、审批记录都很占空间内网机器扩容又麻烦。部署前算好磁盘需求日志做轮转别等磁盘满了才发现。证书问题。内网 HTTPS 常用自签证书Python 的 requests 默认不信任会报 SSL 错误。要么把自签 CA 加进信任链要么在工具里显式指定 verify 参数。这个坑我第一次踩的时候查了半天。6. 一些掏心窝子的实操心得聊了这么多技术和流程最后说几点纯经验的东西都是踩坑踩出来的。别追求一步到位。我见过太多团队想一次性把 Agent 做得完美结果三个月没上线。正确的做法是先跑通最小闭环——一个模型、一个工具、一个 Skill、一级审批能端到端跑起来再逐步加东西。内网环境的复杂度决定了你必须小步快跑。日志是你的救命稻草。内网环境没法随时调试出了问题只能靠日志回溯。Agent 的每一步决策、每一次工具调用、每一次审批流转都要有日志。日志格式要结构化方便检索。我一般用 JSON 格式关键字段包括 trace_id、step、action、result、duration。跟业务方对齐预期。Agent 不是万能的内网环境下的 Agent 能力边界更窄。上线前一定要跟业务方说清楚它能做什么、不能做什么、出错时怎么办。预期没对齐上线后就是无尽的扯皮。留好人工兜底通道。再好的 Agent 也会出错关键是要有快速回滚和人工接管的能力。我的做法是每个 Agent 任务都保留完整的状态快照出问题时能一键回滚到任务开始前的状态同时支持人工接管继续执行。模型不是越新越好。内网环境换模型成本很高选一个稳定的版本别追新。我有个项目用的还是半年前的模型版本跑得好好的没必要为了新特性去折腾升级。这套东西后续还能往几个方向扩展。一是多 Agent 协作把复杂任务拆给多个专职 Agent通过消息机制协同。二是Agent 自评估让 Agent 对自己的输出做质量打分低分的自动重试或转人工。三是知识库增强把内网的文档、规范、历史案例做成 RAG让 Agent 的决策更有依据。这些方向我都在陆续尝试有新的心得再跟大家分享。