☰
局域网私有化 AI Agent 平台搭建:Docker + DeepSeek 全流程实战
2026/10/15 10:12:55 网站建设 项目流程

1. 为什么要在局域网里自建 AI Agent 平台

1.1 从“调用云端 API”到“把模型搬进机房”的转变

过去一年,我身边不少做企业内部工具的朋友都遇到同一个尴尬:业务部门想用大模型做知识问答、文档摘要、工单分类,但数据不能出内网,采购流程又走不动。于是“局域网里跑一个能对话、能调工具、能记住上下文的 AI Agent 平台”就成了刚需。DeepSeek 系列模型在中文理解、代码生成和长上下文上的表现,配合 Docker 的隔离与可移植性,正好能拼出一套低成本、可复现的私有化方案。

这里说的“Harness”不是某个官方产品名,而是我习惯的叫法——它指的是一层编排外壳:把模型推理服务、对话管理、工具调用、会话存储、Web 界面串起来,让模型从“只会聊天”变成“能查库、能读文件、能按流程办事”的 Agent。Docker 负责把这套东西打包成镜像,换台机器docker compose up就能跑,不用再折腾 Python 版本、CUDA 驱动、依赖冲突这些老问题。

1.2 局域网部署到底解决了哪些真实痛点

第一是数据边界清晰。所有对话记录、上传的文档、向量索引都落在内网服务器上,不经过任何外部链路。第二是延迟可控。局域网内千兆带宽下,首 token 延迟通常能压到几百毫秒级别,比走公网 API 稳定得多。第三是成本可预测。一次性投入显卡或 CPU 服务器后,后续只有电费,没有按 token 计费的焦虑。第四是可定制。你可以把公司内部的工单系统、Wiki、数据库通过工具调用接进来,让 Agent 真正干活,而不是只当个玩具。

适合读这篇内容的人有三类:一是手里有一台带显卡的服务器、想给团队搭内部助手的运维或后端;二是做企业软件交付、需要把 AI 能力打包进私有环境的开发者;三是对 Docker 和模型部署都有基础、想找一个完整可抄作业方案的爱好者。下面我会按“整体设计—核心细节—实操过程—问题排查”的顺序,把每一步的选择理由和踩坑点都摊开讲。

2. 整体架构设计与选型思路

2.1 分层拆解:推理层、编排层、接入层各管什么

一套能用的局域网 AI Agent 平台,我习惯把它切成三层。推理层负责把模型权重加载进显存或内存,对外暴露一个兼容 OpenAI 协议的 HTTP 接口,这样上层不用关心底层是 vLLM、TGI 还是 llama.cpp。编排层是 Harness 的核心,管会话历史、系统提示词、工具注册与调用、流式输出转发。接入层就是 Web UI 和 API 网关,让同事用浏览器或内部系统都能连进来。

这样分层的好处是每层可以独立替换。比如今天用 vLLM 跑推理,明天想换更省显存的量化方案,只要接口协议不变,编排层一行代码都不用改。Docker Compose 里每个服务一个容器,网络用自定义 bridge,只有接入层映射端口到宿主机,推理层和编排层完全在内网通信,安全边界很干净。

2.2 为什么选 Docker 而不是裸机部署

裸机部署最头疼的是环境漂移。同一套代码,在 A 机器上因为 CUDA 版本差一个小号就报错,在 B 机器上因为 Python 依赖被系统包管理器污染又跑不起来。Docker 把运行时、依赖、配置全部固化进镜像,docker save导出后拷到离线机器docker load就能用,这对没有外网的生产环境是决定性的。

另一个理由是资源隔离。推理容器可以限制 GPU 显存和 CPU 核数,编排容器限制内存,避免某个服务内存泄漏把整台机器拖垮。配合restart: unless-stopped,服务器重启后服务自动拉起,不用人工干预。当然 Docker 也有代价,比如 GPU 直通需要额外配置--gpus或deploy.resources,镜像体积偏大,这些在后面实操里都会具体处理。

2.3 模型选型:DeepSeek 不同规格怎么挑

DeepSeek 家族有多个规格,局域网部署要按硬件反推。我的经验是:单卡 24G 显存(比如常见的消费级卡)适合跑 7B 到 14B 级别的量化版本,用 4-bit 量化后 14B 大约占 9 到 11G,留出余量给 KV Cache。双卡 48G可以上 32B 的 4-bit 量化,中文长文本表现明显更好。纯 CPU 服务器也不是不能跑,但 7B 模型在 32 核机器上大概每秒 3 到 5 个 token,只适合做异步批处理,不适合交互式对话。

