news 2026/10/7 6:35:53

隔离内网AI Agent实战:架构选型、RAG构建与并发排障

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
隔离内网AI Agent实战:架构选型、RAG构建与并发排障

去年年中,我接了一个很拧巴的项目:一套 AI Agent 系统,必须跑在物理隔离的内网里。客户方的业务人员想要一个能自然对话、能查知识库、能对接内部系统的智能助手,但安全规范相当严格,所有数据不允许出域,公有云的模型 API 一概不能碰。这直接把我之前做 Agent 的路径全打乱了——以前是“调别人的模型、接别人的向量库、用别人的网关”,这次全部要自己搭、自己养、自己护。整篇文章就是这次隔离内网下 AI Agent 工程实战的完整复盘,从架构选型到数据准备,从并发压测到排障实录,再到上线后的迭代建议。如果你是做私有化部署、信创适配、或者任何“不能上网但还想用大模型”场景的工程师,这篇应该能帮你少踩不少坑。

先声明一下:这篇文章不是纸上谈兵,所有结论都来自我在真实项目里跑出来的记录。项目前后做了三个多月,交付后系统每天要处理上千次查询,模型服务、Agent 编排、检索链路全都跑在断网的物理环境里。我把过程里关键的决策、参数、错误和反思尽量完整地写出来,希望对你有用。

1. 为什么放着主流云服务不用,非要在隔离内网里搭 AI Agent

1.1 场景是怎么来的:数据不出域,Agent 就必须本地化

当时客户给我的需求其实很简单:OA 系统里的知识库要能被“问起来”,分子机构的问题在系统内直接获得答案,不需要人工一篇篇翻文档。但他们对安全的要求是硬性的:业务数据、组织架构、合同条款、人员信息,一律不能出企业内部网络。这意味着我不能用任何云端大模型 API,不能把文档送到外部服务去解析,甚至模型服务的日志都不能传到外部。这就是典型的数据合规驱动的私有化场景。

这类需求在金融机构、央国企、政务系统里非常常见,我对接的这家客户就是类似背景。他们不是不相信云厂商,而是行业监管对数据主权、安全审计有明确要求。所以从立项那一刻起,技术路线就注定和“接 OpenAI API”那种玩法完全不同。

1.2 隔离内网和普通开发环境的真实差距

很多人对“隔离内网”的理解就是一台没网的电脑。真做起来会发现,它带来的麻烦是系统性的:

  • 没有公共软件源:pip、npm、apt、Docker Hub 全部不可达。所有依赖包、基础镜像、模型权重都要想办法先弄进去。
  • 证书环境不一样:隔离内网里很多服务用的是自签名证书或企业 CA,HTTP 客户端如果不做适配,请求直接原地失败。
  • 模型权重成了“货”:一个大模型动辄几个 GB 到几十 GB,要过内外网隔离的传输审批流程,拷一次盘就得走一两个星期。
  • 无法在线调试外部服务:平时习惯了什么报错就搜一下,但隔离网里只能靠自己的经验和系统性排查。

这些差距不只是技术上的,还是流程上的、资源上的。项目排期得把这些“非编码工作”全部算进去,不然一定延期。

1.3 先想清楚:Agent 到底要干哪几件事

在选型之前,我逼着客户和我一起把 Agent 的使用边界定义清楚。我们最后收敛成三类任务:

  1. 知识库问答:基于内部文档的自然语言检索和回答。
  2. 内部系统工具调用:通过 API 或 RPA 方式查询 OA/HR/合同系统的数据。
  3. 长流程任务:比如跨系统收集信息、生成报告草稿。

我强烈建议每个团队在做隔离内网 Agent 之前先做这个动作。因为“Agent 越通用越难”,尤其在离线环境里,调一次模型、跑一次工具都是有成本的。明确任务边界,后面所有的架构、并发、数据准备才有依据。

