☰
隔离内网部署AI Agent实战:从模型到工具的全链路方案
2026/10/6 6:28:59 网站建设 项目流程

最近帮客户把一个 AI Agent 从开发机搬到隔离内网,前后折腾了将近一个月。网上大部分 Agent 教程都默认你能随时访问云端模型服务、能在线拉最新依赖、能随便调各种在线 API——可在隔离内网里,这些前提会挨个崩掉:模型文件动辄几十个 G 拿不进来、Python 依赖没法装、Agent 想用搜索工具连个外网都出不去。如果你也要做这件事,会很快发现真正难的从来不是 Agent 框架怎么写,而是怎么让它在没有互联网的环境里活下来。这篇文章就是我踩完坑之后整理的实战笔记,内容包括离线模型部署、RAG 组件自建、工具调用改造、并发与观测,适合要部署生产内网 Agent 的团队参考。

1. 隔离内网里的 Agent:先搞清楚“缺什么”再动手

很多人一听到“隔离内网部署 Agent”,第一反应是“把外网那套搬进来不就完了”。实际做起来会发现完全不是一回事,因为隔离内网不是简单的“没网”,而是整套技术供应链和环境假设都变了。

1.1 “隔离”的是网络,不是问题:三个直接影响 Agent 的硬约束

先说清楚隔离内网和普通办公网的区别。普通办公网虽然出口受限,但还允许访问一些白名单站点,能勉强下载东西;真正的隔离内网则是按照安全管理要求,把生产、研发环境严格限制在可控的网络区域里,里面的机器通常无法直连云端模型服务,也无法访问公共依赖源。

这个环境对 Agent 的影响,不是“少了个搜索功能”那么简单,而是三大硬约束:

  • 模型与依赖进不来:大模型权重文件动辄十几到几十个 G,Python 依赖有几十上百个包,如果没有提前准备好,进到内网就是两眼一抹黑。
  • 外部服务全部失效:Agent 里常用的网页搜索、天气查询、地图服务、OCR 云接口、在线向量库、云端监控面板,在内网环境一个都用不了,必须找内网替代品或自己搭。
  • 算力与权限是固定的:你在开发机上可以随便申请一张高配 GPU,到了内网,显卡数量、显存、CPU 内存、磁盘配额基本是固定的,申请新资源可能要等审批。Agent 的设计必须反过来适配这些约束,而不是让环境迁就你的模型规模。

这三点叠加起来,决定了内网 Agent 项目的首要任务不是“写智能体逻辑”,而是“先把运行环境给它铺平”。我见过太多团队一上来就调 Agent 编排框架,结果模型还没跑起来就卡在依赖安装上。

1.2 动手前,先做“要带进来的资产”和“能调用的内网资源”两份清单

进入内网前,我强烈建议先画两张表,一张是“要带进来的资产清单”,一张是“内网可用资源清单”。这两张表能帮你把所有隐含依赖一次性暴露出来,而不是边写代码边发现缺东西。

要带进来的资产通常包括:

类别具体内容备注
模型权重LLM 主模型、Embedding 模型、Rerank 模型、OCR 模型注意确认量化格式和目录完整性
推理框架vLLM、SGLang、Triton 等版本必须与 CUDA、驱动匹配
Python 依赖Agent 框架、向量库客户端、文档解析库、HTTP 框架用 pip download 打成离线包
工具资源内部 API 文档、数据库连接信息、业务系统说明给 Agent 写工具描述时要用

内网可用资源清单则完全不同,它问的是“我的 Agent 能调用哪些东西”:

  • 内部业务系统的 API 网关地址和鉴权方式;
  • 数据库、数据仓库的只读账号;
  • 文件服务、内部文档站、知识库系统;
  • 消息队列、任务调度平台;
  • 内部监控和日志系统。

这两份清单画完后,你在设计 Agent 时就能明确知道:哪些能力是现成的,哪些能力必须本地搭,哪些工具需要找业务方开接口。没有这个前置步骤,后面一定会遇到“Agent 都写好了,发现数据源连不上”的尴尬。

1.3 核心策略:把 Agent 的大脑、记忆、工具全部“内网化”

