☰
隔离内网部署AI Agent实战:MCP协议、Skills机制与SQLite持久化
2026/10/3 23:54:55 网站建设 项目流程

1. 为什么要在隔离内网里折腾 AI Agent

第一次接到"隔离内网部署 AI Agent"这个需求时,我脑子里冒出来的第一个念头是:这不是自找麻烦吗?公网环境下一行pip install就能搞定的事,放到隔离内网里,光是依赖包搬运就能让人崩溃。但真正做过几个项目之后,我的看法完全变了——隔离内网反而是检验一个 AI Agent 工程是否"扎实"的最佳试金石。

所谓隔离内网,指的是物理或逻辑上与公网完全断开的网络环境。这类场景在制造业、能源、医疗、金融的某些业务线里非常普遍。它的核心诉求很简单:数据不出网、模型不出网、依赖不出网。但问题在于,当前主流的 AI Agent 生态——无论是 MCP 协议、Skills 机制,还是各种 Agent 框架——几乎都是为"联网即用"设计的。你把这些东西搬到隔离环境里,会发现处处是坑。

这篇文章想聊的,就是我在隔离内网环境下搭建 AI Agent 工程的完整实战经验。核心关键词包括AI Agent、MCP、Skills、SQLite、Vue。我会从架构选型讲到具体落地,从依赖搬运讲到前端交互,把那些官方文档里不会写的细节全部摊开。适合的读者是:需要在受限网络环境下部署智能体应用的工程师、对 MCP 协议和 Skills 机制感兴趣的技术负责人,以及想搞清楚"AI Agent 到底怎么在企业内网跑起来"的开发者。

先给一个整体判断:隔离内网下的 AI Agent 工程,难点从来不在模型本身,而在依赖治理、协议适配、数据持久化和前端交互这四件事上。把这四件事理顺了,剩下的就是体力活。

2. 隔离内网 AI Agent 的整体架构怎么定

2.1 三层结构:Agent 核心、工具层、交互层

在隔离内网里,我建议把整个系统拆成三层,这个划分方式直接决定了后续依赖管理的复杂度。

Agent 核心层负责意图理解、任务规划和工具调度。这一层通常跑一个大语言模型(本地部署的量化模型居多),加上一个编排框架。编排框架的选择很关键,我实测下来,在隔离环境下优先考虑依赖少、纯 Python 实现的框架,避免引入需要联网校验的组件。

工具层是 Agent 真正"下地干活"的部分。这里就是 MCP 和 Skills 发挥作用的地方。MCP(Model Context Protocol)本质上是一套让模型和外部工具、数据源通信的协议规范,你可以把它理解成"模型和工具之间的 USB 接口标准"。Skills 则是更上层的概念,指的是把一组相关能力打包成一个可复用的技能单元,比如"数据库查询技能""文件处理技能"。

交互层负责把 Agent 的能力暴露给最终用户。在隔离内网里,Web 前端是最现实的选择,Vue 因为生态成熟、构建产物是纯静态文件,非常适合内网部署。

三层之间的数据流是这样的:用户在 Vue 前端发起请求,请求打到后端的 Agent 服务,Agent 解析意图后通过 MCP 协议调用具体的工具(比如查 SQLite 数据库),拿到结果后再组织语言返回给前端。整个链路里,没有任何一个环节需要访问公网,这是隔离内网部署的底线。

2.2 为什么选 SQLite 而不是其他数据库

很多人第一反应是用 MySQL 或 PostgreSQL,但在隔离内网的单机或小集群场景下,SQLite 的优势非常明显。

第一,零运维。SQLite 就是一个文件,不需要独立的数据库进程,不需要配置用户权限,不需要开端口。在隔离环境里,少一个服务就少一堆麻烦。第二,依赖极简。Python 标准库自带sqlite3模块,不需要额外安装任何驱动。第三,迁移方便。整个数据库就是一个.db文件,拷贝走就能用,这在需要把系统从测试环境搬到生产内网时特别省事。

当然,SQLite 也有它的边界。它不适合高并发写入场景,如果你的 Agent 需要同时处理几十个写操作,那还是老老实实上 PostgreSQL。但对于大多数内网 Agent 应用——配置管理、日志记录、知识库存储、任务状态跟踪——SQLite 完全够用。

我踩过的一个坑是:SQLite 默认的并发模式在 Agent 多线程调用时会出现database is locked错误。解决办法是在连接时设置timeout参数,并开启 WAL 模式。这个后面会详细讲。

2.3 MCP 协议在隔离环境下的适配要点

MCP 协议本身是软件协议,不是硬件协议——经常有人把它和硬件接口协议搞混。它的核心是定义了一套标准的消息格式,让模型能以统一的方式调用外部能力。