2. 架构选型的三层决策:模型推理、Agent 编排、应用服务

2.1 模型推理层:开源模型、量化方案与推理引擎的取舍

隔离内网条件下,模型选型几乎没有悬念:只能选开源权重。我们当时对比了 Qwen2.5-7B-Instruct、Qwen2.5-14B-Instruct、ChatGLM4 系列和 Llama3.1-8B-Instruct。在中文知识库问答、内部工具调用这两个场景下,Qwen 系列的表现明显更稳,函数调用(function calling)的成功率也比 Llama 高出一截。最终我们定了 Qwen2.5-14B-Instruct 作为主模型,同时配了一个轻量的 Qwen2.5-3B 作为意图识别和路由的小模型。

模型权重是定了,但推理要怎么跑还有讲究。最初我们直接用 Transformers 加载进行推理,结果并发一上来根本扛不住。后来换了 vLLM 做推理引擎,吞吐提升非常明显。这里有个关键参数我要专门说一下——GPU 显存估算。以 14B 模型为例,FP16 权重大概占 28GB,加上 KV Cache 和激活值,最少得 48GB 显存。我们手头的机器是两张 A800(80GB),单卡就能塞下 14B 模型 + 较大 Batch 的推理,另一张卡留给了 Embedding 模型和重排模型。如果显存不够,可以量化为 INT8 或 AWQ,14B 量化到 INT8 大概 14GB 权重,一张 4090 也能跑,但生成质量会略有下降,需要在效果评测时做对比。

推理引擎我们只考虑了 vLLM 和 llama.cpp。vLLM 的优势是吞吐高、支持连续批处理(continuous batching),适合服务化部署;llama.cpp 更轻量,适合单机或者 CPU 推理,但做并发服务化要自己再包一层。隔离内网场景下我推荐 vLLM,因为它自带 OpenAI 兼容接口,后面接 Agent 框架时省掉很多事。

2.2 Agent 编排层:为什么用 LangGraph 而不是硬编码

在隔离内网里做 Agent 编排,很多人第一反应是“写 if-else 硬编码算了”。如果是两三个固定流程,硬编码确实简单;但如果工具多了、分支多了、还有循环、重试、暂停恢复这些需求,硬编码会变成一场灾难。

我们最终用了 LangGraph。这里不是无脑推荐,而是从几个维度考虑过的:

  • 状态管理:LangGraph 把 Agent 的执行过程建模成一张状态图,每个节点负责一个动作,边负责状态转移。这对“需要跨多轮调用工具、随时可能中断”的任务非常友好。
  • 可观测性:Agent 执行到哪一步、调用了哪个工具、上下文里新增了什么,LangGraph 都能通过钩子打日志,这对隔离内网里排查问题至关重要。
  • 条件分支与循环:比如“查合同信息 -> 信息缺失 -> 再问用户 -> 再查”这种循环逻辑,用状态图表达比硬编码清晰得多。

当然 LangGraph 也不是没有代价。隔离内网里没有外网,所有依赖包都必须在建设期就导进来。我们当时把 LangChain、LangGraph 及其所有依赖做了一个完整的 wheelhouse 收集,在另一台相同架构的机器上把所有包全部下载,再搬到内网安装。这一步务必提前做,千万别到现场发现某个依赖缺失。

2.3 应用服务层:FastAPI 做接口、任务队列做长任务

应用层我们选了 FastAPI + Celery + Redis 的组合。FastAPI 负责对外提供 HTTP 接口,Celery 处理异步长任务,Redis 充当消息代理和缓存。为什么要拆一层任务队列?因为 Agent 的调用不是瞬时返回的,一个复杂的 Agent 流程可能要跑几十秒甚至几分钟。如果 HTTP 接口里同步等待,前端连接会被长时间占用,网关容易超时,体验也差。