选型时还要看上下文长度。Agent 场景经常要把工具返回结果塞进上下文,8K 往往不够,建议至少 32K。显存估算有个粗略公式:显存 ≈ 参数量 × 量化字节数 + 上下文长度 × 层数 × 隐藏维度 × 2 × 字节数,实际用的时候直接看推理框架启动日志里的显存占用最准。我一般会先用小模型把整条链路跑通,再换大模型压测,避免一上来就卡在显存不足上。

3. 核心细节解析与实操要点

3.1 推理服务的容器化配置要点

推理容器是整个平台最吃资源的一环。以常见的 vLLM 为例,启动命令里几个参数必须调对。--model指向挂载进容器的模型目录,建议用 volume 把宿主机上的权重目录映射进去,而不是打进镜像,否则镜像动辄几十 G,传输和更新都痛苦。--tensor-parallel-size要和 GPU 数量一致,单卡就写 1。--gpu-memory-utilization控制显存占用比例,默认 0.9,如果发现 OOM 可以降到 0.85。

--max-model-len决定最大上下文,设太大 KV Cache 会吃掉大量显存,设太小 Agent 多轮对话会被截断。我的做法是先设 32768,启动后看日志里的KV cache usage,如果超过 80% 就往下调。--dtype一般用auto,让框架自己判断。还有一个容易忽略的点:--served-model-name要和编排层配置里的模型名完全一致,否则调用时会报 model not found,这个坑我踩过不止一次。

3.2 编排层的会话管理与工具注册

编排层要解决三个问题:会话怎么存、工具怎么调、流式怎么转发。会话存储我推荐用 Redis 或 SQLite,前者适合多实例横向扩展,后者适合单机简单场景。每条会话记录包含 session_id、历史消息列表、创建时间、最后活跃时间。历史消息不能无限增长,要有截断策略,常见做法是保留最近 N 轮加上系统提示词,或者用摘要压缩早期对话。

工具注册我用的是声明式配置:每个工具一个 JSON 描述,包含名称、功能说明、参数 schema、实际执行的后端地址。模型返回工具调用请求后,编排层解析参数、转发到对应内部服务、把结果包装成消息再喂回模型。这里的关键是参数校验,模型偶尔会生成格式不对的 JSON,必须在编排层做 schema 校验和重试,否则一个坏参数就能让整轮对话崩掉。流式转发则用 SSE,把推理层的 token 流原样透传给前端,用户看到的就是逐字输出。

3.3 网络与端口规划:只暴露该暴露的

局域网部署的安全原则是最小暴露。Docker Compose 里默认所有服务在同一个自定义网络内,互相用服务名通信。只有接入层需要ports映射到宿主机,比如8080:8080。推理层和编排层不要写ports,它们只在内部网络可达。如果宿主机有防火墙,再限制 8080 只对特定网段开放。

数据库和 Redis 同样不对外映射端口,密码通过环境变量注入,不要硬编码在 compose 文件里。我习惯用一个.env文件管理所有敏感配置,.gitignore里排除掉,部署时手动放到服务器上。另外 Docker 默认的 bridge 网络在宿主机重启后 IP 可能变化,所以服务间通信一律用服务名而不是 IP,Compose 会自动做 DNS 解析,这点比手写 IP 靠谱得多。

4. 完整实操过程与关键环节实现

4.1 环境准备与依赖检查

先在服务器上确认三件事:Docker 版本、GPU 驱动、磁盘空间。Docker 建议 24 以上,docker compose version能看到 v2 即可。GPU 机器要装好驱动,nvidia-smi能正常输出,然后安装 NVIDIA Container Toolkit,否则容器里看不到显卡。磁盘方面,模型权重加镜像加数据,建议预留 100G 以上,SSD 优先,机械盘加载模型会慢到怀疑人生。

# 检查 Docker 与 Compose docker --version docker compose version # 检查 GPU(CPU 机器跳过) nvidia-smi # 检查磁盘 df -h /data

如果nvidia-smi报错,先解决驱动再往下走,容器层面再怎么调都没用。磁盘空间不足时,可以把模型目录挂到独立的数据盘,通过 volume 映射进容器,避免占满系统盘导致 Docker 守护进程异常。

4.2 目录结构与配置文件组织

我习惯在/data/ai-agent下建这样一套目录,结构清晰,备份和迁移都方便:

/data/ai-agent ├── docker-compose.yml ├── .env ├── models/ │ └── deepseek-xxx/ ├── data/ │ ├── redis/ │ └── sqlite/ └── config/ ├── harness.yaml └── tools.json

models放权重,data放持久化数据,config放编排层配置。这样docker compose down再up,数据不会丢。.env里放端口、密码、模型路径这些变量,compose 文件里用${VAR}引用。这种组织方式的好处是迁移时只要打包整个目录,换台机器改一下.env里的路径就能跑。

4.3 编写 docker-compose 编排文件

下面是一份我实际用过的 compose 骨架,推理层用 vLLM,编排层用一个自研的 Python 服务,缓存用 Redis。注意 GPU 部分用的是deploy.resources,Compose v2 支持这种写法。