依赖清单理清楚之后,整个 Agent 的架构思路就很清晰了。本质上,一个 Agent 系统由三部分组成:大脑(大模型)、记忆(向量库、知识库、会话历史)、工具(各种可调用的能力)。在隔离内网里,这三部分都要换成内网方案。

  • 大脑:用私有化部署的 LLM,通过 OpenAI 兼容接口或推理框架自带接口对外服务,不再调用云端模型。
  • 记忆:用本地向量数据库 + 内部数据库保存知识、历史、状态,不依赖任何托管服务。
  • 工具:把能做的操作封装成内部 API、数据库查询、命令行脚本,通过函数调用机制暴露给 Agent。

用一个简单的文字架构描述我实际搭建的系统:

内部用户 → 内部门户/API网关 → Agent编排层(LangGraph/自研状态机) → LLM推理网关(vLLM,提供openai兼容接口) → 工具层(内部业务API、参数化SQL查询、文档检索服务) → 日志与审计(trace_id贯穿全链路)

组件之间全部走内网域名或 Service 地址,不依赖任何外部出口。这个架构看起来平淡无奇,但恰恰是它能上生产的关键——每一层都是内网里已有的组件,或者能顺利部署的组件。对大多数团队来说,把复杂留在编排和工具设计里,而不是留在网络访问上,才是正路。

2. 模型与推理底座:把“大脑”搬进门的完整流程

模型是整个 Agent 系统里最重的依赖,也是内网部署时最容易出问题的地方。这一节我把从选型、下载校验到启动调优的完整流程展开讲,每一步都标注了值得注意的细节。

2.1 选模型的判断标准:不是在选“最强”,是在选“能跑且好维护”

开发阶段你可能习惯用云端最强模型跑效果,但在隔离内网,模型选型的逻辑必须完全反过来:先看算力,再选模型。否则就算你把 70B 的模型文件辛苦搬进内网,发现单卡显存放不下,推理速度慢到 Agent 无法用,那时候再换就非常痛苦了。

我整理了一个适用于内网 Agent 的模型选型参考表:

参数量级精度/量化显存需求参考适合场景
7BFP16 约 14GB,INT4 约 5GB单卡即可工具调用、简单问答、低延迟场景
14BFP16 约 28GB,AWQ/INT4 约 9GB单卡 24G 可跑量化版中文理解、中等复杂任务,内网首选
32BFP16 约 65GB,INT8 约 35GB多卡复杂推理、长流程规划
70B+INT8 约 70GB+多卡且资源充足非必要不上,维护成本高

我给客户的首选一般是 14B 量化版:能力足够做工具调用和中等复杂度的 Agent 任务,显存压力小,推理速度快,出了问题也好排查。如果你所在团队有大量中文文档处理需求,14B 级别的模型在语言理解上通常已经够用。

另外要提醒一点:不要迷信模型宣传的“超长上下文”。Agent 场景下,上下文越长,显存占用越大,推理越慢。内网环境资源固定,很多时候把 max-model-len 设置在 8192 或 16384 就够了,强行上几十万上下文只会拖垮整个系统。

2.2 离线模型文件导入前的校验与“预跑”验证

模型选好之后,接下来就是最容易被忽视、也最坑的一步:把模型文件安全、完整地带进隔离内网。关于如何获取模型文件,我只能说一种合规且通用的做法:在你获准访问外网资源的环境中,提前下载好所需模型,经过完整性校验后,再按单位的数据导入审批流程导入内网。整个过程必须走正规程序,不要在安全边界上动脑筋。

操作上有几个关键细节:

  • 优先下载 safetensors 格式,相比老式的 .bin 权重,safetensors 加载更安全、更稳定,很多框架默认优先读取它。
  • 下载完整目录,不只是权重文件,还要包含 config.json、tokenizer 相关的几个文件。缺 tokenizer 文件是内网启动失败的头号原因。
  • 校验 sha256,不要只信文件大小。推荐下完后在本地算一遍哈希值,和官方公布的哈希比对,确认没有文件损坏。
  • 先“预跑”再进内网:这一步强烈建议做。在还有外网或本地有同等 GPU 环境时,用官方推理脚本把模型跑一次,确认能正常加载、能正常生成回复,再导入内网。否则文件进内网后才发现启动失败,排查成本会高很多。

别小看预跑这一步。我有一次在内网部署时启动报错,查了半天发现是某个依赖版本不匹配,而这是完全可以在预跑阶段发现的。内网环境里很多问题不是因为环境特殊,而是因为少了一次“事前验证”。

2.3 推理服务的启动参数与并发容量预估