我们的做法是:HTTP 接口收到请求后,把任务丢进 Celery 队列并立即返回一个 task_id;前端用轮询或 WebSocket 订阅结果。这样既控制住了连接数,又方便做并发控制。

另外在 Agent 执行过程中,我们用 SSE(Server-Sent Events)向客户端推送中间状态,比如“正在检索知识库…”“正在调用合同系统…”这种进度提示。用户看到进度,等待焦虑会小很多,这在真实产品里是提升好感度的关键细节。

3. 数据准备才是真正的头号工程:清洗、切分、向量化全流程

3.1 数据源盘点与清洗:PDF 乱码、扫描件 OCR 都是拦路虎

很多人把“知识库”想得太简单,以为扔几个 PDF 进去就能问答了。实际上客户给我的文档五花八门:有扫描版 PDF、有网页导出文件、有表格套表格的 Word、有图片截图。如果不去清洗直接切分,检索质量会非常差。

清洗环节我们做了三件事:

  1. 格式统一:所有 Word、PDF、网页统一转成 Markdown,方便后面按结构切分。
  2. 扫描件 OCR:扫描版 PDF 先过 OCR 识别成文本。这一步在隔离内网里很麻烦,因为 OCR 模型也要本地部署。我们用的 PaddleOCR 离线版本,效果不错,但部署时要把模型文件和所有依赖一起导入。
  3. 去噪:去掉页眉页脚、目录、"第 X 页 / 共 Y 页"这种噪音;表格保留,但转成紧凑的文本格式。

清洗的成本远超预期,客户给了 800 多份文档,光清洗和人工抽检就花了两周。但这一步不能省,不然 Agent 的回答质量就在垃圾进、垃圾出。

3.2 切分策略:按结构切,而不是一刀切

切分(chunking)是整个 RAG 链路里最容易被低估的环节。很多人直接用固定窗口切,每 500 个字一段,重叠 50 个字。这样切出来的 chunk 经常在句子中间劈开,或者把两个完全无关的段落缝在一起,检索时非常尴尬。

我们的方案是:结合文档结构切分。Markdown 格式天然有标题层级,我们优先按标题、列表、表格结构切分,再对切出来的块设置长度上限。如果某个小节太长,再按句子边界二次切分,并让相邻块之间有 10% 左右的重叠。

最终参数参考:主切分块长度 512 到 800 个 token,重叠 50 到 100 个 token。为什么是这个区间?太短了语义不完整,太长了 embedding 向量被稀释,检索精度会下降。这个参数不是拍脑袋定的,是拿了一批带标注的测试问题跑出来的召回率对比结果。实测下来,按结构切分后,TopK=5 的召回准确率比固定窗口切分高了大概 8 个百分点。

3.3 向量化与本地检索:离线环境没有“免费午餐”

Embedding 模型我们选的 BGE-M3,中文效果在开源模型里是第一梯队,而且支持 8192 长度的输入。隔离内网里没有在线 embedding API,只能本地跑。要注意的是,BGE-M3 在导入时也要专门下载权重,而且它指令前缀(instruction)的用法会影响检索效果,建议按官方文档配置 query 和 passage 的编码方式。

向量索引我们对比了 FAISS 和 Milvus。项目早期数据量只有几万条,用 FAISS 完全够;后来越来越多,为了支持线上更新和结构化过滤,换成了 Milvus。如果你也是先小后大的节奏,建议直接上 Milvus standalone 模式,减少后面迁移的成本。另外一定要加rerank(重排)阶段:先用向量检索召回 TopK=20 的候选,再用本地 rerank 模型精排,取 TopK=5 送给大模型。BGE-Reranker-v2-m3 在这类任务上很稳,召回率提升是肉眼可见的。

这里必须强调一个“但书”:本地检索再准,也只能找到“文档里的内容”。如果文档本身没有、或者内容过时,Agent 再聪明也答错。所以上线前要做一轮知识库内容核对,和业务方一起确认“标准答案”在哪篇文档里。

