ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

隔离内网AI Agent落地实战:从架构选型到并发扛量

隔离内网AI Agent落地实战:从架构选型到并发扛量 隔离内网这个词做AI Agent的人第一反应往往是“麻烦”但干过一遍之后我更愿意叫它“照妖镜”所有在公网Demo里被隐藏起来的工程问题在这里全都会暴露出来。上个月我接了一个内部工单知识助手的活对方IT负责人见面第一句话就是“办公网和生产网隔离没有公网出口所有东西必须私有化部署”。这句话其实直接否定了市面上九成“AI Agent”方案——不能调云端大模型API不能在线pip install不能拉模型权重甚至连SaaS版的智能体平台都没戏。这篇文章就围绕这个真实场景说说在隔离内网下从架构选型、依赖搬运到并发扛量、故障排查的完整实战过程。这类环境在企业里非常普遍研发内网、生产专网、涉敏数据区它们和互联网物理隔离或逻辑隔离目的只有一个——数据不出域。在这种前提下做AI Agent本质不是在“做一个聊天机器人”而是在“离线再造一套完整的大模型应用基础设施”。如果你正准备接手类似项目或者只是好奇离线Agent怎么落地这篇文章值得看完。1. 这不是云上Demo先搞懂隔离内网缺什么1.1 一个看似普通的Agent项目为什么在内网会翻车先还原一下项目需求某机构内部有大量工单、运维手册和制度文档工程师每天花大量时间翻文档、查工单流转记录。目标是用AI Agent做一个内部知识助手员工问一句“打印机驱动怎么装”“这个报错码什么意思”Agent自己检索知识库、调工单系统查历史记录实在答不了再转人工。听着不难对吧但一旦落到隔离内网环境变成这样依赖项公网开发常态隔离内网现实大模型能力直接调用云厂商API只能本地部署开源模型Python依赖pip在线安装必须离线包或内网私有源模型权重huggingface下载需要审批后人工导入向量数据库可用云服务私有化部署外部工具各类SaaS API全部换成内网HTTP接口我看到过不少团队在这个环节翻车开发机跑得好好的一到交付现场依赖装不上、模型加载失败、服务监听地址不对、内网调用超时……看起来都是小问题但叠在一起能把工期拖垮。隔离内网项目的核心约束就一句话任何外部依赖都要提前变成“本地资产”。1.2 为什么不能把PoC方案直接搬进内网很多PoC方案默认用的技术栈是“云端大模型 在线向量库 开源RAG框架”这套东西在公网环境确实跑得飞快但它有一个隐藏假设——所有资源在线可得。到了隔离内网这个假设崩塌你需要重新审视每一层模型层没有公网API只能选可在内网部署的模型权重。参数规模、量化精度、显存占用就成了头等大事。推理层需要自己搭建模型服务这时候 vLLM、Ollama 这类本地推理引擎就成了必需品。编排层Agent 的思维链、工具调用、记忆管理放到哪里跑如果团队主语言是PythonFastAPI LangGraph 是一个成熟解如果是Java团队Spring AI 也值得评估。知识层向量化、存储、检索必须全部离线Embedding模型也要部署在本地。工具层Agent 要调用的所有系统只能是内网已有的系统且接口权限要单独设计。我把这个项目的最终形态总结成一句话一个跑在内网GPU服务器上的模型推理服务加上一组含RAG和工具调用的Agent编排服务再通过FastAPI暴露给前端全程不依赖任何公网资源。这个思路基本是隔离内网AI Agent的“默认解”。2. 技术选型离线约束下的主流Agent架构2.1 先对照主流的Agent架构别上来就堆复杂度当下AI Agent的主流架构大致分这么几类单Agent ReAct循环一个Agent边推理边调用工具适合工具数量少的场景。多Agent协作多个Agent各司其职通过消息协调适合复杂任务拆解但调试成本高。工作流式Agent把流程显式编排成节点和边每一步可观测、可回滚适合企业内部确定性强的业务。带记忆和反思的增强循环引入长期记忆、自我纠错适合开放域对话。如果你只看了几篇“多Agent协作”的文章很容易一上来就设计三四个Agent互相聊天觉得这才叫智能。但在这个内网项目里工具调用只有“知识库检索”和“工单系统查询”两条任务链路清晰状态有限最合适的是“工作流式Agent 单Agent ReAct”的组合。我选了LangGraph的StateGraph把节点、边显式画出来每一步输入输出都可追踪。为什么不用SaaS平台比如Coze扣子这类低代码工具因为隔离内网没有公网SaaS可用很多企业也不允许数据出域如果需要私有化部署低代码平台又是另一套成本。所以企业内部这类项目自研Agent编排依然是主流选择。2.2 模型服务层本地推理引擎怎么选Agent的大脑是本地模型服务这里有两个选择vLLM 和 Ollama。Ollama的优势是安装简单、模型管理方便一个ollama serve就能起来适合单机验证和小并发场景。vLLM的优势是高并发吞吐能力强支持continuous batching和PagedAttention适合真实业务流量。隔离内网项目往往会持续运行我建议直接把底层定为vLLMOllama可以作为开发阶段验证用生产不推荐。模型参数选择上受限于GPU显存我用了一个32B参数的量化模型4比特量化后权重约20GB单张48GB显卡可以跑。如果你们的GPU只有24GB建议降到14B级别的模型或者用更激进的量化4位或2位。别看热搜上一堆“7B模型跑满效果”的话题企业内部文档问答场景对准确率很敏感模型太小幻觉率会明显上升。2.3 应用层FastAPI LangGraph 的分层结构应用层我拆成了三层API层FastAPI负责鉴权、参数校验、流式响应。Agent编排层LangGraph负责跑状态图串联RAG节点、工具节点、生成节点。工具与知识层封装内网工单系统接口、向量库检索接口、上下文压缩等。这样分层的好处是每一层都能独立测试尤其适合离线交付——你可以在不连模型服务的情况下先用Mock数据跑通Agent编排逻辑。这里有个贴士FastAPI选async模式因为Agent编排过程中大量时间是IO等待模型推理、向量检索、HTTP调用asyncio能把等待时间让给其他请求后续并发章节会再讲。2.4 顺带看一眼Spring AI和Rust生态既然热搜里有Spring AI和Rust Agent我多说两句。如果你的团队是Java栈Spring AI确实提供了统一的模型访问和工具调用抽象能复用Spring生态但它在复杂状态编排方面没有LangGraph这么细离线交付时依赖冲突也要花时间处理。Rust生态做Agent的优势是资源占用小、并发能力强适合嵌入式环境或对延迟极度敏感的场景但开发效率相比Python还是低一些对于企业内部知识助手这种业务逻辑频繁迭代的项目不是最优选。技术栈选择始终要服从团队维护能力和业务节奏。3. 依赖、模型与服务的离线落地3.1 Python依赖的三条离线路线这是内网项目的第一道鬼门关。公网环境下pip install一条命令的事在内网会无限重试然后超时因为pip默认指向公网源。三条离线方案我全试过第一直接下载wheel包。在能上网的开发机上执行pip download -d offline_packages -r requirements.txt然后把整个offline_packages目录拷贝到内网服务器安装时用pip install --no-index --find-linksoffline_packages -r requirements.txt注意这里--find-links指向的目录里可以包含wheel包和依赖的递归下载结果但有些包会动态拉取依赖建议加上--no-deps的变体逐步排查。第二构建内网私有源。用Nexus或devpi搭一个pip镜像把公网包提前同步进去内网服务器配置index-url指向私有源即可。这个方法适合项目会持续迭代、依赖经常变化的场景前期建设成本高但一劳永逸。第三Docker镜像离线搬运。如果内网允许Docker可以在开发机构建好完整镜像然后docker save导出内网docker load导入。这是最省心的方式因为镜像自带全部依赖连系统库都包含了我这次Agent服务本身就是这么交付的。3.2 模型权重和Embedding模型怎么进内网模型权重是另一个重头。在一个允许U盘和移动硬盘经过审批导入的内网环境里最稳妥的做法是在开发机上下载好模型文件包括权重、tokenizer、配置文件用移动介质拷入GPU服务器。以vLLM加载本地模型为例目录结构大致是/data/models/internal-llm/ ├── config.json ├── generation_config.json ├── model-00001-of-000xx.safetensors ├── model-00002-of-000xx.safetensors ├── tokenizer.json └── tokenizer_config.json加载时直接把--model参数指向本地目录即可不要依赖Hugging Face的在线下载。同理RAG链路里的Embedding模型也要一并离线导入我用的是一个中量级的bge系列模型生成向量维度和检索质量都能满足内部场景。补充一点内网如果有多台机器都要跑模型服务建议把模型文件放到共享存储或统一目录避免每台机器各存一份后期升级模型权重也只需要替换一份。3.3 推理引擎的启动参数一个都不能改错vLLM的启动命令我调了很多次最终稳定参数如下python -m vllm.entrypoints.openai.api_server \ --model /data/models/internal-llm \ --served-model-name internal-chat \ --host 0.0.0.0 \ --port 8001 \ --gpu-memory-utilization 0.80 \ --max-model-len 8192 \ --max-num-seqs 32逐个解释这些参数为什么重要--served-model-name客户端调用时使用的模型名建议固定成业务代号防止以后换模型时客户端跟着改。--host 0.0.0.0必须监听所有网卡否则内网其他机器访问不到。--gpu-memory-utilization 0.80告诉vLLM最多用80%显存做KV Cache留出余量给输入输出激活和临时计算。设太满容易OOM设太少并发吞吐上不去。--max-model-len 8192控制最大上下文长度。设太长会让KV Cache占用暴涨单并发能吃好几个GB显存。--max-num-seqs 32限制一次推理Batch里最多32个请求vLLM会做continuous batching这是扛并发的基础。启动后可以先用一行命令验证模型服务curl http://127.0.0.1:8001/v1/models返回模型列表说明服务没起错。3.4 Agent编排的首次运行一张图把流程钉死LangGraph的玩法是定义状态图。我建了一个AgentState包含对话历史、检索结果、工具返回和最终回答然后把节点串起来预测节点先判断要不要检索/调工具检索节点查向量库工具节点查工单系统生成节点组织最终答案。from typing import TypedDict, Annotated, List from langgraph.graph import StateGraph, START, END import operator class AgentState(TypedDict): messages: Annotated[List[dict], operator.add] need_retrieval: bool need_tool: bool retrieved_docs: List[str] tool_result: dict final_answer: str def predict_actions(state: AgentState) - dict: # 调用模型判断是否需要检索和工具调用 ... def retrieval_node(state: AgentState) - dict: # 查向量库返回知识片段 ... def tool_node(state: AgentState) - dict: # 调内网工单系统接口 ... def generate_node(state: AgentState) - dict: # 组装上下文生成最终回复 ... builder StateGraph(AgentState) builder.add_node(predict, predict_actions) builder.add_node(retrieval, retrieval_node) builder.add_node(tool, tool_node) builder.add_node(generate, generate_node) builder.add_edge(START, predict) builder.add_conditional_edges(predict, lambda s: retrieval if s[need_retrieval] else tool) builder.add_edge(retrieval, generate) builder.add_edge(tool, generate) builder.add_edge(generate, END) app builder.compile()第一次跑通时我强烈建议把每个节点的输入输出都打印出来确认Agent的决策符合预期。这一步看起来慢但能帮你提前发现“模型根本没理解该调哪个工具”之类的问题而不是等到联调才炸。4. AI Agent怎么扛并发核心矛盾与工程手段4.1 瓶颈其实在模型推理层热搜里“AI Agent怎么扛并发”是大家最关心的问题。先说结论Agent服务的瓶颈几乎永远在模型推理层而不是FastAPI进程或向量库。一次用户提问Agent内部可能要先做意图判断再检索知识库再生成答案其中两次到大模型。大模型生成是逐token来的一句话几十到几百个token一次请求耗时2到10秒都不稀奇这比普通接口慢了好几个数量级。如果业务方一次性涌进来几十个请求你狂开线程是没用的——GPU显存和算力撑不住反而会把服务打挂。所以要分三层来处理模型服务层的批处理能力、应用层的流量控制和流式响应的体验优化。4.2 应用层三件套信号量、队列、流式输出应用层我用了三件套第一asyncio.Semaphore限制并发进入模型服务的请求数。FastAPI的接口是async函数里面用一个全局信号量控制同时只有N个请求在跑Agent链路超过N的请求直接返回429让前端提示“系统繁忙”。import asyncio from fastapi import FastAPI, HTTPException app FastAPI() agent_semaphore asyncio.Semaphore(8) app.post(/v1/chat) async def chat(payload: dict): if agent_semaphore.locked(): # 已有8个请求在跑直接拒绝 raise HTTPException(status_code429, detailtoo many requests) async with agent_semaphore: result await run_agent(payload[query]) return result第二请求排队和超时控制。光有Semaphore还不够还要设置整体超时时间比如一个Agent请求最长20秒超过就取消并返回兜底话术。内网环境网络波动少但模型偶尔会抽风没有超时控制很容易把进程拖死。第三流式输出SSE。答案不是一次性返回而是通过Server-Sent Events把token一个个推给前端。这不能提高总吞吐但能显著降低用户感知延迟。FastAPI里可以用StreamingResponse配合sse-starlette更方便。4.3 容量估算一张卡到底能扛多少并发容量估算别拍脑袋。我按这个思路算先看单卡显存比如48GB模型4比特量化权重占约20GB留给KV Cache的显存约48 * 0.8 - 20 18.4GB。再看单个请求在8192上下文下的KV Cache占用根据层数、头数和量化精度不同大概1到2GB。也就是说显存允许同时驻留约10到18路请求。所以我把应用层信号量保守设在8给突发和长文本留缓冲。但显存只是其中一个约束。模型本身的总吞吐量更重要。vLLM这类引擎靠continuous batching把多个请求凑成一个Batch推理单卡总吞吐可能是每秒几百到一千多token。假设平均每个回答300 token10个并发请求同时进行理论上每秒能完成3到5个完整回答。也就是说这个配置对外扛住2到4个QPS问题不大。真实业务往往远低于这个量级所以可行。如果并发目标更高要么换更大显存的卡要么加多张卡做分布式推理要么把Agent链路拆成多个模型服务做负载均衡。总之先算清显存账再谈并发调优。4.4 内网压测的简单方法压测不需要复杂平台一个Locust脚本就能撑起几百并发。我在内网环境写过一个简单脚本直接模拟用户调用/v1/chat统计响应时间和错误率。压测时重点看两组曲线一是吞吐量有没有随着并发上升达到平台期二是错误率在什么并发量开始飙升。平台期就是当前配置的安全上限。压测时还要注意连带打爆下游系统。Agent一检索、一调工单系统下游服务会不会挂所以压测不要只压Agent服务要把工具链里的内网工单系统、向量数据库一起纳入监控。我这次压测就发现工单系统的查询接口在20QPS时就出现明显延迟最后给它加了一层本地缓存问题才缓解。5. 知识库与工具接入的细节5.1 离线的RAG链路切分、向量化、召回、重排知识库是内部文档问答的核心。文档进入RAG链路的第一步是解析和切片。PDF、Word、PPT要变成纯文本再按标题层级和段落语义切成800到1200字的片段切太细丢上下文切太粗检索容易漂。Embedding模型要在本地加载。用LangChain的向量库封装直接连接内网部署的Milvus或轻量的Qdrant索引维度要跟Embedding模型的输出维度严格一致比如bge系列是1024维建Collection时别配错。召回阶段我用的方法是向量检索 关键词检索双路召回再做重排。重排模型也是个小模型CROSS-encoder类型把召回的几十个片段重新打分选Top3送给Agent。这个环节对回答准确率提升非常明显哪怕你纠结要不要上重排我的建议是内部知识问答场景值得。5.2 企业内部系统工具怎么接工具层是Agent能不能“干活”的关键。这次内部工单查询因为工单系统没有现成API只有内部HTTP接口我封装了一个工具函数def query_work_order(order_no: str) - dict: # 内网HTTP接口带签名鉴权设置5秒超时 response httpx.get( fhttp://ticket.internal/api/v1/order/{order_no}, headers{X-Internal-Token: token}, timeout5.0, ) response.raise_for_status() return response.json()然后把工具函数暴露给LangGraph的tool_node再给模型一份工具描述。这里的关键不是写代码而是安全边界。5.3 权限、白名单和审计日志Agent的工具调用权限必须最小化。一个查询工单的Agent不应该能删除工单一个内部知识助手不应该能改数据库。我在工具层做了三件事工具URL白名单只允许Agent访问预先声明的内部域名和路径。每次工具调用都写入审计日志记录用户、问题、Agent决策、工具参数、返回结果一旦有问题可以追溯。对工具返回内容做长度截断和敏感信息过滤。某些工单包含员工手机号、账号信息Agent不能原样输出。这些在内网项目里不是加分项是合规底线。你在验收时如果答不上来“Agent能查到哪些数据、调用日志在哪”项目大概率过不了安全评审。6. 隔离内网实战中的常见故障6.1 依赖装不上的最常见原因“pip install 一直Retrying”是最高频问题。原因很简单默认源不可达。解决办法就是前面提过的离线包或者内网私有源。这里有个容易忽略的坑有些包在pip download阶段会因为平台标记不同下载不同版本比如Linux和Mac的wheel不一样最好在目标环境的同架构机器上执行下载。如果某次pip install --no-index --find-linksoffline_packages提示找不到某个包大概率是下载时没有递归拉依赖可以用pip download -r requirements.txt --no-deps分步排查或者直接换Docker镜像方案让所有依赖固化成镜像。6.2 模型加载OOMvLLM启动时报CUDA out of memory通常是显存规划问题。按这个顺序排查先看nvidia-smi确认没有残留进程占显存再调低--gpu-memory-utilization或者减小--max-model-len。如果还不行检查模型本身是不是FP16权重大模型考虑换4比特量化版本。还有一个冷门原因输入了超长文本单个请求上下文爆炸把显存瞬间吃满。这种情况需要在应用层限制用户输入长度。6.3 服务监听0.0.0.0还是127.0.0.1把服务跑起来内网另一台机器访问不到第一反应是防火墙第二反应是监听地址。FastAPI默认启动时uvicorn如果没写--host 0.0.0.0默认只监听127.0.0.1从别的机器当然连不上。vLLM和Ollama同理OLLAMA_HOST不设置时也只在本机可达。这类问题排查要按“端口通不通、地址对不对、防火墙放没放”三步走。6.4 流式响应一直等到超时用SSE推送时如果经过内部网关转发网关往往有缓冲等Agent生成完才一次性吐给前端这就失去了流式意义。排查方向是看内部网关是否开了HTTP响应缓冲或者FastAPI是否把StreamingResponse错误地包在了非流式中间件里。把这些超时参数调到30秒以上并禁用对SSE的缓冲基本能解决。故障现象大概率原因处理方向pip安装反复重试公网源不可达离线包或私有源vLLM启动OOM显存规划不足调低利用率/换量化跨机器访问不通监听127.0.0.1改0.0.0.0检查防火墙SSE首字一直不返回网关缓冲关闭流式缓冲调大超时Agent答非所问上下文/RAG召回弱加双路召回和重排做完这个项目我最大的体会是隔离内网下的AI Agent比公网版本更考验工程基本功因为所有“顺手能用”的便利都消失了每一步都得自己铺路。若让我给准备接这类项目的朋友一个建议那就是——别急着调Agent的提示词先把依赖离线闭环、模型服务启动、内网连通性这几个月要才摸清的关键路径打通再谈智能。系统稳定之后Agent的聪明程度才有意义。
返回列表