services: inference: image: vllm/vllm-openai:latest container_name: ai-inference volumes: - ./models:/models command: > --model /models/deepseek-xxx --served-model-name deepseek-local --tensor-parallel-size 1 --gpu-memory-utilization 0.88 --max-model-len 32768 --dtype auto deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] restart: unless-stopped networks: - ai-net redis: image: redis:7-alpine container_name: ai-redis command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - ./data/redis:/data restart: unless-stopped networks: - ai-net harness: build: ./harness container_name: ai-harness environment: - INFERENCE_BASE_URL=http://inference:8000/v1 - MODEL_NAME=deepseek-local - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 volumes: - ./config:/app/config - ./data/sqlite:/app/data ports: - "${HARNESS_PORT}:8080" depends_on: - inference - redis restart: unless-stopped networks: - ai-net networks: ai-net: driver: bridge

这份文件里几个细节值得说。inference没有ports,只在内部网络暴露 8000,外部访问不到。harness的INFERENCE_BASE_URL用的是服务名inference,Compose 自动解析。depends_on只保证启动顺序,不保证推理服务已经加载完模型,所以编排层要有重试逻辑,启动时连不上推理服务就等几秒再试,别直接崩。

4.4 启动、验证与首次对话

配置写好后,docker compose up -d启动,然后docker compose logs -f inference看模型加载进度。第一次加载 14B 模型大概要一到三分钟,看到Uvicorn running on http://0.0.0.0:8000就说明推理服务就绪。接着验证编排层:

# 查看所有容器状态 docker compose ps # 测试推理接口 curl http://localhost:${HARNESS_PORT}/health # 发一条测试对话 curl -X POST http://localhost:${HARNESS_PORT}/v1/chat \ -H "Content-Type: application/json" \ -d '{"session_id":"test-1","message":"你好,介绍一下你自己"}'

如果返回正常,打开浏览器访问http://服务器IP:端口,就能看到 Web 界面。第一次对话建议问一个需要多轮的问题,比如“帮我列个周计划,然后逐条展开”,观察上下文是否保持、流式输出是否顺畅。这一步能同时验证会话存储和流式转发两条链路。

4.5 接入内部工具:让 Agent 真正干活

Agent 的价值在于调工具。我在tools.json里注册过一个“查内部文档”的工具,参数是关键词,后端指向内网的检索服务。模型判断需要查资料时会返回工具调用请求,编排层执行后把结果拼回上下文。配置大概长这样:

{ "name": "search_docs", "description": "根据关键词检索内部文档库,返回最相关的三段内容", "parameters": { "type": "object", "properties": { "keyword": {"type": "string", "description": "检索关键词"} }, "required": ["keyword"] }, "endpoint": "http://doc-search:9000/query" }

工具描述要写得具体且带边界,比如说明返回几条、什么格式,模型才知道什么时候该调、怎么用结果。我试过把描述写得太笼统,模型要么不调,要么乱调。另外工具后端要做超时和降级,检索服务挂了不能把整轮对话卡死,编排层设个 5 秒超时,超时就返回“检索暂不可用”,让模型基于已有知识回答。

5. 常见问题与排查技巧实录

5.1 推理服务启动失败与显存不足

最常见的报错是CUDA out of memory。排查顺序是:先看nvidia-smi确认没有其他进程占卡,再降--gpu-memory-utilization到 0.8,还不行就降--max-model-len。有时候是量化配置和模型不匹配,比如模型本身是 FP16 却按 4-bit 加载,会直接报错。日志里搜Error和Traceback,定位到具体是哪一步失败。

另一个坑是模型路径权限。容器内用户和宿主机用户 UID 不一致时,读不到挂载的权重目录,报Permission denied。解决办法是在 compose 里加user: "${UID}:${GID}",或者把权重目录权限放开。我一般用前者,更干净。

5.2 编排层连不上推理服务的排查思路

Connection refused通常有三种原因:推理服务还没加载完、服务名写错、网络不在同一个。先docker compose logs inference确认服务就绪,再docker compose exec harness ping inference测网络连通性。如果 ping 不通,检查两个服务是否都在ai-net网络下。如果 ping 通但 HTTP 连不上,看端口对不对,vLLM 默认 8000,别写成 8080。

还有一种隐蔽情况:推理服务加载完模型后监听的地址是127.0.0.1而不是0.0.0.0,导致容器间访问不到。启动参数里确认有--host 0.0.0.0,或者看日志里的监听地址。这个坑我在早期版本里遇到过,排查了半天才发现是监听地址问题。

5.3 对话卡顿、超时与流式中断