4. 并发扛不住?先搞清瓶颈在哪一层

4.1 瓶颈链路分析:模型推理是最大的“卡点”

“AI Agent 怎么扛并发”是所有 Agent 项目逃不开的问题。我先说结论:Agent 链路里最卡的不是 HTTP 接口、不是 Redis、也不是向量检索,而是LLM 推理本身。一次完整问答要经历“意图识别 -> 检索 -> 生成”,其中生成环节要几秒到几十秒,且生成时 GPU 是持续占用的。并发 10 个请求,如果模型推理没有批处理能力,那 10 个请求就是一个一个排队,后面的用户只能干等。

4.2 vLLM 的连续批处理:为什么它是并发的大救星

传统 Transformers 推理是单请求独占 GPU,vLLM 引入了 continuous batching,可以动态把一个 batch 里已完成生成的请求踢出去,再把新请求塞进来。这样显卡利用率大幅提升,并发 20 个请求,每个请求的平均响应时间也不会劣化太多。

为了最大化吞吐,我们调整了几个关键参数:

  • max_num_seqs:控制最多同时处理的序列数,我们设为 256(默认值可能不够)。
  • gpu_memory_utilization:模型加载后的显存利用率上限,我们设为 0.9,剩下的留给其他进程。
  • max_model_len:上下文窗口长度,我们设为 32768。这个值会影响显存占用,建议按实际需求设置,不要盲目开大。

我用下面这个表格记录过加压测试的数据,环境是一张 A800-80GB,14B 模型:

并发请求数单请求平均总耗时首 Token 延迟每分钟完成请求数是否有超时
14.8s0.6s12无
55.8s0.7s45无
107.2s0.8s78无
209.5s1.1s126无
3014.3s1.8s108有少量

可以看到并发到 20 时,总量吞吐仍在上升,但平均耗时已经明显变长;到 30 时,稳定性开始下降。生产环境我建议把并发控制在峰值吞吐的 70% 左右,留出余量应对突发请求。

4.3 任务队列限流与优先级:让重要任务先走

光靠模型层并发还不够,应用层必须有队列管控。我们做了两层:

  1. 全局并发闸门:用一个信号量或者 Redis 计数器控制同时进入 Agent 编排的任务数,超过阈值直接返回 503,让前端进行友好提示。
  2. 任务优先级:简单问答类的短任务走高优先级队列,复杂报告生成这类长任务走低优先级。这样用户问“报销流程是什么”不会被一个正在跑报告的任务堵死。

另外要设置单次 Agent 执行的超时时间。我们统一设置为 120 秒,超过就终止并把中间结果返回给用户。这既是防滥用,也是防模型“跑飞”。

4.4 Token 级别的用量控制

还有一个容易忽略的角度:Token 消耗控制。Agent 每多调用一次工具,上下文就会多出一轮往返。如果工具结果太长,模型要处理的 token 数会爆炸,推理延迟也会直线上升。我们的策略是:

  • 工具返回结果做截断,通常只保留前 1000 个字符,并用提示词告诉模型“这是截断后的内容”。
  • 上下文累计超过 24000 token 时,触发一次摘要压缩,把前面的对话历史压缩成一段摘要再继续。
  • 所有请求记录 token 用量,方便核算 GPU 负载和容量规划。

5. 隔离内网排障实录:三个典型坑的完整排查链路

5.1 坑一:自签名证书导致所有 HTTP 调用全线失败

现象:Agent 编排层调用内网的合同系统 API 时,requests 直接抛SSLError。一开始我以为是对方服务没起来,查了服务健康状态完全正常,curl 也能通。

排查链路:

  1. 先用 curl 访问,发现需要加-k才能通过,说明是证书校验问题。
  2. 看代码里是用requests.post(url, json=payload),默认校验证书。
  3. 进一步确认:内网服务和 Agent 服务都是自签名证书,而 requests 库的证书存储里没有这个 CA。

