1. 为什么要在局域网里自建 AI Agent 平台
1.1 从“调用云端 API”到“把模型搬进机房”的转变
过去一年,我身边不少做企业内部工具的朋友都经历了同一个心路历程:一开始图省事,直接调云端大模型的 API,几行代码就能跑通一个问答机器人,演示效果也漂亮。但真到了要落地到业务场景里,问题就一个接一个冒出来——数据不能出内网、调用量一大成本失控、网络抖动导致响应不稳定、敏感业务文档不敢往上传。尤其是做内部知识库、工单辅助、代码审查这类场景,数据合规这条线一卡,云端方案基本就废了。
于是“把模型部署到自己的局域网里”成了很多团队的刚需。而 Docker 这套容器化方案,恰好把部署这件事的门槛拉到了普通人也能上手的程度。你不需要懂 CUDA 编译、不需要手动配 Python 环境、不需要跟各种依赖冲突搏斗,一条docker compose up -d就能把一个完整的 AI Agent 平台拉起来。这就是我今天想聊的核心:用 Docker 部署一套本地化的 AI Agent 平台,让局域网内的所有设备都能访问,数据全程不出内网。
这里说的“AI Agent 平台”不是单纯跑一个聊天模型,而是包含模型推理服务、Agent 编排逻辑、工具调用能力、会话管理的一整套东西。你可以把它理解成一个“局域网版的小型 AI 中台”,前端同事的网页、后端同事的服务、甚至运维的脚本,都能通过一个统一的接口去调用它。
1.2 这套方案到底解决了谁的痛点
先明确一下适用人群,免得你看到一半发现不是自己需要的:
- 中小企业内部工具开发者:公司没有专门的 AI 基础设施团队,但业务上确实需要 AI 能力,希望用最低成本搭一套自己能维护的平台。
- 对数据合规有硬要求的团队:处理的是合同、病历、财务、内部代码这类不能外传的数据,云端 API 直接排除。
- 想学习和折腾 AI Agent 的技术爱好者:想搞清楚 Agent 到底是怎么编排的、工具调用是怎么实现的,自己搭一套比看文档理解得深十倍。
- 有闲置 GPU 或高性能 CPU 机器的团队:机器闲着也是闲着,跑个本地模型服务,内部随便用。
反过来说,如果你只是偶尔问几个通用问题、对数据合规没要求、也不想维护任何服务,那直接用现成的云端产品更省事,没必要折腾这套。自建的核心价值在于“数据主权”和“可控成本”,而不是“效果一定比云端好”——这一点得先摆正预期。
1.3 整体架构长什么样
在动手之前,先把整个平台的骨架讲清楚,不然一会儿看到一堆容器名容易懵。我这套方案的核心思路是“分层解耦”,每一层各司其职,坏了一层不影响其他层:
| 层级 | 作用 | 典型组件 |
|---|---|---|
| 模型推理层 | 真正跑大模型、输出 token | 本地推理服务容器 |
| Agent 编排层 | 处理对话逻辑、工具调用、上下文管理 | Agent 服务容器 |
| 接入层 | 统一 API 网关、鉴权、限流 | 反向代理容器 |
| 存储层 | 会话历史、向量数据、配置持久化 | 数据库容器 + 挂载卷 |
| 管理界面 | 网页端对话、调试、监控 | 前端容器 |
这么分层的好处是:模型想换就换,Agent 逻辑想改就改,前端想重做就重做,彼此之间通过标准 HTTP 接口通信。最忌讳的就是把所有东西塞进一个容器里,那样一旦某个环节出问题,排查起来就是一团乱麻,而且没法单独升级。
2. 部署前的环境准备与关键决策
2.1 硬件选型:别被“必须上 GPU”吓到
很多人一听本地部署大模型,第一反应是“我没显卡搞不了”。实际情况要分场景看:
- 纯 CPU 推理:7B 到 14B 级别的量化模型,在 16 核以上的服务器 CPU 上跑,响应速度大概每秒几个到十几个 token,做内部问答、文档摘要完全够用。缺点是并发一高就顶不住。
- 单张消费级显卡:24G 显存能比较舒服地跑 14B 到 32B 的量化模型,响应速度能到每秒几十 token,支持几个人同时用。
- 多卡或专业卡:适合 70B 以上模型或者高并发场景,但成本和运维复杂度陡增,一般团队用不上。
我的建议是:先用 CPU 或手头现有的卡把流程跑通,确认业务真的需要再升级硬件。我见过太多团队一上来就买卡,结果平台搭好了没人用,纯属浪费。显存估算有个粗略公式:模型参数量(B)× 量化位数 ÷ 8 ≈ 显存占用(GB)。比如 14B 模型用 4bit 量化,大约需要 14×4÷8 = 7GB 显存,再留 2-3GB 给上下文和框架开销,10GB 显存就能跑起来。
2.2 系统与 Docker 环境
操作系统我推荐 Ubuntu 22.04 LTS 或者同级别的长期支持版本,内核新、驱动兼容性好、社区资料多。Windows 上虽然也能用 Docker Desktop,但涉及到 GPU 直通和网络配置时会多不少坑,生产环境不建议。
Docker 环境需要装三样东西:
# 安装 Docker Engine(以 Ubuntu 为例) curl -fsSL https://get.docker.com | sh # 安装 Docker Compose 插件 sudo apt-get install docker-compose-plugin # 把当前用户加入 docker 组,免 sudo sudo usermod -aG docker $USER # 执行完记得重新登录,组权限才生效装完之后用docker run hello-world验证一下。如果要用 GPU,还得装对应的容器运行时和驱动,装完用docker run --rm --gpus all 基础镜像 nvidia-smi测试能否在容器里看到显卡。这一步千万别跳过,我踩过的坑就是驱动版本和容器运行时版本对不上,容器里死活看不到卡,排查了大半天。
2.3 网络规划:局域网访问的核心
既然是局域网平台,网络规划必须提前想清楚。核心是三件事:
- 固定 IP:跑服务的机器一定要设静态 IP,或者在路由器里做 DHCP 绑定。不然机器一重启 IP 变了,所有客户端的配置全得改。
- 端口规划:给每个服务分配固定端口,避免冲突。我习惯用 8000 段给 API、3000 段给前端、5432 给数据库,一目了然。
- 防火墙放行:只放行局域网网段访问,别图省事直接开公网。用
ufw的话,规则写成ufw allow from 192.168.1.0/24 to any port 8000这种形式,精确到网段。
提示:如果你的局域网里有多个网段(比如办公网和研发网分开),记得在网关设备上配好路由,否则跨网段访问会失败。这个坑我在一个客户现场遇到过,服务明明起来了,另一个网段的同事就是连不上,最后发现是路由没配。
2.4 存储与数据持久化
容器最大的特点是“用完即弃”,所以所有需要保留的数据都必须挂载到宿主机上,否则容器一重建数据全没。需要持久化的东西包括:
- 模型文件(动辄几个 G 到几十个 G,重新下载太痛苦)
- 会话历史数据库
- 向量库数据
- 配置文件
- 日志
我习惯在宿主机上建一个统一的数据目录,比如/data/ai-platform/,下面按服务分子目录。然后在 compose 文件里用volumes把容器内路径映射出来。这样即使把容器全删了,数据还在,重建容器后挂载回去就能恢复。
3. 核心服务的容器化拆解
3.1 模型推理服务:平台的“发动机”
模型推理服务是整个平台最吃资源的部分,也是配置最讲究的部分。我把它单独拎出来讲,因为它的参数直接决定了平台能不能用、好不好用。
推理服务容器一般会暴露一个兼容 OpenAI 格式的 HTTP 接口,这样上层的 Agent 服务就能用统一的 SDK 去调用,换模型的时候上层代码几乎不用改。启动参数里几个关键项:
- 模型路径:指向挂载进来的模型文件目录。
- 上下文长度:决定了单次对话能塞多少内容。设太大吃显存,设太小长文档处理不了。一般 8K 到 32K 是常见区间。
- 并发数:同时能处理多少个请求。CPU 推理建议设 1-2,GPU 推理可以设 4-8,具体看显存。
- 量化方式:4bit 量化省显存但精度略降,8bit 平衡,不量化最准但最吃资源。
# compose 片段示意 model-service: image: 本地推理服务镜像 ports: - "8000:8000" volumes: - /data/ai-platform/models:/models command: > --model /models/你的模型目录 --context-length 16384 --max-concurrent 4 deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]注意:模型文件建议提前用下载工具拉到本地再挂载,别在容器启动时现下。几十 G 的文件下载中断一次,前面的全白费,而且容器启动会卡住很久,看起来像“启动失败”。
3.2 Agent 编排服务:平台的“大脑”
Agent 服务和模型服务是两回事。模型服务只负责“根据输入生成输出”,而 Agent 服务负责的是“决定该不该调用工具、调用哪个工具、怎么把多轮结果拼起来”。这是整个平台最有技术含量的部分。
一个典型的 Agent 服务要处理这些逻辑:
- 会话管理:每个用户的每轮对话都要有独立的上下文,不能串。
- 工具注册与调用:把内部 API、数据库查询、文件检索这些能力包装成“工具”,让模型在需要时调用。
- 提示词模板:不同场景用不同的系统提示词,比如客服场景和代码审查场景的提示词完全不同。
- 结果后处理:模型输出的原始文本要经过清洗、格式化才能返回给前端。
agent-service: image: 本地 Agent 服务镜像 ports: - "8001:8001" environment: - MODEL_ENDPOINT=http://model-service:8000/v1 - DB_HOST=db-service - DB_NAME=agent_platform volumes: - /data/ai-platform/agent-config:/app/config depends_on: - model-service - db-service这里有个关键点:Agent 服务和模型服务之间用容器名互相访问(比如http://model-service:8000),而不是用 IP。Docker Compose 会自动创建一个内部网络,容器名就是域名,这样即使 IP 变了也不影响。这是容器编排相比手动部署的一大优势。
3.3 数据库与向量存储:平台的“记忆”
会话历史、用户配置这些结构化数据,用关系型数据库存就行,PostgreSQL 是稳妥选择。而如果你要做知识库检索(RAG),还需要一个向量数据库来存文档的向量表示。
向量库的选择上,轻量场景用内置的向量扩展就够了,不用单独起一个服务;数据量大、检索要求高的场景再考虑独立的向量数据库容器。新手建议先用最简单的方案跑通,别一上来就上重型组件,否则光是调通各个组件之间的连接就够你喝一壶的。
数据库容器一定要配好持久化卷和初始化脚本。初始化脚本负责建表、建索引、插入默认配置,放在/docker-entrypoint-initdb.d/目录下,容器首次启动时会自动执行。这样重建数据库时不用手动敲 SQL。
3.4 反向代理与统一入口
局域网里访问,最好有一个统一的入口,而不是让用户记一堆 IP 加端口。用反向代理容器做这件事,好处有三:
- 统一域名:用户只记一个地址,比如
http://ai.内网域名。 - 路径分流:
/api转发到 Agent 服务,/转发到前端,用户无感知。 - 基础防护:可以加简单的访问控制、请求大小限制、超时设置。
# 反向代理配置示意 server { listen 80; server_name ai.内网域名; location /api/ { proxy_pass http://agent-service:8001/; proxy_read_timeout 300s; } location / { proxy_pass http://frontend-service:3000/; } }提示:
proxy_read_timeout一定要调大,默认 60 秒对于大模型生成来说太短了,长回答还没生成完连接就被掐断了。我一般设 300 秒起步。
4. 完整部署流程与实操记录
4.1 目录结构与 compose 文件组织
先把整个项目的目录结构规划好,这是后续维护的基础:
ai-platform/ ├── docker-compose.yml ├── .env ├── configs/ │ ├── agent/ │ ├── nginx/ │ └── db-init/ ├── data/ │ ├── models/ │ ├── postgres/ │ └── vectors/ └── logs/.env文件用来放环境变量,比如数据库密码、端口号、模型路径。千万别把密码硬编码在 compose 文件里,一是容易泄露,二是换环境时改起来麻烦。用.env文件,compose 会自动读取。
# .env 示例 POSTGRES_PASSWORD=换成你自己的强密码 MODEL_PORT=8000 AGENT_PORT=8001 WEB_PORT=804.2 分步启动与验证
我强烈建议不要一次性把所有服务全拉起来,而是分层启动、逐层验证。这样出问题时能快速定位是哪一层的问题。
第一步,先起数据库:
docker compose up -d db-service docker compose logs -f db-service # 看到 "database system is ready to accept connections" 就说明好了第二步,起模型服务,这一步最慢,因为要加载模型:
docker compose up -d model-service docker compose logs -f model-service # 等到出现监听端口的日志然后用 curl 测一下模型服务是否正常:
curl http://localhost:8000/v1/models # 应该返回模型列表的 JSON第三步,起 Agent 服务,验证它能不能连上模型服务和数据库:
docker compose up -d agent-service curl -X POST http://localhost:8001/api/chat \ -H "Content-Type: application/json" \ -d '{"message": "你好", "session_id": "test-001"}'第四步,起前端和反向代理,最后从局域网另一台机器访问验证。
4.3 局域网访问验证与性能实测
服务全起来之后,从局域网内另一台电脑访问http://跑服务机器的IP,看能不能正常打开界面、正常对话。这一步验证的是网络配置,如果打不开,按这个顺序排查:
- 宿主机本地
curl localhost通不通?不通是服务问题。 - 宿主机
curl 本机IP通不通?不通是服务监听地址问题(可能只监听了 127.0.0.1)。 - 局域网其他机器
ping 宿主机IP通不通?不通是网络或防火墙问题。 - ping 通但端口连不上?是防火墙端口没放行。
性能方面,我在一台 16 核 CPU、64G 内存、无显卡的机器上实测:7B 量化模型,单用户对话首 token 延迟约 1-2 秒,生成速度约每秒 8-12 个 token。这个速度做内部问答够用,但做实时对话会有点卡顿感。换成单张 24G 显卡跑 14B 量化模型后,首 token 延迟降到 0.5 秒以内,生成速度每秒 30+ token,体验就顺畅多了。
| 配置 | 模型规模 | 首 token 延迟 | 生成速度 | 适用场景 |
|---|---|---|---|---|
| 16核 CPU | 7B 量化 | 1-2 秒 | 8-12 token/s | 内部问答、文档摘要 |
| 单张 24G 卡 | 14B 量化 | <0.5 秒 | 30+ token/s | 多人并发对话 |
| 双卡 | 32B 量化 | <1 秒 | 20-30 token/s | 复杂推理、代码辅助 |
4.4 让平台真正“好用”的几个配置
平台能跑起来只是第一步,要让它好用,还得做几件事:
配置模型预热:模型服务启动后第一次请求会特别慢,因为要加载权重到显存。可以在服务启动脚本里加一个预热请求,让它启动完就自动跑一次,用户第一次访问时就不用等。
设置合理的超时和重试:Agent 服务调用模型服务时,超时要设够,同时要有重试机制。网络抖动或者模型服务短暂卡顿时,重试能避免用户看到报错。
日志分级与轮转:容器日志默认会一直堆积,把磁盘撑爆。在 compose 里配置日志轮转:
logging: driver: "json-file" options: max-size: "50m" max-file: "5"健康检查:给每个服务配 healthcheck,这样 compose 能知道服务是不是真的健康,而不是“容器在跑但服务挂了”。
healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/v1/models"] interval: 30s timeout: 10s retries: 3 start_period: 120sstart_period对模型服务特别重要,因为加载模型可能要一两分钟,这段时间内健康检查失败是正常的,不能算服务挂了。
5. 常见问题排查与避坑经验
5.1 容器起不来?先看这三处
容器启动失败是最常见的问题,排查顺序我总结成一张表:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 容器秒退 | 启动命令错误、配置语法错 | docker compose logs 服务名看报错 |
| 一直重启 | 依赖服务没起来、健康检查失败 | 看依赖服务日志,检查 healthcheck 配置 |
| 端口占用 | 宿主机端口被其他程序占了 | ss -tlnp | grep 端口号查占用 |
| 权限拒绝 | 挂载目录权限不对 | 检查宿主机目录的属主和权限 |
我踩过最坑的一次是挂载目录权限问题:容器里的服务以非 root 用户运行,但挂载出来的宿主机目录属主是 root,导致服务没权限写日志,容器起来就退。解决办法是提前把宿主机目录的属主改成容器内运行用户的 UID,或者在 compose 里指定user参数。
5.2 模型加载慢或加载失败
模型加载失败通常有几个原因:模型文件不完整(下载中断)、模型格式和推理框架不匹配、显存不够。显存不够是最隐蔽的,因为容器不会直接报“显存不足”,而是加载到一半崩掉,日志里可能只有一句含糊的错误。
排查方法:先在宿主机上用nvidia-smi看显存占用,确认没有其他程序占着;然后看模型服务日志里加载到哪一步崩的。如果是显存问题,换更小的量化版本,或者减小上下文长度。
5.3 局域网能 ping 通但访问不了
这个问题我遇到过好几次,基本都是防火墙或监听地址的问题。分两种情况:
- 服务只监听了 127.0.0.1:容器内服务默认可能只监听本地回环,需要在启动参数里指定监听
0.0.0.0。这个在容器里尤其要注意,因为容器有自己的网络命名空间。 - 宿主机防火墙拦截:
ufw status看一下规则,确认局域网网段被放行。
还有一种情况是 Docker 的网络模式用了host之外的模式,端口映射写错了。检查 compose 里的ports配置,格式是宿主机端口:容器端口,别写反了。
5.4 对话响应慢或中途断开
响应慢的原因可能是模型太大、并发太高、或者上下文太长。中途断开多半是超时设置太短。我的经验是:
- 反向代理的
proxy_read_timeout设 300 秒以上。 - Agent 服务调用模型服务的客户端超时也设 300 秒。
- 如果用了流式输出,确认每一层都支持流式转发,中间任何一层做了缓冲都会导致“看起来卡住”。
提示:流式输出(SSE)在反向代理层特别容易出问题,因为默认会缓冲响应。Nginx 里要加
proxy_buffering off;和proxy_cache off;,否则用户会看到回答“憋”很久然后一次性蹦出来,体验很差。
5.5 数据丢失与备份
容器化部署最大的风险就是数据丢失。我见过有人docker compose down -v一把梭,把数据卷全删了,几个月的会话记录瞬间蒸发。记住-v参数会删除数据卷,除非你确定不要数据了,否则永远别加这个参数。
备份策略上,数据库用定时pg_dump导出到宿主机另一个目录,模型文件本身不用备份(重新下载即可),配置文件纳入版本管理。这样即使整台机器挂了,换台机器把数据恢复回去,半小时就能重新跑起来。
6. 平台上线后的扩展方向
6.1 接入内部知识库做 RAG
平台跑通之后,最有价值的扩展就是接入内部知识库。做法是把内部文档切块、向量化、存进向量库,用户提问时先检索相关片段,再把片段作为上下文喂给模型。这样模型就能回答“我们公司的报销流程是什么”这类只有内部资料才有的问题。
关键点在于文档切块策略:切太大检索不准,切太小上下文不完整。一般按语义段落切,每块 300-500 字,块之间留一点重叠。这个参数没有标准答案,得根据你的文档类型实测调整。
6.2 多模型路由与降级
平台稳定运行后,可以接入多个模型,按任务类型路由:简单问答用小模型省资源,复杂推理用大模型保质量。同时配置降级策略,大模型服务挂了自动切到小模型,保证平台始终可用。
6.3 用量统计与成本核算
虽然是本地部署没有 API 费用,但电费、硬件折旧也是成本。可以在 Agent 服务里记录每次请求的 token 数、耗时、用户,定期统计。这样能看清楚平台到底被谁在用、用在什么地方,为后续扩容提供依据。
我在实际维护这套平台的过程中最大的体会是:部署只是开始,运维才是长期工作。一开始追求“功能全”,什么组件都想上,结果维护成本高得吓人。后来做减法,只保留真正被用到的功能,平台反而更稳定。如果你也打算搭一套,我的建议是先跑通最小可用版本,让业务先用起来,再根据真实反馈逐步加功能,别一上来就追求大而全。