
从去年年底开始我所在的项目组接到一个“硬骨头”在完全隔离的内网环境里搭建一套能跑起来的 AI Agent 系统。所谓隔离内网就是物理上跟公网断开没有外部 API、没有在线模型服务、甚至连 pip 都连不上 PyPI 的那种环境。我们原本设想的“调一个云端大模型、接几个插件、跑个 Agent 循环”的方案在进场第一天就全线失效。这篇文章就是我在这大半年里把隔离内网下的 AI Agent 从“跑不通”干到“能干活”的全过程复盘重点讲清楚架构选型、离线部署、Agent 编排、业务对接和调试排障这五块内容。如果你也在政企内网、生产专网这类封闭环境里做 AI 应用这篇文章应该能帮你少走不少弯路。1. 隔离内网下的真实约束先看清 Agent 上路的三座大山在动手之前我先把团队在隔离内网里遇到的“不可抗力”列了个清单。这不是劝退而是为了搞清楚我们到底在跟什么对抗。很多人第一次听到“隔离内网”这个概念第一反应是“没网而已麻烦一点”。真实情况远不止如此。1.1 依赖获取的全面切断我们内部的开发机原本都是连外网的装依赖直接 pip install拉模型直接 huggingface-cli download一切都很顺。进了隔离区之后最直接的问题就是没有任何一个包管理源是可用的。pip 默认指向的 PyPI 连不上apt 源连不上npm 源连不上Docker Hub 连不上。这意味着什么呢意味着你连一个最基础的requests库都装不上除非你手上提前备好了对应版本的 wheel 包。我们当时第一周几乎全在干一件事把 Python、Node、Rust 工具链以及所有第三方依赖的离线包全部手动收集一遍这活儿说起来简单做起来极其痛苦尤其是传递依赖的版本锁定关系稍微错一个版本后面整个环境就是一团乱麻。1.2 模型获取与服务化的“断供”大模型时代的应用命脉就是模型本身。隔离内网意味着 HuggingFace、ModelScope 这些模型托管平台全部不可达。你要用什么模型必须在外网提前下载好然后“搬运”进内网。我们项目选型时比较务实用的是基于 Llama 架构的 7B 和 14B 量级的中文模型。但这里有个特别容易被忽略的问题光把模型权重文件拷进来是远远不够的。模型跑起来还需要分词器、配置文件、生成参数模板以及一套能把它加载起来做推理的服务框架。我们最早的方案是直接用 HuggingFace Transformers 配合 PyTorch 加载权重做推理但后来发现这个方案在隔离内网里“太重了”每次拉起推理服务的耗时、显存占用、并发处理能力都不尽如人意。1.3 Agent 工具的“失联”Agent 之所以叫 Agent核心在于可以调用工具。大多数 Agent 场景里的工具都依赖外部服务搜索用 web 搜索 API、新闻要请求第三方接口、地图要调地图平台 SDK。这些东西在隔离内网里全部不可用。我们当时接到的业务需求是做一个能辅助做数据分析和自动生成报表的 Agent。这导致它的工具链必须是“向内看”的——连接的是内网数据库、内网知识库、内网工单系统。这反而逼着我们思考了一个更本质的问题Agent 工具的核心价值是“连接外部世界”但在隔离内网里外部世界的定义变成了企业内部的业务系统。你得重新设计一套工具协议让 Agent 跟内网的数据和服务打交道。这三座大山看清之后我们的破局思路就清晰了。一句话概括把 AI Agent 当成一个完全自洽的离线软件系统来建设而不是一个依赖公共云服务的玩具。所有组件、依赖、模型、服务全部走“提前准备、离线灌入”的路径。2. 离线模型供给与推理服务化让 Agent 有一颗能用的“大脑”Agent 的核心是一个能理解意图、规划步骤、生成回复的语言模型。在隔离内网里这个“大脑”必须完全本地化。这一节我把模型选型、格式转换、推理服务化这几步拆开讲每一步都有我们实测过的具体参数和踩坑记录。2.1 模型选型先看显存再谈效果模型选型的第一原则不是“效果最好”而是“硬件扛得住”。我们内网用来跑模型的机器是两台英伟达 4090 的服务器单卡 24GB 显存不支持多卡并行也没那个条件所以 7B 和 14B 是我们能触及的上限范围。拿 7B 模型来说如果是 FP16 精度加载光权重就要占 14GB 左右显存加上 KV Cache 和激活值基本就是贴着 24GB 的边跑并发稍微一上来就 OOM。后来我们把模型量化到了 INT4用 GPTQ 方案权重体积压缩到 4GB 左右显存富余出来了推理速度也快了不少。效果上不能说完全没有损失但 7B 模型在 INT4 下跑日常任务生成质量下降在可接受范围内。14B 模型我们也试过INT4 量化后权重大约 8GB单卡 4090 能跑但并发能力和响应速度明显比 7B 吃力。我们最后的结论是在隔离内网这种“算力自有、无法弹性扩容”的环境里优先选更小、更快、更稳的模型把效果优化放在 prompt 设计和工具编排上而不是盲目追大模型。2.2 离线环境下的模型转换与拷贝模型选型定了之后最麻烦的一步来了怎么把模型搞进内网。网上很多教程的做法是直接拿 HuggingFace 格式的权重文件配合 Transformers 库加载。这在能连外网时当然没问题但隔离内网里我们要提前考虑两件事一是文件格式的兼容性二是文件体积的搬运成本。我们最终采用的是 GGUF 格式。原因很简单GGUF 是 llama.cpp 社区的标准格式支持 CPU/GPU 混合推理而且配合 Ollama 这类工具可以极简部署。把 HuggingFace 格式转成 GGUF 的步骤这里分享一个我们验证过的稳定流程整条链路是外网下载原始权重 → 用llama.cpp的转换脚本生成 GGUF → 用llama-quantize做 INT4 量化 → 把.gguf文件通过移动硬盘拷贝进内网。光是做这一个转换我们就折腾了大概三天。最大的坑在于转换脚本的版本必须和推理框架版本匹配否则会出现“模型加载成功但生成乱码”的情况而且这种问题在日志里极难排查因为报错信息非常泛——往往就是一句“illegal instruction”或者直接进程崩溃。2.3 推理服务化的三种落地姿势模型文件进去了下一步是搭建推理服务。我们对比了三种方案各有取舍方案部署复杂度并发能力功能特性适用场景Ollama极低几乎开箱即用中等内置 API、模型管理快速验证、小规模使用vLLM较高需要 Python 环境高支持 Continuous BatchingOpenAI 兼容接口适配 LangChain 等框架生产环境、高并发llama.cpp server中等需自行编译中等轻量资源占用小算力有限、嵌入式场景我们最终选了 vLLM因为 Agent 场景对推理服务的稳定性、并发响应速度要求很高vLLM 的 OpenAI 兼容接口对我们后续接编排框架非常友好。但这里要提醒一句vLLM 的离线部署依赖那个“全套依赖打包”的能力它依赖的vllm包、torch、transformers、xformers这些全部要在外网准备好对应版本的 wheel 文件一个都不能漏。我们在这一步吃了大亏后面第三章会细说。2.4 统一推理网关让 Agent 用一套接口调用所有模型模型服务化之后我们还做了一层“推理网关”。原因是内网里可能同时存在不同业务线的多个模型服务有的跑在 GPU 上有的是纯 CPU 推理的小模型比如做 embedding 的。如果每个 Agent 都直连各个模型服务接口不统一、负载不均衡、故障不隔离后期运维就是灾难。我们的做法是用一个轻量级的 FastAPI 应用做统一网关对外暴露一套标准的 OpenAI 风格接口/v1/chat/completions和/v1/embeddings。网关内部再根据请求中的model字段把请求路由到对应的后端推理服务上。这样无论底层是 vLLM、Ollama还是自己用 Transformers 写的服务Agent 那一侧永远只认这一套接口。这一步做完Agent 的“大脑”算是正式上线了。但光有大脑还不够Agent 要运行起来还需要一个完整的离线依赖环境这也是我们踩坑最多、最值得单独拿出来讲的一块。3. 依赖与组件的离线分发搭建内网自有的“弹药库”Agent 是个复杂的软件系统跑起来需要 Python、Node、各种 C 库、推理框架、编排框架等等一大堆东西。在隔离内网里所有这些东西都要靠人肉搬运。这一节我把我们的离线依赖管理方案完整讲一遍包括工具链准备和最终验证方法。3.1 工具链准备外网“进货”的正确姿势我们当时是这么操作的找了一台有外网权限的机器把这台机器当成“采购站”所有要进内网的东西都在上面准备好。采购分三个层次系统级工具、Python 依赖、模型权重。系统级工具比较容易提前下载好 deb/rpm 安装包拷进去之后用离线方式安装就行。Python 依赖最麻烦因为第三方库之间存在大量依赖关系单纯把requests的 wheel 拷进去还不够它的依赖urllib3、certifi、charset-normalizer一个都不能少。我们的做法是锁定一个 requirements.txt然后在外网机器上用 pip download 一次性把所有依赖的 wheel 包全部拉下来。注意这里一定要加--no-deps以外的参数组合最关键的是用pip download -r requirements.txt -d ./packages/它会自动分析依赖树把每个包的所有依赖都下载出来然后我们在内网用pip install --no-index --find-links./packages/ -r requirements.txt安装。这个过程有几个必须注意的点必须用和生产环境完全一致的 Python 版本去下载比如内网是 Python 3.10外网采购站也要用 Python 3.10否则可能遇到“这个 wheel 不支持当前平台”的问题。优先下载manylinux标签的 wheel 包这类包自带 C 扩展不需要在内网现场编译。如果一不小心拿了个 sdist 源码包进去内网没有编译工具链就直接歇菜。把整份 requirements.txt 锁定到精确版本号不要用兼容范围否则内网离线安装时版本解析容易出幺蛾子。3.2 私有 PyPI 的搭建和使用光把 wheel 包拷进去手动安装在小规模验证的时候没问题但一旦 Agent 系统要扩容、要多台机器部署这种手工方式就得被替代。我们后来在内网搭了一个私有 PyPI 源用 devpi 实现把采购站下载的所有 wheel 包全部上传进去内网机器统一配置pip.conf指向这个内网源。这一步带来的收益非常直接后续安装依赖就跟外网体验基本一致只是源换成了内网地址。而且 devpi 支持缓存功能谁在这个内网源上装过一次包后面下载就走缓存速度飞快。搭建私有 PyPI 的核心配置我这里贴一下方便你照着操作# /etc/pip.conf [global] index-url http://pypi.internal.example.com/simple/ trusted-host pypi.internal.example.com timeout 60trusted-host这几行必须加上因为我们内网用的是自签名 HTTPS 证书或者干脆就是 HTTP 明文pip 默认会校验证书不加这个就会直接拒绝连接。这个问题在部署文档里经常被忽略但我们实际排障时发现十次连接失败有八次是这个原因。3.3 Rust 工具链的离线安装与 cargo vendor前面说的都是 Python 生态但我们的 Agent 编排层有一部分核心逻辑是用 Rust 写的这也是我们团队的一次尝试后面第五章会专门聊。Rust 工具链在隔离内网里的安装方式比较特殊Rust 不像 Python 有 wheel 包它必须通过rustup安装工具链而且安装过程中需要从静态服务器下载组件。我们的做法是在外网用rustup-init下载完整的工具链压缩包包括rustc、cargo、标准库、rust-analyzer然后拷贝到内网执行离线安装脚本。这里有个小技巧安装时设置RUSTUP_DIST_SERVER和RUSTUP_UPDATE_ROOT环境变量指向内网的文件服务这样后续如果需要更新组件也能走离线模式。Rust 项目的依赖处理走的是cargo vendor。cargo vendor会把项目依赖的所有 crate 源码下载到本地vendor/目录然后构建时通过.cargo/config.toml指定# .cargo/config.toml [source.crates-io] replace-with vendored-sources [source.vendored-sources] directory vendor这样cargo build就完全不需要网络了。要注意的是crate 的版本锁文件Cargo.lock必须提交到代码仓库里否则每次构建时 cargo 会尝试联网更新版本索引这在离线环境直接就报错了。3.4 容器镜像的离线化docker save 与内网 Registry我们最终的生产部署形态是容器化这又带来一个问题基础镜像怎么进内网。方案其实很简单在外网把需要的镜像拉下来然后docker save -o打成 tar 包拷贝进内网之后docker load -i导入再docker tag并push到内网自建的 Registry我们用 Harbor。这一步看似简单但坑也不少很多基础镜像的镜像层非常大比如 pytorch 镜像动辄 10GB而且存在多个平台变体保存之前要先确认架构是amd64还是arm64否则导入后跑不起来。docker save打出来的 tar 包不保留镜像的 tag 层级关系导入后必须重新 tag。如果内网有多台节点需要跑同一个镜像一定不要每台机器手动 load而是统一推到 Harbor 上节点从 Harbor 拉取。否则后续镜像更新一次你就得一台一台地拷 tar 包人会疯掉。3.5 依赖清单与完整性校验最后说说依赖清单。我们在采购站做完所有准备后拉了一份“物料清单”格式大概是这样的系统工具NVIDIA 驱动、CUDA 工具包、nvidia-container-toolkitPython 依赖requirements.txt 对应的所有 wheel 包共 147 个Rust 工具链rustc 1.75.0、cargo 1.75.0、rustup 组件模型文件llama-7b-chat-int4.gguf约 4GB、embedding 模型约 0.5GB容器镜像python:3.10-slim、pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime这份清单就是后面所有环境搭建的依据。每一件物料进来之后我们还会用 checksum 做一次完整性校验防止拷贝过程中文件损坏。文件损坏这个问题在隔离内网里特别容易被忽视——U 盘拷贝、移动硬盘传输都可能有静默的数据错误模型文件一旦损坏跑起来可能只是生成结果变“飘”完全不在代码层面报错排查难度极高。4. Agent 编排逻辑的工程化从 Demo 到可用的关键一跃环境问题解决之后终于可以聊 Agent 本身了。很多团队在“能调通模型 API”之后就宣告 Agent 成功了但真正到了工程化阶段你才会发现模型只是轮船的发动机编排逻辑才是整条船的舵和帆。4.1 ReAct 循环就是从“想一步做一步”到“想完再做”目前绝大多数 Agent 框架的底层逻辑都是 ReAct 模式Reason Act。它的工作流是这样的用户输入一个问题。Agent 把问题交给大模型让它推理我当前有哪些工具可用下一步应该调用哪一个。模型输出一个“动作”比如调用某个工具、传入某些参数。Agent 框架执行这个动作拿到结果。把结果和之前的对话历史一起再次交给模型让它继续推理。循环直到模型认为不需要再调用工具直接生成最终答案。听起来不复杂但工程化落地时有几个关键点处理不好整个系统就跑不稳。4.2 工具注册、调用与结果解析最容易出错的三个环节工具注册阶段我们用的是 JSON Schema 协议。每个工具都有一个名字、一段自然语言描述、一份 JSON Schema 格式的入参定义。模型根据这个 Schema 生成调用请求。这里有个特别重要的细节工具的描述必须写得极其具体。同样的一个“查询数据库”工具如果你只写“查询数据库”模型根本不知道怎么用但如果你写“查询 MySQL 数据库参数包括 sql必填SQL 查询语句、limit可选返回行数上限默认 10”模型就能很准确地生成调用参数。工具调用结果解析也是一个大坑。我们发现大模型生成的工具调用参数经常不合法最常见的是漏掉必填参数、参数类型不匹配、返回结果太长导致上下文爆炸。我们当时的应对办法是异常重试机制如果模型生成的参数解析失败把错误信息反馈给模型让它重新生成参数最多重试三次。三次还不行就放弃这次工具调用直接把“工具调用失败”作为观察结果返回给模型让它走下一步。4.3 上下文窗口管理养鱼还是养金鱼就看这一步Agent 的上下文窗口是有限的哪怕模型支持 128K 上下文你也不能无限制地往里塞内容。因为窗口越长推理越慢、越贵而且模型在超长上下文下的注意力集中度会显著下降。我们的策略是分层管理上下文固定系统提示词包括 Agent 的人设、可用工具列表、安全规则。这部分始终保留在上下文最前面。历史消息压缩当对话轮数超过阈值后不再把历史消息原文放入上下文而是用一个大模型对历史消息做摘要只保留摘要。工具结果截断工具返回的结果非常长比如 SQL 查询返回几万行数据时只截取前 2000 字符并注明“完整结果已截断当前显示前 2000 字符”。这套上下文管理策略我们实测下来能让 Agent 在长时间运行中保持稳定。没有这套策略之前Agent 大概聊到第十几轮就开始“精分”有时候会忘记自己已经调用过某个工具重复执行同样的动作上下文管理的价值在这个场景下体现得淋漓尽致。4.4 Python 编排栈与 Rust 编排栈的取舍在编排层实现语言上我们做了一次非常大胆的尝试主体编排用 Python主要是 LangGraph但有一个核心的调度模块用 Rust 重写。为什么这么折腾因为团队里那位 Rust 味儿很浓的同事坚持认为Python 的 GIL 限制和异步调度的不确定性在某类高并发工具调用的场景下会成为瓶颈。当时我们实测了一个极端场景50 个并发对话每个对话每分钟要调用 3 次工具Python 那套在调度层确实出现了大量的 async 阻塞和 CPU 空转。Rust 版调度模块做的事情其实很纯粹维护一个任务队列每个任务包含“对话 ID、当前状态、需要调用的工具、参数、回调地址”。它用tokio异步运行时高效地调度这些任务再把调用请求通过 HTTP 转发给 Python 那边的工具执行服务。跑完一轮之后返回结果状态更新后再进入下一轮。这个模块在并发场景下的 CPU 占用比 Python 版低了将近 40%延迟也稳定了不少。当然Rust 不是银弹。它的开发效率比 Python 低调试起来也更麻烦。如果不是遇到真实的性能瓶颈我不建议大家在 Agent 初版就引入 Rust。这个决策的关键是拿数据说话而不是为了“用 Rust”而用 Rust。5. 让 Agent 真正干活的工具链内网业务系统的对接实践Agent 编排跑通了接下来就是“给 Agent 配武器”。在隔离内网里Agent 的工具不是搜索引擎而是企业内网的各种系统。我们当时基于业务需求做了三套工具每一套展开讲都能写一篇长文这里挑最核心的分享。5.1 内网知识库检索工具让 Agent 学会“查资料”第一个工具是知识库检索。我们把企业内部的各种制度文档、项目文档、FAQ 全部导入了内网向量数据库用的是 Milvus然后封装了一个search_knowledge_base工具给 Agent 调用。这个工具的逻辑是接收一个 query 参数调用 embedding 模型生成向量在 Milvus 里做近似检索返回 Top-K 条相关文档。这里面有一个细节特别影响最终效果分块粒度。如果分块太小比如 200 字检索回来的文档片段可能语义不完整如果分块太大比如 2000 字向量化之后的表示会非常粗糙检索精度下降。我们实验下来最佳分块大小在 500-800 字之间重叠 100 字左右。5.2 内网数据库查询工具让 Agent 学会“查数”第二个工具是数据库查询。我们的业务场景需要 Agent 能从内网数据库中提取数据并生成分析结论。最初的想法很简单——给 Agent 一个query_database工具它直接生成 SQL 然后执行。这个想法很快就被我们否掉了。原因很简单大模型生成的 SQL 极容易出安全问题比如执行了全表删除。后来我们的方案是双重的第一重约束查询范围。Agent 不能直接连数据库必须通过一个查询网关网关里配置好了白名单数据表清单、查询超时时间、最大返回行数。不在这张白名单里的表直接拒绝。第二重要求 Agent 先生成“查询意图”而非 SQL。我们让 Agent 描述自己想查什么比如“我想知道 6 月份各个项目组的工单量及平均处理时长”系统把这个意图翻译成结构化的查询请求再由后台的模板引擎拼接出最终 SQL。这样做的代价是灵活性下降但安全性大幅提升。5.3 消息通知与自动化推送Agent 的“手脚”第三个工具是消息通知。Agent 需要能主动向指定群组或个人发送消息通知。这个工具实现起来本身不复杂就是调用内网消息平台 API但这里有三个实际部署中的细节问题第一消息频率限流。如果 Agent 在循环里“发疯”短时间内给同一群人发几百条消息那场面会非常尴尬。我们的做法是给消息发送工具加了硬性频控单 Agent 每分钟最多发 10 条并且所有外发消息都必须经过审核队列审核通过才真正发送。第二消息内容格式。Agent 生成的内容默认是纯文本但业务系统里消息常常需要表格、链接甚至按钮等交互卡片。我们是让 Agent 在消息内容里带上结构化标记后台再渲染成对应平台的富文本格式。第三所有工具调用行为都必须有审计日志。这也是隔离内网项目的老规矩——每一条 Agent 执行过的指令、每个触发过的动作都要留痕、可追溯。这个不是形式主义是出事之后唯一能自证清白的手段。5.4 一个真实的业务案例自动生成日报并推送为了让你更直观地理解这套工具链是怎么协同工作的我拿一个我们实际跑的日报告警 Agent 来举例。每天早上 9 点定时任务触发一个 Agent 任务用户消息是“请生成昨日的工单处理日报并发送给项目群”。Agent 的思考过程大致是这样的调用query_database从工单表中查询昨天所有已处理工单的状态分布、平均时长、超时工单明细。拿到查询结果后再调用search_knowledge_base检索一下近期是否有针对超时工单的处置规则或公告避免日报内容与最新政策脱节。模型综合两个工具的返回结果生成日报全文内容包括整体数据、趋势对比、异常点标注。调用send_message把日报以图文格式推送到指定的企业微信群组。最后生成一条简短的确认消息表示任务已完成。整个流程看起来顺理成章但为了让它从“偶尔成功”变成“稳定成功”我们在工具描述、参数设计、失败重试上迭代了不下二十轮。尤其要注意的是Agent 生成的日报内容必须经过一轮格式校验任何包含可疑数字比如负工单量、超时率超过 100%的内容都会被拦下重新生成避免把模型幻觉直接推向生产环境。6. 隔离内网特有的调试与观测看不见的网络里怎么找问题隔离内网环境里最痛苦的事情不是写代码而是出了问题根本没有外网资料可以查。Stack Overflow 打不开GitHub Issues 连不上官方文档只能凭记忆。这种“断网式开发”逼着我们建立了自己的调试与观测体系。6.1 全链路日志从用户输入到模型生成的每一步我们的第一条铁律是全链路日志一个环节都不能漏。日志记录点包括用户原始输入、Agent 每一轮的推理结果、工具调用的请求与响应、token 消耗、延迟指标、最终生成结果。每一次 Agent 执行都会生成一个唯一的trace_id贯穿整个链路。隔离内网里调试 Agent 最常见的一个场景是用户反馈“Agent 回答得不对”。想在代码里复现这个错误如果日志不全基本就是大海捞针。有了 trace_id 和全链路日志我们可以直接打开该次执行的日志精确看到模型在哪个环节做了什么样的推理、调了什么工具、拿到了什么样的返回、最终为什么给出了这样的回答。这个排查成本降了一个量级。6.2 模型输出的结构化纠错比想象中更重要的兜底机制在调试 Agent 的过程中我们花时间最多的其实是模型输出的“不听话”。大模型生成 JSON 不合法、生成工具参数遗漏、推理链中断这些问题在离线小模型上尤其常见。我们的兜底方案是设计了一个“输出修复器”当模型输出不是合法的 JSON 时先把原文本交给一个轻量级的修复模型尝试修复修复失败再去解析文本中的 JSON 片段还不行就放弃这次工具调用要求模型重新生成。这个多级修复机制让我们的工具调用成功率从 82% 提升到了 95% 以上。剩下的 5% 我们选择接受因为再花成本去优化边际收益已经非常低了。6.3 延迟瓶颈的定位与静态化优化另一个高频问题是性能调优。在隔离内网里所有的服务都是自建的任何一层的性能问题都会被无限放大。我们遇到的最典型场景是Agent 处理一个问题需要 30 秒以上用户体验很差。定位过程是这样的先看日志里的分段时间戳发现大部分时间花在了模型推理上。再看模型服务监控发现单次推理确实慢但主要慢在 prompt 太长——工具返回的知识库片段和 SQL 结果占了大半上下文。于是我们做了一个“静态化”优化把经常使用的知识库检索结果做缓存把 SQL 查询结果缩到最精简的形式再送回给模型最终单轮响应时间从 30 秒降到了 12 秒左右。还有一个让我们印象深刻的瓶颈是 embedding 服务。Agent 每次调用知识库工具都要走一次 embedding 模型如果 embedding 服务响应慢整个工具链的节奏就会被拖垮。我们后来专门对 embedding 做了批量处理和 GPU 常驻优化这个不起眼的环节反而是提升整体体验的重要杠杆。7. 踩过的坑和沉淀的经验清单最后把这半年踩过的坑整理成一份“经验清单”不一定全面但每一条都是真金白银换来的。7.1 最容易“翻车”的五个细节离线依赖版本不锁定。最初我们有一个依赖没锁版本号内网安装时 pip 自动选了一个兼容版本结果那个版本的某个行为变化导致 Agent 推理结果断断续续排查了一整天才定位到是依赖版本问题。永远使用精确版本号锁定。模型格式与推理框架版本不匹配。GGUF 文件格式本身在演进新版 llama.cpp 可能已经改了量化格式的解析逻辑模型文件也要跟着版本走。转换和部署必须记录好对应的版本号。自签名证书引发的 HTTP 连接失败。pip、conda、docker 这些工具默认都会校验 HTTPS 证书内网自建服务如果没有配好证书会带来大量“看似是网络不通、实际是证书校验失败”的问题配置信任关系要趁早。向量化检索的“分块焦虑”。分块大小、重叠长度、embedding 模型的选择显著影响知识库检索的效果。建议先用测试集做好标注评估再上线不要凭感觉调参数。Agent 的“自激循环”。没有工具调用频控和步数限制的 Agent在遇到循环推理场景时可能会无限调用工具白烧算力。必须在框架层加最大步数限制我们设的是 15 步和工具调用频控。7.2 给同样在隔离内网里做 Agent 的人几条建议架构上优先选择“模型服务与编排层解耦”的架构模型服务单独一个团队维护编排层只依赖统一推理网关这样两边可以独立演进排障时也更容易划分责任边界。交付物上一定要把“离线安装包、物料清单、安装手册”三者作为一个整体交付。很多内网项目失败不是因为技术不行而是因为文档缺失交接时根本不知道某个依赖是从哪来的、为什么这么配。运维上Agent 不是写完了就结束了它是一个需要持续观察、持续调优的系统。日志监控、告警规则、定期复盘一个都不能少。我们后来专门给 Agent 加了“异常会话自动转存”功能任何一次失败的对话都会被自动保存方便后续复盘和分析。7.3 最后说点心里话做隔离内网下的 AI Agent最大的感受是“慢”反而是优势。外网生态里你总想着今天发布的新模型、新框架明天就要用上但在隔离内网里你没有这个选项只能把手头的模型和工具打磨到极致。这种限制反而逼着我们深入理解了 Agent 的每一个环节——从模型推理、依赖管理到工具链设计、运维观测。技术上的收获比在外网“什么都拿来试试”要大得多。如果你所在的环境也是隔离内网希望这篇实战记录能给你提供一个可参考的路线图。先把模型推理服务跑稳再把依赖分发搞顺然后一步步地丰富 Agent 的工具链最后让 Agent 在业务场景里创造价值。这中间没有捷径但每条路都有人帮你蹚过了你会走得快很多。