news 2026/10/6 15:17:14

隔离内网AI Agent工程实战:从离线部署到稳定运行

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
隔离内网AI Agent工程实战:从离线部署到稳定运行

去年末,我们接了一个在隔离内网里交付 AI Agent 项目的活儿。机房物理断网,业务数据不能出域,但客户要求"智能体"必须能对话、能查内部系统、能写总结。一开始团队里有人认为:内网跑 Agent,无非是把模型文件拷进去、装个环境、起个服务,不就行了?结果真正动手才发现,隔离内网不是"没有网",而是整个技术栈的获取方式、依赖管理方式、工具调用方式、故障排查方式全部变了。这篇就围绕"隔离内网下 AI Agent 工程实战"这个主题,把我们踩过的坑、筛选出能复用的方案,以及一套从架构到部署的落地框架完整写出来。内容覆盖模型私有化选型、离线环境构建、技能层工具接入、知识库搭建、并发改造、运维监控与模型迭代,适合正在做私有化 Agent 交付、对数据安全要求较高的读者参考。

1. 隔离内网部署AI Agent,核心矛盾从"智能"转向了"交付"

1.1 为什么内网环境会把Agent项目拖入地狱模式

先说结论:隔离内网下做 Agent,技术难度本身不高,真正折磨人的是"依赖管理"和"环境一致性"。外网环境里装一个 Python 包可能就是一条 pip install 命令,缺什么补什么;但在隔离内网里,没有公网源、没有镜像源、没有预编译 wheel 缓存,你面对的只有一台干净的服务器和一块装有安装包的移动硬盘。

更要命的是 Agent 系统的依赖是"多层叠加"的:底层有 PyTorch、Transformers 这类重型框架,中层有 LangChain、LangGraph 这类编排框架,上层还有向量库客户端、HTTP 服务框架、任务队列、OCR 识别库等等。任何一个包版本不对,或者某个传递依赖缺失,整个链路都跑不起来。我们在交付时碰到过一次特别典型的状况:开发机上是 Python 3.11 + LangChain 0.1.x,内网服务器是 Python 3.9 + 旧版 NumPy,结果 Embedding 模型加载后做向量检索时,数组广播直接报错,排查了两天才定位到是依赖版本组合问题。

所以在隔离内网里做 Agent,第一优先级不是"模型效果多好",而是"交付链路多稳"。你需要把整个项目当成一个"自包含的离线制品"来构建,而不是把一堆代码扔到内网里再现场解决环境问题。

1.2 我最终定下的分层架构

我们最终采用的架构,是按"能力边界"分层的,每一层都尽量解耦:

层次职责典型组件
模型层提供大模型推理与 Embedding 能力vLLM、SGLang、Ollama、本地 Embedding 模型
语义层负责 Agent 的规划、记忆、工具调度LangGraph 状态机、LangChain 工具抽象
技能层把内部系统能力封装成工具FastAPI + 内置函数、MCP 协议服务、HTTP 调用
知识层私有知识库的写入、检索、重排Milvus、Chroma、BM25、本地 Rerank 模型
调度层管理多轮任务、队列、并发、流式FastAPI、Celery、Redis Stream、SSE
服务层对外提供统一 API 网关、权限校验Nginx、网关服务、统一身份认证
运维层日志、监控、模型热更新、备份Prometheus、Grafana、systemd、K8s

这套分层不是一开始就想清楚的,而是被内网环境的"孤岛效应"逼出来的。比如在技能层,我们要求所有工具调用都必须走统一的协议,内网里不能出现"开发机上有这个环境变量、内网没有"这种隐性依赖;在模型层,所有模型文件固定在同一个目录结构下,便于内网服务器批量拷贝。后面所有章节讲的,基本都是在这套分层里逐个击破的实战记录。

1.3 模型选型:一切从"能私有化"开始

既然要在隔离内网跑,第一步必然是模型选型。我们的约束很明确:模型必须能私有化部署、必须能用单卡或双卡 GPU 带动、中文效果要过得去。在这个前提下,7B 到 14B 规模的开源模型是主力区间。