模型文件就位后,就轮到推理服务上线。我习惯用 vLLM 这类高效推理框架,因为它支持连续批处理,能让 GPU 在并发请求下保持高吞吐,这对 Agent 场景尤其重要——Agent 一次任务要多次调用 LLM,单请求如果慢,整个任务的体验就会非常差。

一个典型的 vLLM 启动命令长这样:

vllm serve ./models/Qwen2.5-14B-Instruct \ --quantization awq \ --max-model-len 8192 \ --gpu-memory-utilization 0.90 \ --tensor-parallel-size 2 \ --served-model-name local-llm \ --port 8001

几个关键参数解释一下:

  • --quantization awq:指定量化方式,必须和模型文件的量化格式一致,否则会报错;
  • --max-model-len 8192:限制最大序列长度,直接影响显存占用,超长文本需求不强烈时别调太大;
  • --gpu-memory-utilization 0.90:控制预留给模型 KV Cache 的显存比例,不是越高越好,建议给系统留一点余量;
  • --tensor-parallel-size 2:跨两张卡做张量并行,如果只有单卡就不加这个参数;
  • --served-model-name local-llm:给模型起一个内部服务名,之后 Agent 调用时用这个名称。

推理服务启动后,我建议第一时间做一个简单的并发压测,确认它能扛住多少并发。用 requests 写一个小脚本就很够用:

import requests import time import concurrent.futures def call_llm(prompt): t0 = time.time() resp = requests.post( "http://model-gateway:8001/v1/chat/completions", json={"model": "local-llm", "messages": [{"role": "user", "content": prompt}], "max_tokens": 512}, timeout=120, ) return {"code": resp.status_code, "elapsed": time.time() - t0} with concurrent.futures.ThreadPoolExecutor(max_workers=10) as pool: results = list(pool.map(call_llm, ["内网部署需要注意什么?"] * 10)) print(results)

压测时重点看两个指标:TTFT(首 Token 耗时)和TPOT(每个 Token 的平均生成耗时)。Agent 场景下,TTFT 过长会让用户觉得“卡住了”,TPOT 过长则会让长回复难以等待。一般来说,TTFT 控制在 2 秒内、TPOT 控制在每 Token 30-50 毫秒左右,体验才勉强可用。

并发容量的估算有一个很朴素的口诀:先测单请求耗时,再看 GPU 能同时跑多少个请求。比如单请求生成 1000 个 Token 需要 40 秒,如果你想做到 10 个并发同时跑,就要保证 vLLM 的 Continuous Batching 在同样的 40 秒窗口内吃掉 10000 个 Token,这取决于显存和模型优化程度。这也是外界老问“AI Agent 怎么扛并发”的答案起点——先扛住 LLM 网关的并发,再谈 Agent 编排层的并发。

3. 记忆与知识库:向量检索和文档解析要做到“自给自足”

隔离内网里的 Agent,知识来源不能靠云端搜索,只能依赖本地知识库和内部数据。这一节讲我把 RAG 链路完整搬到内网的过程中,沉淀下来的选型逻辑和实操经验。

3.1 Embedding模型与向量库选型:别让记忆组件拖垮Agent

RAG 链路的第一步,是把文本向量化。在隔离内网里,Embedding 模型同样要本地部署。选型时我只看三个指标:中文效果、向量维度、上下文长度。中文效果不解释,向量维度直接决定向量库占用内存的大小,上下文长度则决定了单条文本能编码多长的语义。

以常见的开源 Embedding 模型为例,有的默认维度 1024,有的支持长文档,有的轻量到 CPU 也能跑。我的建议很直接:如果知识库以中文短文本为主,选轻量中文模型即可,加载快、占用少;如果知识库里混着大量长文档,再考虑支持长上下文的模型,同时接受更高的显存或内存开销。

向量库的选型和 Embedding 一样,不要一上来就上重武器。我按实际规模做了一个对比:

向量库部署模式适合场景注意点
FAISS库文件形式,嵌入你的服务百万级以下、单机为主写入和检索在同一进程,多服务共享麻烦
LanceDB嵌入式向量库本地轻量服务、快速原型支持多进程,数据落在本地目录
Milvus独立分布式服务生产级、千万级向量、多团队共享组件多,部署和运维成本较高
Elasticsearch已有 ES 则复用需要全文检索和向量混合查询必须提前设计向量字段 mapping