在隔离内网里用 MCP,有几个必须注意的点。首先,MCP Server 的依赖必须提前打包。很多开源的 MCP Server 实现会依赖一些需要联网下载资源的库,你得把这些资源提前缓存好。其次,MCP 的传输方式要选对。标准 MCP 支持 stdio 和 HTTP 两种传输,隔离环境下我强烈建议用 stdio,因为它是进程内通信,不涉及网络端口,天然规避了防火墙和安全策略的问题。

还有一个容易被忽略的点:MCP Server 的启动超时。在隔离环境里,如果 MCP Server 启动时尝试做任何网络探测(比如检查更新),都会卡住直到超时。所以要么改配置关掉这些行为,要么在启动脚本里设置合理的超时时间。

3. 依赖搬运:隔离内网最磨人的环节

3.1 离线依赖包的完整打包流程

隔离内网部署最痛苦的就是依赖搬运。公网环境下pip install -r requirements.txt一行搞定,内网里你得把所有 wheel 包提前下载好,还要处理依赖树里的传递依赖。

我的标准做法是:在一台和隔离内网操作系统版本、Python 版本完全一致的"跳板机"上,用pip download把所有依赖下载到本地目录。

pip download -r requirements.txt -d ./offline_packages --platform manylinux2014_x86_64 --python-version 310 --only-binary=:all:

这里有几个参数必须说清楚。--platform指定目标平台,如果你的内网服务器是 x86_64 的 Linux,就用manylinux2014_x86_64。--python-version指定 Python 版本,必须和内网环境完全一致,否则下载的 wheel 包可能装不上。--only-binary=:all:强制只下载二进制包,避免下载源码包后在内网编译时缺少编译工具链。

下载完成后,把整个offline_packages目录拷贝到内网,然后:

pip install --no-index --find-links=./offline_packages -r requirements.txt

--no-index告诉 pip 不要访问任何在线索引,--find-links指定本地包目录。这两个参数是离线安装的关键。

注意:如果依赖树里有包只提供源码分发(sdist),--only-binary=:all:会直接报错。这时候你需要单独处理这个包,要么在跳板机上编译好 wheel,要么在内网机器上准备好编译工具链。

3.2 模型文件的搬运与校验

本地模型文件通常有几个 GB 到几十个 GB,搬运过程容易出错。我的经验是:分卷压缩 + 校验和。

用tar分卷压缩:

tar -czf - model_dir/ | split -b 2G - model_dir.tar.gz.part_

到内网后合并解压:

cat model_dir.tar.gz.part_* | tar -xzf -

搬运前后都要算 SHA256 校验和,确保文件完整。我遇到过一次因为拷贝过程中断导致模型文件损坏,Agent 加载模型时直接段错误,排查了大半天才发现是文件不完整。

3.3 那些容易漏掉的系统级依赖

Python 包只是依赖的一部分。很多 AI 相关的库还依赖系统级的动态链接库,比如libgomp、libopenblas、libsqlite3等。这些库在隔离内网里如果缺失,报错信息往往很隐晦。

我的做法是:在跳板机上用ldd检查关键二进制文件依赖了哪些系统库,然后把这些.so文件一并打包。对于 SQLite,特别要注意版本——Python 自带的sqlite3模块链接的是系统 SQLite 库,如果系统版本太老,可能不支持 WAL 模式或某些新特性。

ldd $(which python3) | grep -i sqlite python3 -c "import sqlite3; print(sqlite3.sqlite_version)"

这两条命令能帮你确认 SQLite 的实际版本。如果版本低于 3.7.0,WAL 模式就用不了,需要升级系统 SQLite 库。

4. Skills 机制怎么设计才不鸡肋

4.1 Skills 和 MCP 的分工边界

很多人搞不清楚 Skills 和 MCP 的关系,我用一个类比说明:MCP 像是电脑的 USB 接口标准,规定了"怎么插";Skills 像是具体的 USB 设备驱动,规定了"插上之后能干什么"。

在实际工程里,我的划分原则是:MCP 负责通信协议和工具注册,Skills 负责业务逻辑封装。比如,一个"查询数据库"的 MCP Server 提供了execute_sql这个工具,但具体怎么组织 SQL、怎么处理结果、怎么格式化输出,这些属于 Skill 的范畴。

这样划分的好处是,MCP Server 可以保持通用和稳定,而 Skills 可以灵活迭代。当业务需求变化时,你只需要改 Skill,不用动 MCP Server。

4.2 一个可复用的 Skill 应该长什么样

我设计 Skill 时遵循一个原则:单一职责 + 明确输入输出 + 可独立测试。