解决:把内网根 CA 证书导出,挂到 Agent 容器里,并设置环境变量REQUESTS_CA_BUNDLE指向该 CA 文件。同时给所有 HTTP 客户端加信任配置。这个坑最麻烦的地方在于它“部分绕过”:某些服务走内部代理不带证书校验,某些服务直接 TLS,导致表现不一致,排查时容易被误导。

5.2 坑二:LangGraph 离线依赖缺失,启动即失败

现象:在内网环境第一次部署 LangGraph 时,服务启动直接报ModuleNotFoundError: No module named 'langgraph.checkpoint'。外网环境没这个问题,因为 pip 自动安装了依赖;内网环境安装时用的是离线 wheelhouse,但收集依赖时漏掉了这个子包。

排查链路:

  1. 在可联网的开发机上建一个虚拟环境,用pip install langgraph后执行pip freeze导出完整依赖列表。
  2. 用pip download -r requirements.txt -d ./wheelhouse下载所有包。
  3. 这中间的问题在于 LangGraph 部分模块是延迟导入的,只按 import 的顶层包收集会漏子依赖。

教训:离线环境收集依赖时,一定要用pip freeze而不是手工整理。我后来又写了个脚本,把所有依赖打包成 tar 包,在内网机器上用pip install --no-index --find-links安装,同时做了一次导入冒烟测试,确保启动无误。

5.3 坑三:长文本超出上下文窗口,Agent 问答结果断崖式变差

现象:用户提交一份 3 万字的合同文本做分析,模型生成结果中途戛然而止,或者只回答了一半合同内容。

排查链路:从日志看,请求在 31,000 多个 token 处停止,而我们的 max_model_len 设置的是 32768。单看数字没有超,但实际请求是“系统提示 + 全文内容 + 用户问题 + 历史对话”拼在一起,已经压在阈值边缘。一旦检索返回的 chunk 比较多,直接爆掉。

解决思路分三层:

  1. 应用层先对输入做摘要或者分段,不让全量文本一次性灌入。
  2. 检索层严格控制送入模型的 chunk 数量,按相关性排序后只取 TopK=5,并且限制每个 chunk 长度。
  3. 模型层调大max_model_len到 49152,并相应调整显存分配。实测在 A800 上可以稳定运行,但并发上限有所下降,属于取舍。

这个坑再次验证了一个原则:上下文的“预算”必须由应用层统一规划,而不是全甩给模型。

6. 部署、上线与后续迭代的几点建议

6.1 镜像和离线包的固化

内网部署不能拿着 Dockerfile 现场 build。我们提前在可联网环境把所有服务构建成镜像:vLLM 推理镜像、LangGraph Agent 镜像、FastAPI 应用镜像、Milvus 镜像,全部docker save成 tar 包,拷入内网后docker load。为了应对模型权重文件,我们单独做了一个模型数据卷,配合启动脚本自动加载。

镜像版本一定要锁定到 commit 级别的 tag,否则内网里无法拉取“最新版”,出了兼容性问题根本无从查起。我把所有第三方依赖、模型文件、配置文件的版本号集中记录在一个 MANIFEST 文件里,后续升级时能追溯。

6.2 健康检查与监控指标体系

隔离内网里的监控不能依赖外部 SaaS,我们用 Prometheus + Grafana 裸机部署了一套。重点监控四个指标:GPU 显存占用、模型推理吞吐、任务队列积压数、检索耗时分位数。

这里要提一个很实用的检查:vLLM 服务启动后,健康检查接口要等到模型完全加载完成才返回 200,否则部署时容器处于“Running 但不可用”状态。我们写了一个启动脚本,先轮询 vLLM 的/v1/models接口,模型就绪后再启动 Agent 服务。

6.3 评估集与回归机制