我当时选择的是 FAISS 起步、后续切 Milvus 的路线。原因很简单:初期知识库只有几十万条,FAISS 单机完全够用,部署成本几乎为零;等数据量涨上来或者需要多团队共享时,再平滑迁移到独立向量库服务。

这里有两个容易踩的坑。第一,别把 Embedding 模型直接加载在 Agent 进程里。如果 Agent 进程又要跑 LangGraph、又要加载 LLM、又要加载 Embedding,内存妥妥爆掉。正确做法是把 Embedding 服务单独封装成一个小服务,Agent 通过 API 调用它做向量化。第二,向量维度要提前固定。如果你之后换一个维度不同的 Embedding 模型,历史向量全部作废,得重新索引。所以先定好模型,再建库,不要中途换。

3.2 文档解析与清洗:知识库质量比模型参数数量更关键

RAG 效果好不好,七分在文档解析和分块,三分在检索。内网知识库常见的文件是 PDF、Word、扫描件,每类都有对应的处理方案:

  • 文本型 PDF:用 PyMuPDF 这类库直接提取文字,速度快,适合电子版文档;
  • 扫描件 PDF:需要 OCR,我用 PaddleOCR 或 RapidOCR 做本地识别,精度够用且完全离线;
  • Word 文档:用 python-docx 提取段落和表格,注意表格要单独结构化处理;
  • Excel 报表:最好转成结构化记录或 Markdown 表格,再决定是入库还是走数据库查询。

解析之后不能直接切片丢进向量库,必须先做清洗。我一般按这套流程处理:

  1. 提取正文,去掉页眉页脚、水印、干扰符号;
  2. 按标题层级切分成块,每块控制在 300~500 字,块与块之间留 50 字左右重叠;
  3. 表格单独提炼成结构化文本,保留表头和关键数值;
  4. 每块打上元数据,包括来源文档、章节路径、页码,方便之后引用溯源;
  5. 最后才做向量化入库。

这个流程看起来平淡,但很多人省略了第二步和第四步。如果不保留章节路径和页码,Agent 回答时就算检索到了内容,也说不清它来自哪份文档,这样的回答在生产环境是不敢直接展示给用户的。引用溯源能力,在封闭环境里尤其重要,因为它决定你对 Agent 的回答有没有信心。

3.3 RAG检索链路的本地化实现:从检索到引用的关键细节

向量库和文档处理就位后,RAG 的检索链路我建议做成一个独立服务,而不是把检索逻辑散落在 Agent 代码里。服务化的好处是可以复用,团队其他人也能直接调用这个检索能力。

一个本地化 RAG 检索链路,通常包含这几步:

  1. Query 改写:用户口语化提问先让 LLM 转成更适合检索的关键词或子问题,能明显提升召回质量;
  2. 混合检索:向量检索 + 关键词检索并行,关键词检索可以借用 ES 或数据库的全文索引,能兜住向量检索在专有名词上的短板;
  3. Rerank 重排:用 Rerank 模型对召回结果重新排序,把最相关的几段顶到前面;
  4. 组装 Prompt:把检索结果按“引用编号”塞进上下文,明确要求 LLM 只依据这些内容回答,并在答案中标出引用编号;
  5. 输出带溯源的答案:后端再把引用编号映射回具体文档和章节。

你可能会问,在内网部署一套包含向量化、检索、重排的链路是不是太重了?我的体会是:前期可以砍掉 Rerank,先靠向量 + 关键词跑起来;但 Query 改写和引用溯源最好一开始就做,因为它们直接影响回答质量和可信度。而且这些组件全部是内网开源自建,不会产生外部依赖,完全符合隔离要求。

4. Agent 编排与工具调用:把每个“在线能力”换成“内网能力”

模型和知识库解决了“大脑”和“记忆”,接下来最核心的是“工具”。Agent 和普通问答最大的区别就是它能调用工具去完成真实任务。在互联网环境里,工具到处都能接;在隔离内网里,每个工具都要重新设计。

4.1 LangChain/LangGraph本身没问题,问题出在工具层

先给一个结论:LangChain、LangGraph 这类编排框架本身不依赖外网,它们只是 Python 代码,打包好依赖之后在内网完全可以运行。真正需要改造的是框架里默认接的那些在线工具,以及你为 Agent 注册的每个工具调用逻辑。

我见过一个蛮常见的情况:团队在外网 Demo 阶段用了大量现成的在线组件,比如网页搜索、在线文档加载器、云端向量库,Demo 跑得很爽。结果一到内网,所有组件全部失效,代码层层报错,最后只能推倒重来。