一个典型的 Skill 定义包含几个部分:名称、描述、触发条件、输入参数 schema、执行逻辑、输出格式。在隔离内网里,我建议把 Skill 定义写成结构化的配置文件(YAML 或 JSON),而不是硬编码在代码里。这样非开发人员也能参与 Skill 的维护。

name: query_device_status description: 查询指定设备的最新状态 trigger: 用户询问设备状态时 input_schema: device_id: type: string required: true output_format: json implementation: skills.device.query_status

这个配置文件的好处是,Agent 在规划任务时可以直接读取 Skill 列表,根据描述判断该调用哪个 Skill。这比把所有逻辑塞进一个大函数里要清晰得多。

4.3 Skill 的版本管理和灰度发布

在内网环境里,Skill 的更新不像公网那么频繁,但一旦更新出问题,回滚成本很高。我的做法是给每个 Skill 加版本号,并且保留最近三个版本。

更新流程是:新版本先在一个测试 Agent 实例上验证,确认无误后再切换到生产实例。切换时只需要改配置文件里的版本号,然后重启 Agent 服务。这个流程虽然土,但在隔离内网里非常可靠。

还有一个细节:Skill 的依赖要单独管理。如果某个 Skill 依赖了额外的 Python 包,这个包必须提前打包进离线依赖里。我建议在 Skill 的配置文件里显式声明依赖,这样打包脚本可以自动收集所有 Skill 的依赖。

5. SQLite 在内网 Agent 里的实战用法

5.1 表结构设计与字段类型选择

SQLite 的类型系统比较灵活,它支持动态类型,但这不意味着你可以随便写。我的建议是:该用严格类型的地方就用严格类型,避免后期数据混乱。

Agent 应用里常见的几张表:会话记录表、任务状态表、工具调用日志表、知识库表。以会话记录表为例:

CREATE TABLE conversations ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system')), content TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, metadata TEXT ); CREATE INDEX idx_session ON conversations(session_id);

这里metadata字段用 TEXT 存 JSON 字符串,因为 SQLite 没有原生的 JSON 类型(虽然新版本有 JSON1 扩展)。created_at用 DATETIME 类型,SQLite 会自动处理。

关于修改字段类型,这是 SQLite 的一个痛点。SQLite 不支持直接ALTER COLUMN,要改字段类型只能:创建新表、拷贝数据、删除旧表、重命名新表。这个过程一定要在事务里做,并且提前备份。

BEGIN TRANSACTION; CREATE TABLE conversations_new (...); INSERT INTO conversations_new SELECT ... FROM conversations; DROP TABLE conversations; ALTER TABLE conversations_new RENAME TO conversations; COMMIT;

5.2 并发写入的坑与 WAL 模式

前面提到过database is locked的问题。SQLite 默认的日志模式是 DELETE,写操作会锁住整个数据库。在 Agent 场景下,多个工具调用可能同时写日志,很容易触发锁冲突。

解决方案是开启 WAL(Write-Ahead Logging)模式:

import sqlite3 conn = sqlite3.connect('agent.db', timeout=30) conn.execute('PRAGMA journal_mode=WAL') conn.execute('PRAGMA synchronous=NORMAL')

WAL 模式下,读操作不会阻塞写操作,写操作也不会阻塞读操作,并发性能大幅提升。timeout=30表示遇到锁时最多等待 30 秒,避免立即报错。

注意:WAL 模式会生成额外的-wal和-shm文件,备份数据库时要一起备份,否则可能丢数据。

5.3 用 DB Browser for SQLite 做内网数据排查

在内网环境里,没有方便的在线数据库管理工具,DB Browser for SQLite(简称 DB4S)就是救星。它是一个开源跨平台的 SQLite 图形化管理工具,可以离线安装。

我通常会在内网的一台运维机上装一个 DB4S,需要排查数据时,把.db文件拷过来打开。它能直观地浏览表结构、执行 SQL、导出数据。对于 Agent 调试特别有用——你可以直接看到 Agent 写了什么日志、任务状态对不对。

如果内网连图形界面都没有,那就只能用命令行:

sqlite3 agent.db ".tables" sqlite3 agent.db "SELECT * FROM conversations ORDER BY created_at DESC LIMIT 10;"

.tables列出所有表,后面那条查询看最近的会话记录。这些命令在纯命令行环境下非常实用。

6. Vue 前端在内网里的部署与交互设计

6.1 构建产物如何做到零外部依赖

Vue 项目在内网部署的核心原则是:构建产物必须是纯静态文件,且不引用任何外部 CDN 资源。

默认的 Vue 项目构建后,index.html里可能引用了 Google Fonts 或某些 CDN 上的库。这些在隔离内网里全部会加载失败。解决办法是在vue.config.js或vite.config.js里配置:

// vite.config.js export default { build: { assetsInlineLimit: 4096, }, base: './', }

