1. 为什么要在局域网里自建 AI Agent 平台1.1 从“调用云端 API”到“把模型搬进机房”的转变过去一年我身边不少做企业内部工具的朋友都经历过同一个阶段一开始大家都是直接调用云端大模型 API写个脚本、搭个前端跑得挺欢。但真到了要落地到业务场景里问题就一个接一个冒出来。数据不能出内网、调用延迟不稳定、按量计费成本不可控、并发一上来就被限流这些都是实打实的痛点。尤其是做内部知识库、代码助手、运维问答这类场景数据敏感度摆在那里把原始文本传到外部服务上合规这关就过不去。于是“把模型部署到局域网里”这件事从可选项变成了必选项。但真正动手之后你会发现部署一个模型只是第一步难的是把它包装成一个能对话、能调用工具、能记住上下文、能接入内部系统的 Agent 平台。单纯跑一个推理服务用户问一句答一句那叫聊天机器人不叫 Agent。Agent 的核心在于它能规划任务、调用外部工具、根据结果调整下一步动作这背后需要一套完整的编排框架来支撑。这就是我这次折腾Docker 部署 DeepSeek Harness的出发点。Harness 这个词在软件工程里本来就是“脚手架、编排框架”的意思放到 AI Agent 语境下它扮演的正是那个把模型能力、工具调用、会话管理、权限控制串起来的中间层角色。而 DeepSeek 系列模型在中文理解、代码生成、长上下文处理上的表现配合 Harness 的编排能力刚好能拼出一套适合局域网内部使用的 Agent 平台。1.2 这套方案到底解决了什么问题我把这套东西定位成“局域网 AI Agent 平台”它解决的核心问题可以拆成四层来看。第一层是数据不出内网。所有推理请求、对话历史、工具调用记录都在局域网内部流转不经过任何外部服务。对于有合规要求的团队来说这是硬性门槛。第二层是成本可控。云端 API 按 token 计费用得越多花得越多而且价格随时可能调整。自建之后硬件是一次性投入电费和运维是固定成本用得越多边际成本越低。对于高频使用的内部工具这笔账算下来是划算的。第三层是能力可扩展。Harness 框架通常支持自定义工具注册你可以把内部的数据库查询、日志检索、工单系统、代码仓库检索都封装成工具挂上去让 Agent 真正能“干活”而不只是“聊天”。第四层是部署可复制。用 Docker 打包之后整套环境可以一键在任意一台局域网服务器上复现不用再担心“在我机器上能跑”的经典问题。镜像里锁定了依赖版本、运行时配置、模型加载路径换台机器照样跑。适合读这篇内容的人我大致分三类一是有一定 Linux 和 Docker 基础想给团队搭内部 AI 助手的后端工程师二是做企业信息化、想把 AI 能力接进现有系统的运维或架构同学三是对 Agent 编排感兴趣想找个真实项目练手的技术爱好者。如果你完全没碰过 Docker建议先补一下基础命令再来不然中间排查问题会比较痛苦。1.3 整体架构长什么样在动手之前先把架构在脑子里过一遍后面每一步操作你才知道自己在干什么。这套平台的架构我习惯分成四层模型推理层负责加载 DeepSeek 模型权重对外暴露推理接口。可以是本地加载也可以是独立的推理服务容器。Agent 编排层也就是 Harness 本体负责会话管理、提示词组装、工具调用决策、多轮对话状态维护。工具与数据层内部 API、数据库、文件检索等被封装成工具供 Agent 按需调用。接入层对外提供 HTTP 接口或 WebSocket供前端页面、内部系统、命令行工具接入。这四层里推理层和编排层是必须的工具层按需扩展接入层决定你的用户怎么用。Docker 的价值在于把这四层分别容器化用 docker-compose 或者自定义网络把它们串起来层与层之间通过内部网络通信只有接入层对外暴露端口。提示局域网部署不等于完全没有安全风险。内部网络同样需要做端口访问控制尤其是接入层端口建议只对特定网段开放不要图省事直接监听 0.0.0.0 全放通。2. 部署前的环境准备与选型考量2.1 硬件配置怎么估算硬件是这套方案里最花钱的部分也是最容易拍脑袋决策的部分。我先说结论显存决定你能跑多大的模型内存决定你能开多少并发磁盘决定你加载模型快不快。DeepSeek 系列有不同参数规模的版本常见的有 7B、13B、33B 以及更大的 MoE 版本。参数量和显存的关系有个粗略估算公式FP16 精度下每 10 亿参数大约需要 2GB 显存。也就是说 7B 模型 FP16 加载大约需要 14GB 显存13B 大约 26GB33B 大约 66GB。如果用量化版本比如 4-bit 量化显存需求可以降到原来的四分之一左右7B 模型 4-bit 量化后大约 4GB 显存就能跑起来。但注意这只是加载模型的显存实际推理时还需要额外的显存来存放 KV Cache这部分和上下文长度、并发数直接相关。上下文越长、并发越高KV Cache 占用越大。我的经验是在加载显存的基础上再预留 30% 到 50% 的余量比较稳妥。内存方面模型加载时会有一次从磁盘到内存再到显存的搬运过程系统内存至少要能装下模型文件。如果模型文件是 15GB那内存建议 32GB 起步。磁盘方面模型文件动辄十几 GB 到几十 GB加上 Docker 镜像和日志建议预留 200GB 以上的 SSD 空间机械硬盘加载模型会慢到让你怀疑人生。下面这张表是我整理的不同规模模型的硬件参考具体还要看你选的量化和上下文配置模型规模精度加载显存建议显卡显存建议系统内存建议磁盘7BFP16约 14GB24GB32GB100GB SSD7B4-bit约 4GB8GB16GB50GB SSD13BFP16约 26GB32GB48GB150GB SSD13B4-bit约 7GB12GB24GB80GB SSD33B4-bit约 17GB24GB48GB200GB SSD注意如果你用的是消费级显卡注意检查是否支持所需的计算能力。部分老卡虽然显存够但算力跟不上推理速度会非常慢体验很差。2.2 操作系统与驱动准备操作系统我推荐 Ubuntu 22.04 LTS 或者同级别的长期支持版本。原因很简单驱动生态成熟、Docker 支持好、社区资料多遇到问题好搜。CentOS 系也不是不能用但新版本的一些变化会让驱动安装多绕几步。显卡驱动这块如果你用的是 NVIDIA 显卡需要装好驱动和 CUDA 运行时。这里有个关键点宿主机驱动版本要和容器内 CUDA 版本兼容。Docker 容器里跑的是 CUDA 应用但驱动是宿主机的容器通过 NVIDIA Container Toolkit 来访问显卡。所以流程是宿主机装驱动装 NVIDIA Container Toolkit然后在 docker run 时加--gpus all参数。验证驱动是否正常用这条命令nvidia-smi能正常输出显卡型号、驱动版本、显存占用就说明驱动没问题。如果报错先解决驱动再往下走不然后面全是坑。Docker 和 Docker Compose 的安装官方文档有标准流程我这里不重复。只提醒一点装完之后把当前用户加入 docker 组不然每条 docker 命令都要 sudo很烦。sudo usermod -aG docker $USER执行完要重新登录一次才生效。2.3 模型文件从哪来、放哪里模型文件是这套方案的核心资产。获取渠道上建议从模型官方发布渠道下载确保文件完整性和版本正确。下载方式通常是 Git LFS 或者直接下载权重文件具体看发布方的说明。下载完之后我建议把模型文件放在一个独立的、路径固定的目录里比如/data/models/deepseek/。这样做的好处是Docker 容器通过 volume 挂载这个目录模型文件和容器解耦将来换模型、升级模型都不用动容器本身。目录结构我习惯这样组织/data/models/ └── deepseek/ ├── config.json ├── tokenizer.json ├── tokenizer_config.json └── pytorch_model.bin (或分片文件)挂载的时候用只读模式:ro防止容器内误写导致模型文件损坏。这个细节很多人不注意等到模型文件被写坏了才后悔。提示模型文件下载后建议校验一下哈希值尤其是分片文件任何一个分片损坏都会导致加载失败而且报错信息往往不直观排查起来很费时间。3. Docker 镜像构建与容器编排实操3.1 镜像分层设计思路构建镜像这件事很多人上来就写一个巨大的 Dockerfile把所有东西塞进去结果镜像几个 GB构建一次十几分钟改一行代码又要重新构建。我的做法是分层设计把变化频率不同的内容分开。基础层放操作系统、Python 运行时、CUDA 基础库这层几乎不变构建一次可以复用很久。中间层放项目依赖比如推理框架、Harness 框架、工具库这层在依赖版本调整时才变。应用层放 Harness 的配置和启动脚本这层变化最频繁但构建最快。这样设计的好处是改应用层配置时前面两层直接命中缓存构建时间从十几分钟降到几十秒。下面是一个简化的 Dockerfile 结构示意# 基础层 FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04 RUN apt-get update apt-get install -y python3.10 python3-pip # 中间层依赖 COPY requirements.txt /app/requirements.txt RUN pip install -r /app/requirements.txt # 应用层配置与代码 COPY harness_config/ /app/config/ COPY entrypoint.sh /app/entrypoint.sh WORKDIR /app CMD [bash, entrypoint.sh]requirements.txt 里锁定版本号不要用这种模糊约束否则今天构建和明天构建出来的环境可能不一样排查问题时会疯掉。3.2 docker-compose 编排配置详解单容器能跑但生产环境我强烈建议用 docker-compose 编排。原因有三一是推理服务和 Harness 服务分开各自独立重启不影响对方二是网络配置集中管理容器间通信用服务名而不是 IP三是环境变量、volume、端口映射都写在一个文件里一目了然。下面是我实际用的一份 compose 配置骨架关键部分我都加了注释version: 3.8 services: inference: image: deepseek-inference:latest runtime: nvidia environment: - NVIDIA_VISIBLE_DEVICES0 - MODEL_PATH/models/deepseek volumes: - /data/models:/models:ro networks: - agent-net deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] harness: image: deepseek-harness:latest depends_on: - inference environment: - INFERENCE_URLhttp://inference:8000 - LOG_LEVELinfo ports: - 127.0.0.1:9000:9000 volumes: - ./harness_config:/app/config:ro - ./logs:/app/logs networks: - agent-net networks: agent-net: driver: bridge几个关键点解释一下。runtime: nvidia和deploy.resources是让容器能访问显卡的配置两者配合使用。NVIDIA_VISIBLE_DEVICES0指定用第几块卡多卡机器上这个很重要。INFERENCE_URL用的是服务名inference而不是 IP这是 Docker 内部 DNS 的功劳容器重建后 IP 变了也不影响。端口映射我写的是127.0.0.1:9000:9000只监听本机外部要访问得通过反向代理这样更安全。3.3 启动流程与验证步骤配置写好后启动流程分三步走。第一步先单独启动推理服务确认模型能正常加载docker compose up -d inference docker compose logs -f inference日志里看到模型加载完成、推理服务监听的提示说明推理层 OK。这一步可能要等几分钟模型越大加载越慢耐心等。第二步启动 Harnessdocker compose up -d harness docker compose logs -f harnessHarness 启动时会去连推理服务日志里能看到连接成功的提示。如果报连接失败先检查两个容器是不是在同一个 network 里再检查INFERENCE_URL配置对不对。第三步做端到端验证。用 curl 发一个测试请求curl -X POST http://127.0.0.1:9000/chat \ -H Content-Type: application/json \ -d {message: 你好做个自我介绍, session_id: test-001}能正常返回模型回复说明整条链路通了。如果返回超时去看推理服务日志大概率是显存不够或者模型加载失败。注意第一次请求往往会比较慢因为模型需要做一次预热。建议部署完成后先发几个测试请求把模型“跑热”再交给用户使用不然第一个用户会等很久。4. Agent 能力配置与工具接入4.1 会话管理与上下文策略Agent 和普通聊天机器人的第一个区别就在会话管理上。普通聊天是无状态的问一句答一句Agent 需要记住上下文才能做多轮任务规划。Harness 框架通常提供会话管理能力核心要配置的是上下文窗口策略和会话过期策略。上下文窗口策略决定了一次请求带多少历史对话给模型。带得越多模型越“记得住”但显存和延迟也越高。我的经验是对于日常问答场景保留最近 10 到 20 轮对话足够对于长文档分析场景可能需要配合摘要压缩把早期对话压缩成摘要再带上。会话过期策略决定会话数据保留多久。内部工具场景下我一般设置 24 小时过期既保证用户当天连续使用体验又不会让存储无限膨胀。配置项通常长这样session: max_history_rounds: 15 ttl_hours: 24 storage: redis用 Redis 存会话数据是个常见选择读写快、支持过期自动清理。如果不想引入 Redis用文件存储也行但并发高的时候会有锁竞争问题。4.2 工具注册与调用机制Agent 真正“能干活”的关键在工具调用。Harness 框架一般提供工具注册接口你把内部能力封装成函数注册进去模型在需要时会生成工具调用请求Harness 负责执行并把结果回传给模型。工具定义通常包含三部分名称、描述、参数 schema。描述写得越清楚模型越知道什么时候该调用这个工具。我见过很多人工具描述写得含糊结果模型该调用的时候不调用不该调用的时候乱调用排查半天发现是描述的问题。举个内部工单查询工具的例子tools [ { name: query_ticket, description: 根据工单号查询工单详情包括状态、处理人、创建时间, parameters: { type: object, properties: { ticket_id: { type: string, description: 工单编号格式为 TK 开头加数字 } }, required: [ticket_id] } } ]工具的实际执行逻辑要单独实现注册时把名称和实现函数关联起来。执行时要做好异常处理工具调用失败不能让整个会话崩掉要把错误信息作为工具结果回传给模型让模型决定下一步怎么办。4.3 权限控制与审计日志内部平台绕不开权限和审计。权限控制分两个层面一是谁能访问平台二是谁能调用哪些工具。第一层靠接入层的认证做第二层靠 Harness 的工具权限配置做。工具权限我建议按角色划分。比如普通用户只能调用知识库查询工具管理员才能调用系统操作类工具。配置上可以给每个工具打标签会话建立时根据用户角色加载对应的工具集。审计日志要记录的关键信息包括谁在什么时间发起了什么请求、模型调用了哪些工具、工具返回了什么结果、最终回复是什么。这些日志一方面用于问题排查另一方面用于合规审计。日志建议结构化存储方便后续检索分析。audit: enabled: true log_tool_calls: true log_full_response: false retention_days: 90log_full_response我一般设为 false只记录摘要避免日志体积过大也减少敏感信息落盘的风险。5. 常见问题排查与性能调优5.1 模型加载失败的几类原因模型加载失败是部署阶段最高频的问题我把它归成三类。第一类是文件问题包括文件缺失、分片不完整、路径配置错误。排查方法是进容器里ls一下挂载目录确认文件都在再检查配置里的路径和实际路径是否一致。第二类是显存问题报错通常是 CUDA out of memory。这时候要确认模型大小和显卡显存是否匹配如果显存紧张换量化版本或者减小上下文长度。有时候显存看起来够但被其他进程占用了用nvidia-smi看一下实际占用。第三类是版本兼容问题比如推理框架版本和模型格式不匹配、CUDA 版本和驱动不匹配。这类问题报错信息往往比较晦涩我的经验是先把版本对齐到官方推荐的组合不要自己乱搭。下面这张表是我整理的常见报错和对应排查方向报错关键词可能原因排查方向FileNotFoundError模型路径错误检查 volume 挂载和配置路径CUDA out of memory显存不足换量化版本或减小上下文version mismatch版本不兼容对齐框架与模型版本connection refused服务未启动检查容器状态和网络配置timeout推理过慢检查显卡算力和并发量5.2 推理速度慢的优化思路推理速度慢用户体感差这是自建平台最常见的抱怨。优化思路按性价比排序我一般这么排第一用量化模型。4-bit 量化相比 FP16显存占用降到四分之一速度通常也有提升精度损失在多数场景下可以接受。这是性价比最高的一招。第二调整 batch 和并发配置。推理服务通常支持批处理把多个请求合并成一个 batch 推理能显著提升吞吐。但 batch 太大会增加单请求延迟要找到平衡点。第三优化上下文长度。上下文越长推理越慢这是注意力机制决定的。能短则短历史对话该压缩就压缩。第四升级硬件。这是最后手段前面三招都用完了还慢那就是硬件瓶颈了。提示优化之前先做基准测试记录当前的首 token 延迟和每秒生成 token 数优化后再测一次对比。没有基准数据的优化都是瞎猜。5.3 容器运行时的稳定性问题容器跑着跑着挂了或者内存泄漏导致 OOM这类问题在长时间运行的服务里很常见。我的应对策略有这么几条。一是配置健康检查。在 compose 里给服务加 healthcheck容器不健康时自动重启。健康检查的接口要轻量别搞个复杂查询把服务拖垮。healthcheck: test: [CMD, curl, -f, http://localhost:9000/health] interval: 30s timeout: 10s retries: 3二是限制容器资源。给容器设置内存上限防止单个容器吃光宿主机内存影响其他服务。但注意显存没法通过 Docker 限制得靠推理服务自己控制。三是日志轮转。容器日志不轮转跑几天就能把磁盘写满。在 Docker daemon 配置里设置日志大小和文件数上限或者用 compose 的 logging 配置。logging: driver: json-file options: max-size: 100m max-file: 5四是定期重启。有些内存泄漏问题短期内修不了那就设置定时任务在业务低峰期重启容器用时间换稳定。这不是优雅方案但实用。6. 我踩过的坑和几条实在建议6.1 那些文档里不会写的细节第一个坑是模型文件权限。从别处拷贝过来的模型文件属主可能不是容器内运行用户导致容器读不了。解决办法是提前chmod或者chown别等到报错才想起来。第二个坑是Docker 网络和宿主机防火墙打架。容器间通信走的是 Docker 内部网络但如果你在宿主机上配了防火墙规则可能会误伤容器网络。排查网络问题时先把防火墙规则理清楚。第三个坑是GPU 被多个容器争抢。一台机器上跑了多个用 GPU 的容器显存互相挤占谁都跑不好。要么用NVIDIA_VISIBLE_DEVICES明确分配要么就一台机器只跑一个 GPU 服务。第四个坑是时区问题。容器默认 UTC 时区日志时间和你本地时间差 8 小时排查问题时对不上。启动时加个TZAsia/Shanghai环境变量就解决了但不知道的人能纠结半天。6.2 给准备动手的朋友几句实在话如果你现在正准备动手我给你几条建议。先跑通最小闭环再谈优化。别一上来就追求完美架构先把模型跑起来、Harness 连上、能对话这个闭环通了后面都是锦上添花。配置全部版本化。Dockerfile、compose 文件、Harness 配置全部放进版本控制。改配置之前先提交改坏了能回滚。我见过太多人直接在生产环境改配置改出问题找不回原样。留好回退方案。新版本模型、新版本框架先在测试环境验证别直接上生产。生产环境保留上一个可用版本的镜像和配置出问题能快速切回去。监控比日志更重要。日志是事后排查用的监控是事前预警用的。显存占用、请求延迟、错误率这几个指标盯住了大部分问题能在用户感知之前发现。这套 Docker 部署 DeepSeek Harness 的方案我从第一次跑通到现在稳定运行前后迭代了七八个版本。最开始那版镜像臃肿、启动慢、配置散落各处现在这版分层清晰、配置集中、重启只要几十秒。这个过程里最大的体会是部署这件事没有一步到位的方案都是根据实际使用中暴露的问题一点点磨出来的。你现在看到的这套结构也是被各种坑教育出来的结果。