交互卡顿先分清楚是推理慢还是网络慢。在服务器上直接 curl 推理接口,如果也慢,就是模型或硬件问题,考虑换更小的量化版本或加显卡。如果服务器上快、客户端慢,就是网络或编排层转发问题,检查 SSE 是否被中间层缓冲。有些反向代理默认会缓冲响应,导致流式变成一次性输出,需要在代理配置里关掉缓冲。

超时方面,编排层到推理层的 HTTP 超时要设得足够长,Agent 多轮加工具调用可能跑几十秒,默认 30 秒经常不够。我一般设 120 秒,同时在前端加“正在思考”的提示,避免用户以为卡死。流式中断多半是客户端断连,编排层要捕获异常并保存已生成的部分,下次同 session 继续时能接上。

5.4 常见问题速查表

现象可能原因排查动作解决方式
容器启动即退出配置语法错误docker compose logs修正 yaml 或命令参数
CUDA OOM显存不足nvidia-smi看占用降 utilization 或 max-model-len
连不上推理服务服务名/网络错误exec进容器 ping统一网络、检查服务名
流式变一次性代理缓冲看响应头关闭代理缓冲
工具调用失败参数格式错看编排层日志加 schema 校验与重试
模型加载极慢磁盘 IO 瓶颈iostat看磁盘换 SSD 或预加载

这张表是我几次部署下来攒的,基本覆盖了八成以上的问题。遇到新问题先按“容器状态—日志—网络—资源”四步走,绝大多数都能定位。

5.5 几个让我少走弯路的实操心得

第一个心得是先小后大。别一上来就上最大的模型,先用 7B 把整条链路跑通,确认 Docker 网络、编排、工具调用都没问题,再换大模型。这样出问题时能快速判断是架构问题还是模型问题。

第二个心得是日志要集中。每个容器日志分开看很累,我习惯用docker compose logs -f一次看全部,或者接一个轻量日志收集。关键是给日志加时间戳和服务名前缀,排查时序问题时特别有用。

第三个心得是配置全部外置。模型名、端口、密码、超时时间,全部走环境变量或配置文件,不要写死在代码里。这样换环境时只改.env,不用重新构建镜像。我吃过这个亏,早期把模型名写死在代码里,换模型时重新 build 了一遍镜像,浪费了半小时。

第四个心得是定期备份数据卷。会话记录、工具配置、向量索引都在data目录下,写个 cron 每天打包一次,出问题能快速回滚。Docker 的 volume 虽然持久,但误删容器时如果没备份就麻烦了。

6. 性能调优与扩展方向

6.1 提升并发与吞吐的几种手段

单实例推理服务的并发能力有限,vLLM 支持连续批处理,但显存是硬约束。想提升并发,第一是量化,4-bit 量化能把显存占用降到 FP16 的三分之一左右,代价是精度略降,中文场景下一般感知不明显。第二是多实例加负载均衡,如果有多张卡,每张卡起一个推理容器,前面挂一个 Nginx 做轮询。第三是请求排队与限流,编排层加一个队列,超过并发上限的请求排队等待,避免全部涌进去导致 OOM。

吞吐和延迟是一对矛盾。批处理能提升吞吐但增加单请求延迟,交互式场景要优先保延迟,把 batch size 控制小一点。我一般会压测一下,找到延迟和吞吐的平衡点,再定并发上限。

6.2 后续可以扩展的能力

跑通基础对话后,可以往上加的东西不少。RAG 检索增强是最实用的,把内部文档切块、向量化、存进向量库,Agent 回答前先检索,准确率提升明显。多 Agent 协作也值得试,一个负责规划、一个负责执行、一个负责校验,适合复杂任务。权限控制在企业场景必不可少,不同用户能访问的工具和文档要隔离,可以在编排层加一层鉴权。

再往后可以考虑模型热切换,编排层根据请求类型路由到不同模型,简单问题走小模型、复杂问题走大模型,兼顾成本和效果。这些扩展都建立在当前这套 Docker 架构之上,加服务、加配置即可,不用推倒重来。

7. 写在最后的一点个人体会

这套方案我从头到尾搭过三遍,每次都会发现新的细节可以优化。最开始我以为难点在模型部署,后来发现真正花时间的是编排层的健壮性——工具调用失败怎么降级、会话太长怎么截断、推理服务重启后怎么恢复,这些才是决定平台能不能长期用的关键。Docker 帮我把环境问题挡在门外,但业务逻辑的坑还得自己一个个填。

如果你也打算在局域网里搭一套,我的建议是别追求一步到位。先把推理服务和最简单的对话界面跑起来,让同事用起来,收集真实反馈,再逐步加工具、加检索、加权限。真实需求往往和你最初设想的不一样,早点拿到反馈比闷头优化更有价值。硬件上如果预算有限,一张 24G 卡加 14B 量化模型,足够支撑一个小团队日常使用,等需求明确了再扩也不迟。

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

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

立即咨询