☰
从依赖搬运到Rust重构:AI Agent隔离网落地的完整链路
2026/10/8 20:56:43 网站建设 项目流程

如果你在一个完全无法访问外网的研发环境里做过 AI Agent,应该能体会那种荒诞感:项目文档里写着“只需执行 pip install”,现实却是所有包管理源全部指向内网镜像,而镜像里恰好没有你要的新版本;模型权重文件下好了,却卡在合规审查和介质摆渡流程里;Demo 在个人电脑上跑得行云流水,一到内网服务器就各种报错,而且报错信息连搜索引擎都搜不到。

这篇文章不是通用教程,而是我和团队在集团级隔离内网里做 AI Agent 工程落地的过程记录。我们试过用 Python 搭建 Agent Runtime,中途又用 Rust 重写了调度核心;踩过依赖搬运的坑,被本地模型的上下文窗口反复教育过,也花了大量时间做权限和可观测性。如果你正在规划 AI Agent 落地方案,尤其需要把 Agent 部署到网络受限的离线环境,这篇内容应该能帮你少走不少弯路。

1. 先盘家底:隔离内网里做 Agent,缺的不是模型而是工程链路

1.1 在线 Demo 与离线工程是两种物种

网上大量 AI Agent 学习路线和实战教程,默认前提都是“能访问外网大模型 API、能自由 pip/npm install、能拉 GitHub 代码”。一旦切到隔离内网,这些前提全部失效,项目性质瞬间从“算法应用”变成“系统工程”。

隔离内网和普通离线环境还不一样。普通离线环境只是没有公网,服务器之间可能还有一台能联网的中转机;隔离内网通常连包管理器代理、代码仓库、模型网关都要先申请、先搭建,甚至服务器清单都要审批。我们这次面对的是一套完整的生产管理网络,服务器不能访问公网,外部流量进不来,内部服务之间也有严格的访问控制。这意味着 AI Agent 的每一个依赖都要提前准备好,每一段外呼都要改成内网协议,每一次模型调用都要在自己部署的推理服务上完成。

所以我的第一个建议是:开工之前,先盘一下网络边界、包管理系统、介质摆渡流程和 GPU 资源。这四个东西决定了你的技术选型上限,比选什么 Agent 架构重要得多。

1.2 第一周的真实踩坑清单

项目启动第一周,我们就踩了一条完整的坑链,现在回看全是经典错误。

第一坑:Python 依赖装不上。我们最初想用 LangGraph 搭 Agent,结果拉取依赖时发现内网 PyPI 镜像里没有 graph 相关的几个子包,版本也偏旧。解决方案只能是在有外网的“摆渡机器”上把整套 wheelhouse 打包,然后人工审批后搬到内网,再让内网服务器从本地目录安装。

第二坑:模型权重缺一部分。团队最开始定的模型是 14B 量级的开放权重,因为对中文任务效果好。但权重文件分片压缩后超过 20GB,而且个别分片在传输过程中损坏,加载时直接报错。后来我们给所有模型包都加了 SHA256 校验,才算根治。

第三坑:Docker 镜像也断供。vLLM 的官方镜像体积很大,内网没有镜像仓库,直接 docker pull 肯定失败。我们把镜像在外网机器上 docker save 成 tar 包,切成多个压缩分卷,搬进内网再 docker load。这个流程本身不难,但审批周期和传输时间被严重低估了。

这些坑有个共同点:它们和“模型聪明不聪明”完全无关,却决定了项目能不能跑起来。如果你也准备在隔离网里做 Agent,请务必定一个原则——“先把地基验证完,再写一行业务代码”。

1.3 项目目标:要能长期维护,不要一次性 Demo

项目目标也很重要。老板一开始想看到的是“AI 自动处理工单”“AI 自动写报告”,但我们经过评估后,把目标收敛成两个可度量的方向:一是让 Agent 能稳定复用内部工具和知识库,解决重复性数据查询和整理工作;二是建立一套可持续升级的离线 Agent 基座,后续接入新场景时不用推倒重来。