主流选型上有几条路线:Qwen 系列是我们在中文场景的首选,文档全、生态好、指令遵循能力在同尺寸里算是前排;GLM 系列在中文对话上有自己的优势,特别是长文本场景;如果是纯英文为主的技术文档问答场景,Llama 系列也能打。我们最终选了 Qwen2.5-7B-Instruct 作为主对话模型,搭配一个刚过 1B 的轻量模型做意图分类和摘要,Embedding 走 BAAI/bge-m3 本地部署。

有个很关键的细节:模型量化一定要提前在开发机上测好。内网 GPU 型号五花八门,有些老卡只支持 FP16,有些新卡可以跑 INT4/INT8。我们遇到过客户服务器是 V100,结果我们带过去的 AWQ 量化模型格式加载不了,只能现场现场重新导出。所以无论你倾向 GPTQ、AWQ 还是 GGUF,都要先在目标机器同型号的显卡上跑一遍完整加载和推理测试,别只在本机验证完就打包。

2. 先解决依赖环境的离线问题,再谈智能

2.1 离线 Python 环境的三种思路,我推荐最笨的一种

隔离环境下构建 Python 环境,传统的有三条路:pip download 打包 wheel、全量离线 wheelhouse、容器镜像迁移。我们三路都实际试过。

  • pip download 方案:在开发机上用pip download -r requirements.txt -d ./packages下载所有 wheel,但有个坑是不少包在 PyPI 上同时提供 sdist 源码包和 wheel 二进包,如果下载策略没指定--only-binary=:all:,会混入一堆需要现场编译的源码包,内网没有编译工具链就卡死了。即便打包完整,内网服务器系统是 CentOS 7 还是 Ubuntu 22.04,Python 是 3.9 还是 3.11,glibc 版本差异都会让 wheel 失效。
  • wheelhouse 全量方案:提前准备一个包含所有安装包的文件夹,然后内网 pip install --no-index --find-links。思路没问题,但本质上和第一种一样受限于同架构同系统同 Python 版本。
  • 容器镜像迁移法:在开发机上用 Docker 构建一个完整镜像,这个镜像里已经装好了 Python 环境、所有依赖、甚至代码和模型路径规划都做好了。然后把镜像 save 成 tar 包,拷进内网后 docker load。内网服务器只要支持 Docker 运行,基本不需要关心宿主机 Python 版本和依赖冲突问题。

我们最终选的就是第三种,虽然业内调侃"容器迁移是最笨的办法",但它恰恰是隔离内网交付里故障率最低、复现性最强的方式。第一版我们图省事,想用 Python 虚拟环境直接搬运,结果内网服务器缺 libgomp.so.1、缺 OpenSSL 1.1,一个个补系统依赖补到崩溃。换成容器后,宿主机只需要有 NVIDIA 驱动和 container runtime,其余全部封装进镜像,干净利落。

2.2 容器镜像迁移法实操记录

具体操作上,建议把整个 Agent 项目做成一个 Dockerfile 多阶段构建。第一阶段用 python:3.11-slim 作为基础镜像,安装 PyTorch 等重型依赖;第二阶段拷贝项目代码,安装 requirements;第三阶段设置启动命令。注意几个工程细节:

第一,基础镜像不要用带 CUDA 的大镜像,因为内网服务器的 GPU 驱动版本未知,镜像里带 CUDA 很容易和宿主机驱动起冲突。建议基础镜像用纯 CPU 版本,然后在运行时通过--gpus all和宿主机驱动配合。PyTorch 在 Docker 里加载 CUDA,靠的是宿主机 NVIDIA 驱动 + 容器内 CUDA 运行库,这个组合我们测下来最稳。

第二,在开发机上提前把模型文件放在镜像外部的独立目录,避免把几十 GB 的模型打进容器镜像。这样做的好处是:模型更新只需要替换外部目录,不用重新构建整个镜像;坏处是镜像内代码要写清楚模型路径的配置,统一从环境变量读取。

