1. 隔离内网 AI Agent 的架构选型与设计思路
1.1 为什么"隔离"才是 Agent 工程的真正试炼场
大多数团队做 AI Agent,第一个 demo 通常是这样的:本地写个 Python 脚本,调 OpenAI 的 API,配一个 LangChain 的 Agent 模板,跑通一个"帮我查天气再写个邮件草稿"的流程,然后拍个屏幕录屏,PPT 里写上"AI Agent 已跑通"。
但真正到了生产环境,尤其是有数据合规要求的企业内网、政务内网、制造车间的产线内网,你会发现上面的 demo 一件也跑不通。原因很简单:你调的所有外部能力,在内网里全部是断的。
隔离内网意味着什么?没有公网 DNS、没有外网 API、不能直接 pip install、不能访问 HuggingFace、不能调 OpenAI 兼容接口的云上服务。连手机热点都救不了你,因为很多生产内网是物理隔离的,网线拔掉都不让插,别说连外网了。
这恰恰是 AI Agent 工程真正的试炼场。Agent 的本质是"模型 + 工具 + 编排",在隔离内网里,这三个要素全部需要换成内网可用的替代方案。模型要私有化部署,工具要换成内网的搜索、内网的数据库、内网的 API 网关,编排框架要能在没有云端依赖的情况下稳定跑起来。
我最初接到这个需求的时候,团队里有人提议"先在内网里跑通了再说,实在不行就用 A 模型凑合"。如果你也这么想,建议趁早改思路。隔离内网的 Agent 工程,第一件事不是选模型,是确认边界:哪些东西在内网里是可用的,哪些是必须自建的,把这个清单列出来,后面所有的工作都是在这些约束下做选择题。
1.2 技术栈选型:FastAPI + LangGraph,还是 Dify / RagFlow?
内网部署 AI Agent,首先要定的是编排层用什么。目前主流方案分两类:一类是代码优先的框架,以 LangGraph、LangChain、LlamaIndex 为代表;另一类是平台化产品,以 Dify、RagFlow、FastGPT 为代表。
我个人更推荐FastAPI + LangGraph的组合,理由有三点:
第一,隔离内网里你大概率不会只跑一个 Agent,而是要把 Agent 能力嵌入到现有的内部系统里。FastAPI 作为服务层非常合适,它天然支持异步、并发、任务队列,和 LangGraph 的状态机模型配合也很顺手。你完全可以把 LangGraph 的图逻辑封装成一个个服务端点,前端也好、内部系统也好,统一走 HTTP 接口调用。
第二,LangGraph 在编排复杂流程时优势明显。Agent 不是简单的"问一句答一句",它有状态、有分支、有循环、有并行节点。LangGraph 的 Graph/State 模型,把每个节点、每条边、每次状态更新都显式地表达出来,这在排障的时候价值巨大。内网环境没有那么多现成的日志分析工具,你能打印每一步的 state 快照,基本就赢了一半。
第三,平台化产品比如 Dify 当然有它的优势——界面化配置、内置 RAG、内置工具调用,中小团队上手极快。但它的定制上限卡得很死。你在隔离内网里部署 Dify,用的是它的 API 直连模型配置,虽然也支持本地模型走 Ollama 或者 Xinference,但一旦涉及复杂的 Agent 编排逻辑,比如多 Agent 协作、嵌套任务、自定义工具协议,平台产品的灵活性就不够了。而且平台自身的依赖包和镜像体积非常大,在内网离线安装时要解决的依赖问题一点不比代码框架少。
# FastAPI + LangGraph 的最小服务骨架,适合作为内网 Agent 服务的初始模板 pip install fastapi uvicorn langgraph langchain langchain-community如果你团队里 Python 功底一般,又不想写太多代码,Dify 作为一个 MVP(最小可行产品)快速验证业务可行性是可以的。但长期跑,我建议至少把核心编排逻辑抽出来,用 LangGraph 重写一遍。这不是为了炫技,是因为内网环境一但出问题,你能拿到的东西只有日志和自己的代码,平台的黑盒会让人很被动。
1.3 整体架构:四层模型,缺一不可
隔离内网 AI Agent 的整体架构,我习惯拆成四层:接入层、编排层、能力层、基础设施层。每一层都有自己的内网适配点。
接入层就是对外提供的服务入口。推荐用 FastAPI 写一套统一的 API Gateway,负责鉴权、流量控制、请求日志。内网里一般有现成的统一认证体系(LDAP、OAuth、企业微信/钉钉的私有化版本),你在这个层直接对接就行,不需要自己在 Agent 内部重复造一套用户体系。
编排层是 Agent 的大脑。LangGraph 的状态机在这里跑,每个 Agent 节点通过 LLM 判断下一步动作,调用工具函数,更新状态,循环直到拿到最终结果。这一层需要关注的是:上下文窗口管理、Token 消耗量控制、节点超时处理、失败重试策略。内网模型上下文窗口普遍比商用 API 小,所以你在编排层更要做"上下文裁剪",只把必要的信息灌给模型。
能力层是 Agent 能调用的所有工具的集合。内网里常见的工具包括:内部文档搜索引擎、SQL 查询接口、消息推送机器人(如企业微信/钉钉的自建应用)、工单系统 API、监控告警 API。每个工具封装成 LangGraph 的 Tool Node 时,要特别注意协议格式的统一,尽量都用 JSON in / JSON out,这样 LLM 在意图识别和参数填充的时候会省很多力气。
基础设施层是最容易被低估的。模型推理服务、向量数据库、内网包镜像源、模型文件仓库、离线模型管理工具,这五样是隔离内网跑 Agent 的硬支撑。它们单独拿出来每一个都有一堆坑,后面我会逐个展开。
2. 内网部署中最难啃的硬骨头:模型与依赖离线化
2.1 LLM 私有化部署的两条主流路径:vLLM 与 Ollama/UUID
隔离内网里跑 Agent,LLM 推理服务是绕不开的第一步。目前比较务实的方案就两类:一个是 vLLM,一个是 Ollama(或 Xinference 这类封装工具)。选哪条路,取决于你的硬件情况和并发要求。
vLLM 适合 GPU 资源相对充足的场景,尤其是你有 A100、V100、4090 这一类显存还过得去的卡。vLLM 的 PagedAttention 机制能显著提高吞吐量,并发能力也强,Agent 这种高频度、多轮调用的场景,vLLM 的收益非常明显。部署时直接用 Docker 起服务,然后暴露一个 OpenAI 兼容的/v1/chat/completions接口,LangChain/LangGraph 里的ChatOpenAI类只要把 base_url 指过去就能用,代码都不用改。
# vLLM 启动命令示例:用 7B/13B 级别的模型做内网 Agent 底座 docker run --runtime nvidia --gpus all \ -v /opt/models:/models \ -p 8000:8000 \ vllm/vllm-openai:latest \ --model /models/Qwen2.5-7B-Instruct \ --served-model-name agent-llm \ --gpu-memory-utilization 0.92 \ --max-model-len 16384 \ --enforce-eagerOllama 则更适合 GPU 资源一般或者压根只有 CPU 的机器。你也许觉得 CPU 跑大模型慢到没法用,但实测下来,在隔离内网里如果不追求极致的响应速度,用 Ollama 加载 7B/9B 级别的 Qwen 或 DeepSeek 蒸馏模型,配合量化版本(Q4_K_M 这类),给中小规模的内部使用完全跑得动。一条单路 32 核的服务器,跑一个 7B 量化模型,响应时间大概在 3-8 秒,Agent 场景勉强能接受,关键是省下了昂贵的 GPU 采购成本。
如果你有 GPU 但不想折腾 NVIDIA 驱动那一堆环境问题,也可以用 Ollama 跑在 GPU 上,性能虽然比 vLLM 差一截,但胜在部署快。我见过不少团队先用 Ollama 跑通流程,后期并发上来了再迁 vLLM。这个思路没什么问题,但要注意一点:Ollama 自己维护了一套模型仓库的逻辑,和标准 HuggingFace 格式有差异,迁移到 vLLM 的时候要重新拉模型或者转换格式,建议从一开始就把原始模型文件(最好是 GGUF 和 safetensors 两种格式都存一份)统一放在内网的模型文件仓库里。
2.2 内网包镜像源与模型文件同步方案
这是隔离内网项目里最劝退人的环节。你以为只装 Python 依赖就够了?实际上要装的还有 Node 依赖(前端和工具链)、系统级依赖(CUDA、FFmpeg、编译工具链)、模型权重文件,甚至部分 pip 包在编译安装时还要拉 C 库源码。机器如果只有一台还好,如果是几十台服务器,每台都要搞一套,不做仓库直接原地爆炸。
内网 pip 源搭建,最简单的方案是devpi或者nexus。我用的是 nexus,因为它在企业内网里很常见,好多公司本来就用它做 Maven 和 npm 仓库,只需要加一个 PyPI proxy repository 就行。
# 内网机器的 pip 配置:/etc/pip.conf [global] index-url = http://nexus.internal.corp/repository/pypi-group/simple trusted-host = nexus.internal.corp这里有个非常容易踩的坑:如果你在公网上有代理或者镜像缓存,先把所有需要的包下载到本地,生成一个 requirements 的离线 wheel 包目录,然后再导入到内网的 nexus 里。千万别让内网机器直接访问公网(本来也访问不了),也别在外网机器上一个个手动下载,你必须写一个脚本去抓取完整依赖树。坑就在依赖解析:pip install xxx和pip download xxx解析出来的版本可能不一致,所以下载的时候必须指定好目标版本,生产环境和临时下载环境版本对齐。
模型文件同步也是一个重灾区。HuggingFace 的模型动辄 10GB 以上,内网机器又不能直连。方案是:在一台有外网的临时机器上把模型完整下载到移动硬盘,然后拷进内网。看起来简单,但 HF 上传的模型很多是多个分片文件,下载不完整会导致加载失败。建议用huggingface-cli download或者modelscope的下载命令,它们支持断点续传。下载完之后,放在内网的 NFS 或者 MinIO 对象存储里,所有推理服务统一从这个存储挂载模型目录。
2.3 向量化与 RAG 组件:不能直连 Embedding API 怎么办
隔离内网里做 Agent 的知识库增强(RAG)会比正常环境复杂得多。核心原因是最常用的 Embedding API——OpenAI 的text-embedding-3、智谱的embedding-2、阿里云的text-embedding-v1这些,全都是云端服务,内网完全不可用。你得换本地方案。
本地方案有两个方向:一是本地跑 embedding 模型,二是干脆绕开 embedding,用关键词检索兜底。
本地 embedding 模型推荐用BAAI/bge-m3,或者更轻量的bge-small-zh-v1.5。前者效果最好,支持 8K 长度的中文文本,后者速度快、部署成本低。跑 embedding 模型可以直接用text2vec框架或者 FastAPI 包一个sentence-transformers的推理服务,一次性把所有文档向量化,还是要注意对长文档先做切片再 embedding。
# 本地 embedding 推理服务的极简示例,供 LangChain 直接对接 from sentence_transformers import SentenceTransformer from fastapi import FastAPI app = FastAPI() model = SentenceTransformer("/models/bge-m3") @app.post("/embed") def embed(texts: list[str]): vectors = model.encode(texts, normalize_embeddings=True) return {"data": [v.tolist() for v in vectors]}向量数据库选型上,内网场景我比较推荐Milvus或者Elasticsearch(带向量插件)。Milvus 性能好,但组件多(依赖 etcd、MinIO),部署复杂。ES 如果你内网本来就有日志系统在跑,复用一套就行,索引即文档,管理起来简单。小规模使用(几十万条向量以内)直接用chromadb或者FAISS存本地文件也够了,少一个服务就少一个故障点。
里面还有个大坑是向量化的一致性。你做文档预处理用的 embedding 模型,和 Agent 运行时查询检索用的 embedding 模型必须是同一个,版本也要锁死,否则向量空间不一致,检索效果直接崩盘。我见过有人离线文档用的 bge-m3 的 v1 版本跑,线上推理又换了个 v1.5 版本,结果 TopK 检索结果完全偏掉,查半天才发现是 embedding 版本不一致。
3. Agent 编排层的工程细节:从 LangGraph 到并发控制
3.1 用 LangGraph 搭一个可维护的 Agent 状态机
LangGraph 的核心思想是把 Agent 的一次完整执行过程定义成一个图。每个节点是一个函数,函数接收当前的 State,处理后返回新的 State 更新。节点之间用边连接,边可以是条件分支,也可以是固定跳转。
我推荐把 Agent 设计成三层图:Router(路由)→ Worker(执行)→ Critic(校验)。路由节点让 LLM 分析用户意图,决定走哪个工具分支;执行节点去调用具体工具;校验节点检查执行结果是否真的解决了用户的问题。这个结构的好处是,你可以在每个层打印非常清晰的日志,出了问题一眼就能定位是意图识别错了还是工具执行失败了还是校验太严格了。
from langgraph.graph import StateGraph, END from typing import TypedDict, Literal class AgentState(TypedDict): user_input: str context: dict tool_output: str final_answer: str def router_node(state: AgentState) -> dict: # 用 LLM 识别意图,返回要执行的工具名 tool_name = llm.invoke(f"请判断以下请求该调用哪个工具:{state['user_input']}") return {"context": {"tool": tool_name}} def worker_node(state: AgentState) -> dict: tool = state["context"]["tool"] # 调用内网工具函数 result = dispatch_tool(tool, state["user_input"]) return {"tool_output": result} def critic_node(state: AgentState) -> dict: # 校验结果,决定是直接结束还是回到 worker ok = verify_result(state["tool_output"], state["user_input"]) return {"final_answer": state["tool_output"] if ok else "需要重试"} def route_after_worker(state: AgentState) -> Literal["critic", "worker"]: return "critic" if state["tool_output"] else "worker" graph = StateGraph(AgentState) graph.add_node("router", router_node) graph.add_node("worker", worker_node) graph.add_node("critic", critic_node) graph.add_edge("router", "worker") graph.add_conditional_edges("worker", route_after_worker, {"critic": "critic", "worker": "worker"}) graph.add_edge("critic", END)实际工程中要注意 State 的定义。不要一股脑把所有内容都塞进 State,尤其是 Agent 存在多轮对话的时候。LangGraph 的 State 是全局共享的,每一轮对话状态没清理干净,下一轮就会被旧内容干扰,要么 Token 浪费,要么模型被旧上下文带偏。我习惯在 State 里只有两类字段:本轮会话的临时数据(user_input、tool_output)和需持久化的持久字段(历史摘要、用户偏好),并且每次新的会话开启时,清空临时字段。
3.2 工具层的内网替代方案:搜索、浏览器、数据库
Agent 在公网上最常用的工具是什么?Web 搜索、浏览器访问、在线翻译、网页解析。到了内网,这些工具一律没有。你绝对不能拿一个公网通识知识库去回答内网用户问的"XX 系统的工单处理流程是什么",模型不知道,搜索工具也搜不到。你必须给 Agent 装配内网工具集。
最基本的内网工具是文档检索。把公司内部的 Wiki、知识库、产品文档全部清洗后导入向量库,然后封装成一个retrieve_documents(keyword)工具。这个工具的实现方式很直接:先做关键词检索(ES 的 match 查询)和向量检索并行,再用 RRF(Reciprocal Rank Fusion,倒数排名融合)算法对两个结果做一个融合排序,然后截断取 TopK 返回。
其次是内部数据库查询。很多内部系统的数据都存在 MySQL / PostgreSQL 里,Agent 工具可以封装成"表名 + 查询条件 → 结构化数据"。注意安全问题:Agent 生成的 SQL 不能直接往生产库执行,最好封装一层只读 API,只允许白名单表的SELECT操作,且每次都带上 LIMIT 限制返回条数。
浏览器工具在内网场景也有替代方案:Playwright 可以跑在内网,去访问那些没有开放 API 的老旧内部系统。比如一些人资系统、ERP 系统,没有对外 API 但网页端可以操作。Agent 通过 Playwright 模拟点击、抓取页面内容,再把结果返回给 LLM 做分析和决策。这个方案是真实可落地的,但你要处理页面的验证码、动态加载、登录态保持等问题,这条路非常容易踩坑,后面我会单独讲。
3.3 扛并发:内网 Agent 服务的限流、重试与排队
热搜词里有人问"AI Agent 怎么扛并发",这个问题在隔离内网场景下尤为尖锐。公网场景你可以随便扩容加机器,内网环境 GPU 就那几张,资源是硬约束,你必须从架构层面控制并发。
内网 Agent 服务的并发瓶颈不在 FastAPI 本身,FastAPI 是异步的,扛几千个 HTTP 请求问题不大。真正的瓶颈在 LLM 推理服务。vLLM/Ollama 的并发窗口是有限的,超过之后请求会排队甚至超时。所以编排层必须做三重防护:
第一重是 API 网关级别的限流。每个用户、每个应用都设置独立的 QPS 限制,例如单用户 2 QPS、单应用 5 QPS,防止某个上游系统发疯把 Agent 服务打爆。
第二重是异步任务队列。Agent 的响应时间很长,如果一个用户的请求要跑 30 秒,同步阻塞式 HTTP 调用会让用户疯狂刷新。我建议所有 Agent 请求都走"提交任务 → 轮询结果/回调通知"的模式,FastAPI 配合 Celery 或者 Redis Stream,把 Agent 执行过程放到 worker 里去跑。用户提交请求后立刻获得一个 task_id,前端轮询/task/{task_id}拿结果。这个思路在长耗时 AI 场景里基本是标配。
第三重是重试策略的精细化设计。LLM 调用偶尔会失败,原因可能是显存不够触发 OOM(Out Of Memory,内存溢出)、服务重启、并发打满超时。不能简单要求"重试三次"就完事,得按失败原因区分策略:连接超时可以直接重试;如果返回的是模型推理线程打满的错误,则不能马上重试,要先退避;如果是上下文超长(max_model_len超了),重试一百次也没用,只能裁剪上下文。我在工程里会给每个错误类型配置不同的重试逻辑,并且把重试次数和最终失败都记录到日志里,方便事后复盘到底哪一类问题最频发。
4. 知识库增强与其他敏感能力的内网私有化落地
4.1 内部文档清洗与切片策略
接着前面说的文档检索工具,这里专门展开讲一下文档清洗和切片。内网知识库常见的文档格式有 Word、PDF、Markdown、Confluence 导出 HTML。直接把 PDF 塞给解析器然后切片,效果会非常差,尤其是那些扫描版 PDF 或者带复杂表格的文档。
我实测下来,文档清洗的顺序应该是:格式转换 → 结构识别 → 文本清洗 → 语义切片 → 向量化。格式转换把 docx、pdf 统一变成纯文本或者 Markdown,这一步可以用 LibreOffice 的命令行转换;结构识别要识别标题层级、段落关系、表格位置,关键是为了后续切片时保留章节完整性;文本清洗就是去除页眉页脚、去掉无意义的换行和符号、统一全角半角,有些文档里会有"第 X 页 / 共 Y 页"这种页码水印,必须过滤掉,不然检索的时候会被这些噪音干扰。
切片策略上,固定窗口切(比如 512 个 token 一截)虽然简单,但容易把一句话切成两半,也容易在段落中间硬切。更好的做法是基于 Markdown 标题结构做父子级切片:第一遍粗切,每个 H2/H3 标题底下的一块内容作为一个大块;第二遍细切,如果大块长度超过阈值才继续往下切到段落。LangChain 里可以用MarkdownHeaderTextSplitter做这个事,然后子块和父块分别向量化并存储父块 ID,检索命中子块时返回父块的完整内容给 LLM,保证上下文完整。
4.2 内网环境的语音能力:ASR 与 TTS 私有化选型
很多 Agent 场景其实会涉及语音入口——内网办公助手、客服系统对讲、会议纪要整理。语音能力在公网上有一堆 API 可以直连,内网里又是全断。你只能自建 ASR(语音识别)和 TTS(语音合成)服务。
ASR 这块我试过两个方案,一个是 OpenAI Whisper 的开源版本,本地部署后精度不错,特别是对普通话的支持;缺点是对长语音和嘈杂环境的处理一般,推理速度在三方 ASR 引擎里有差距。另一个是基于 SenseVoice 或者 PaddleSpeech 的模型,中文识别效果上两者都有大量基准测试,PaddleSpeech 在中文领域直接开箱即用,而且模型比较小,CPU 也能跑。要求不高的话,ASR 用 PaddleSpeech 就够了。
TTS 就更有意思了。内网场景往往是受限的,很多企业不允许把内部语音内容发到云端。把 TTS 私有化之后,你可以做很多事情:给 Agent 加上语音播报能力、给内部培训系统生成语音课件、或者做智能客服的语音交互。声音克隆类的方案,比如 GPT-SoVITS、CosyVoice 这类,也完全可以离线部署,只要有干净的参考音频和足够的标注数据,就能训练出符合特定音色的合成模型。好消息是这类方案对算力的要求不高,一张普通消费级显卡就能训练,推理时 CPU 也能用。
但要注意一点:TTS/ASR 服务在内网里的调用方式要和 Agent 编排衔接好。不要每个 Agent 请求都实时语音合成,那会极大加重推理负载。合理做法是 TTS 做成异步任务,Agent 生成文本后把内容推到消息队列,由 TTS worker 去合成并返回音频文件路径。这样即便有 50 个用户同时请求语音播报,服务也不会被打爆。
4.3 多 Agent 协作的一个简化落地模式
隔离内网里做多 Agent 协作,听起来很高大上,但真正落地的时候你会被现实疯狂教育。两个 Agent 之间的协作本质上就是两个 LLM 的循环调用,每一轮都要过一遍模型,Token 消耗翻倍,时间延迟翻倍,错误率也翻倍。所以我给的建议是:谨慎上多 Agent,优先单 Agent + 工具链。
如果你的业务确实需要多角色协作(比如一个写方案、一个审方案),可以用 LangGraph 的并行节点来做,让两个 Agent 各跑各的,最后汇总。就像上面架构里说的那样,每个 Agent 是独立的 subgraph,主图的节点负责调度它们。实操中建议给每个子 Agent 分配独立的会话 ID 和独立的上下文窗口,不要让它们共享 State,否则根本不知道谁改了什么。
如果你连多 Agent 都嫌重,还有一个很实用的折中方案:"人机协作 Agent"——Agent 负责出初稿和执行数据检索,关键决策节点让人来确认。这在很多内网业务场景其实最靠谱:让模型承担重复机械劳动,把决策压力保留给人。不要迷信"全自动",在隔离内网这种容错率低、数据敏感的环境,半自动反而更受欢迎。
5. 常见问题与排障实录
5.1 内网部署最容易踩的坑:从网络到显存一网打尽
先说一个最基础的坑:DNS 与主机名解析。隔离内网通常没有公网 DNS,所以你配置 LangChain 连接到本地服务时,一定要使用 IP 或者内网域名,并且保证所有服务之间可以通过内网域名互相访问。我遇到过一次 Agent 调用向量库服务不通,查了半天发现是服务注册用的主机名在另一台机器上解析不了,改成 IP 或者统一写入/etc/hosts就好了。
再一个高频坑是SSL 证书。内网服务普遍自签证书,Python 的 requests / httpx 默认会校验证书链,直接报SSL: CERTIFICATE_VERIFY_FAILED。对于内网服务,最省事的办法是在代码里指定verify=False,但这只在可信内网可用;如果公司安全规范不允许关验证,那就把内网 CA 证书导入到系统的 ca-bundle 里,一劳永逸。
模型服务相关的坑也很多。vLLM 启动时显存设置太贪(比如gpu-memory-utilization 0.98),服务虽然在空闲时能起来,但只要并发请求一多,KV cache 膨胀就会触发 OOM。建议 0.90~0.92 起步,稳定后再往上试。另一个 vLLM 经典问题:max-model-len设得太大,显存不够,启动失败;设得太小,上下文一长就报input length exceeds max_model_len。这个值需要根据实际数据和显存反复调,不要参考别人的"推荐值",因为你的显存和模型输出偏好可能完全不同。
还有一个小坑容易被忽略:机器时间同步。Agent 服务要做日志审计、鉴权(JWT 或 OAuth 2.0),如果服务器时间漂移超过几十秒,token 校验直接失败。内网环境没有公网 NTP,也要记得搭一个内部的 NTP 服务。
5.2 一个典型的"上下文超长"排查实录
我之前有个同事反馈,Agent 跑了一阵子之后开始频繁报错,日志里出现400: invalid_request_error,提示 token 超限。
起初以为是模型并发打满导致服务拒绝。查了一圈,发现报错的请求都集中在某个固定接口上。再细看日志,原来是这个接口的处理流程里,Agent 会把多次工具返回结果拼接成一个大文本,然后再发给 LLM 做总结。这个文本越拼越长,终于在某次查询的记录特别多时,超过模型上下文限制。
解决办法分三步:第一步,在工具返回层加截断逻辑,超过 2000 字符的内容先做摘要,不要一股脑塞给模型;第二步,在编排层使用 LangGraph 的 state 裁剪,历史对话只保留最近 N 轮,更早的对话用摘要代替;第三步,针对超长场景提供"分批次总结"的专用工具函数,让 Agent 先分段总结再合并总结,而不是一次性灌入所有原始数据。
排查这类问题的通用套路是:先看是稳定复现还是偶发,再看是哪个环节的输入变大了,最后定位是哪个组件没有做截断防护。内网 Agent 没有云端那么多数据埋点,所以日志必须打全,每个节点的输入长度和延迟都打出来,不然出了问题只能靠猜。
5.3 实用问题速查表
| 现象 | 可能原因 | 快速处理办法 |
|---|---|---|
| pip 安装超时/找不到包 | 内网源未配置或版本不匹配 | 检查/etc/pip.conf,确认使用的是 nexus 内网源;锁定版本号 |
| LLM 服务偶发 OOM | gpu-memory-utilization设置过高 | 调低到 0.90,减少max-model-len,观察峰值显存 |
| 工具调用返回结果为空 | 内网工具超时或鉴权失败 | 给工具函数加统一超时和错误包装,日志记录具体报错 |
| 检索效果突然变差 | embedding 模型版本不一致 | 核对文档初始化和在线推理的模型路径是否完全一致 |
| 对话历史被旧内容污染 | 全局 State 未清理 | 每次会话开始时重置本地 State,仅保留持久字段 |
| 服务重启后模型加载慢 | 模型文件在 NFS 上,无本地缓存 | 定期用脚本把模型文件预拉取到各节点本地盘 |
| JWT 鉴权偶发失败 | 服务器时间偏差 | 部署内网 NTP,统一所有机器时间源 |
5.4 最后的工程经验:日志、监控与灰度
隔离内网环境没有那么多现成 SaaS 监控工具可以用,但日志和监控又必须做,否则 Agent 出了问题你根本不知道是哪一步出了问题。我的建议是轻量落地:所有 Agent 节点的输入输出、耗时、Token 数全部结构化写入本地 JSONL 文件,同时用一个内网的 ELK 栈(Elasticsearch + Logstash + Kibana)或者 Loki 收集起来,仪表盘上只看三个核心指标:成功率、平均耗时、Token 总消耗。这三个指标能覆盖 90% 的异常判断。
上线新模型或者改 Agent 流程的时候,一定走灰度。最简单的灰度是"白名单部门先测"——把参数或模型版本做成可配置的,针对特定用户 ID 使用新版本,其他人继续走旧版本。跑三到五天,看日志里新版本的成功率和反馈质量,再决定全量切。内网 Agent 一旦全量上错了,返工成本比公网高得多,因为连快速回滚都未必有镜像备份。
我个人在多个隔离内网 Agent 项目里最深的一个体会是:隔离内网不是把外网的那套东西搬进来,而是重新做一次设计。你精心调优的提示词、你依赖的云端 API、你习惯的在线可视化调试,很多在内网里都是奢侈品。反而是在这种资源受限、处处受限的环境里,你会被迫把工程做扎实:日志打全、错误分类清晰、依赖版本锁死、每个服务的地址显式可配。这些习惯带到任何项目里,都是长期收益。如果你正准备在隔离内网里落地 AI Agent,我的建议是先从最小的闭环跑起来——一个本地 LLM、一个文档检索工具、一个 FastAPI 服务——把链路走通,再逐步加东西。别一上来就画几十个节点的多 Agent 大图,那个复杂度在内网里面基本属于给自己挖坑。