在隔离内网里,工具层的基本替代原则是:

在线工具/能力内网等价方案
网页搜索内部文档站检索、知识库检索、数据库查询
天气、日历等公共 API内部业务系统接口或直接不做
云端 OCR本地 PaddleOCR 服务
云端向量库本地 FAISS / Milvus
第三方消息推送内部消息中间件或办公系统接口

在做工具规划时,建议先盘点内网已有的数据和系统,把“能通过 API 查询的信息”和“能通过脚本操作的能力”分别列出来,再决定给 Agent 暴露哪些工具。工具不是越多越好,而是越精准越好——每个工具都要让 Agent 容易理解、容易调用。

4.2 工具注册与Schema设计:让LLM准确选工具的关键

Agent 选工具靠的是大模型的 Function Calling 能力。LLM 会根据工具的“名称、描述、参数说明”判断该调用哪个工具、传入什么参数。所以工具描述写得清不清楚,直接决定 Agent 能不能正确使用工具。

以内网订单查询为例,一个工具注册的代码片段大概是这样的:

@tool def query_order_status(order_id: str) -> str: """ 查询内部订单状态,仅支持公司内部订单编号。参数说明: order_id: 内部订单号字符串,例如 "SO-2025-001" """ # 内部实现:调用内部订单系统的 API 或数据库 result = internal_order_client.query(order_id) return result

但光有这个函数还不够,真正喂给 LLM 的是 JSON Schema。我在实践中会显式声明参数类型、格式和必填项:

{ "name": "query_order_status", "description": "根据内部订单号查询订单当前状态。仅支持公司内部订单编号,格式为 SO-YYYY-NNN。", "parameters": { "type": "object", "properties": { "order_id": { "type": "string", "description": "内部订单号,例如 SO-2025-001" } }, "required": ["order_id"] } }

关键词都在描述里,包括编号格式和示例值,LLM 照着填参数的成功率会明显上升。反过来,如果工具描述写得很笼统,比如“查询订单信息”,LLM 可能不知道 order_id 该传什么,甚至会自己编一个编号出来。这类问题在封闭环境调试时特别明显,因为查不到真实数据时,模型更容易“自由发挥”。

我还有一个习惯:对高风险工具加上参数校验和白名单校验。比如数据库查询工具,绝不允许 Agent 传任意 SQL,只能走封装好的参数化查询;涉及内部人员信息查询时,还要限制只能查特定范围。这不是保守,是生产环境的基本要求。

4.3 并发控制:Agent扛不住并发,往往先扛不住LLM网关

回到“AI Agent 怎么扛并发”这个话题。很多团队问这个问题时,都以为是要调 Agent 框架的并发参数,其实真正的瓶颈几乎总是在底层。Agent 一次任务要多次访问 LLM,还要串行调用工具,如果 LLM 网关先崩了,Agent 层再怎么优化都没用。

我在内网环境用的策略很简单:控制 Agent 层的在途并发,给 LLM 网关留出稳定空间。用 Python 的话,加一个信号量就能做最基本的限制:

import asyncio sem = asyncio.Semaphore(8) async def run_agent(query: str): async with sem: # 内部会多次调用 LLM 和工具 result = await agent_executor.run(query) return result

这里的 8 就是允许同时运行的 Agent 任务数,要根据压测数据来定,不能拍脑袋。如果 LLM 网关测出来最多能同时处理 20 个请求,而每个 Agent 任务平均要调用 LLM 3 次,那 Agent 层并发设置在 6-8 比较稳妥。留点余量,比把 GPU 打满然后全网超时要好得多。

除了并发上限,还有两个必须做的设置:

  • 超时:LLM 调用和工具调用都要设超时,Agent 整体执行也要设超时。内网服务偶尔会慢,没有超时,一个卡住的任务会占住一个并发额度不放。
  • 重试:内部服务抖动时,重试能救回很多请求。我一般用指数退避,第一次 1 秒、第二次 2 秒、第三次 4 秒,超过 3 次就放弃并记录错误。

另外,如果 Agent 任务本身耗时长,别用 HTTP 同步请求硬扛。正确做法是接入内部消息队列,把 Agent 任务异步化:用户提交请求后立刻返回“任务受理”,后台 Worker 消费任务执行 Agent 流程,执行完再通过内部消息或查询接口反馈结果。这在大并发场景几乎是必须的。