这个定位让我们在后面的架构选择上少了很多纠结:不追 ChatBot 式的花哨交互,不做完全自主的无人决策,核心是“可控、可审计、可回滚”。如果你的项目也是生产网内部系统,建议早点把这种边界意识定下来,不然后面会不断被“让 Agent 多干一点”的需求推着走偏。

2. 依赖搬运、模型入场与推理服务选型:地基阶段的四件事

2.1 Python、npm、Rust 三方依赖的离线交付

离线交付依赖,原则是“把互联网当软件源,把内网当最终目的地”。我们在摆渡机上做了三套离线方案,分别对应 Python、Node 和 Rust。

Python 那边,关键是用pip download下载所有依赖到本地目录,注意不要下源码包。我们统一在 x86_64 Linux 环境执行,指定平台标签,避免下载到 macOS 或 Windows 的包:

pip download -r requirements.txt \ -d wheelhouse \ --platform manylinux2014_x86_64 \ --python-version 3.11 \ --only-binary=:all:

这里的--only-binary=:all:能避免源码包在目标机器上现场编译。很多包(尤其带 C 扩展的)在内网编译会缺编译器和头文件,一旦走源码安装,坑会成倍增加。

到了内网服务器上,安装命令必须加--no-index:

pip install --no-index --find-links=/srv/wheelhouse -r requirements.txt

Node 那边,我们在内网搭了 Verdaccio 私有 npm 仓库,但这只解决后续增量安装。首次入场还是用npm pack或直接缓存整个 node_modules 目录搬进内网更实际。Rust 项目更简单也更要提前规划,它用 Cargo 拉的是 crate 源码,离线方案一般用cargo vendor:

cargo vendor --versioned-dirs

然后在项目.cargo/config.toml里指定使用本地 vendor 目录:

