前阵子帮朋友在办公内网搭了一套 AI Agent 平台标题写作“Docker 部署 DeepSeek Harness”听起来像是某个神秘项目实际拆开看本质并不复杂它是一套把模型服务、Agent 编排、前端界面、消息队列串起来的容器化组合。我自己的体会是这类平台真正难的点不是“跑起来”而是想清楚每个组件之间怎么解耦、数据往哪里流、局域网里别人怎么访问。这篇文章我按实操顺序把整套流程复盘一遍从架构设计、Docker Compose 编写、模型接入到故障排查全程用我踩过的坑当反面教材希望能帮你少走几步弯路。1. 为什么要在局域网里部署一个 Agent 平台1.1 数据在内网心才踏实先说最现实的动机现在公网上的 AI 服务确实方便但聊天内容、文档、代码片段一旦送出去就等于把内部信息交到了别人手里。我自己做内部工具的习惯是涉密内容、研发中间产物、客户资料这类东西一律不进公网。局域网里自建一个 Agent 平台最大的优势就是数据从生产到消费全链路留在内网模型推理也在本地完成网络层面上根本不依赖外部链路。这个安全感是任何“声称加密传输”的在线服务都给不了的。另一个很实际的考量是成本。团队十个人、二十个人都要用 AI 助手的时候按人头开账号的订阅费一年下来也不少。自建平台一次投入换长期使用模型跑在自己机器上推理成本只跟电费和硬件折旧挂钩。尤其适合研发团队、实验室、中小公司这类对数据敏感又有一定技术力的场景。1.2 DeepSeek Harness 解决什么问题标题里的“Harness”这个词很关键它通常指“装配、束具”放在 AI 领域可以理解为把零散的模型服务、工具调用、对话记忆、任务执行串联起来的编排层。单独一个模型 API 只能做“你问我答”但一个 Agent 平台需要处理的是“理解意图-拆解步骤-调用工具-汇总结果-生成回复”这样的完整链路。DeepSeek Harness 做的事情就是把这套链路包装成几个独立服务再通过 Docker 把它们塞进同一套运行环境里。举个例子团队里有人想让它“查一下研发 Wiki 里的接口文档然后按模板写一份周报”。这中间涉及文档检索、信息提取、格式整理、内容生成四个环节。Harness 的角色就是调度员先触发检索工具把相关段落捞出来交给模型做摘要再把摘要填入周报模板。如果不加这层编排单靠模型本身的上下文窗口很难稳定地完成这种多步任务。Docker 的介入则是把调度逻辑、模型服务、前端界面分别打包避免“在我机器上明明是好的换台机器就崩”的尴尬。1.3 适合谁来玩如果你是后端开发、运维、或者在公司里做内部工具支撑的人这套东西属于典型的“今天搭好、明天就能给同事用”的实用项目。需要的基础能力也不高懂一点 Linux 命令、会用 Docker 基本操作、知道什么是 API 调用就行。前端界面部分我直接挂了现成的 Web UI完全不用自己开发页面。如果只是想在自己的笔记本上试试 AI Agent这套方案也可以精简到只跑模型服务和 Agent 引擎两个容器不装前端、不加数据库照样能通过命令行跟 Agent 对话。所以下面的内容我尽量做到“可裁剪”覆盖从极简测试到多人使用两种场景。2. 架构拆解与容器的职责边界2.1 逻辑上的五个核心组件我把一套能用的局域网 Agent 平台拆成五个逻辑组件Web 网关 / 前端控制台提供聊天界面、会话管理、配置页面是用户直接接触的一层。Agent 编排引擎接收用户消息调用大模型做推理同时负责跟工具、知识库交互。模型推理服务本地跑开源模型暴露一个兼容标准 Chat 接口的调用端点。记忆与向量库存历史会话摘要、知识库切片让 Agent 能“记住”之前聊过什么、找到相关资料。消息队列 / 缓存处理并发请求、异步任务避免多人同时用的时候互相阻塞。这个拆分不是拍脑袋想的而是我在实际部署里踩过“大杂烩”的坑之后总结的。最开始我把 Agent 引擎和前端塞在同一个进程里结果前端一个长轮询请求堵住之后Agent 的推理任务也跟着卡死整个系统表现为“要么能聊天要么能干活”。拆成独立容器之后每层出问题都能单独重启、单独扩容日志也清爽得多。2.2 数据流到底怎么走一次完整的用户请求走的是这个链路用户在 Web 界面输入消息前端把消息 POST 给 Agent 引擎。Agent 引擎分析意图判断是否需要检索知识库或调用工具。如果需要检索引擎先从向量库里查出相关文档片段。引擎把用户消息加上检索结果拼成提示词发给模型推理服务。模型返回生成结果引擎再决定是直接回复、还是触发第二轮工具调用。前端拿到最终结果渲染给用户同时把会话摘要写入记忆库。值得强调的是第 5 步——真正的 Agent 不是说一次模型调用就完事它可能要跟模型来回交互好几轮。比如模型第一次决定“需要查一下数据库”返回一个工具调用指令引擎执行查询再把查询结果喂给模型让模型生成最终答案。这个“Agent 循环”是整个平台最核心的机制也是 Harness 这类编排层存在的根本意义。2.3 部署上的物理划分逻辑上五个组件部署的时候却可以按物理环境合并。我常用的方案是分成两类节点应用节点跑 Web 网关、Agent 引擎、Redis、向量库对 GPU 没要求。模型节点跑模型推理服务必须有足够显存最好有独立的 GPU 机器。这两类节点之间用 Docker 网络打通。如果只有一台机器那就全塞进同一个 docker-compose.yml 里靠容器名互相访问。如果有多台机器我对模型节点单独写一份 compose 文件应用节点通过局域网 IP 端口访问模型服务。这种“逻辑上分开、物理上可变”的设计让平台在单机和分布式场景下都能平滑切换。3. Docker 环境准备与 Compose 配置3.1 环境准备只要三件事部署前要确认的基础条件只有三项Docker 引擎、Compose 插件、以及能访问镜像仓库的网络。Linux 服务器上装 Docker 已经是常规操作我在准备阶段会多检查一个点docker compose version是否可用。很多老机器上只装了早期版本的 docker-compose命令风格和参数都不一样。现在的 Compose 插件是跟随 Docker 一起安装的用docker compose中间有空格而不是docker-compose。还有一个容易被忽视的准备工作是内外网镜像策略。有些环境拉取 Docker Hub 镜像经常超时我的办法是准备一个内网镜像仓库作为中转先把镜像拉到中转机再推给目标机器。如果连中转条件都没有就提前在有网环境docker pull之后用docker save导出成 tar 包再拷进内网docker load。离线部署的场景我实测过只要镜像 tar 包齐全整个平台在内网里构建完全没问题。3.2 docker-compose.yml 主结构拆解下面这份 compose 文件是我在单机上跑通的最小版本注释里标了每个服务的职责name: deepseek-harness services: web: image: nginx:1.27-alpine ports: - 8080:80 volumes: - ./frontend-dist:/usr/share/nginx/html:ro - ./nginx-conf:/etc/nginx/conf.d:ro depends_on: - engine networks: - agent-net engine: image: harness-agent:latest # 实际使用时替换为自己构建的引擎镜像 environment: LLM_BASE_URL: http://model-service:11434/v1 LLM_MODEL: qwen2.5:7b REDIS_URL: redis://redis:6379/0 VECTOR_STORE_URL: http://vector-store:8000 TOOL_WHITELIST: wiki_search,template_render volumes: - engine-data:/app/data ports: - 8765:8765 depends_on: model-service: condition: service_healthy redis: condition: service_healthy networks: - agent-net model-service: image: ollama/ollama:latest volumes: - ollama-models:/root/.ollama ports: - 11434:11434 healthcheck: test: [CMD, ollama, list] interval: 30s timeout: 10s retries: 3 networks: - agent-net redis: image: redis:7-alpine command: [redis-server, --appendonly, yes] volumes: - redis-data:/data healthcheck: test: [CMD, redis-cli, ping] interval: 10s timeout: 5s retries: 5 networks: - agent-net vector-store: image: chromadb/chroma:0.4.24 volumes: - chroma-data:/data ports: - 8000:8000 networks: - agent-net networks: agent-net: driver: bridge volumes: engine-data: ollama-models: redis-data: chroma-data:有几个配置细节值得单独说。depends_on配合condition: service_healthy的作用是控制启动顺序防止 Agent 引擎起来时模型服务还没就绪导致一连串无谓的重试。我之前跳过健康检查直接用depends_on结果引擎报了几百行连接错误日志才慢慢恢复加了健康检查之后就顺多了。LLM_BASE_URL指向http://model-service:11434/v1而不是localhost:11434这是因为容器之间不能靠 localhost 互访。这个地址在 Compose 自定义网络里会被自动解析到模型服务的容器 IP是整个配置里最基础也最容易写错的地方。另外自定义网络名agent-net我用的是 bridge 驱动这样同一台机器上的容器互通没问题需要对外暴露的端口再单独映射。3.3 网络模式与数据持久化的取舍关于网络模式我强烈建议不要图省事把所有服务都设成network_mode: host。host 模式虽然让容器直接共享宿主机网络容器访问 localhost 就能互通端口也不用映射但带来的问题很麻烦多个服务抢端口时没有 Compose 层面的隔离保护和报错提示而且不同机器的网络配置不一样换一台机器部署就得改端口。我用 bridge 网络 显式端口映射虽然多写几行配置但胜在可迁移性换机器时docker compose up就能直接跑起来。数据持久化方面模型目录一定要用命名卷挂载。ollama-models:/root/.ollama这个卷装的是本地模型权重如果不挂载容器一删模型就全没了拉一次模型要下好几个 GB谁删谁知道。Redis 我开了 appendonly 持久化向量库的数据目录同样挂到命名卷。建议你们一开始就把该挂的卷都挂好别像我第一次部署时偷懒少挂了一个向量库卷后来清理容器重建知识库全部重建教训相当深刻。4. 模型接入与 Agent 工作流配置4.1 用 Ollama 跑本地模型的完整过程模型服务我选了 Ollama原因是它对硬件要求友好、安装干净、对 OpenAI 兼容接口的支持直接能用。整个部署里模型服务就是单容器运行不需要额外配 Python 环境也不需要自己写推理代码。启动模型服务之后第一步是往里面灌模型。我通常这样操作# 进入模型服务容器执行拉取命令 docker exec -it deepseek-harness-model-service-1 ollama pull qwen2.5:7b # 如果你更倾向 DeepSeek 系参数规模更大的模型 docker exec -it deepseek-harness-model-service-1 ollama pull deepseek-r1:14b这几个模型都是开源权重拉取的是本地推理所需文件。跑什么模型主要看机器配置16GB 内存的纯 CPU 机器我建议用 7B 及以下的中小模型有 8GB 以上显存可以上 14B 级别的模型真要跑 32B 以上内存和显存都得做好心理准备。模型拉完之后可以验证一下这个命令能否正常输出curl http://127.0.0.1:11434/v1/chat/completions \ -H Content-Type: application/json \ -d {model:qwen2.5:7b,messages:[{role:user,content:你好}]}如果返回正常的 JSON 结构说明模型服务已经就绪Agent 引擎的LLM_BASE_URL指向这个地址就能工作。4.2 Agent 引擎的初始化配置引擎容器起来之后需要关注的核心环境变量有四类模型地址、模型名、Redis 连接、向量库地址。以我上面的 compose 为例环境变量作用示例值LLM_BASE_URL模型推理服务的兼容接口地址http://model-service:11434/v1LLM_MODEL默认使用的模型名qwen2.5:7bREDIS_URL会话缓存和异步队列的连接串redis://redis:6379/0VECTOR_STORE_URL知识库向量检索服务的地址http://vector-store:8000这些值配错一个系统表面上看都能启动但用起来就会出现“回复空内容”“历史记录丢失”“知识库检索不到结果”之类的怪问题。建议每次改完配置之后先用docker compose logs engine看一遍启动日志确认引擎有没有成功连上模型服务和 Redis。Agent 引擎启动后的表现可以做一个很直观的测试在聊天界面向它提问一个需要两步推理的问题比如“总结一下对话历史里提到过的项目风险再排个优先级”。如果引擎能分两步给出结果说明 Agent 循环生效了。如果答得很空洞往往是上下文窗口或提示词没配好跟模型推理本身关系不大。4.3 工具调用与知识库接入真正的 Agent 平台不能只有聊天功能工具调用和知识库是让它“有用”的两个关键插件。工具调用方面我的做法是预先定义几个工具函数检索内部 Wiki、查询数据库、生成周报模板、解析工时数据等等。引擎根据用户意图决定调用哪个工具而工具名单我在环境变量里用白名单控制了TOOL_WHITELIST这样可以约束 Agent 不要乱来。最初我没加白名单模型经常自作主张用一个语义相近但完全错误的工具加了白名单之后明显收敛。知识库场景我用 Chroma 做向量存储用文本嵌入模型给文档切片生成向量。具体流程是先把内部文档清洗成 Markdown 格式的切片放进知识库时给每条切片打上来源标签比如“研发Wiki”“需求文档”检索时按标签过滤避免不同项目的内容互相干扰。检索到了相关资料之后Agent 引擎会把这些内容拼接到提示词里再让模型基于拼接结果生成回答。这里有一个参数需要调知识库检索返回的片段数。设置太少模型看到的信息不够设置太多上下文膨胀导致回答冗长、重点不突出。我的经验是先给 3 个片段每个片段控制在 500 字以内跑几个真实问题再微调。在多轮对话里还要在提示词中明确“如果用户新问题与历史上下文无关请以新问题为准”否则模型容易被旧上下文带偏。5. 实操过程中的坑与排查实录5.1 镜像拉取与冷启动的那些糟心事第一次启动整个平台十有八九会遇到镜像拉取卡住的问题。我碰到的情况是某些层下载到一半就断重启 Docker 之后能续上但很慢。解决思路是给镜像仓库做本地缓存或者干脆用离线导入的笨办法前面已经说过这里不再展开。冷启动的另一个坑是 Ollama 首次拉模型时的进度感知。通过docker exec拉模型终端里会显示进度条但如果你中途断开了终端拉取任务不会自动停。我曾经历过一次明明等了好久再查模型列表却还是空的后来才发现是终端断连导致任务中断。解决方法是让拉取模型的操作在后台执行或者用nohup挂起最后再确认模型列表。5.2 模型服务的内存与显存问题本地模型最磨人的是资源控制。我尝试在一台 32GB 内存的纯 CPU 机器上跑 14B 模型启动倒是没问题但一推理 CPU 占用立刻拉满单次响应等了两分多钟。这个问题本质上不是配置错误是硬件天花板。我的建议是CPU 机器老老实实用 7B 量化模型8GB 显存的 GPU 跑 7B 或 14B真要跑 32B建议起码有 24GB 显存。资源不够的情况下体验垮得很直接——聊天界面一直转圈手一抖还会把内存打爆。Ollama 有个有用的配置是环境变量OLLAMA_KEEP_ALIVE控制模型在内存/显存中的驻留时间。默认值是 5 分钟这意味着连续对话也会不断释放再加载模型。我把它改成了 10 分钟短时间内的多次请求响应速度快了很多。代价是显存占用不会被释放属于“以空间换时间”的典型取舍。5.3 局域网访问不了按这三层排查从本机访问没问题但局域网里其他电脑访问不了这是我被问得最多的问题。按顺序排查基本不会漏第一层容器端口是否映射正确。docker compose ps里看端口映射列如果显示0.0.0.0:8080-80说明端口绑定没毛病。如果端口映射被占用导致容器没起来就得查宿主机上有没有别的进程抢占了端口。第二层宿主机防火墙是否放行端口。Linux 里检查ufw status或firewalld-cmd --list-ports确认 8080 和 11434 这两个端口对局域网网段开放。没放行的话外网机器 ping 得到宿主机但访问特定端口一定超时。第三层容器间网络是否正常。在引擎容器里执行curl http://model-service:11434看能否连通。如果容器间不通多半是 Compose 网络配置或 DNS 解析出问题了。这个时候重新docker compose down docker compose up -d重建网络往往就能解决。5.4 常见问题速查表症状可能原因快速处理前端能打开但发消息没反应Agent 引擎挂了或模型服务没就绪查看docker compose logs engine确认LLM_BASE_URL是否正确模型回答特别慢模型太大、硬件不足或OLLAMA_KEEP_ALIVE太短换小模型调大驻留时间知识库检索总是空结果文档没成功切片或向量库服务没连上检查向量库日志重新执行索引脚本重启容器后会话记录全丢Redis 没做持久化或没挂卷确认 redis 服务配置里加了--appendonly yes并挂载数据卷局域网其他人访问不了防火墙或安全组未放行放行端口检查docker compose ps的端口映射列模型调用报 404模型名写错或模型未拉取执行ollama list核对模型名再检查LLM_MODEL这三个大方向排查完之后剩下的基本都是配置细节不用慌慢慢看日志就行。6. 从能用到好用的一点个人经验最后分享几条我反复踩坑之后养成的习惯。第一docker-compose.yml 里的配置参数能用.env文件管理就不要硬编码尤其是模型名、端口、数据目录这几个频繁改的值。第二日志轮转一定要早配容器日志不加限制的话长时间运行下来日志文件能膨胀到几个 GB拖垮磁盘。我一般这样配logging: driver: json-file options: max-size: 20m max-file: 5第三模型权重目录千万不要放在系统盘里一旦系统盘满了整个 Docker 环境都会跟着出问题。把这些基础运维习惯提前做好剩下的就是把精力花在调模型效果、调 Agent 工作流这些真正有价值的事情上。这套平台的扩展空间其实很大比如加入定时任务、接入企业内部的统一登录、对接审批流都会让它在团队里发挥更大作用。但地基先打好——理解容器间的协作方式、掌握模型接入的套路、养成日志排查的习惯后面加什么功能都不慌。