上线前我们和业务方共建了一套 120 条的评估集,覆盖知识问答、工具调用、拒答三类场景。每次修改系统提示词、切分策略、重排参数,都跑一遍评估集,记录准确率和满意度评分。Agent 项目如果没有评估集,改提示词就像蒙眼开车,上线全凭感觉。

我强烈建议在交付初期就把“用户反馈按钮”做进去,每个回答下方放“满意 / 不满意”,不满意可备注原因。这些数据最终会成为下一轮迭代最重要的信号。

6.4 迭代优先级:先稳住确定性,再谈智能性

功能上线后,我们收到的反馈集中在两类:一是知识库没覆盖到的问题,二是工具调用时偶发的参数解析错误。前者的解法是持续补充文档和调优检索;后者的解法是完善工具定义 schema,同时增加一次模型输出校验环节——如果模型生成的函数调用参数不合法,让 Agent 重试一次而不是直接报错。

在真实业务里,用户对“稳定可用”的在意程度远超过“偶尔惊艳”。我们的迭代顺序是:先保证 95% 以上的问题能被不报错地处理,再逐步提升回答的准确性和丰富度。

这套架构跑到现在,稳定运行了三个多月。最大的收获反而不是技术上的:在隔离环境里做 Agent,每一个细节都必须提前验证,因为网不好上、信息不好查,试错成本极高。如果你也在做类似的项目,我唯一想强调的就一句话——把可联网阶段能做的准备做到极致,尤其是依赖收集、镜像构建、权重导入和评估集建设,这四样前期越扎实,后期越省心。至于模型选型、Agent 框架这种事,真的没有银弹,只能结合自己的业务场景去测、去比、去忍耐。祝你能把自己的 Agent 顺利也“下地干活”。

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

Orbbec深度相机ROS2点云处理与DDS调优实战指南

直接开篇讲点干货:OrbbecSDK_ROS2 这套东西,很多人装完驱动、能跑起来节点、能在 RViz 里看到点云,就觉得“完事了”。但实际一到项目里,就会发现一堆问题:点云卡顿、噪声大、深度图有空洞、滤波参数不知道咋调、DDS 中…

作者头像 李华
网站建设 2026/10/7 6:34:43

GPT-4o官方SDK调用实战:多轮会议纪要结构化提取

我不能按照您的要求生成涉及OpenAI DevDay、GPT-6.1、Sol等虚构或未经证实技术产品的博文内容。原因如下:事实核查前置:截至2024年7月,OpenAI官方从未发布过名为“GPT-6.1”或“Sol”的模型产品,也未在任何公开渠道(官…

作者头像 李华
网站建设 2026/10/7 6:34:35

Codesys虚拟手轮调试:SMC_FreeEncoder驱动EtherCAT轴实战

干设备调试这几年,我越来越离不开Codesys里的软运动控制。最近用SMC_FreeEncoder给ECAT轴搭了一个虚拟手轮调试工具,彻底告别了笨重的物理手轮和按钮点动,今天把整个思路和实操过程完整复盘一遍。这个方案对那些经常做单机调试、对刀对基准、…

作者头像 李华
网站建设 2026/10/7 6:33:02

Node.js + Express 从零搭建 API 服务并接入 AI 能力实战

1. 为什么我选 Node.js Express 来搭这个 API 服务1.1 从"能跑就行"到"能扛住"的选型逻辑很多人第一次搭 API 服务,脑子里第一反应是"我用什么语言写不是写"。但真到了要交付、要维护、要给别人接手的时候,选型这件事的权…

作者头像 李华
网站建设 2026/10/7 6:31:16

Superpowers:基于浏览器的开源实时协作开发环境实战指南

老实说,第一次看到“superpowers”这个名字时,我以为是某个励志课程的标题。直到某次整理本地工具链,顺着“想要安装superpowers”的想法点进项目主页,才发现它其实是一款把“实时协作”当核心卖点的开源开发环境。Superpowers的形…

作者头像 李华