第三,整个镜像 build 完成后,用docker save agent-image:latest | gzip > agent-image.tar.gz压缩。在隔离内网里用zcat agent-image.tar.gz | docker load导入。压缩率大概能到 30%-50%,一个 6GB 的镜像压缩后可能只有 2-3GB,U 盘就能带走。

2.3 模型文件的离线加载,隐藏的坑很多

模型文件在内网加载,和在线环境下最大的区别就是 HuggingFace Hub 的行为。默认情况下,Transformers 加载模型时会尝试访问 HF Hub 检查是否有更新,即使你已经指定了本地路径,某些版本库还是会发起网络请求。解决方式是设置环境变量:

export HF_HUB_OFFLINE=1 export TRANSFORMERS_OFFLINE=1

这两个变量要写进容器启动脚本,否则内网环境下的模型加载会出现"卡住几分钟然后超时"的诡异现象。我们在第一轮交付时就因为这个浪费了整整半天,在线环境从没暴露过这个问题。

另一个坑是 transformers 的缓存目录结构。如果你在开发机上用snapshot_download下载过模型,缓存目录里会有 blobs 和 snapshots 两套结构。直接把这个缓存目录拷到内网是可以的,但路径字段必须保持一致。更稳妥的做法是:在开发机上把模型文件整理成标准目录(例如models/qwen2.5-7b-instruct/下直接放 config.json、model-00001-of-00004.safetensors 等),然后用代码指定本地路径加载,不要依赖缓存机制:

model = AutoModelForCausalLM.from_pretrained( "/opt/models/qwen2.5-7b-instruct", trust_remote_code=True, torch_dtype=torch.float16, device_map="auto" )

2.4 版本一致性:不可复现的构建就是灾难

这一点我单独拎出来强调:Agent 项目不是一个简单的单体脚本,是多个库之间深度耦合的系统工程。LangChain 0.1 时代和 0.2/0.3 时代的 API 变化巨大,Flow 概念的引入、工具调用的参数格式、回调机制都改过;Pydantic 从 V1 迁移到 V2 时,很多 LangChain 组件的模型配置写法也完全不同。我们内部就出过一次事故:开发机用 LangChain 0.2 跑通的功能推到内网时,结果发现内网镜像里锁的是 0.1.17,工具调用的参数是从arguments字段解析的,两边格式对不上,Agent 一步工具都没调用成功。

所以,必须在项目仓库里同时锁定三个层级的版本:Python 版本、核心依赖库版本、以及所有组件的传递依赖版本。具体要求:

  • 用pip freeze > requirements-lock.txt生成完整的锁文件,不能只写langchain>=0.1这样的宽松约束;
  • 镜像内统一使用同一个虚拟环境路径,环境变量里不要出现多个 Python 版本混淆;
  • 记录基础镜像的 tag,包括python:3.11-slim@sha256:xxx这种不可变引用,别用 floating tag。

如果团队里有多个人协作,建议所有依赖变更都必须走"重新构建镜像 -> 完整回归测试 -> 导出交付镜像"这个流程,别让开发机上的临时性调整悄悄流到交付物里。

3. 技能层改造:把工具调用搬到内网

3.1 外网时代的工具调用,在内网基本失效

Agent 的核心能力之一是工具调用。外网环境下,大家的 Agent 动辄接上几十个 API:天气查询、新闻聚合、网页检索、代码仓库操作等等。但在隔离内网场景下,这些外部工具全部失效。你面对的是一堆内部系统:OA 审批接口、运维工单系统、数据库查询平台、内部Wiki、资产管理系统,甚至还有一堆老掉牙的 CGI 接口,全是非标准协议、非标准认证。

