1. 为什么要在局域网里自建 AI Agent 平台
很多人第一次接触大模型,习惯直接打开网页版对话窗口,用完即走。但只要你稍微深入一点,就会发现这条路有几个绕不开的坎:对话记录散落在各家平台、模型能力被网页端阉割、无法接入自己的知识库、多人协作时权限混乱,更别提把 Agent 能力嵌进内部工具链里。尤其是团队场景,数据不出内网几乎是硬性要求,这时候"把模型跑在自己机器上"就从可选项变成了必选项。
DeepSeek 系列模型这两年在开源社区热度很高,推理能力和中文理解都相当能打,而且官方放出了多个尺寸的权重,从消费级显卡到小型服务器都能找到合适的档位。但"下载权重"和"跑起来一个能用的 Agent 平台"之间,隔着环境配置、依赖冲突、服务编排、接口封装一大堆琐事。Harness 这类编排框架的价值就在这里——它把模型加载、对话管理、工具调用、会话持久化这些脏活累活打包好,你只需要用容器把它拉起来,配好端口和挂载目录,一个局域网内可访问的 AI Agent 平台就成型了。
这篇文章面向的是有一定 Linux 基础、手里有台带显卡的机器(或者愿意用 CPU 慢速跑小模型)、想给团队搭一套内部 AI 助手的读者。我会从选型逻辑讲到 Docker 编排细节,再到局域网访问、模型挂载、常见报错排查,把整个链路拆开揉碎。全程不依赖任何外部网络服务,所有组件都在你自己的机器上闭环运行。读完你至少能拿到一套可以直接抄的 compose 配置,以及一份踩坑清单。
需要先说明一点:本文提到的"Harness"泛指一类用于承载和编排 AI Agent 的运行时框架,不同实现细节有差异,但核心思路一致——容器化封装、配置驱动、接口标准化。你完全可以把下面的方案套用到自己熟悉的同类框架上。
2. 部署前的选型与硬件账本
2.1 模型尺寸和显存的那笔账
动手之前先把硬件账算清楚,否则拉起来跑不动,白折腾。DeepSeek 不同参数量的模型对显存的需求差异很大,我按实际部署经验给个粗略对照,注意这是"能跑起来"的底线,不是"跑得舒服"的推荐值。
| 模型参数量 | FP16 显存需求 | 4-bit 量化显存需求 | 适用硬件档位 |
|---|---|---|---|
| 1.5B 级别 | 约 4GB | 约 2GB | 入门独显、核显勉强 |
| 7B 级别 | 约 16GB | 约 6GB | 主流消费级显卡 |
| 14B 级别 | 约 32GB | 约 10GB | 高端消费级、专业卡 |
| 32B 级别 | 约 70GB | 约 20GB | 多卡或专业卡 |
| 70B 级别 | 约 150GB | 约 40GB | 多卡服务器 |
这张表的核心逻辑是:FP16 下每 10 亿参数大约占 2GB 显存,量化到 4-bit 后压缩到约四分之一。实际还要留出 KV Cache 和框架本身的开销,所以真实占用往往比理论值高 20% 到 30%。我的建议是,显存按理论值的 1.3 倍来准备,宁可留余量也别卡在临界点上,否则长对话一上来就 OOM。
如果你手里只有 CPU,也不是完全不能玩。1.5B 到 7B 的量化模型在纯 CPU 上能跑,只是响应速度会从"秒回"退化到"等一杯咖啡"。适合做功能验证,不适合多人并发。
2.2 为什么选 Docker 而不是裸装
有人会问,直接 pip install 一套环境不香吗,为什么要套一层容器?我踩过的坑告诉我,容器化在这个场景下几乎是刚需,理由有三条。
第一是依赖地狱。AI 框架对 CUDA、cuDNN、Python、PyTorch 的版本要求极其挑剔,一个版本对不上就是满屏红色报错。容器把整套运行时冻结在镜像里,宿主机只需要装好显卡驱动和容器运行时,剩下的版本问题全部隔离。
第二是可复现。今天在 A 机器上跑通的配置,明天换到 B 机器,只要镜像和 compose 文件一致,行为就一致。裸装环境里"我这里明明能跑"的扯皮能少一大半。
第三是清理方便。AI 环境动辄几十 GB,装错了想卸载干净非常痛苦。容器删掉镜像,磁盘立刻回收,不留残留。
注意:容器化隔离的是用户态依赖,不是内核态驱动。宿主机上的显卡驱动和容器运行时(如 NVIDIA Container Toolkit)必须提前装好,容器里才能看到 GPU。这一步没做,容器里
nvidia-smi会直接报找不到设备。
2.3 网络与端口规划
局域网部署意味着你要提前想清楚访问方式。我的习惯是给这台机器固定一个内网 IP,然后规划好端口占用,避免和现有服务打架。常见规划如下:
- 平台 Web 界面:8080 或 3000
- 模型推理 API:8000 或 11434
- 管理后台:9090
- 数据库(会话持久化):5432 或 6379
端口冲突是新手最容易忽略的问题。部署前先用ss -tlnp看一眼当前监听情况,把要用的端口列个清单,别等容器起来了才发现端口被占,反复改配置。
3. 从零到一的 Docker 编排实操
3.1 宿主机环境的前置检查
在写任何配置文件之前,先把宿主机的地基打好。我一般按这个顺序检查,每一步都有它的道理。
先确认显卡驱动正常。执行nvidia-smi,能看到显卡型号、驱动版本、CUDA 版本就说明驱动层没问题。这里显示的 CUDA 版本是驱动支持的最高版本,不代表你装了 CUDA Toolkit,别混淆。
接着装容器运行时。以常见的 Linux 发行版为例,安装完容器引擎后,还需要额外安装 GPU 支持组件,让容器能访问显卡设备。装完后跑一个测试容器验证:
docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi如果这条命令能打印出显卡信息,说明容器访问 GPU 的链路通了。这一步是整个部署里最关键的前置条件,通不过后面全是白搭。
然后检查磁盘空间。模型权重动辄十几 GB,加上镜像和缓存,建议预留至少 100GB 可用空间。用df -h看一眼挂载点,别把模型放在快满的分区上。
最后确认内存。除了显存,系统内存也要够,模型加载、会话缓存、数据库都要吃内存。7B 级别模型建议系统内存 32GB 起步。
3.2 目录结构的设计
我习惯把所有相关文件集中在一个工作目录下,结构清晰,迁移和备份都方便。推荐这样组织:
ai-platform/ ├── docker-compose.yml ├── .env ├── models/ │ └── deepseek/ ├── data/ │ ├── postgres/ │ └── redis/ ├── config/ │ └── harness.yaml └── logs/models目录挂载模型权重,data目录持久化数据库,config放框架配置,logs收集日志。这样设计的好处是,容器本身可以随时删掉重建,所有有价值的数据都在宿主机目录里,不会随容器生命周期消失。
提示:模型目录建议用只读方式挂载(
:ro),防止容器内进程意外修改权重文件。权重是只读资产,没有写入需求。
3.3 compose 文件逐段拆解
下面这份 compose 是我根据多次部署经验整理出来的骨架,字段含义我逐段解释,你按自己环境改。
version: "3.9" services: harness: image: your-harness-image:latest container_name: ai-harness restart: unless-stopped ports: - "8080:8080" - "8000:8000" volumes: - ./models:/models:ro - ./config/harness.yaml:/app/config.yaml:ro - ./logs:/app/logs environment: - MODEL_PATH=/models/deepseek - API_PORT=8000 - WEB_PORT=8080 - DB_HOST=postgres - REDIS_HOST=redis depends_on: - postgres - redis deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] postgres: image: postgres:16-alpine container_name: ai-postgres restart: unless-stopped environment: - POSTGRES_PASSWORD=change_me - POSTGRES_DB=harness volumes: - ./data/postgres:/var/lib/postgresql/data redis: image: redis:7-alpine container_name: ai-redis restart: unless-stopped volumes: - ./data/redis:/data先看harness服务。restart: unless-stopped保证机器重启后容器自动拉起,除非你手动停过它。端口映射把容器内的 Web 和 API 端口暴露到宿主机,局域网其他机器就能通过宿主机 IP 访问。
volumes三行分别对应模型、配置、日志。模型只读挂载,配置只读挂载,日志可写。这种"能只读就只读"的原则能减少很多意外。
environment里把关键参数外置,改配置不用动镜像。depends_on声明启动顺序,保证数据库先起来。
deploy.resources这段是 GPU 分配的关键,count: all表示把所有可见 GPU 都给这个容器。如果你有多张卡想指定某一张,把all换成设备编号。
再看postgres和redis。会话历史、用户配置这类结构化数据放 Postgres,缓存和临时状态放 Redis。两者都用 alpine 镜像,体积小。数据目录挂载到宿主机,容器删了数据还在。
3.4 配置文件的关键字段
harness.yaml是框架的行为中枢,字段因实现而异,但核心几块大同小异:
model: path: /models/deepseek context_length: 8192 gpu_layers: 99 quantization: q4_k_m server: host: 0.0.0.0 port: 8000 workers: 2 agent: max_iterations: 10 tool_timeout: 30 enable_memory: true database: host: postgres port: 5432 name: harnesscontext_length是上下文窗口,越大越吃显存,8192 是个平衡点。gpu_layers表示有多少层放到 GPU 上跑,99 基本等于全放 GPU,显存不够就往下调。quantization指定量化方式,q4_k_m 是质量和体积的常见折中。
server.host设成0.0.0.0才能被局域网访问,设成127.0.0.1就只有本机能连,这是新手常犯的错。workers是并发工作进程数,按 CPU 核数调整。
agent段控制 Agent 行为。max_iterations限制工具调用的最大轮数,防止死循环。tool_timeout是单个工具的超时时间,避免某个工具卡死拖垮整个会话。
4. 局域网访问与多用户接入
4.1 让服务真正"被看见"
容器跑起来只是第一步,局域网里其他机器能不能访问,取决于几个环节。先确认宿主机防火墙放行了对应端口。以常见的防火墙管理工具为例,需要显式放行 8080 和 8000:
sudo ufw allow 8080/tcp sudo ufw allow 8000/tcp然后从另一台机器用curl测试连通性:
curl http://<宿主机内网IP>:8000/health返回健康检查通过的信息,说明网络链路没问题。如果连不上,按"容器是否监听 0.0.0.0 → 宿主机端口是否映射 → 防火墙是否放行 → 两台机器是否同网段"的顺序逐层排查,别一上来就怀疑框架。
4.2 反向代理与域名访问
直接用 IP 加端口访问能用,但不优雅,而且没法上 HTTPS。局域网内我一般会加一层反向代理,用域名访问,顺便统一入口。
server { listen 80; server_name ai.internal; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /api/ { proxy_pass http://127.0.0.1:8000/; proxy_read_timeout 300s; } }这里有个细节值得说:API 路径的proxy_read_timeout要调大。大模型推理是长耗时请求,默认 60 秒超时经常不够,尤其是长文本生成。设成 300 秒能覆盖绝大多数场景。
proxy_set_header那几行是为了把真实客户端 IP 透传给后端,方便做访问日志和限流。不加的话后端看到的全是代理的 IP。
4.3 多用户并发下的资源分配
局域网平台一旦多人用,资源竞争就来了。几个实操经验:
第一,给推理服务设并发上限。GPU 显存是硬约束,同时处理太多请求必然 OOM。在配置里限制max_concurrent_requests,超出的请求排队而不是直接崩。
第二,会话隔离。每个用户的对话历史要按用户 ID 分开存储,别混在一起。这既是隐私要求,也避免上下文串味。
第三,给不同用户或团队分配不同的 API Key,方便统计用量和限流。框架一般支持多 Key 配置,配合 Redis 做速率限制。
第四,监控显存占用。写个简单的定时脚本,显存超过阈值就告警,别等崩了才发现。
注意:多人并发时,KV Cache 会随对话数量线性增长。如果发现用着用着变慢,多半是缓存堆积,检查框架有没有做缓存淘汰策略。
5. 那些让我熬夜的报错与排查链路
5.1 容器里看不到 GPU
这是最高频的问题。现象是容器启动后日志报"no CUDA device found",但宿主机nvidia-smi正常。
排查链路是这样的:先确认容器运行时组件装没装,docker info里能看到nvidia运行时才算装好。然后确认 compose 里的deploy.resources段写对了,缩进错了会导致整段被忽略。再确认镜像本身带不带 CUDA 支持,有些精简镜像不含运行时。最后确认驱动版本和镜像要求的 CUDA 版本兼容。
我遇到过一次,折腾半天发现是 compose 文件里devices的缩进多了一个空格,YAML 解析时静默忽略了整段。这种问题最难查,因为不报错,只是"没生效"。
5.2 模型加载到一半 OOM
日志显示加载进度到某个百分比就崩,典型显存不足。解决办法按优先级:先降gpu_layers,把部分层放回 CPU;再换更激进的量化,比如从 q4 换到 q3;最后考虑换更小的模型。
这里有个反直觉的点:有时候降gpu_layers反而让整体变慢很多,因为 CPU 和 GPU 之间的数据传输成了瓶颈。所以能加显存就加显存,实在不行再降层。
5.3 端口占用导致启动失败
容器起不来,日志报"address already in use"。用ss -tlnp | grep 8080找到占用进程,要么停掉它,要么改映射端口。改端口时记得同步改反向代理配置,否则访问会 502。
5.4 数据库连接超时
depends_on只保证启动顺序,不保证服务就绪。Postgres 启动需要几秒初始化,harness 如果起太快会连不上。解决办法是在 harness 的启动脚本里加等待逻辑,或者用健康检查配合depends_on的condition: service_healthy。
depends_on: postgres: condition: service_healthy配合给 postgres 加healthcheck,就能优雅解决启动竞态。
6. 性能调优与长期维护的实战心得
6.1 让推理快起来的几个开关
模型跑起来之后,下一步是让它跑得快。几个实测有效的方向:
批处理(batching)能显著提升吞吐。多个请求合并成一批送进 GPU,利用率上去了,单请求延迟略增但整体吞吐翻倍。适合并发场景。
KV Cache 复用。多轮对话里,历史部分的 KV 可以复用,不用每轮重算。框架一般默认开启,确认配置里没关掉。
量化选型。q4_k_m 是通用推荐,如果追求速度可以试 q4_0,追求质量用 q5 或 q8,代价是显存和速度。
线程数匹配。CPU 推理时,线程数设成物理核数,别设成逻辑核数,超线程在推理场景下往往帮倒忙。
6.2 数据备份与版本管理
平台跑起来后,数据就是资产。我的做法是:Postgres 每天定时 dump 到宿主机目录,保留最近 7 天;模型权重单独备份,因为重新下载很耗时;compose 和配置文件纳入版本管理,每次改动留记录。
恢复演练也很重要。别等真出事才发现备份不能用,定期在测试环境跑一遍恢复流程。
6.3 日志与监控的最小配置
不需要上重型监控系统,几个轻量手段就够用:
- 容器日志用
docker logs配合日志轮转,防止磁盘被撑爆 - 关键指标(显存、请求数、延迟)写个定时脚本采集,输出到文件
- 配置日志级别,生产环境别开 debug,日志量太大会拖慢服务
logging: driver: "json-file" options: max-size: "100m" max-file: "5"这段配置限制单个日志文件 100MB,最多保留 5 个,自动轮转,省心。
6.4 升级框架时的注意事项
框架更新频繁,升级前先看变更日志,重点看配置字段有没有改名、API 有没有破坏性变更。升级流程建议:先备份数据和配置,再在测试环境验证,最后才动生产。镜像 tag 别用latest,用具体版本号,避免某天拉镜像拉到一个不兼容的新版本。
我自己踩过一次坑,某次升级后配置字段从model_path改成了model.path,服务起不来还找不到原因,最后翻变更日志才发现。所以升级前读文档这一步,真的不能省。
7. 把平台用起来的几个扩展方向
平台搭好只是起点,真正产生价值的是把它接进日常工作流。几个我实践过觉得有用的方向。
接入内部知识库。把团队文档、手册、FAQ 向量化后存进向量数据库,Agent 回答时先检索再生成,答案就带上了内部上下文。这一步的关键是文档切分策略,切太碎丢上下文,切太大检索不准,一般按语义段落切,每段几百字。
对接内部工具。通过工具调用机制,让 Agent 能查数据库、发通知、创建工单。每个工具定义好输入输出 schema,Agent 自己决定什么时候调。这里要注意权限控制,别让 Agent 拿到过大的权限。
做多 Agent 协作。复杂任务拆给多个专职 Agent,一个负责检索,一个负责推理,一个负责校验。框架如果支持 Agent 编排,可以试试;不支持就用 API 自己串。
最后提醒一句,任何接入生产系统的操作都要先在隔离环境验证,尤其是涉及写操作的场景。AI 的输出有不确定性,加一层人工确认或规则校验,能避免很多麻烦。
这套方案我从第一次折腾到现在,前后迭代了七八个版本,配置越来越简洁,稳定性越来越高。核心体会就一条:把复杂度关进容器和配置文件里,宿主机保持干净,出问题时排查范围就小。希望这份记录能帮你少走点弯路,把时间花在真正有价值的应用上,而不是和环境搏斗。