base: './'确保所有资源用相对路径引用,这样无论部署在哪个子目录都能正常加载。字体文件、图标库全部本地化,不要用任何在线资源。

构建完成后,把dist目录整个拷贝到内网的 Web 服务器(Nginx 或宝塔面板)的站点目录下即可。

6.2 动态路由与 Agent 会话的联动

Vue 的动态路由在 Agent 应用里很有用。比如,每个会话可以对应一个路由/chat/:sessionId,用户切换会话时路由变化,组件根据sessionId加载对应的历史记录。

// router/index.js const routes = [ { path: '/chat/:sessionId', name: 'Chat', component: () => import('@/views/Chat.vue'), props: true, }, ]

在Chat.vue里通过props接收sessionId,然后在onMounted里调用后端 API 拉取历史消息。这样用户刷新页面或直接访问某个会话链接,都能正确恢复上下文。

一个细节:Agent 的流式输出(streaming)在前端要用 SSE 或 WebSocket 处理。在隔离内网里,SSE 更简单,因为它就是普通的 HTTP 长连接,不需要额外的协议升级。Vue 里用EventSource就能接。

6.3 内网环境下的前端调试技巧

内网里没有浏览器开发者工具的远程调试那么方便,但有几个技巧很实用。

第一,用 console 日志 + 后端日志对照。前端关键操作打日志,后端也打日志,通过时间戳和请求 ID 对照,能快速定位问题。

第二,构建一个"调试模式"。在 URL 上加?debug=1,前端就显示额外的调试信息,比如原始 API 响应、WebSocket 连接状态等。这个功能在生产环境默认关闭,排查问题时手动开启。

第三,善用 Nginx 的访问日志。如果前端请求 404 或 502,Nginx 日志里会有记录。宝塔面板的 Nginx 日志查看功能在内网里很好用。

7. 踩坑实录:那些让我加班到深夜的问题

7.1 MCP Server 启动卡死的排查过程

有一次部署完,Agent 一直不响应。查日志发现 MCP Server 启动后就卡住了,没有任何输出。

排查过程是这样的:先确认进程在不在,ps aux | grep mcp能看到进程;然后用strace跟踪系统调用,发现它卡在一个connect调用上——它在尝试连接一个外部地址。原来这个 MCP Server 启动时会检查更新,在隔离内网里这个连接会一直超时。

解决办法是在它的配置文件里关掉自动更新检查,或者设置一个极短的超时。这个坑的教训是:任何开源组件在隔离内网部署前,都要审查它是否有网络探测行为。

7.2 SQLite 锁冲突导致 Agent 假死

另一个深夜问题是 Agent 偶尔"假死"——不报错,但也不返回结果。查下来是 SQLite 锁冲突。

Agent 的一个工具在写日志时拿到了写锁,另一个工具同时在读同一个表,默认模式下读操作被阻塞。如果写操作因为某种原因没及时释放锁,读操作就一直等,表现为 Agent 卡住。

修复方案就是前面说的 WAL 模式加 timeout。另外,我把日志写入改成了异步队列,避免在关键路径上直接写数据库。

7.3 Vue 打包后白屏的三种常见原因

Vue 项目在内网部署后白屏,我遇到过三次,原因各不相同。

第一次是base路径配错了,资源 404。第二次是某个依赖用了eval,被内网的安全策略拦截。第三次最隐蔽——构建时用了某个需要联网的插件,构建产物里残留了一个外部请求,加载失败导致整个应用挂掉。

排查白屏的通用方法是:打开浏览器控制台看 Network 面板,看哪个资源加载失败;再看 Console 面板有没有报错。内网里如果连控制台都不方便开,就用curl直接请求index.html和主要的 JS 文件,确认能返回 200。

8. 一些让工程更稳的收尾经验

做隔离内网的 AI Agent 工程,技术难度其实不算特别高,难的是耐心和细致。每一个依赖、每一个配置、每一个网络行为,都要提前想到、提前处理。

我现在的一个习惯是:在跳板机上完整模拟一遍内网环境。用 Docker 起一个断网的容器,把整个部署流程跑一遍,所有问题在跳板机上暴露完,再搬到真正的内网。这个习惯帮我省了无数次往返内网的时间。

另外,文档一定要写。隔离内网里的部署,往往不是一次性的,后续可能有新的机器要部署、有新的人接手。把依赖清单、部署步骤、常见问题都记下来,比什么都强。

最后分享一个小技巧:在内网里准备一个"应急工具包",里面放好 DB4S、常用命令行工具、依赖包目录、模型文件校验和。需要排查问题时,直接拿这个包,不用临时找工具。这个包我维护了两年,救过我好几次。

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

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

立即咨询