4.4 长耗时任务别用HTTP硬扛:异步化与重试策略

承接上面说的,Agent 任务一旦涉及多轮工具调用,耗时很容易超过 30 秒甚至几分钟。此时如果前端页面一直等待 HTTP 响应,体验会很差,而且网关层也可能断连。

我的做法是分两类处理:

  • 短任务(预期 10 秒内完成):同步执行,前端转圈等待,超时设 30 秒;
  • 长任务(涉及数据库批量操作或多轮流程):提交到内部任务队列,后台执行,前端轮询任务状态。

任务队列可以用团队已有的 RabbitMQ、RocketMQ 或 Kafka,如果没有,先用 Redis 列表也能撑一阵子。重点是设计好任务状态表:待执行、执行中、成功、失败、超时,并在每个状态变更时记录 trace_id、入参、出参。这样后面排查问题才有据可循。

5. 可观测性与效果调优:内网没有“云端监控”,就自己造一个

在线环境里,各种监控面板、日志平台、链路追踪服务都是现成的。隔离内网里这些也未必齐全,所以必须用最小成本把“可观测性”做出来。否则 Agent 一旦出问题,你会发现自己对着黑盒完全无从下手。

5.1 用trace_id把一次Agent请求从头串到尾

Agent 应用比普通接口复杂得多,一次用户请求可能会触发多次 LLM 调用、多次工具调用,中间还可能经过异步队列。如果日志里没有统一的追踪标识,出了问题根本没法把链路串起来。

我在所有 Agent 项目里都会在入口生成一个 trace_id,然后在每个关键节点输出结构化日志,格式类似:

{ "ts": "2025-01-15T10:30:00.123Z", "trace_id": "0a1b2c3d4e5f6a7b", "event": "tool_call", "tool": "query_order_status", "args": {"order_id": "SO-2025-001"}, "latency_ms": 234, "status": "success" }

建议在以下节点都打日志:请求入口、Agent 编排开始、每次 LLM 调用(含 prompt 摘要和耗时)、每次工具调用(含参数和返回摘要)、Agent 最终输出、异常和超时。日志统一走内部的日志采集通道,如果内网有 ELK 或 Loki 就用,没有就先落本地文件按天滚动,后期再接入采集。

有了统一的 trace_id,再配合每个步骤的耗时记录,你就能回答三个核心问题:用户这个请求经历了哪些步骤?每一步花了多久?哪一步是瓶颈?

5.2 BadCase库:离线Agent效果迭代的燃料

模型和 RAG 的效果不是一锤子买卖,需要持续迭代。迭代的第一步是收集失败案例。我建议做一个简单的 BadCase 库,把每次 Agent 表现不佳的情况存下来,字段包括:

  • 用户原始问题;
  • Agent 最终回答;
  • 中间工具调用记录;
  • 用户反馈或人工标注(满意/不满意/错误原因);
  • 关联的 trace_id;

这些数据存在内部数据库里,每周做一次聚类分析,看看失败主要来自哪几类:检索没召回、工具参数填错、模型不遵循指令、知识库内容缺失、还是上下文超长被截断。

没有这个 BadCase 库,你对 Agent 的优化基本靠猜。有了它,你可以针对性地调 Query 改写逻辑、调检索分块大小、调工具描述、甚至换模型。内网环境没有在线评测集可以依赖,自己维护 BadCase 和评测集就是最靠谱的迭代方式。

5.3 关键性能指标:用数据和日志判断“要不要调”

除了效果问题,性能问题也要用数据说话。我通常维护一张简单的指标表:

指标含义健康参考
TTFT首 Token 耗时2 秒以内
TPOT每 Token 生成耗时30-50 毫秒
总耗时单任务从开始到结束的时长看任务复杂度
工具调用失败率工具执行失败次数 / 总调用次数低于 5%
Agent 任务成功率成功完成任务数 / 总任务数目标 95%+
超时率触发超时的任务占比低于 1%

这些指标完全可以从日志里统计出来,不需要额外引入监控平台。我一般每天跑一个定时脚本,统计前一天的数据,超过阈值就拉出来分析。千万别等到用户投诉了才想起来查日志,那会非常被动。

6. 隔离内网实战中反复出现的坑与排查经验

