1. 为什么“隔离内网 + AI Agent”是个真问题
先把场景说清楚。所谓隔离内网,就是一台或者一批机器,物理上或者策略上跟公网断开,装不了外网包,连不上外部模型 API,甚至连 pip、npm 这类包管理器的默认源都访问不了。很多做 AI Agent 的朋友第一反应是:这玩意儿离了公网还能跑吗?答案是能跑,而且跑得还挺好,前提是你得把整套工程思路从“在线优先”切换成“离线自洽”。
我最早接触这类需求,是帮一个做工业质检的团队搭一套内部知识问答 Agent。他们的产线网络跟办公网是隔离的,办公网又跟公网隔离,三层结构。一开始我按常规思路,用云端大模型 API + 在线向量库,结果连第一行代码都跑不起来——机器根本出不去。后来整套方案推倒重来,改成“本地模型 + 本地向量库 + 本地工具链 + 内网自建服务”,才真正落地。
这里面的核心矛盾在于:AI Agent 的工程实践,天然依赖三类外部资源——模型推理能力、工具调用能力(也就是常说的 MCP、Skills 这类机制)、以及依赖包和运行时。隔离内网把这三条路全掐了。所以你要做的不是“想办法连出去”,而是“把这三样东西全部搬进来,并且让它们在内网里自己转起来”。
这篇文章面向的是这样几类人:一是在内网环境做 AI 应用落地的工程师;二是想搞清楚 Agent 工程到底由哪些模块拼起来的技术负责人;三是手里有内网服务器、想自己搭一套能用的 Agent 平台的折腾型选手。我会把模型部署、MCP 协议落地、Skills 机制设计、并发扛压、依赖离线化这几块拆开讲,每一块都给可复现的思路和踩过的坑。
提示:本文所有方案都基于“完全离线、不依赖任何外部网络”的前提设计。如果你的内网其实有受控的出口,那选择会多很多,但本文不讨论那类场景。
2. 把模型搬进内网:选型、量化与推理服务
2.1 内网模型选型的三条硬约束
在公网环境选模型,你主要看效果和价格。在内网选模型,约束条件完全变了,我总结成三条硬约束:
第一是显存约束。内网服务器往往是几年前采购的,显卡可能是 V100、T4,甚至只有 CPU。你得先摸清楚手上有多少显存,再倒推能跑多大的模型。经验值是:FP16 精度下,模型参数量乘以 2 就是大致显存需求(单位 GB),7B 模型约 14GB,13B 约 26GB,再算上 KV Cache 和框架开销,实际要留 20% 余量。
第二是量化容忍度。内网机器不够强,就得量化。INT8 量化大概能把显存砍一半,INT4 再砍一半,但效果会掉。我的经验是,做知识问答和工具调用这类任务,INT4 的 Qwen 系列、GLM 系列还能用,但做复杂推理就会明显退化。所以量化策略要跟任务难度匹配。
第三是许可证与合规。内网环境往往对许可证敏感,选模型时要确认权重可以内部使用。这一点很多人会忽略,等到部署完了才发现有问题,返工成本很高。
下面这张表是我实际用过的几个模型在内网场景下的对比,供参考:
| 模型 | 参数量 | INT4 显存占用 | 工具调用能力 | 中文表现 | 内网适配建议 |
|---|---|---|---|---|---|
| Qwen2.5-7B-Instruct | 7B | 约 6GB | 较强 | 优秀 | 首选,均衡 |
| Qwen2.5-14B-Instruct | 14B | 约 11GB | 强 | 优秀 | 显存够就上 |
| GLM-4-9B-Chat | 9B | 约 8GB | 较强 | 优秀 | 备选 |
| Llama3.1-8B-Instruct | 8B | 约 7GB | 中等 | 一般 | 英文场景 |
| Baichuan2-13B | 13B | 约 10GB | 中等 | 良好 | 老项目兼容 |
2.2 推理框架怎么选:vLLM、llama.cpp 还是 Ollama
内网部署推理服务,主流就三个选择,各有适用场景。
vLLM适合有像样显卡、要扛并发的场景。它的 PagedAttention 机制对显存利用率很高,吞吐量比朴素实现高好几倍。缺点是安装依赖比较重,离线安装要提前把 wheel 包和 CUDA 相关依赖全部下好。我一般用pip download在联网机器上把依赖拉全,再拷进内网。
llama.cpp适合 CPU 或者显存很小的场景。它支持 GGUF 格式的量化模型,CPU 上也能跑出可用的速度。缺点是并发能力弱,适合个人用或者低并发场景。它的离线部署最省心,一个二进制文件加一个模型文件就能跑。
Ollama是 llama.cpp 的封装,用起来最舒服,一条命令拉模型。但它的模型拉取默认走公网,内网要手动把模型文件放进去,或者自建一个模型仓库。它的 API 兼容 OpenAI 格式,这点对 Agent 工程很友好。
我的建议是:有显卡、要扛并发,上 vLLM;纯 CPU 或者小显存,用 llama.cpp;想快速验证、不想折腾,用 Ollama。三者都可以通过 OpenAI 兼容接口暴露出来,这样上层 Agent 代码不用改。
2.3 离线部署推理服务的完整链路
以 vLLM 为例,讲一下内网离线部署的完整链路。这套流程我在多个内网环境跑通过。
第一步,在联网机器上准备依赖。建一个干净的虚拟环境,执行:
pip download vllm -d ./vllm_packages --platform manylinux2014_x86_64 --python-version 310 --only-binary=:all:这一步会把 vllm 及其所有依赖的 wheel 包下到vllm_packages目录。注意--platform和--python-version要跟内网机器的环境对齐,否则装不上。
第二步,把模型权重下好。从模型仓库把权重文件完整下载,包括 config、tokenizer、safetensors 等所有文件。这一步最容易出错的是漏文件,建议下载后用huggingface-cli的校验功能确认完整性。
第三步,把 wheel 包和模型文件拷进内网。用移动硬盘或者内网文件服务器都行。
第四步,在内网机器上安装:
pip install --no-index --find-links=./vllm_packages vllm--no-index表示不走任何在线源,--find-links指向本地包目录。这一步如果报缺依赖,说明第一步没下全,回去补。
第五步,启动推理服务:
python -m vllm.entrypoints.openai.api_server \ --model /path/to/model \ --served-model-name local-model \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9启动后,内网其他机器就能通过http://内网IP:8000/v1访问,接口格式跟 OpenAI 一致。这样上层 Agent 代码里只要把 base_url 改掉就行。
注意:
--gpu-memory-utilization别设成 1.0,留一点给系统和其他进程,否则容易 OOM。我一般设 0.85 到 0.9。
2.4 模型服务的内网高可用考虑
内网环境虽然流量不大,但 Agent 一旦跑起来,模型服务就是单点。我的做法是至少起两个实例,前面挂一个内网 Nginx 做负载均衡。Nginx 配置很简单:
upstream model_backend { server 192.168.1.10:8000; server 192.168.1.11:8000; } server { listen 8080; location /v1/ { proxy_pass http://model_backend; proxy_read_timeout 300s; } }proxy_read_timeout要设大,因为大模型推理有时候要几十秒,默认 60 秒会断。这个坑我踩过,Agent 调用到一半连接被切断,排查了半天才发现是 Nginx 超时。
3. MCP 协议在内网怎么落地
3.1 MCP 到底解决了什么问题
MCP 全称 Model Context Protocol,你可以把它理解成“Agent 和外部工具之间的标准插座”。在没有 MCP 之前,每接一个工具就要写一套适配代码,工具多了就是一团乱麻。MCP 把这件事标准化了:工具方实现一个 MCP Server,Agent 方实现一个 MCP Client,双方通过统一的协议通信,谁也不用关心对方内部怎么实现。
在内网环境,MCP 的价值反而更大。因为内网工具往往是自研的、五花八门的,有查数据库的、有调内部 API 的、有操作文件的。如果每个都单独适配,维护成本极高。用 MCP 统一起来,新增工具只要起一个 Server,Agent 侧几乎不用改。
MCP 的通信方式主要有两种:stdio(标准输入输出)和 SSE(Server-Sent Events)。stdio 适合本地进程,Agent 直接拉起一个子进程通信;SSE 适合远程服务,通过 HTTP 长连接通信。内网环境两种都能用,我一般本地工具用 stdio,跨机器的用 SSE。
3.2 内网 MCP Server 的部署模式
内网部署 MCP Server,我总结出三种模式,按复杂度递增。
模式一:单机 stdio 模式。所有 MCP Server 跟 Agent 跑在同一台机器上,通过 stdio 通信。这种最简单,适合工具少、单机部署的场景。缺点是工具跟 Agent 绑死,没法复用。
模式二:内网 SSE 服务模式。把 MCP Server 做成独立的 HTTP 服务,部署在内网某台机器上,Agent 通过 SSE 连接。这种模式工具可以复用,多个 Agent 共享同一批工具。缺点是要处理服务发现和鉴权。
模式三:MCP 网关模式。在内网起一个 MCP 网关,所有 MCP Server 注册到网关,Agent 只跟网关打交道。网关负责路由、鉴权、限流、日志。这种模式最适合工具体系庞大的场景,但实现成本也最高。
我的建议是,刚开始用模式一快速验证,工具超过五个就切模式二,工具超过二十个或者有多个 Agent 共享需求,再上模式三。
3.3 一个内网 MCP Server 的最小实现
下面给一个基于 Python 的 MCP Server 最小实现,功能是查询内网数据库。这个例子我在实际项目里用过,可以直接改。
from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import sqlite3 app = Server("internal-db-server") @app.list_tools() async def list_tools(): return [ Tool( name="query_employee", description="根据员工姓名查询员工信息", inputSchema={ "type": "object", "properties": { "name": {"type": "string", "description": "员工姓名"} }, "required": ["name"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "query_employee": conn = sqlite3.connect("/data/employee.db") cursor = conn.cursor() cursor.execute("SELECT * FROM employees WHERE name = ?", (arguments["name"],)) rows = cursor.fetchall() conn.close() return [TextContent(type="text", text=str(rows))] raise ValueError(f"Unknown tool: {name}") async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())这个 Server 暴露了一个query_employee工具,Agent 调用时传入姓名,返回员工信息。实际项目里你可以把 sqlite 换成内网的 MySQL、PostgreSQL,逻辑一样。
3.4 MCP 在内网的鉴权与安全
内网不等于安全,这点必须强调。我见过太多内网系统裸奔,任何能连上内网的人都能调。MCP Server 如果暴露了敏感操作(比如删数据、发消息),一定要加鉴权。
最简单的做法是在 SSE 模式下加一个 Token 校验。Agent 连接时带上 Token,Server 校验通过才响应。Token 可以写死在配置里,也可以走内网的统一认证服务。
from fastapi import FastAPI, Request, HTTPException from mcp.server.sse import SseServerTransport app = FastAPI() sse = SseServerTransport("/messages") VALID_TOKEN = "internal-secret-token" @app.get("/sse") async def handle_sse(request: Request): token = request.headers.get("Authorization") if token != f"Bearer {VALID_TOKEN}": raise HTTPException(status_code=401, detail="Unauthorized") async with sse.connect_sse(request.scope, request.receive, request._send) as streams: await mcp_app.run(streams[0], streams[1], mcp_app.create_initialization_options())注意:Token 别硬编码在代码里提交到版本库,用环境变量或者内网配置中心注入。这个习惯在内网环境同样重要,因为内网代码也可能被审计。
4. Skills 机制:让 Agent 真正“会干活”
4.1 Skills 和 MCP 的区别,别再搞混了
很多人把 Skills 和 MCP 混为一谈,其实两者定位完全不同。MCP 解决的是“Agent 怎么调用工具”的通信问题,是连接层。Skills 解决的是“Agent 在什么场景下、按什么步骤、用什么工具”的编排问题,是能力层。
打个比方:MCP 是插座和电线,Skills 是“怎么用电饭煲煮饭”的菜谱。插座再多,没有菜谱你也煮不出饭。反过来,菜谱再详细,没有插座你也用不了电饭煲。
一个 Skill 通常包含几部分:触发条件(什么时候用这个 Skill)、执行步骤(分几步做)、用到的工具(通过 MCP 调用)、输出格式(结果长什么样)。在内网环境,Skills 往往是团队沉淀下来的“最佳实践”,把老师傅的经验固化下来,让 Agent 照着做。
4.2 内网 Skills 的设计原则
内网 Skills 的设计,跟公网场景有几个明显区别。
第一,步骤要更明确。公网模型能力强,可以给个模糊指令让它自己发挥。内网模型往往是量化过的,能力弱一些,Skill 的步骤必须写得非常具体,每一步做什么、输入什么、输出什么都要说清楚。
第二,容错要更强。内网工具可能不稳定,Skill 里要设计重试和降级逻辑。比如查数据库失败,是重试三次还是返回缓存,要在 Skill 里定义清楚。
第三,输出要结构化。内网 Agent 往往要对接下游系统,输出格式必须严格。我一般要求 Skill 的输出是 JSON,字段名和类型都固定,方便下游解析。
下面是一个内网 Skills 的配置示例,用 YAML 描述:
name: employee_query_skill description: 查询员工信息并生成结构化报告 trigger: keywords: ["查员工", "员工信息", "工号查询"] steps: - name: extract_name description: 从用户输入中提取员工姓名 tool: llm_extract input: "{{user_input}}" output: "{{employee_name}}" - name: query_db description: 调用数据库工具查询 tool: mcp__internal-db-server__query_employee input: name: "{{employee_name}}" retry: 3 fallback: "返回'查询失败,请稍后重试'" output: "{{raw_result}}" - name: format_output description: 格式化为标准 JSON tool: llm_format input: "{{raw_result}}" output_schema: type: object properties: name: {type: string} department: {type: string} position: {type: string}这个 Skill 定义了三步:提取姓名、查数据库、格式化输出。每一步都指定了工具、输入、输出,还有重试和降级。Agent 拿到这个 Skill,就知道该怎么干活了。
4.3 Skills 的加载与热更新
内网环境改代码成本高,Skills 最好支持热更新。我的做法是把 Skills 配置放在一个目录里,Agent 启动时加载,同时起一个文件监听,配置变了自动重载。
import os import yaml from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class SkillLoader: def __init__(self, skill_dir): self.skill_dir = skill_dir self.skills = {} self.load_all() def load_all(self): for filename in os.listdir(self.skill_dir): if filename.endswith(".yaml"): path = os.path.join(self.skill_dir, filename) with open(path, "r", encoding="utf-8") as f: skill = yaml.safe_load(f) self.skills[skill["name"]] = skill def get_skill(self, name): return self.skills.get(name) class SkillWatcher(FileSystemEventHandler): def __init__(self, loader): self.loader = loader def on_modified(self, event): if event.src_path.endswith(".yaml"): self.loader.load_all() print(f"Skills reloaded: {event.src_path}")这样运维改 Skill 配置不用重启 Agent,改完保存就生效。这个机制在内网特别实用,因为内网重启服务往往要走审批流程,能省一次是一次。
4.4 Skills 的测试与灰度
Skills 上线前一定要测。内网环境没法像公网那样快速迭代,一个 Skill 出问题可能影响整个业务流程。我的做法是给每个 Skill 配一组测试用例,包含正常输入、边界输入、异常输入,跑通了才允许上线。
test_cases = [ {"input": "查一下张三的信息", "expected_name": "张三"}, {"input": "帮我查李四", "expected_name": "李四"}, {"input": "查一下不存在的员工", "expected_error": True}, ] def test_skill(skill, cases): for case in cases: result = run_skill(skill, case["input"]) if "expected_name" in case: assert case["expected_name"] in result, f"Failed: {case}" if case.get("expected_error"): assert "失败" in result or "错误" in result, f"Failed: {case}"灰度的话,可以给 Skill 加一个enabled字段,先在小范围 Agent 实例上开启,观察一段时间没问题再全量。这个思路跟公网灰度一样,只是内网观察周期要拉长。
5. 内网 Agent 的并发扛压实战
5.1 并发瓶颈到底在哪
很多人一上来就优化模型推理,其实内网 Agent 的并发瓶颈往往不在模型,而在几个容易被忽略的地方。
第一个瓶颈是模型服务的并发上限。vLLM 虽然支持并发,但显存有限,并发数上去之后请求会排队。你要根据显存和模型大小算出一个合理的并发数,超过就限流。
第二个瓶颈是MCP 工具调用的阻塞。如果工具是同步的,一个请求卡住会拖垮整个 Agent。必须把工具调用改成异步,或者用线程池隔离。
第三个瓶颈是Agent 自身的状态管理。多轮对话要维护上下文,如果上下文存在内存里,并发一高内存就爆。要么用 Redis 之类的内网缓存,要么限制单实例的会话数。
第四个瓶颈是日志和监控。并发高的时候日志写入本身就会成为瓶颈,尤其是同步写文件。要用异步日志或者写到内网的消息队列。
5.2 用异步和连接池扛并发
Python 的 asyncio 是内网 Agent 扛并发的基础。所有 IO 操作都要异步化,包括模型调用、工具调用、数据库查询。
import asyncio import aiohttp from aiohttp import ClientSession class AgentRuntime: def __init__(self, model_url, mcp_urls): self.model_url = model_url self.mcp_urls = mcp_urls self.session = None self.semaphore = asyncio.Semaphore(10) # 限制并发数 async def start(self): self.session = ClientSession( timeout=aiohttp.ClientTimeout(total=300), connector=aiohttp.TCPConnector(limit=50, limit_per_host=20) ) async def call_model(self, messages): async with self.semaphore: async with self.session.post( f"{self.model_url}/v1/chat/completions", json={"model": "local-model", "messages": messages} ) as resp: return await resp.json() async def call_mcp(self, url, tool_name, args): async with self.session.post( f"{url}/call_tool", json={"name": tool_name, "arguments": args} ) as resp: return await resp.json() async def close(self): await self.session.close()这里有几个关键点:Semaphore限制并发数,防止把模型服务打爆;TCPConnector的连接池复用连接,避免频繁建连;ClientTimeout设大,因为模型推理慢。
5.3 请求队列与背压
并发再高也有上限,超过上限的请求要排队,不能直接拒绝也不能无限堆积。我用的是asyncio.Queue加背压机制。
class RequestQueue: def __init__(self, max_size=100, worker_count=5): self.queue = asyncio.Queue(maxsize=max_size) self.worker_count = worker_count self.workers = [] async def submit(self, request): try: self.queue.put_nowait(request) return {"status": "accepted"} except asyncio.QueueFull: return {"status": "rejected", "reason": "queue_full"} async def worker(self): while True: request = await self.queue.get() try: await self.process(request) finally: self.queue.task_done() async def start(self): for _ in range(self.worker_count): self.workers.append(asyncio.create_task(self.worker()))队列满了直接返回“忙”,让调用方自己决定重试还是放弃。这比无限堆积导致雪崩要好得多。
5.4 压测与容量规划
内网上线前一定要压测。我用的是 locust,写一个简单的压测脚本:
from locust import HttpUser, task, between class AgentUser(HttpUser): wait_time = between(1, 3) @task def chat(self): self.client.post("/v1/chat", json={ "message": "查一下张三的信息", "session_id": "test-session" })压测时重点看几个指标:P99 延迟、错误率、模型服务的 GPU 利用率、MCP 工具的响应时间。根据压测结果反推容量,比如单实例能扛 20 QPS,业务峰值是 100 QPS,那就至少部署 5 个实例加负载均衡。
注意:压测要在内网做,别拿公网数据估。内网网络延迟、机器性能都跟公网不一样,估出来的数不准。
6. 依赖离线化:把整个工具链搬进内网
6.1 Python 依赖的离线打包
Python 依赖离线化是内网部署最烦的一环,因为依赖树深、平台相关性强。我的标准流程是:
# 在联网机器上,用跟内网一致的环境 pip download -r requirements.txt -d ./packages \ --platform manylinux2014_x86_64 \ --python-version 310 \ --only-binary=:all:--only-binary=:all:强制只下 wheel 包,避免下到源码包在内网编译。如果某个包没有 wheel,就得单独处理,要么找替代包,要么在内网准备好编译环境。
拷进内网后安装:
pip install --no-index --find-links=./packages -r requirements.txt如果报缺依赖,用pip check查,然后回联网机器补下。
6.2 模型权重的离线搬运
模型权重动辄几个 G 到几十个 G,搬运是个体力活。我的做法是用huggingface-cli download在联网机器下好,然后用rsync或者移动硬盘拷进内网。
# 联网机器下载 huggingface-cli download Qwen/Qwen2.5-7B-Instruct --local-dir ./qwen2.5-7b # 校验完整性 huggingface-cli download Qwen/Qwen2.5-7B-Instruct --local-dir ./qwen2.5-7b --resume拷进去之后,用sha256sum校验关键文件,确保没损坏。大文件传输损坏是常事,校验这一步不能省。
6.3 内网自建包仓库
如果内网机器多,每次都手动拷包太低效。我的做法是在内网起一个私有的 PyPI 仓库,用devpi或者pypiserver。
# 用 pypiserver 起一个最简单的私有源 pip install pypiserver pypi-server run -p 8080 -a . -P . /data/pypi-packages然后把联网机器下好的包上传进去:
twine upload --repository-url http://内网IP:8080 --username admin --password admin ./packages/*内网机器配置 pip 源指向这个私有仓库:
pip config set global.index-url http://内网IP:8080/simple pip config set global.trusted-host 内网IP这样内网机器装包就跟公网一样方便了,只是源换成了内网。
6.4 容器镜像的离线导入
如果内网用容器部署,镜像也要离线导入。流程是:联网机器docker pull,然后docker save成 tar 包,拷进内网docker load。
# 联网机器 docker pull vllm/vllm-openai:latest docker save vllm/vllm-openai:latest -o vllm.tar # 内网机器 docker load -i vllm.tar镜像大的话,docker save出来的 tar 包也大,可以用pigz压缩加速传输。另外,如果内网有 Harbor 之类的私有镜像仓库,直接 push 进去更省事。
7. 几个我踩过的坑和对应的解法
7.1 模型加载慢到怀疑人生
第一次在内网部署 14B 模型,加载花了将近二十分钟,我以为卡死了。后来发现是磁盘 IO 太慢,模型文件放在机械盘上。换成 SSD 之后,加载时间降到三分钟以内。
解法:模型文件一定放 SSD,别放机械盘或者网络存储。如果内网只有机械盘,考虑用mmap方式加载,或者把模型转成更紧凑的格式。
7.2 MCP 工具调用超时导致 Agent 卡死
有一次 Agent 调用一个内网 API 工具,那个 API 挂了,Agent 一直等,整个会话卡死。后来加了超时和熔断才解决。
解法:所有 MCP 工具调用必须设超时,超时后返回错误而不是一直等。再加一个熔断器,某个工具连续失败就暂时禁用,过一段时间再试。
from circuitbreaker import circuit @circuit(failure_threshold=5, recovery_timeout=60) async def call_tool_with_breaker(url, tool_name, args): return await call_mcp(url, tool_name, args)7.3 Skills 配置写错导致 Agent 乱调工具
有个 Skill 的触发关键词写得太宽泛,结果用户随便说句话就触发,Agent 乱调工具。后来把触发条件收紧,加了意图识别才解决。
解法:Skill 的触发条件要尽量精确,能用意图识别就别用关键词匹配。关键词匹配容易误触发,尤其是在中文场景下。
7.4 并发一高日志就丢
内网 Agent 并发上去之后,发现日志丢了不少。排查发现是同步写文件,高并发下写入冲突。改成异步日志队列之后解决。
解法:日志用logging.handlers.QueueHandler加QueueListener,写入操作放到单独线程,主流程只往队列里丢。
import logging import logging.handlers import queue log_queue = queue.Queue(-1) queue_handler = logging.handlers.QueueHandler(log_queue) root = logging.getLogger() root.addHandler(queue_handler) file_handler = logging.FileHandler("/var/log/agent.log") listener = logging.handlers.QueueListener(log_queue, file_handler) listener.start()7.5 内网时间不同步导致 Token 过期
内网机器时间不同步,导致 JWT Token 校验失败,Agent 调工具一直 401。排查了半天才发现是时间问题。
解法:内网一定要配 NTP 服务,所有机器时间同步。这个坑很隐蔽,但一旦踩上很难查。
8. 内网 Agent 工程的持续演进思路
内网 Agent 上线只是开始,后面怎么演进才是关键。我分享几个实际用过的方向。
第一个方向是能力沉淀。每解决一个新场景,就把它固化成一个 Skill,慢慢积累成内网的能力库。时间长了,Agent 能干的活越来越多,价值越来越大。
第二个方向是效果评估。内网没法像公网那样快速 A/B 测试,但可以建一个内部评测集,每次模型或 Skill 更新都跑一遍,确保效果不退化。评测集要覆盖核心场景,包含正常和异常输入。
第三个方向是资源优化。内网资源有限,要持续优化。比如模型量化策略调整、推理框架升级、缓存机制引入,都能在不加硬件的情况下提升吞吐。
第四个方向是可观测性。内网 Agent 出问题排查难,要把日志、指标、链路追踪做起来。至少要有请求级别的日志,能追溯每个请求经过了哪些步骤、调了哪些工具、耗时多少。
第五个方向是安全加固。内网不等于安全,权限控制、审计日志、敏感操作二次确认,这些都要逐步补上。尤其是 Agent 能操作内部系统之后,安全边界要划清楚。
我在实际项目里的体会是,内网 Agent 工程最难的不是技术,而是心态。公网环境习惯了“缺什么装什么”,内网环境必须“有什么用什么”。这个转变需要时间,但一旦转过来,你会发现内网 Agent 反而更可控、更稳定,因为所有环节都在自己手里,没有外部依赖的不确定性。