隔离内网部署AI Agent实战:MCP协议、Skills机制与SQLite持久化

发布时间:2026/10/3 23:55:10
隔离内网部署AI Agent实战:MCP协议、Skills机制与SQLite持久化
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 发挥作用的地方。MCPModel 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.0WAL 模式就用不了需要升级系统 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 场景下多个工具调用可能同时写日志很容易触发锁冲突。解决方案是开启 WALWrite-Ahead Logging模式import sqlite3 conn sqlite3.connect(agent.db, timeout30) conn.execute(PRAGMA journal_modeWAL) conn.execute(PRAGMA synchronousNORMAL)WAL 模式下读操作不会阻塞写操作写操作也不会阻塞读操作并发性能大幅提升。timeout30表示遇到锁时最多等待 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 上加?debug1前端就显示额外的调试信息比如原始 API 响应、WebSocket 连接状态等。这个功能在生产环境默认关闭排查问题时手动开启。第三善用 Nginx 的访问日志。如果前端请求 404 或 502Nginx 日志里会有记录。宝塔面板的 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、常用命令行工具、依赖包目录、模型文件校验和。需要排查问题时直接拿这个包不用临时找工具。这个包我维护了两年救过我好几次。