这就带来一个原则性调整:内网 Agent 的工具集不是"能接什么就接什么",而是"必须接客户真正需要的、且权限边界清晰的系统"。我们交付时,第一个版本把 Agent 描述成"万能办公助手",结果客户试用时提出一堆"帮我查下今天的天气"这种根本没法支持的需求。后来学乖了,工具列表完全围绕客户的实际业务流来设计,比如"提交运维工单""查询业务报表数据""检索内部知识库""生成周报摘要"。Agent 的能力边界和客户预期对齐了,项目验收才顺利。

3.2 用MCP协议统一工具接入

工具接入的协议,我们建议直接用 MCP(Model Context Protocol)。虽然它最初是为了解决模型与外部工具的标准通信问题,但在内网环境下,它带来的"标准接入层"价值更大——不同内部系统可以各自封装成一个 MCP Server,Agent 主程序只负责和多个 MCP Server 通信,内部系统怎么改都不影响 Agent 主体。

我们在内网技能层做了这么一套设计:

  • Agent 主服务通过 MCP client 连接本地 MCP Server;
  • 每个内部系统一个独立 MCP Server 进程,互相隔离;
  • MCP Server 内部完成认证、参数校验、调用内部 API、返回结构化结果;
  • 所有工具通过 MCP 暴露的能力都经过统一审计日志记录。

实际实现一个内网工具的 MCP Server,核心代码并不复杂。下面是一个从内部工单系统查询状态的简单示例,用 Python 实现:

from mcp.server.fastmcp import FastMCP mcp = FastMCP("ticket-tool") @mcp.tool() def query_ticket(ticket_id: str) -> str: """查询内部工单状态,返回工单进度摘要""" # 这里调用内部工单系统 HTTP API,并做鉴权 response = internal_ticket_api.query(ticket_id) if response.code != 0: return f"查询失败: {response.msg}" return f"工单 {ticket_id} 当前状态: {response.data.state},处理人: {response.data.assignee}" if __name__ == "__main__": mcp.run(transport="stdio")

Agent 主服务侧,用 LangChain 的 MCP 适配器把工具加载进来,注册进 Agent 的工具列表:

