
隔离内网环境下做 AI Agent最直接的感受是外面那些“开箱即用”的方案在真正落地时几乎全都要打上问号。模型不能调云端 API依赖包没法随时 pip install甚至连一份训练好的权重文件都可能在传输环节折腾一整天。但这类需求恰恰是政企、军工、金融等场景最普遍的刚需——数据不出域、模型不出网、系统全栈自建。这篇文章我会以“隔离内网 AI Agent 工程实战”为主线把从模型获取、推理服务搭建、知识库增强到 Agent 编排、权限控制、以及最后的踩坑排查完整串一遍。不聊概念只讲我在这种受限环境下怎么真正把 Agent 跑起来。1. 隔离内网下的技术选型与整体设计思路1.1 先想清楚约束再谈架构设计很多人一上来就开始选 Agent 框架这其实是本末倒置。隔离内网环境的第一原则是“约束驱动设计”。你要先搞清楚自己手里有什么牌再决定打法。我把最常见的约束条件列一下你可以对号入座没有公网出口OpenAI、阿里云百炼、智谱开放平台这类 API 全部不可用所有模型必须私有化部署。外网依赖不可达pip、npm、apt 的默认源全部失效GitHub 也无法访问必须走内网私服或离线安装包。算力资源通常紧张可能只有单机 1~2 张消费级显卡或几块 A10/A30别指望 72B 这种大模型的“满血版”。数据敏感度高企业内部制度、业务数据、生产库表、代码资产都不能出境甚至不能跨网段传输。系统环境碎片化存在老的 CentOS、国产化系统、容器平台、物理机混合部署跨部门协调成本极高。这些约束叠加在一起逻辑上就直接决定了技术路线的走向必须采用全栈私有化的开源模型 本地知识库 自托管 Agent 服务。注意这里说的是“服务”不一定是某个重量级框架。隔离内网里你可以先把所有组件拆成独立服务再用标准的 HTTP/gRPC 接口串联这样即使某个组件替换掉影响面也能控制住。1.2 模型选型别只看参数规模要看显存账模型选型是整个项目里最决定体验的一步。我在实际项目中反复权衡后得到一个比较稳妥的经验7B~14B 这个量级的模型才是隔离内网里性价比最高的区间。比如 Qwen2.5-7B-Instruct、Qwen2.5-14B-Instruct、DeepSeek-R1-Distill-Qwen-14B 这一类。原因也简单这个规模的模型可以在 1~2 张 24GB 显存的卡上跑 FP16/BF16 推理兼顾上下文长度和并发能力如果做量化一张 16GB 的卡就能带起来部署门槛大幅降低。模型规模与资源的大致对照表如下以单卡推理、BF16 精度估算模型参数量权重存储推理显存需求单机可部署条件适用场景1.5B ~ 3B3~8GB8~12GB单卡家用卡也能跑简单分类、摘要、脚本辅助7B ~ 8B15~17GB20~28GB单卡 24GB问答、中等推理、工具调用14B28~32GB40~56GB双卡 24GB 或单卡 80GB较复杂推理、长文本理解32B ~ 72B65GB~150GB100GB多卡集群高难度推理但运维成本陡增在 Agent 场景里模型不仅要“会聊天”还得会“看工具描述、做计划、处理 JSON 输出”。我做过对比测试7B 模型在简单工具调用上问题不大但在多步规划、API 参数纠错上明显弱于 14B。如果你的 Agent 要处理比较复杂的业务流程比如跨系统查数据、自动填报尽可能选择 14B 这个档位。如果预算实在有限再退回 7B 并配合优质提示词兜底。1.3 Agent 框架选型重度框架未必是优解目前市面上的 Agent 框架很多LangChain、LlamaIndex、Dify、Coze 开源版、FastGPT 都各有拥趸。但在隔离内网里我发现一个很容易被忽略的问题框架本身的依赖树和插件体系可能比你的业务逻辑还要重。比如某个组件底层要拉一个在线模型配置某个工具插件要连某个云端服务在离线环境下要么绕开要么自己改一个不小心就卡死在环境依赖上。从项目实战角度我更倾向于一个“分层组合”的思路流程编排层用 Dify 或自研轻量工作流引擎负责 Agent 的状态管理、节点调度、多轮对话。模型接入层统一封装为 OpenAI 兼容的 HTTP 接口后端接 vLLM / Ollama这样上层框架永远通过一个标准接口对话。工具层按照统一的函数调用规范名称、描述、参数 JSON Schema注册内部接口可以是 Python 装饰器也可以是一个独立的工具网关服务。这么做的好处是每一层之间解耦如果 Dify 太重你可以随时替换成自研的调度器如果推理框架性能不行你也只改接入层上层业务代码完全不用动。隔离内网环境里模块化、低耦合的优先级永远高于“单一框架全家桶”。2. 模型落地从公网下载到内网推理服务2.1 模型获取的完整搬运动线这一节是最容易被新手低估的。很多人以为“把模型下载下来拷贝进去就能跑”结果在传输环节反复踩坑。先给出一条我在多个项目中验证过的完整路径在能访问外网的办公机上下载模型。首选 ModelScope 魔搭社区国内源速度快、断点续传友好如果必须用 Hugging Face建议配置 hf-mirror 镜像下载速度会好一些。这里提一个细节下载前先记录模型的 SHA256 校验值模型文件在传输过程中一旦损坏后面跑到一半报错会非常难排查。打包传输而非直接拷目录。transformers 模型目录里少则几百个、多则上千个 shard 文件直接拷贝会产生大量小文件 IO内网带宽再快也会被拖垮。建议在办公机上先打包成一个大文件tar 包或带 .tar.zst 压缩然后通过移动硬盘、FTP、内网共享等方式搬到隔离区的存储节点。传输完成后立即校验。在隔离区 compute 节点上做sha256sum比对。如果走的是移动硬盘还要检查磁盘空间是否足够别出现“拷了一半才发现空间不足”的尴尬局面。我实操中遇到过一个非常典型的坑某次在 Hugging Face 下载的是 symlink 解引用后的文件树里面有个别 GB 级文件因为网络原因被截断但下载工具没有报错。拷到内网加载模型时死活报safe_open failed。最后排查了整整两天才用校验发现权重文件头尾不完整。所以“下载后核对 checksum”这件事无论如何不要省。2.2 推理服务端选型vLLM、Ollama 还是 SGLang模型文件到位之后推理服务的选择直接影响 QPS、显存利用率和接口复杂度。三者我都用过各有适用场景直接给结论推理框架核心优势适用场景内网部署难度vLLMPagedAttention、连续批处理、高吞吐、OpenAI 兼容接口生产级 Agent 服务并发要求高中需要 Python 环境、CUDA 版本匹配Ollama开箱即用、GGUF 格式友好、模型管理简单快速验证、单机小并发、资源受限机器低几乎零配置SGLangRadixAttention、复杂采样控制、多模态支持长上下文、复杂 Agent 推理中高依赖较新从工程化角度我强烈建议在生产环境用vLLM。原因有三一是它原生提供/v1/chat/completions接口上层 Agent 框架零改造接入二是它在高并发多路推理时的吞吐表现明显优于 Ollama三是它内置了 OpenTelemetry 监控方便接日志链路。Ollama 更适合做原型验证或给测试组快速出一版效果。在最终交付时如果业务并发要求不高Ollama 也能胜任但要注意它的并发控制不如 vLLM 精细单个长请求可能阻塞其他请求。2.3 vLLM 启动参数与量化选择的经验值这里贴一份我在内网环境常用的 vLLM 启动命令模板python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen2.5-14B-Instruct-AWQ \ --served-model-name qwen14b \ --max-model-len 8192 \ --gpu-memory-utilization 0.92 \ --tensor-parallel-size 2 \ --enable-auto-tool-choice \ --tool-call-parser hermes \ --port 8001几个关键参数的注意点--max-model-len不是越大越好。它决定模型能处理的最长上下文但也会直接吃掉显存。如果你的 Agent 场景单轮交互在 4K tokens 以内设 8192 足够了不必盲目追求 32K。--gpu-memory-utilization建议留出 8%~15% 的显存余量给 KV Cache 碎片和临时张量否则高并发下容易 OOM。我一般推荐 0.9~0.93。--tensor-parallel-size在多卡场景下设为卡数但注意单卡显存小于 20GB 时建议优先用量化模型而不是强行 TP。--enable-auto-tool-choice和--tool-call-parser是给 vLLM 开启原生工具调用解析用的如果你的模型不是专门的 tool-call 微调版本可能不稳定需要自己做一层工具调用解析。关于量化很多人在内网环境纠结模型精度。我的建议是如果显存足够优先 BF16/FP16 原始权重量化是“显存不够时不得不做”的方案不是“一劳永逸优化性能”的方案。以 Qwen2.5-14B 为例BF16 权重约 32GB推理显存需求约 56GB如果用 AWQ 4bit 量化权重大约 9GB推理显存需求约 20GB。后者能落到单张 24GB 显卡上但推理质量和复杂工具调用的准确性会有下降。如果 Agent 场景对逻辑要求高我宁愿用 7B 原始精度也不用 14B 的激进量化版本。2.4 服务可用性验证与性能压测模型服务启动后不要急着接 Agent。先做一轮简单的 smoke test确保接口响应正常curl http://127.0.0.1:8001/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen14b, messages: [{role: user, content: 11等于几}], max_tokens: 64, temperature: 0.1 }然后观察首 token 延迟、总生成时长这两个指标。内网环境如果是单卡 14B 模型首 token 延迟通常在 300~800ms 之间取决于上下文预填充长度。如果明显偏高优先检查是否做满了 KV Cache或是否被其他任务占用显存。压测阶段我建议直接用 5~10 个并发线程请求不同问题观察 vLLM 日志中的排队时间和吞吐。如果 Agent 在后续运行时总报“token 耗尽”或“请求排队超时”八成不是模型问题而是业务侧的频繁长上下文请求把服务打满了。3. 知识库与 RAG让 Agent 在内网里“有据可查”3.1 为什么内网 Agent 一定绕不开 RAG坦白说Agent 在隔离内网最早落地的业务形态往往不是“自由对话”而是私有知识库问答。原因天然成立企业制度文档、设备手册、代码库注释、历史故障记录这些数据本身就存在内网对外部模型来说是完全不可见的。如果让 Agent 直接“空对空”回答它只能靠训练记忆里的常识碰到专属名词和内部流程几乎必然产生幻觉。RAG检索增强生成的运作逻辑本质上是“先查资料再写答案”。Agent 收到用户问题后先转换成向量和关键词去检索本地知识库把命中的文本片段作为上下文塞给模型让模型基于这些材料组织回答。这个链路全部在内网完成既满足数据不出域的要求又大幅提升了回答的准确性和可解释性。3.2 文档解析与分块内网文档的隐藏难题很多人在做 RAG 时把精力都花在向量化模型上结果忽略了最基础的文档解析。实际上内网文档的质量参差不齐常见的坑包括扫描版 PDF很多制度文件是扫描件没有内嵌文字层直接解析出来是一张空纸。必须接 OCR 工具比如 PaddleOCR、Tesseract离线安装先识别再走后续流程。复杂表格PDF 里排版歪七扭八的表格直接文本抽取会丢失行列结构。优先用 camelot、pdfplumber 提取表格结构再按行转成 Markdown 或 JSON。Word 和 Markdown 混排建议统一转成 Markdown 或 JSON 再入库后续分块时能利用标题层级信息。分块策略上我采用的是一套比较稳妥的经验参数chunk_size 设为 400~600 个中文字符overlap 设为 50~100 字符。这个大小既能保证单块语义完整又不至于让向量检索时“语义稀释”。如果文档本身有清晰的标题层级优先按“章节段落”的结构化方式切分而不是机械按固定长度切。例如一句“3.2 续费流程”下面的一段内容应该被切为独立一块而不是和上一节混杂。对代码类、配置类知识chunk_size 可以适当调大到 800~1000但 overlap 也要跟着加避免把关键语句截断。为什么要强调 overlap 这个参数因为检索阶段按向量相似度召回片段时如果恰好问题命中了两个 chunk 的边界处overlap 区域可以保证关键句至少完整地出现在某一个 chunk 中减少“总是差半句话”的尴尬。3.3 Embedding 模型选型与向量库落地内网环境没法调用公网向量化 API所以 Embedding 模型也必须本地部署。我在中文场景下最推荐的是BGE-M3它同时支持稠密向量和稀疏向量还能做多语言对中文长文本的语义理解比很多同量级模型都稳。如果显存紧张可以用 bge-large-zh-v1.5 或 text2vec-large-chinese。Embedding 模型维度显存需求推理中文效果备注bge-m31024约 2GB优秀支持稠密稀疏可混合检索bge-large-zh-v1.51024约 1.5GB良好体积小、部署简单text2vec-large-chinese1024约 1GB中上资源最省适合极简环境向量数据库方面如果只是几百份文档级别的知识库用轻量的Qdrant或Milvus Lite就足够了。如果内网里已有 Elasticsearch也可以直接利用它的 kNN 检索能力少维护一个组件。说到底向量库不是重点关键是在索引字段里同时保存文本原文、来源路径、分块序号这样命中之后才能拼引用、给追溯而不是只给一段裸文本。3.4 混合检索关键词和向量必须两条腿走路纯向量检索最大的硬伤是对“精确字面词”不敏感。比如员工查“请休假管理办法第十二条”向量搜索很可能找出一堆“请假”相关却完全没提“第十二条”的段落而 BM25 关键词检索能精准命中带星号条款编号的段落。所以生产环境里我的默认配置是向量检索 BM25 关键词检索并行然后做分数融合。融合公式不一定要复杂我常用的一个简单方案如下score alpha * normalized_vector_score beta * normalized_bm25_score其中 alpha、beta 根据业务调优一般在 0.6~0.8 和 0.2~0.4 之间。如果场景里“严谨条款查询”居多就把 beta 调高如果“开放式问答”居多就把 alpha 调高。实际操作里我会用一组验证集跑一遍不同参数组合下的召回率再固化到配置文件中而不是拍脑袋定。注意如果知识库里的文档有大段重复内容比如同一篇制度在多个月度版本中各出现一次检索时要加一层“按文档名分块序号去重/过滤”的逻辑否则 Agent 的上下文里塞满了互相矛盾的内容回答很容易出问题。4. Agent 工具调用与权限控制的工程化设计4.1 Agent 从“问答”到“行动”的跃迁RAG 解决的问题是“让 Agent 知道”真正让 Agent 产生价值的是“让它能做事”——调用内部接口、查数据库、操作工单系统、生成报表。这时候系统就不再是简单的“我问你答”而是进入经典的 ReAct 循环模型根据用户意图生成计划选择工具执行工具观察结果再决定下一步动作。在内网环境里Agent 要触达的工具五花八门可能是内部的 HTTP API、数据库连接、脚本编排、甚至某个老旧系统的 CLI。我个人的工程经验是不要让 Agent 直接连内部系统在中间加一层标准的“工具网关”。所有 Agent 可调用的动作都由工具网关统一暴露为“名称 描述 入参 JSON Schema”的形式。这样做有几层好处对 Agent 来说它只需要理解统一的函数调用协议不需要关心后端是 REST、gRPC 还是 RPC。对平台方来说工具权限、限流、审计可以在网关统一管控。对业务方来说新接入一个内部系统时开发量被压缩到“写一个适配器”而不是改 Agent 框架。4.2 一个可复用的工具注册与调用实现下面我用 Python 给出一个简化但可直接落地的工具注册示例。核心思想是用装饰器把普通函数变成 Agent 可调用的工具并在运行时自动生成描述和参数 JSON Schemafrom pydantic import BaseModel from typing import Optional TOOL_REGISTRY {} def register_tool(name: str, description: str, params_model: type[BaseModel]): def decorator(func): schema params_model.model_json_schema() TOOL_REGISTRY[name] { function: func, description: description, parameters: schema, } return func return decorator class QueryOrderParams(BaseModel): order_id: str env: Optional[str] prod register_tool( namequery_order_status, description根据订单编号查询订单当前状态支持生产/测试环境, params_modelQueryOrderParams, ) def query_order_status(params: QueryOrderParams): # 内部实现调用后端订单服务的HTTP接口 return {order_id: params.order_id, status: 已发货, env: params.env}这里有个很重要的细节description 必须写得像“给另一个工程师看的说明”要包含这个工具是干嘛的、什么时候用它、入参含义。因为 Agent 选择工具时完全依赖这段文本写得太抽象比如“查询订单”模型很可能摸不准该不该调用也容易填错参数。工具执行的返回结果建议统一为 JSON 序列化文本长度尽量控制在 1~2KB 内。如果工具返回值太长比如数据库查出一百行记录Agent 的上下文窗口会被瞬间塞满既费 token 又拖慢后续推理。正确做法是先在工具内部做摘要、截断、分页只把关键字段返回给模型。4.3 权限管控Agent 的“能动范围”必须有红线这一点我必须单独拿出来强调。隔离内网里 Agent 接触的都是核心系统一旦控制不好后果比在公网环境更严重。我的原则非常简单Agent 默认只读写操作必需显式授权。具体落地时我会把工具分成三类只读工具查询类接口、检索类脚本、报表生成Agent 可以自主调用不需要人工介入。受控写工具比如创建工单、发送消息、修改配置。Agent 生成调用请求后必须先进入一个“人工审批”节点由用户在管理界面确认后才会真正执行。高风险写工具删除数据、批量修改、资金操作。这类工具我干脆不对 Agent 开放或者仅在极少数白名单会话中临时开放。权限控制不仅是功能开关还要落到审计链路。每个工具调用记录都要带全局 trace_id把用户问题、Agent 计划、工具入参、返回结果、审批人、执行时间串成一条完整的日志链。这也是隔离内网项目验收时审查方最看重的部分没有审计日志的 Agent 系统架构评审大概率过不了。实践建议在 Agent 的 System Prompt 里明确写入“只能在允许的工具列表中执行操作当用户请求超出能力范围时必须明确拒绝并给出原因”。这是兜底防线。模型幻觉不可避免但你可以通过提示词和工具层双重约束把风险控制在可接受范围内。4.4 多轮对话与上下文管理Agent 一旦开始干活就涉及多轮对话和中间状态管理。隔离内网里跑模型上下文越长推理越慢、越占显存而且费用如果按内部资源计价也会上涨。所以上下文管理不是可选项是必选项。我的做法是按 session 维度维护消息历史同时在每次 Agent 进入推理前做一次“上下文裁剪”只保留最近 N 轮对话比如最近 5 轮老内容如果涉及关键信息可以用摘要取代原文。工具调用记录单独存储。模型上次调用某工具返回了很长结果这次推理时就没必要把完整历史再塞一遍而是用一个简短的“工具结果缓存”替换。设定最大迭代轮数上限比如单任务最多 8 轮 tool call。超过上限强制结束返回“任务超限请简化目标”防止 Agent 陷入死循环空耗资源。关于 token 的理解简单说一句大模型每次推理能处理的文本长度以 token 为单位一个中文汉字大约等于 1~2 个 token。Agent 场景里 token 消耗不只是用户问题还包括系统提示词、工具描述、历史消息、检索片段和模型输出。所以做一个上下文管理器非常关键否则一次看似普通的任务可能把 8K 的上下文窗口全部塞满模型反而“迷失”在信息海里。4.5 工作流编排可控优先自由 Agent 为辅最后一层是编排策略。我强烈建议生产环境采用“工作流为主、自由 Agent 为辅”的混合形态凡是能预设路径的任务全部显式编排成固定泳道只有那些需要发散推理的开放问题才交给 Agent 自主规划。固定工作流的优势在于可预测、可测试、可回滚。比如“查订单状态”这个任务你可以直接画一个三步链路解析订单号 → 调用订单服务 → 汇总返回。每一步的参数映射都是确定的Agent 只是承担“语义理解 入口分发”的角色。而自由 Agent 的优势是灵活适合“帮我分析这个系统日志异常的可能原因”这类开放式请求。生产项目中自由 Agent 的任务占比我一般控制在 20%~30% 以内再高就很容易出现不可控的行为。5. 高频问题排查与落地经验总结5.1 实战中最常见的 6 个坑我把隔离内网 Agent 项目里踩过的典型问题整理成一个速查表你在实施过程中可以直接对照现象可能原因解决思路模型服务启动后很快 OOMmax-model-len 设太大或未合理设置 gpu-memory-utilization调低 max-model-len检查张量并行配置必要时切换量化模型Agent 调用工具频繁参数错误工具描述不够详细或者入参 Schema 不合格重写工具 description给每个字段加示例值字段尽量用字符串而非嵌套结构回答引用了知识库内容但答非所问文档分块过粗或 embedding 效果差调整 chunk_size/overlap改用 bge-m3增加混合检索Agent 在某类任务上反复执行同一工具工具描述有歧义或模型把工具选择当成了唯一正确答案在启用工具前加“是否需要调用工具”的判断节点限制最大迭代轮数vLLM 排队时间越来越长请求并发高或上下文过长导致 GPU 吞吐下降增加请求级超时启用 continuous batching必要时扩容多副本pip 安装依赖一直超时内网无外网源且未配置私有仓库在外网机用 pip download 拉取全套依赖传到内网后离线安装或搭建 Nexus/PyPI 私服5.2 内网依赖管理的完整方案离线依赖安装是隔离内网项目里绕不开的“隐形工作量”。我的标准做法是先在一台与目标环境同架构同 CPU、同 CUDA 版本、同 Python 版本的外网机器上用 pip download 把项目依赖整体拉下来pip download -r requirements.txt -d ./offline_packages \ --platform manylinux2014_x86_64 \ --python-version 3.10 \ --only-binary:all: \ -i https://pypi.tuna.tsinghua.edu.cn/simple然后连同离线包目录拷贝进内网执行离线安装pip install --no-index --find-links./offline_packages -r requirements.txt这里很容易忽略的一个点是--platform和--python-version必须与实际运行环境匹配否则拷进去的包可能因为二进制不兼容直接报错。更稳妥的做法是直接用 Docker 封装整个运行环境把 Python 版本、CUDA 依赖、系统库都打进镜像这样内网只需导入镜像不需要挨个装依赖。这个方案在异地交付场景尤其好用。5.3 从项目复盘看隔离内网 Agent 的“真正难点”在哪里最后聊一点个人体会。很多人以为隔离内网做 Agent难在算法、难在模型调优实际上做过一轮你会发现最耗时的永远是环境适配、数据工程和稳定性治理。比如模型在公网上一跑就出好效果搬到内网后却因为一个 CUDA 版本不一致、一个 tokenizer 文件缺失折腾一整天知识库文档解析阶段几百份 PDF 里各种奇葩版式要一套容错性足够强的解析管线而不是炫技式的处理代码Agent 在演示环境能完成的任务因为内部系统响应慢、超时时间没配好到了生产环境就开始随机失败。这些问题没有一个是“模型不够聪明”导致的全是工程问题。所以我的建议很明确如果你要在隔离内网正式落地 Agent先别急着上大模型、上复杂框架而是先跑通一条最小链路——单机部署 7B 模型 → 用 curl 验证接口 → 处理一份真实文档进知识库 → 封装一个只读查询工具 → 让 Agent 完成一次“查文档调工具生成答案”的完整调用。这条链路稳定跑一周再考虑横向扩展工具数量和提升模型规模。善战者无赫赫之功先让系统“稳下来”比“堆功能”重要得多。如果你手头正好也在做类似的项目欢迎在实际部署时对照这篇文章里的参数和步骤做验证。碰到底层依赖、模型精度、工具调用这些具体问题随时可以在评论区交流我尽量给出当年踩坑之后的可用解法。