[source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor"

这样所有 crate 从本地源码编译,不需要内网 crate 镜像。代价是首次编译时间变长,所以 Rust 项目我们直接放到了后续 Runtime 重构阶段处理,没有让它阻塞 Python 版 Agent 的起步。

2.2 模型权重进内网的完整链路

模型权重是类似“物流问题”的存在。它没有依赖关系,却最容易因为传输和校验出事故。我们的标准流程是:

  1. 在摆渡机上从模型平台或对象存储下载权重,校验原始 SHA256。
  2. 做分卷压缩,每卷控制在 2GB 左右,方便介质拷贝和审批扫描。
  3. 把每个分卷的 SHA256 一起随离线段提交。
  4. 到内网后先做完整性校验,再解压,再加载模型。

这里强烈建议在加载脚本里自动做校验,不要靠人手工对哈希。因为我们第一次就是嫌麻烦,省了校验步骤,结果模型加载中途失败,排查了两天才意识到是分片坏了。

选择模型时,我们结合内网 GPU 型号做了取舍。A100/A800 级别显卡可以直接上 14B 甚至更大,但很多传统企业内网只有几台普通训练卡,甚至只有 CPU 节点。我们最终的配置是:生产环境用 32GB 显存的卡跑 14B 量化模型,CPU 边缘节点跑小模型做轻量任务。权重量化格式选了 AWQ 和 GGUF 两套,分场景使用。

2.3 Docker 镜像的导出、导入与内网仓库

内网部署免不了 Docker,镜像同样要走“导出一传输一导入”的流程。我们在摆渡机上执行:

docker pull vllm/vllm-openai:latest docker save vllm/vllm-openai:latest | gzip > vllm.tar.gz split -b 2G vllm.tar.gz vllm.part.

把分卷搬进内网后,合并再导入:

cat vllm.part.* > vllm.tar.gz gunzip vllm.tar.gz docker load -i vllm.tar

如果你的隔离网已经装了 Harbor 或自建镜像仓库,更规范的做法是在内网建一个基础镜像仓库,所有服务镜像都推到这个仓库,避免每台服务器都 load 一遍。我们后来就在内网部署了私有镜像仓库,所有带 Agent 的服务最终都从仓库拉镜像,版本管理总算回到正常节奏。

提示:一定不要依赖在 Dockerfile 里执行apt-get install或pip install,因为构建镜像时访问不了外网。所有依赖要先打进基础镜像,基础镜像也要在摆渡机上提前 build 好。

2.4 推理服务选型:vLLM、Ollama、llama.cpp 怎么选

在隔离网里,推理引擎决定了你后续能开多少个并发 Agent。我们做过一组实际选型对比:

方案吞吐表现部署和维护难度适合场景
vLLM高,支持连续批处理和 PagedAttention较高,依赖 CUDA 环境,镜像体积大生产环境多 Agent 并发调用
Ollama中等,单机够用低,安装简单,自带模型管理团队开发调试、边缘小型任务
llama.cpp单请求为主低,支持 CPU 运行无 GPU 的局面、资源紧张场景

我们生产环境最终用 vLLM,因为多个 Agent 会同时发起请求,vLLM 的高吞吐能明显降低排队时间。开发环境用 Ollama,方便算法同学快速试 prompt 和模型。需要特别提醒的是:本地推理服务最好提供 OpenAI 兼容的 HTTP API,这样 Agent 框架接入成本最低。如果模型本身不支持 Function Calling,就要在解析层做好 JSON 结构提取,我们在后面章节展开讲。

3. Agent 架构与核心链路:规划、工具、上下文三件套

3.1 主流 Agent 架构怎么选:ReAct、Plan-and-Execute、多智能体

内网项目最忌讳“为智能而智能”。我们实际评估了三种主流 Agent 架构,结论很直接:简单任务用 ReAct,复杂长任务用 Plan-and-Execute,多智能体暂缓。

ReAct 是让模型交替输出 Thought、Action、Observation,每一步都基于上一步观察结果决策。优点是实现简单、灵活,适合查询类工具调用;缺点是容易陷入循环,任务步骤多了以后经常来回横跳。Plan-and-Execute 则是先让模型生成一个整体计划,再逐步执行,每一步执行失败还能回到计划层调整方案,适合“查昨天的指标、对比前七天趋势、生成异常说明”这种多阶段任务。

我们最终采用的是“ReAct + 显式状态机”的混合结构:任务启动时先做一次轻量规划,把步骤写出来;中间步骤本质上是 ReAct 循环,但加入了最大步数限制和绕圈检测。一旦 Agent 连续两次调用相同工具且参数相同,就强制中断并让模型重新规划。这个设计让多数任务的步骤数减少了三成,也让线上行为可控得多。

3.2 工具注册表:先定义 Agent 能碰什么,再谈智能

Agent 的“能力”全部来自工具。在隔离内网,工具不能乱开,要像给 API 开权限一样逐个审批。我们的做法是维护一张工具注册表,每个工具包含源码入口、参数 Schema、权限等级和调用频控。

工具清单包括四类:

  • 只读查询类:查数据库、查监控接口、查日志平台。权限最低,Agent 可自由调用。
  • 内网服务调用类:调内部工单接口、资产系统接口。需要调用方身份认证,Agent 用服务账号调用。
  • 文件处理类:读写指定沙箱目录,生成报表、导出 CSV。目录严格限制,禁止越界。
  • 写操作类:建工单、发通知、提交审批。必须经过人工确认,Agent 只能生成草稿。

每个工具的参数要做校验,尤其是在 Rust 版本里,我们用 JSON Schema 直接校验模型输出的参数,不合格就返回错误让模型重新生成。工具注册表本身放在内部管理系统里,Agent 每次启动都会拉取最新版本,避免改代码重新部署才能加工具。

3.3 Token 与记忆:本地模型同样要精打细算

很多人第一次接触 Agent 时会问 “AI Agent token 是什么意思”。简单说,token 是模型处理文本的最小单元,中文大约一个字切成 0.6 到 1 个 token,英文按子词切分。模型一次能“看到”的 token 数量受上下文窗口限制,Agent 每轮对话里塞入的系统提示、工具描述、历史记忆、工具返回结果都会占用这个窗口。

在线调用 API 时,token 直接和费用挂钩;使用本地模型虽然不花钱,但上下文窗口依然是硬约束。我们的 Agent 动辄要查询多次数据,一次工具返回可能几千个 token,如果不管理,很快会把上下文塞爆,导致模型“忘掉”最初的指令。

我们的做法是三层记忆管理:

  1. 短期记忆:保留最近 N 轮对话和工具观察,放在上下文窗口内。
  2. 工作记忆:把当前任务的计划、已确认事实、待办步骤单独维护,用结构化摘要注入上下文。
  3. 长期记忆:任务结束后把关键结论写入内部向量库,后续相似任务可以检索。

上下文组装逻辑的伪代码如下:

def build_context(system, plan, history, tool_results): budget = 8000 # 给输出预留 2000 以上 parts = [system, plan] for obs in reversed(tool_results): line = f"[tool] {obs.name}: {obs.content}" parts.insert(2, line) if estimate_tokens("\n".join(parts)) > budget: break return "\n".join(parts)

这套机制上线之后,Agent 的长任务成功率从六成出头提到了八成以上。关键不是模型变聪明了,而是它把注意力集中在当前最重要的信息上。

3.4 落地案例:内部告警排查 Agent 的一天

纸上谈兵没意思,说一个我们真正落地并坚持跑下来的场景:内部系统告警排查 Agent。

每天早上,它会连接内部监控平台,拉取过去 24 小时的告警列表;然后调用数据库查询接口,把告警相关的接口耗时、错误率、依赖服务状态取回来;接着调用日志平台搜索异常堆栈;最后生成一份排查报告,写到内部工单系统的“待确认”列表里。

报告会明确指出:哪些告警持续超过阈值、可能的根因是什么、建议由哪个团队跟进。原先这个工作每天要占运维同学半小时到一小时,Agent 上线后他们只需要每天扫一眼报告,确认或纠正结论即可。

对应的管理端,我们用 Django 写了一个 Agent 控制台,任务定义、工具注册、执行日志、Token 消耗和人工确认流程都在页面上管理。我们还试过让 Agent 根据字段定义直接生成 Django Form 样板代码,确实能提升开发速度,但生成结果必须人工 review,不能用信任代替校验。

这个案例能被业务接受,核心原因是我们把 Agent 定位成“帮忙收集信息、给出线索”的助手,而不是替代老师傅做决策。这也印证了前面的观点:工具的边界决定了系统的信任边界。

4. 二次开发中我们用 Rust 重写了 Agent Runtime

4.1 为什么 Python 跑得好好的,还要动 Runtime

Python 版 Agent 跑通之后,团队开始考虑一个现实问题:如果在内网部署几十个、上百个 Agent 实例,每个都独立跑一个 Python 进程,内存占用会非常难堪。更何况 Python 的 GIL 在 I/O 密集型场景下表现一般,Agent 每步都要等模型推理和工具响应,虽然模型等待时间占大头,但并发调度效率仍有提升空间。

我们内部做过一次压测:Python Runtime 单机极限大约能稳定承载 20 个并发会话,超过之后延迟明显上升。单纯堆机器当然能解决,但隔离内网的资源申请很麻烦,GPU 和主机都不是想加就加的。于是我们决定用 Rust 写一个轻量 Agent 调度和工具执行核心,只把最热、最需要并发的路径放进来。

Rust 在这个场景的优势不是“跑得快”这么简单,而是三件事:单二进制部署、真实并发模型、内存占用可控。Agent 调度本质上是大量异步任务等待模型响应,Rust 的 async/await 模型比线程模型更节省资源,也比 Python 协程生态更稳。

4.2 静态编译与离线部署的契合点

隔离内网部署最烦的是什么?是每台机器都要装运行环境。Python 版本、系统依赖、动态库缺一个都不行。用 Rust 写 Runtime 后,我们直接交叉编译到x86_64-unknown-linux-musl,得到一个几乎全静态的二进制:

cargo build --release --target x86_64-unknown-linux-musl

这个二进制拷贝到内网任何一台 Linux 机器上就能跑,不需要预装 Python,不需要装 Node,甚至连基础镜像都可以省一层。这在大量边缘节点部署时特别爽,因为 CIO 审核“运行环境”时,我们给出去的只有一个文件。

Rust 版 Runtime 的架构也很简单:调度循环负责从消息队列取任务,把任务交给异步执行器;执行器调用 vLLM 的 OpenAI 兼容接口取模型输出;解析输出里的工具调用后,通过工具注册表分发给内部工具执行。核心模块没有复杂依赖,所以内网部署时的离线依赖问题被显著收敛了。

4.3 Rust 版本的核心设计与踩坑

Rust 版里最核心的难点是“模型输出的不确定性”如何处理。模型可能不按约定输出 JSON,也可能输出正确 JSON 但调用不存在的工具。我们用serde_json做严格的参数解析,工具调用结构如下:

#[derive(Deserialize)] struct ToolCall { name: String, args: serde_json::Value, } async fn dispatch_tool(call: ToolCall) -> Result<String, ToolError> { match call.name.as_str() { "query_metric" => query_metric(call.args).await, "search_log" => search_log(call.args).await, _ => Err(ToolError::NotFound), } }

如果参数校验失败,我们不会让整个任务崩溃,而是把错误信息作为新的观察结果重新喂给模型,让它修正输出。这个过程在 Rust 里写起来比 Python 繁琐,但换来的是更高的稳定性上限。

踩过的比较典型的坑有两个。一个是 tokio 的异步任务在模型响应特别慢时会堆积大量等待中的任务,需要加并发限流,否则工具服务和模型服务都会被请求淹没。另一个是 Rust 的编译时间在依赖多了以后会明显拉长,尤其在内网机器上做增量编译,性能和开发机差距很大,所以我们的策略是在摆渡机上建立预编译缓存,把编译产物整体搬进内网,而不是在内网现编。

4.4 什么情况下不建议上 Rust

Rust 不是万能解药。如果你的 Agent 场景主要是 prompt 迭代、知识库问答、快速接入各类插件,Python 生态的灵活性和第三方库丰富度远超 Rust。写 Python 一个晚上的事,写 Rust 可能要两天。

我们自己的最终结论是:业务 Agent 继续用 Python 写,因为业务逻辑变化太快,工具经常要加;Rust 只负责做高密度的调度网关和工具执行进程,可以把它理解成“用 Rust 写了一个 Agent 专用的消息路由器”。这样两边受益:Python 负责灵活,Rust 负责稳定和并发。

5. 真正上线前的工程硬要求:权限、容错与可观测

5.1 权限边界:把 Agent 当新员工管

Agent 上线前,安全评审问的第一个问题一定是“它能干什么”。我们后面沉淀出一套“按角色分权限”的思路:Agent 不是只有一个系统账号,而是每个场景一个最小权限账号。

告警排查 Agent 只能读取监控和日志,不能改阈值;报表生成 Agent 只能读数、写指定目录,不能连生产库;工单 Agent 只能建草稿,最终确认必须由人工按钮触发。所有工具调用都要求附带任务号,审计日志里能看到“哪个任务、哪个 Agent、在什么时间、调了什么工具、参数是什么”。

严格权限是隔离网内做 Agent 的底线。因为一旦模型被 prompt 注入或产生异常行为,至少权限边界能把破坏范围锁死。我们在设计工具层时就把权限校验放在工具调度层,而不是每个 Agent 里,保证任何新 Agent 接入都必须过这一层。

5.2 模型输出不可靠怎么办

内网部署的模型远没有云端大模型那么稳定,输出格式更飘、幻觉率更高。我们的应对手段有三个层级:

第一层是格式校验。所有模型输出先过 JSON Schema,不合格直接让模型基于错误信息重试。第二层是置信度检查。模型要给出“行动理由”和“前置条件满足情况”,如果某个操作涉及写入类工具,还要输出一个风险等级,我们人为设定高风险操作必须转人工。第三层是熔断和重试。连续两次工具调用失败就停止当前计划,回退到“重新规划”模式,并且记录失败原因供后续分析。

这层设计几乎是 Agent 工程化最重要的部分。一个没有重试和熔断的 Agent,在真实业务里就是一颗定时炸弹,因为它看起来能干活,实际上会在错误路径上越走越远。

5.3 可观测性:Agent 出问题要找得到“现场”

Agent 不像普通接口,它的问题往往不是一次请求 500 那么简单,而是“它在第 5 步做了一个错误的决定”。所以我们从第一天就要求每个 Agent 任务输出完整的执行轨迹。

轨迹包含:系统提示词、规划步骤、每一步的思考文本、工具调用参数、工具返回结果、模型最终输出。早期我们用 JSON Lines 直接落盘,后来统一写进内部的日志检索系统。这样一旦业务方说“今天那份报告分析得莫名其妙”,我们能立刻把轨迹拉出来,定位问题出在规划、工具解析还是模型生成环节。

我们还给每个任务打上了 Token 消耗和耗时标签。Token 消耗不是为了计费,而是为了发现隐性问题:某类任务 token 突然翻倍,往往说明上下文组装出现了重复内容或者 Agent 开始绕圈了。

5.4 离线环境下的评估:没有云端裁判,只能自己攒数据集

在线环境很容易用 GPT-4 当裁判给 Agent 打分,隔离内网里没有这个条件。我们的做法是人工攒一套“任务样例集”,按业务场景分成四类,每类 20 到 50 条任务输入,维护在内部管理系统的评估表里。

每周跑回归测试:Agent 对这些样例任务跑一遍,记录任务成功率、工具调用成功率、报告是否包含指定字段。模型升级或者工具变更之前,先跑回归,结果达到基线才允许上线。为了让评估尽量客观,我们还尝试用内网部署的较大模型给结果打分,和人工抽查结果做对比,但最后还是以人工确认为准。

这套评估体系不完美,但它让我们的 Agent 有了一个可以量化的质量基线。没有基线,后面每一次模型升级都会变成碰运气。

6. 上线后的运维迭代与个人经验

6.1 模型升级不是换一个权重

模型升级在隔离内网里是件全网联动的事,不只是把新权重放到推理服务器。我们经历了两次模型升级,固定流程是:先在摆渡机验证新模型效果,再内网导入权重,接着在开发环境跑回归样例,通过后切小流量试运行,最后才全量替换。

尤其要注意量化版本和推理框架的兼容性。我们曾因为换了新版本权重,但 vLLM 版本没跟上,导致输出格式完全错乱。所以现在每次升级,权重、推理引擎、Agent Runtime 三个版本会绑定在一起出一个“版本列车”,一起测试、一起上线,避免混搭版本。

6.2 让使用者接受“Agent 会犯错”

这个坑比技术坑更难处理。业务同事一开始对 Agent 的期待是“它应该全对”,我们则必须反复强调“它是个实习生,不是专家”。对关键任务,我们保留人工确认环节;对重复性任务,我们允许它在小错误中慢慢积累可信度。

一个反转经验是:我们给报告生成 Agent 加了一个“模型置信度”水位线,当它自己判断不确定时,明确写进报告“此条结论置信度较低,需人工复核”。这个设计反而大幅提高了使用者信任度,因为他们知道 Agent 不会硬编瞎话。

6.3 最终沉淀:什么样的任务适合放进隔离网 Agent

做完整套落地,我个人的筛选标准是四条:

  • 任务重复度高,输入输出结构相对稳定;
  • 涉及的内部系统有清晰 API 或数据库,能形成可靠工具;
  • 决策风险可控,即使偶尔犯错也不影响核心生产;
  • 可以量化评估,至少能判断“完成”和“没完成”。

满足这四条的场景,比如告警初筛、日报生成、工单分类、数据提取、知识库问答,都是隔离网内适合 AI Agent 发挥的地方。反过来,凡是涉及强人工保证、高并发写操作、不可逆动作的业务,请务必保留人工授权环节。

如果你正要启动一个隔离内网里的 AI Agent 项目,我的建议是:不要从“让 Agent 多聪明”开始,先跑通依赖、模型、镜像和权限这条链路,再用最小的只读任务验证价值。这个领域真正难的不是模型,而是让整个系统在封闭环境里可靠地运转起来。我们的 Rust Runtime 重构和 Python 业务 Agent 能并行往前走,靠的也是这个顺序——地基稳了,上面长什么植物都行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询