from langchain_mcp_adapters.tools import load_mcp_tools from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client server_params = StdioServerParameters( command="python", args=["mcp_servers/ticket_tool.py"], env={"INTERNAL_API_KEY": os.environ["INTERNAL_API_KEY"]} ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: tools = await load_mcp_tools(session) # 将 tools 注入 Agent executor / graph

这个方案有一个非常大的好处:MCP Server 和 Agent 主服务是进程隔离的,某个工具进程崩溃了,重启这个 Server 就行,不会拖垮整个 Agent 服务。我们在后期迭代中,新接入一个内部系统只需要新写一个 MCP Server 文件,路径下注册启用即可,不再修改 Agent 主体代码。

3.3 工具设计守则

内网工具设计,绝大多数坑都出在"参数设计太粗"或"返回内容过大"上。总结三条守则:

第一,工具的粒度要"原子"。不要把"处理工单全流程"做进一个工具里,而是拆分成"查询工单""指派处理人""更新状态""添加备注"四个独立工具。模型的工具选择能力受限于 prompt 中工具描述的清晰程度,工具描述要写明"什么时候用它、输入是什么、输出结构是什么",描述越具体,模型选错的概率越低。

第二,工具的返回内容必须是"给模型看的结构化摘要",不是原始 API 的完整报文。我们遇到过一版实现直接返回 JSON 原始大字段,模型读了几千个 token 才能找到关键信息,多轮对话越聊越慢。后来每个工具都做了一次返回裁剪,只保留状态、关键字段、摘要文本,Token 消耗直降 70%。

第三,工具内部必须做超时和异常兜底。内网 API 一样有变慢、宕机的可能。所有工具调用都要设置超时时间(内部系统一般 5 秒足够),超时后返回"系统暂时不可用,请稍后重试",不能让一次工具调用卡死整个 Agent 对话。

4. 知识库私有化:向量、分块与召回

4.1 Embedding模型选型与维度坑

企业内部的 Agent 落地,大概率逃不开知识库问答。隔离内网场景下,在线 Embedding API 不可用,必须本地部署向量模型。我们实测下来,BAAI/bge-m3 是一个很稳的选择:中文效果不错、支持 8192 长度、同时支持稠密向量和稀疏向量两种检索模式,在纯中文企业文档场景下比同规模的通用模型表现更好。

但这里有一个必须提前规避的坑:向量维度不匹配问题。bge-m3 默认输出的向量维度是 1024 维。如果你的现有代码是从 OpenAI Embedding API 迁移过来的,它默认输出 1536 维,两者完全不是一套东西。如果你的知识库之前已经用在线 API 向量化好了,切到本地模型后必须全量重新向量化,否则检索出来的相似度毫无意义。

在工程上,我们建议在代码里做成一个 Embedding 服务抽象层,所有向量化请求统一走这个服务,后续想替换模型,只需要改一处配置即可。类似这样:

class LocalEmbeddingService: def __init__(self, model_name: str = "BAAI/bge-m3"): self.model = SentenceTransformer(model_name, device="cuda") def embed_documents(self, texts: list[str]) -> list[list[float]]: return self.model.encode(texts, normalize_embeddings=True).tolist() def embed_query(self, query: str) -> list[float]: return self.model.encode([query], normalize_embeddings=True).tolist()

4.2 混合检索是内网知识库的底线方案

只做向量检索,在内网知识库场景下很容易出现"语义相似但答案错误"的情况。企业文档里存在大量专有名词、编号、型号,比如"WCS-2200 型传感器",向量模型对这类精确标识的召回能力远不如关键词检索。所以我们采用了混合检索:向量检索 + BM25 关键词检索并行执行,再做结果融合。

具体实现不复杂。向量检索用 Milvus 或 Chroma,BM25 可以用 Elasticsearch,但隔离内网里没必要为一个关键词检索专门部署 ES,直接用 Python 的 rank_bm25 库,把文档切好分块后内存构建索引就行,几十万篇文档以内性能完全够用。然后融合阶段,给向量检索和关键词检索各自设置权重,推荐从 0.6/0.4 起步,根据实际测试效果调整。

另外强烈建议在知识库链路上加一层 Rerank。普通向量检索的 Top 20 结果里混着不少噪声,直接全量丢给大模型会拉低回答质量。我们用本地部署的 bge-reranker-v2-m3 对候选结果重排,取 Top 4-5 给模型。这个额外的重排步骤,对回答准确率提升非常明显,特别是知识库文档之间存在较多相似内容的场景。

4.3 分块策略与召回质量

知识库的召回效果,一半取决于分块策略。我们踩过的分块坑,详细说一下。

第一种常见做法是按固定字符数切块,比如每 500 字切一块,再加 50 字重叠。这个做法简单,但很可能把一张表格拆得七零八落,或者把一个完整概念切断成两半,导致召回的内容语义不完整。

第二种是语义分块,根据文档标题和段落边界进行切分。对 Markdown 或带结构的文档,按标题层级切分效果很好。对 PDF,可以先做版面分析,按标题、段落、表格分区域切块。适当的做法是:优先保留文档结构,按标题层级把每个章节作为单独的检索单元;长章节内部再用固定窗口做二次切分,保证单块长度在 300-500 字之间,避免单块过大。

同时要控制单块召回的"上下文完整度"。文档中经常出现"如上表所示""该模块的接口定义如下"这类依赖前文的表达,如果切块过于碎片化,模型拿到的片段就是一堆指代词,无法生成有效的回答。我们最终采用了两级分块策略:粗分块按章节,细分块按段落,向量化时用细块,但召回结果返回时带上所属章节的完整上下文,模型理解效果会改善一个档次。

4.4 RAG的克制使用

内网知识库项目接多了,会发现一个共同问题:客户对 RAG 的期望过高,总觉得有了知识库,模型就能成为"公司万事通"。实际上的经验是:能不用 RAG 就让模型直接回答的,尽量直接回答;必须用 RAG 的,一定要做好"检索失败兜底"。

检索失败的表现很典型:用户的问法跟知识库文档里的表述差别很大,向量检索和关键词检索都召不回有效内容。这种情况下,如果 Agent 强行编造一篇答案,企业场景是无法接受的。我们的做法是在 RAG 链路里加一道"召回置信度"判断,检索出的最高相关分数低于阈值时,Agent 明确回复"我在内部知识库中没有找到相关内容,请换个说法,或联系文档负责人更新资料"。这个 "不知道就说不知道" 的行为,在很多客户眼里反而是 AI 项目专业度的加分项。

5. 并发:Agent 扛压的不传之秘

5.1 单路调用很好看,并发一来全垮

网上总有人搜"ai agent 怎么扛并发"这类问题,说明这是个项目交付的普遍痛点。单路对话演示时一切正常,用户数一上来,内存暴涨、响应超时、GPU 排队,每一步都可能崩。

Ant Agent 的请求链路比普通 Web 接口长得多。一个对话请求进来,可能要经过鉴权 -> 路由 -> Agent 规划 -> 多次模型推理 -> 多次工具调用 -> 多轮检索 -> 生成回复,整个过程几十秒甚至几分钟,期间还伴随多次上下文拼接。这和普通接口秒回的模式完全不同,并发模型也完全不一样。

我们在内网交付时,第一版用的是同步 FastAPI 直接处理所有请求,结果 5 个并发用户就把系统拖垮了。后来做了一整套异步化改造才稳定下来。

5.2 异步化改造:入口快、重活下沉

核心思路是:长耗时的 Agent 任务不要占着 HTTP worker 不放,而是把请求压进任务队列,立刻返回一个任务 ID,前端通过轮询或 SSE 获取进度和结果。

实操上,我们用了 Redis Stream 做消息队列(也可以用 RabbitMQ)。FastAPI 只负责接收请求、校验参数、把任务塞入队列,然后返回:

queue.add( "agent_tasks", {"task_id": task_id, "session_id": session_id, "query": query} ) return {"task_id": task_id, "status": "queued"}

另一边,独立的 Worker 进程消费队列任务,执行完整的 Agent 编排逻辑。Worker 的数量配置很有讲究,和 GPU 显存、模型并发度强相关。我们最初配了 2 个 Worker,每个 Worker 串行处理任务,但模型推理是单实例并发,两个 Worker 同时请求模型反而会互相抢占显存,后来调整成 1 个 Worker + 模型服务内部并发,反而更稳。

流式输出方面,用 SSE(Server-Sent Events)实现比较合适。Worker 在执行过程中通过 Redis Pub/Sub 推送给 FastAPI 网关,网关再通过 SSE 把 token 逐步推给前端。这样用户端的体验是流式打字效果,不必等待整个任务跑完才看到第一个字。

5.3 模型服务的并发度配置

模型推理层用什么框架,直接影响并发上限。我们对比过 vLLM 和原生 Transformers:Transformers 的 generate 方法在每个并发请求下都要独立加载模型上下文和 KV Cache,很容易 OOM;vLLM 通过 PagedAttention 和连续批处理,在相同显存下能支撑的并发数高得多。推荐在内网部署场景直接用 vLLM 做模型服务,或者用 SGLang。

以 Qwen2.5-7B-Instruct 为例,单张 24GB 显存的显卡上,vLLM 开启连续批处理,实测可以同时服务 8-16 个并发请求,具体取决于最大生成长度和请求频率。配置如下:

vllm serve /opt/models/qwen2.5-7b-instruct \ --served-model-name local-agent-model \ --max-model-len 8192 \ --gpu-memory-utilization 0.85 \ --max-num-seqs 16 \ --enable-auto-tool-choice

注意其中的--max-num-seqs,它代表最多同时处理的序列数。设得太高,显存不够会 OOM,设得太低,并发能力上不去。建议内网交付时先用测试脚本压一把,找到稳定值,再写入参数配置。

这里提一个额外的坑:Agent 场景下每个用户的上下文长度差异巨大,有的用户一个问题很短,有的用户五轮对话后上下文超过 4000 token。vLLM 的--max-model-len设置如果太大,单个请求占用的 KV Cache(key-value缓存,用于解码历史上下文)就多,可用并发量随之降低。合理做法是限制最大模型长度在 8192 或 4096,长对话及时截断摘要,而不是无限放宽长度。

5.4 幂等、重试与熔断

伴随着异步化而来的,是"任务重复执行"的风险。比如 Redis 队列消息被重复消费、网络超时导致 Worker 执行了两次、用户在前端点了两下"发送"产生了两个任务。在 Agent 场景里,最怕的是"重复提交工单""重复扣款"这类不可逆操作。

所以,凡是涉及"写操作"的工具,都必须设计成幂等:工单编号重复提交时直接返回已存在的结果,不做二次创建;数据库更新操作带上操作来源标记,避免重复执行覆盖掉新数据。

另外,任务重试必须有上限。我们给 Worker 包装了一层重试逻辑,单个任务最多重试 2 次,超过次数就进入失败队列,并通知管理员人工处理。还在 Agent 主链路上加了熔断逻辑:如果模型服务连续返回 5 次错误,直接把对应下游服务熔断 30 秒,避免雪崩。

6. 部署、监控与持续迭代

6.1 部署方式:systemd还是K8s

隔离内网场景的部署方式,我们的经验是要看客户运维团队的水平。如果客户只有一两个运维,没有专职容器平台,上 K8s 反而是灾难:出了问题没人会排查、升级流程复杂、遇到故障求助无门。这种客户我们优先用 Docker Compose + systemd 的方式交付。

计划大概是这样的:宿主机装 Docker,docker-compose.yml 里编排模型服务、Agent 主服务、Worker、Redis、向量库这几个容器。systemd 只负责 docker-compose 的启停和开机自启。日志走 Docker 的 json-file driver,统一收进一个日志目录,方便巡检。

如果客户已经有成熟的 K8s 集群,那就按标准做法走:模型服务作为 StatefulSet(有状态应用,需要挂载模型目录),Agent 主服务作为 Deployment,Redis 和向量库用已有集群资源或单独部署。不管哪种方式,都要坚持一个原则:模型文件和镜像分离,这样迭代时不用把几十 GB 模型重新拷贝一遍。

6.2 三类必须盯的指标

内网系统出了问题,最大的痛点是"没有外网查资料"和"复现困难"。因此监控指标必须提前铺好,而不是出了问题再去补。

我把监控分成三层,三类指标必须齐全:

第一,模型服务指标:请求次数、错误率、单 token 生成延迟、平均生成长度、GPU 显存占用率、GPU 利用率。这些指标通过 vLLM 暴露的 Prometheus metrics 直接采集,Grafana 出面板。GPU 利用率低于 20% 说明并发上不去,高于 90% 说明可能需要限流或扩容。

第二,Agent 链路指标:任务队列长度、任务平均耗时、工具调用成功率、检索召回率、上下文 token 消耗分布。这类指标我们是在业务代码里手动埋点的,每个任务在关键环节打点,最后统一输出一条结构化日志。

第三,业务侧指标:用户提问数、有效回复率、超时率、用户反馈的"答非所问"比例。这个可能有人觉得做和不做区别不大,但我们实测下来,这一层是向客户汇报项目效果时最有力度的数据,也是后续优化模型 prompt 和工具描述的依据。

6.3 模型升级的灰度与回滚

模型不是交付完就完事了,每过一两周客户就可能提出新需求,比如"你们能不能让模型更了解我们公司简称"或者"回答风格能不能更简洁"。为了应对这类需求,模型更新流程必须灰度化。

我们的做法是:模型服务启动两个实例,一个挂载旧模型,一个挂载新模型,通过路由规则把 10% 的流量切到新模型上跑一两天,重点看错误率和用户反馈。在隔离内网环境里拉齐两个实例也不难,关键是模型目录独立、版本号打清楚。确认没问题后,再逐步提升新模型流量比例,直到全量切换。

另外,每个 Agent 会话记录里要带上模型版本号。客户如果反馈"今天下午回答风格变了",你能迅速定位到是不是模型切换导致的,快速回滚到上一版。这里没有全局统一的绝对标准,核心原则是:所有变更可回滚、有记录、有对比,才敢在隔离内网里持续迭代。

我在这个项目里最深的体会是:隔离内网并没有让 AI Agent 的"智能"降低,它只是逼着你把工程化细节做扎实。曾经在公网环境里被隐藏掉的问题——依赖冲突、网络超时、缓存失效、工具调用不稳定——在内网里全部暴露无遗。但也正因为如此,一旦把离线镜像、MCP 工具层、混合检索、异步队列这一整套框架搭好,这套 Agent 系统会变得异常稳定,因为它的每一步都不依赖外部环境,不受网络波动影响。至于后续扩展方向,我们正在尝试把更多内部系统以 MCP Server 的形式接入进来,同时把长对话的上下文管理往里做深。内网 Agent 的想象空间不在于"联网能力",而在于把企业内部的数据流转、业务逻辑真正沉淀成可被模型调用的能力。希望这篇实战记录,能给正在隔离内网里挣扎的团队一些参考。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 15:16:29

多模型API统一接入实践:腾讯云网关架构与踩坑总结

先说个背景。我去年把自己主导的一个 AI 应用从“本地直连各家模型 API”重构成“腾讯云上统一接入”,那段时间同事开玩笑说我在搞“硅碳相变”——以前是我这个碳基生物手动接一个模型写一套代码,现在是硅基模型们在一台云服务器上被我统一调度&#xf…

作者头像 李华
网站建设 2026/10/6 15:15:41

RAG不需要向量:非向量检索的工程实践与落地指南

1. 这个标题不是噱头,而是对RAG底层逻辑的一次重新校准“RAG不需要向量?”——看到这个标题,很多刚接触检索增强生成(RAG)的朋友第一反应是:这怎么可能?主流教程、开源框架、企业落地案例&#…

作者头像 李华
网站建设 2026/10/6 15:14:22

华为云CodeArts零基础入门:AI代码智能体与检视修复实战

1. 零基础认识“码道(CodeArts)”:先搞清楚智能体到底是什么1.1 华为云CodeArts的身份定位:它不是普通的AI补代码插件我刚开始接触华为云码道(CodeArts)的时候,第一反应是“这不就是又一个AI写代…

作者头像 李华
网站建设 2026/10/6 15:14:12

Agent工业落地:从AI工具到自主伙伴的范式跃迁

1. 项目概述:当AI不再只是“工具”,而是能主动思考、自主决策的“伙伴” 最近半年,我陆续参与了三个不同行业的Agent落地项目——一个面向金融风控团队的智能尽调助手,一个为制造业产线设计的异常诊断协作者,一个给教育…

作者头像 李华
网站建设 2026/10/6 15:09:55

Agent Skills 工程化实战:从 SKILL.md 规范到 MCP 协议与技能编排

1. Agent Skills 生态现状与核心价值拆解Agent Skills 这个概念从 2024 年底开始密集出现在各类 AI Agent 工程实践中,到 2025 年已经形成了相对清晰的生态格局。如果你到现在还没认真研究过这套东西,那确实有点落后了。我身边做 AI 应用开发的朋友&…

作者头像 李华
网站建设 2026/10/6 15:08:57

GitHub周榜高效阅读指南:四步拆解法与避坑经验

1. 周榜背后的信息筛选逻辑:为什么值得花时间看 每周花半小时翻一遍GitHub周榜,是我保持了三年多的习惯。很多人觉得热榜就是看个热闹,刷过去就完了,但我的实际体验是:周榜的价值远不止"知道最近什么火"&…

作者头像 李华