最后一节,我把这次实战中反复踩到的坑集中整理出来。这些坑看起来都不起眼,但每一个都让我在机房、会议和深夜排查里浪费过不少时间。希望你能直接跳过。

6.1 模型文件和依赖的“最后一公里”问题

内网部署最常见的启动失败原因,往往不是框架问题,而是模型文件或依赖包不完整。

几个我见过的高频问题:

  • 模型目录里缺少 tokenizer 配置文件,启动时报错“tokenizer config not found”;
  • 下载的是量化模型,但没有把量化配置文件一起放入目录,vLLM 启动时识别不了;
  • 模型的 safetensors 文件在传输过程中损坏,加载时直接报错;
  • Python 依赖包版本和推理框架不兼容,比如 vLLM 依赖的 CUDA 版本和机器驱动不匹配。

解决方法分两条腿走:一是导入前严格执行 sha256 校验和预跑验证,二是在内网准备一个私有的依赖包仓库或离线 wheel 包目录。依赖版本最好锁定,不要用“最新版”,隔离环境升级一次代价很大,锁定版本更稳妥。

6.2 显存、磁盘、权限:环境问题比代码问题更容易卡住

代码层面的问题往往好查,环境层面的问题反而能卡几天。举几个真实的例子:

  • 模型文件导入后,发现所在磁盘分区快满了,向量库索引根本建不完;
  • 服务用非 root 用户启动,但模型目录权限没放开,启动即 Permission Denied;
  • 多机部署时,把模型文件同步到另外几台机器,其中一台软链接断了一半,运行到中途才报错;
  • GPU 驱动版本太旧,vLLM 启动时提示 CUDA 版本不支持。

这些问题的共同特点是:在开发机上根本不会出现,只有到了真实环境才爆出来。我建议在内网部署前就做一次“环境体检”,逐项确认磁盘空间、目录权限、GPU 驱动、CUDA 版本、可用的端口,省得部署到一半被环境问题打断。

6.3 封闭环境里Agent“幻觉”会被放大:怎么压制

在互联网环境里,Agent 偶尔胡说八道一下,用户可能当个乐子。但在内网业务场景里,如果 Agent 把一个订单状态说错了、把一个审批结论编出来了,后果很严重。我总结了三个压制幻觉的实用手段:

  1. 工具先行:凡是能通过工具查到的信息,一律先调用工具,不要让模型凭记忆回答。比如用户问“这个订单到哪一步了”,必须触发订单查询工具,而不是让模型猜。
  2. 严格限定回答来源:在系统提示词里明确写“只能基于检索内容和工具返回结果作答,如果信息不足,直接告知用户不知道,不要编造”。
  3. 关键回答加引用溯源:涉及业务数据时,强制要求模型在答案中标注信息来源编号,后端再把编号映射到具体文档或查询记录。

另外,从设计角度讲,高风险操作(比如审批、删除、转账)不应该由 Agent 直接执行。Agent 可以帮你把操作准备好,但最终执行要有人工确认环节。这不是技术保守,而是生产环境的基本安全意识。

6.4 从Demo到生产:最小闭环和回滚意识

内网 Agent 项目很容易陷入两种极端:要么一直在技术 Demo 阶段打转,要么一上来就想做一个“超级智能体”然后被复杂度拖垮。我的建议是,先从最小闭环开始。

最小的生产级闭环是:一个本地 LLM + 一个知识库检索工具 + 一个业务数据查询工具,完成一个具体场景(比如“查询订单进度并说明当前卡点”)。把这个闭环跑稳、指标跑好、BadCase 库跑起来,再逐步加工具、加编排节点。每次新增能力,都要有对应的评测数据和回滚方案。

模型文件也一样,保留上一版模型文件的备份,新模型上线后如果效果回退,能快速切回旧版本。Agent 系统的配置最好能集中管理,这样切换模型、调整 prompt、开关工具都只要改配置,不需要重新发版。

最后说点个人体会。项目结束后我最大的感受是:隔离内网不是 Agent 工程的限制,反而是一种倒逼你把架构做干净的约束。没有外部服务可用,你只能把每一层都搞清楚、把每个依赖都固化下来,这会让系统变得可控、可维护。如果你正准备做类似的事情,别贪大,先让一个最小 Agent 稳定跑通,再一点一点往上加复杂度。内网里的每一次试错成本都比在开发机高得多,所以前期的规划、资产清单和环境体检,比写代码本身更重要。

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

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

立即咨询