1. 为什么要在局域网里自建 AI Agent 平台
把大模型跑在自己手边的机器上,再通过局域网给团队里的几台设备同时用,这件事在两年前还属于“折腾一整天未必跑得起来”的范畴。现在情况完全不一样了,模型量化技术成熟、推理框架迭代快、容器化封装到位,一个稍微懂点命令行的开发者,半天时间就能把一套可用的 AI Agent 平台搭起来。我这次做的项目,就是用 Docker 把 DeepSeek 系列模型和一套 Agent 调度框架打包部署到局域网服务器上,让办公室里的笔记本、平板、甚至测试机都能连上来调用。
先说清楚这套东西是什么。DeepSeek 是当前开源模型里综合表现相当能打的一个系列,覆盖了从轻量对话到复杂推理的多个规格。而 Harness 在这里指的是“套具”这个概念——它不只是一个模型文件,而是一整套把模型加载、对话管理、工具调用、会话保持、接口暴露串起来的运行框架。你可以把它理解成一个“AI 员工的操作台”:模型是员工的大脑,Harness 是工位、电话、文件夹和任务看板的集合体。部署完成之后,局域网内任何一台设备打开浏览器或者用 API 客户端,就能像使用在线服务一样跟这个 Agent 对话,还能让它调用本地定义的工具函数。
这套方案解决的核心问题有三个。第一是数据不出内网,所有对话内容、上传的文档、模型推理过程全部在局域网内部完成,不经过任何外部链路,对于处理内部资料、代码片段、业务文档的场景来说这是硬需求。第二是多人共享一个模型实例,不需要每台电脑都装一遍环境、下载一遍权重,显存和算力集中在一台机器上,其他人轻量接入即可。第三是可控可定制,Agent 的工具列表、系统提示词、会话策略全部由自己掌握,想加一个查数据库的工具就加一个,想换模型规格就换规格,没有平台限制。
适合看这篇内容的人大概分三类。一类是团队里负责内部工具建设的技术同学,想给非技术同事提供一个能用的 AI 入口;一类是自己在做 side project 的开发者,需要一个稳定的本地 Agent 后端来对接自己的前端或自动化流程;还有一类是纯粹想学习容器化部署和 Agent 框架运作原理的爱好者,拿这套东西当练手项目非常合适。下面我会从整体设计思路开始,一步步拆到具体命令和参数,把踩过的坑和验证过的配置都摊开讲。
2. 整体架构设计与方案选型思路
2.1 为什么选 Docker 而不是裸机部署
裸机部署模型推理环境最头疼的问题是依赖冲突。Python 版本、CUDA 驱动、推理框架的编译选项、各种加速库的版本,这些东西之间互相牵制,换一台机器就要重新折腾一遍。我早期在一台测试机上直接装过一套环境,跑起来之后因为系统里另一个项目升级了某个底层库,整个推理服务直接起不来了,排查了大半天才发现是版本回退导致的算子不兼容。
Docker 把整个运行环境连同依赖一起封进镜像,模型权重通过挂载卷的方式外置,这样镜像本身可以复用,权重可以独立更新。换机器的时候只需要保证宿主机装了显卡驱动和容器运行时,剩下的东西原样搬过去就能跑。对于局域网部署这种“一次搭好、长期运行”的场景来说,容器化带来的可复现性是第一位的。
另一个考虑是资源隔离。Agent 平台在运行过程中会加载模型、维护会话状态、处理并发请求,如果和宿主机上其他服务混在一起,容易出现内存争抢或者端口冲突。用容器把 CPU、内存、显存的使用边界划清楚,出问题的时候也好定位——直接看容器日志和资源监控就行,不用在一堆系统进程里翻找。
2.2 模型规格与硬件匹配的取舍
DeepSeek 系列有多个规格,选哪个取决于你手头机器的显存大小。这里给一个粗略的对照关系,是我实际测试下来比较稳妥的配置:
| 模型规格 | 量化方式 | 最低显存 | 推荐显存 | 适用场景 |
|---|---|---|---|---|
| 轻量对话版 | 4-bit 量化 | 6GB | 8GB | 日常问答、文本润色 |
| 标准通用版 | 4-bit 量化 | 12GB | 16GB | 代码辅助、文档分析 |
| 标准通用版 | 8-bit 量化 | 20GB | 24GB | 复杂推理、长文本 |
| 高精度版 | 原始精度 | 40GB+ | 多卡 | 专业推理任务 |
这张表里的数字是经验值,实际占用会因为上下文长度、并发数量、推理框架的优化程度有所浮动。我的建议是留出至少 20% 的显存余量,因为 Agent 运行过程中除了模型本身,还要存放键值缓存、工具调用的中间结果、会话历史等数据。显存吃满的直接后果是推理速度断崖式下降,甚至触发内存交换导致服务卡死。
如果机器显存确实有限,优先考虑 4-bit 量化版本。量化会带来一定的精度损失,但在对话和常规任务上感知不明显,换来的是显存占用大幅下降和推理速度提升。我对比过同一模型 4-bit 和 8-bit 的输出,在代码生成任务上差异主要体现在极少数边界情况,日常使用完全够用。
2.3 Agent 框架的职责划分
Harness 这一层做的事情比很多人想象的要复杂。它不只是把请求转发给模型然后返回结果,中间还涉及几个关键环节。会话管理负责维护每个用户的对话上下文,决定哪些历史消息要带进当前推理、哪些可以丢弃。工具调度负责解析模型输出的工具调用意图,执行对应的本地函数,再把结果拼回对话流。接口层负责对外暴露统一的 API,屏蔽底层模型的差异,让前端或者自动化脚本不用关心后面跑的是什么规格的模型。
这套框架的设计思路是“模型负责思考,框架负责执行”。模型只输出文本和工具调用请求,真正去查数据库、读文件、发请求的动作由框架完成。这样做的好处是模型不需要具备直接操作系统的能力,安全性可控,同时工具的实现可以用任意语言编写,只要符合框架定义的接口规范就行。
我在选型时对比过几种常见的 Agent 框架实现方式。一种是纯 API 网关模式,只做请求转发不做工具调度,简单但功能弱;一种是全功能框架,内置大量工具和复杂调度逻辑,开箱即用但定制成本高;还有一种是模块化框架,核心调度和工具实现分离,需要自己组装但灵活度最高。最终我选了模块化方案,因为局域网部署的场景下,工具往往需要对接内部系统,灵活度比开箱即用更重要。
3. 部署前的环境准备与关键检查项
3.1 宿主机基础环境确认
在拉镜像之前,先把宿主机的基础条件确认清楚,这一步偷懒后面会花更多时间补。首先是操作系统,主流的 Linux 发行版都可以,内核版本建议不要太老,否则容器运行时对显卡的支持可能有问题。我用的是较新的长期支持版本,内核版本在 5.x 以上,实测稳定。
显卡驱动是重点检查项。在终端执行显卡状态查询命令,确认驱动正常加载、显卡被正确识别。如果这条命令报错或者输出为空,说明驱动没装好,后面容器里再怎么配都是白搭。驱动版本要和后续容器内使用的运行时版本匹配,版本差距过大可能导致容器启动时报错。
容器运行时本身也要确认版本。执行版本查询命令,确保输出正常。如果提示命令不存在,需要先安装容器引擎。安装过程这里不展开,各发行版的包管理命令不一样,按照官方文档操作即可。装完之后建议把当前用户加入容器用户组,否则每条容器命令都要加权限前缀,很烦。
提示:显卡驱动、容器运行时、容器内推理框架这三者的版本兼容关系是个高频踩坑点。我的经验是驱动版本尽量新,容器运行时用稳定版,推理框架镜像用官方提供的对应版本,三者不要跨太多大版本。
3.2 目录结构与挂载规划
容器化部署的一个核心原则是“数据外置”。模型权重、配置文件、日志、会话数据这些需要持久化的内容,全部通过挂载卷的方式放在宿主机上,容器本身保持无状态。这样升级镜像或者重建容器的时候,数据不会丢。
我规划的目录结构是这样的:
/opt/ai-agent/ ├── models/ # 模型权重文件 ├── config/ # 框架配置文件 ├── data/ # 会话数据与工具数据 ├── logs/ # 运行日志 └── tools/ # 自定义工具脚本模型权重目录单独放,因为文件体积大,更新频率低,可以独立备份。配置目录存放框架的启动参数、模型路径映射、工具注册表等。数据目录是运行时会写入的内容,需要保证容器内进程有写权限。日志目录方便排查问题,建议挂载出来,容器重启后日志还在。工具目录是自定义工具脚本的存放位置,框架启动时会扫描这个目录加载工具。
权限方面有个细节要注意。容器内进程通常以非 root 用户运行,如果挂载目录的属主是 root 且权限是 755,容器内进程写不进去。解决办法是在宿主机上把目录属主改成容器内运行用户的 UID,或者在启动容器时指定用户参数。我图省事直接把数据目录权限放宽到 775 并设置了组权限,内部环境够用。
3.3 网络与端口规划
局域网部署意味着服务要监听在可以被其他设备访问的地址上。容器启动时通过端口映射把内部端口暴露到宿主机,其他设备通过宿主机的局域网 IP 加端口来访问。
端口规划上我分了两个:一个给 Web 界面,一个给 API 接口。Web 界面端口用于浏览器访问,方便非技术同事直接使用。API 端口用于程序化调用,对接自动化脚本或者其他系统。两个端口分开的好处是可以独立控制访问策略,比如 Web 界面只对办公网段开放,API 端口只对特定服务器开放。
防火墙规则也要提前确认。宿主机的防火墙如果默认拒绝入站连接,需要放行这两个端口。我遇到过部署完容器跑得好好的,但局域网其他机器连不上的情况,排查半天发现是防火墙没放行。另外如果局域网内有网段划分,确认服务所在的机器和访问端在同一个可互通网段。
4. 容器化部署的完整实操流程
4.1 镜像拉取与模型准备
第一步是把推理框架的容器镜像拉到本地。镜像来源选择官方维护的版本,不要用来路不明的第三方镜像,避免里面夹带奇怪的东西。拉取命令执行后等待完成,镜像体积通常不小,网络条件好的话几分钟,差的话可能要更久。
docker pull <推理框架官方镜像名>:<版本标签>拉取完成后用镜像列表命令确认镜像存在。接着准备模型权重。权重文件从模型发布页面下载,注意选择与推理框架兼容的格式。下载完成后放到前面规划的模型目录下,目录结构保持模型发布时的原始布局,不要自己重命名文件夹,框架通常按固定路径查找。
权重文件的完整性要校验。下载过程中断导致文件损坏的情况不少见,校验方式通常是比对文件哈希值。如果框架启动时报模型加载失败,第一件事就是检查权重文件是否完整。
4.2 配置文件编写与参数说明
框架的配置文件决定了模型怎么加载、服务怎么暴露、工具怎么注册。我用的配置文件核心部分大概长这样:
model: path: /models/deepseek quantization: 4bit max_context: 8192 gpu_layers: 99 server: host: 0.0.0.0 port: 8000 api_key: <自定义访问密钥> agent: system_prompt: "你是一个内部助手..." tools_dir: /tools max_turns: 20 logging: level: info path: /logs逐项说明一下。模型路径指向容器内的挂载点,不是宿主机的路径,这点容易搞混。量化方式根据显存情况选,显存够就 8bit,紧张就 4bit。最大上下文长度影响显存占用和推理速度,8192 是个比较平衡的值,再往上显存消耗增长很快。GPU 层数表示有多少层模型跑在显卡上,设成 99 基本就是全部放显卡,如果显存不够可以调低这个值让部分层跑在 CPU 上,但速度会明显下降。
服务监听地址设成 0.0.0.0 表示接受所有网络接口的连接,这样局域网其他设备才能访问。访问密钥建议设置一个,虽然在内网但基本的访问控制还是要有的,防止误操作或者内部扫描。Agent 部分的系统提示词决定了 Agent 的基础行为风格,工具目录指向挂载进来的工具脚本位置,最大轮次限制防止 Agent 陷入无限循环调用。
4.3 容器启动命令与参数详解
配置文件准备好之后,用容器运行命令启动服务。完整的启动命令包含端口映射、目录挂载、显卡透传、环境变量等部分:
docker run -d \ --name ai-agent \ --gpus all \ -p 8000:8000 \ -p 8080:8080 \ -v /opt/ai-agent/models:/models \ -v /opt/ai-agent/config:/config \ -v /opt/ai-agent/data:/data \ -v /opt/ai-agent/logs:/logs \ -v /opt/ai-agent/tools:/tools \ -e CONFIG_PATH=/config/agent.yaml \ --restart unless-stopped \ <推理框架官方镜像名>:<版本标签>参数逐个解释。后台运行模式让容器在终端关闭后继续跑。容器命名方便后续管理。显卡透传参数让容器能访问宿主机的显卡,没有这个参数容器里看不到显卡。端口映射把容器内端口对应到宿主机端口,前面是宿主机端口后面是容器内端口。挂载卷把宿主机的目录映射到容器内路径,冒号左边是宿主机右边是容器内。环境变量传递配置文件路径。重启策略设为除非手动停止否则自动重启,这样宿主机重启后服务能自动恢复。
启动之后用容器状态命令确认运行正常。如果状态是退出,用日志命令查看报错信息。常见问题包括显卡透传失败、挂载目录权限不足、配置文件格式错误、模型路径不对等。
4.4 服务验证与局域网访问测试
容器跑起来之后,先在宿主机本地验证服务是否正常响应。用接口测试命令向本地端口发一个简单请求,看是否返回预期结果。如果本地通,再换局域网内另一台设备访问宿主机 IP 加端口,确认网络层面没问题。
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <访问密钥>" \ -d '{"model":"deepseek","messages":[{"role":"user","content":"你好"}]}'返回结果里应该包含模型生成的回复内容。如果返回错误,根据错误码排查:401 是密钥不对,404 是路径不对,500 是服务内部错误需要看日志。局域网访问测试如果本地通但远程不通,检查防火墙规则和宿主机网络配置。
Web 界面端口用浏览器访问,应该能看到对话界面。第一次加载可能稍慢,因为前端资源要初始化。界面里输入问题测试对话功能,再测试一下工具调用是否正常——比如让 Agent 执行一个查询当前时间的工具,看它是否能正确返回。
5. 自定义工具接入与 Agent 能力扩展
5.1 工具脚本的编写规范
Agent 框架的工具机制是这套平台最有价值的部分。工具本质上就是一个函数,框架把函数的名称、描述、参数定义告诉模型,模型在需要的时候输出调用请求,框架执行函数并把结果返回给模型。整个过程对模型来说就是“我调用了某个工具并拿到了结果”,对框架来说就是“解析请求、执行函数、拼装响应”。
工具脚本的编写遵循框架定义的接口规范。一个典型的工具定义包含名称、描述、参数列表和执行逻辑。名称要简短明确,描述要写清楚这个工具做什么、什么时候用,因为模型是根据描述来判断是否调用这个工具的。参数定义要标明类型和是否必填,模型会按照定义生成参数。
def get_current_time(timezone: str = "local") -> str: """获取当前时间,可指定时区""" from datetime import datetime import pytz if timezone == "local": return datetime.now().strftime("%Y-%m-%d %H:%M:%S") tz = pytz.timezone(timezone) return datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") TOOL_DEFINITION = { "name": "get_current_time", "description": "获取当前时间,当用户询问时间相关问题时使用", "parameters": { "type": "object", "properties": { "timezone": { "type": "string", "description": "时区名称,默认为本地时间" } } } }工具脚本放到前面规划的 tools 目录下,框架启动时会扫描加载。修改工具后需要重启容器或者触发框架的热加载机制才能生效。
5.2 实用工具场景举例
在局域网 Agent 平台里,我实际接入过几类工具,效果都不错。第一类是信息查询类,比如查询内部文档库、查询数据库中的业务数据、查询系统状态。这类工具的价值在于把 Agent 和内部数据源打通,用户不用自己去翻文档或者写查询语句,直接问 Agent 就行。
第二类是操作执行类,比如创建工单、发送通知、触发构建任务。这类工具需要谨慎设计,因为 Agent 可能会误触发操作。我的做法是给这类工具加上确认机制,Agent 调用后先返回一个待确认状态,由用户在界面上确认后才真正执行。
第三类是计算处理类,比如格式转换、数据统计、文本处理。这类工具适合把一些常用的脚本封装起来,让 Agent 按需调用,避免每次都要手动跑脚本。
工具描述的质量直接影响 Agent 的调用准确率。描述写得太模糊,模型不知道什么时候该用;写得太宽泛,模型可能在不该用的时候调用。我的经验是描述里明确写出“当用户需要 XXX 时使用这个工具”,并且给出参数的具体含义和格式要求。
5.3 工具调用的调试方法
工具接入后需要调试,确认模型能正确识别调用时机并生成正确的参数。调试方法是在对话中构造需要调用工具的场景,观察返回结果里是否包含工具调用记录。如果模型没有调用工具,可能是描述不够清晰或者系统提示词里没有强调工具的使用。如果调用了但参数不对,检查参数定义是否明确。
框架的日志里会记录工具调用的详细信息,包括模型输出的原始请求、解析后的参数、执行结果、返回给模型的内容。排查问题时看日志最直接。我遇到过模型把参数名拼错的情况,原因是参数描述里用了容易混淆的命名,改成更明确的名称后就正常了。
注意:工具执行时间不宜过长。如果某个工具需要几十秒才能返回,模型在等待期间会一直占用会话,用户体验很差。耗时操作建议改成异步模式,工具先返回一个任务 ID,用户后续再查询结果。
6. 常见问题排查与性能调优经验
6.1 启动阶段高频问题速查
部署过程中遇到的问题大部分集中在启动阶段,整理成速查表方便对照:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 容器启动后立即退出 | 配置文件格式错误 | 查看容器日志,检查 YAML 缩进 |
| 日志报显卡不可用 | 显卡透传参数缺失 | 确认启动命令包含显卡参数 |
| 模型加载失败 | 权重文件不完整或路径错误 | 校验文件哈希,确认挂载路径 |
| 端口被占用 | 宿主机端口冲突 | 换端口或停止占用端口的进程 |
| 局域网无法访问 | 防火墙未放行 | 检查宿主机防火墙规则 |
| 权限拒绝 | 挂载目录属主不对 | 调整目录权限或指定运行用户 |
这张表里的问题我基本都遇到过。印象最深的一次是容器启动后日志显示模型加载成功,但服务就是起不来,最后发现是配置文件里端口写成了已经被占用的端口,容器内服务绑定失败但日志级别不够没打出来。后来我把日志级别调到 debug 才看到具体报错。
6.2 运行阶段性能问题与调优
服务跑起来之后,性能问题主要体现在响应速度和并发能力上。响应速度慢的原因可能是模型规格太大、量化方式不够激进、上下文太长、显卡层数设置不合理。调优方向是先用小规格模型验证流程,再逐步换大模型;上下文长度按需设置,不要盲目开大;显卡层数根据显存占用调整,找到速度和显存的平衡点。
并发能力受限于显存和推理框架的调度策略。多个请求同时进来时,框架会把它们排队处理,如果显存不够就会触发等待。提升并发的手段包括降低单请求的显存占用(缩短上下文、降低量化精度)、增加显卡数量、使用支持连续批处理的推理框架。局域网内小团队使用的话,单卡加合理配置基本够用。
还有一个容易被忽略的点是会话数据的清理。Agent 平台会保存每个会话的历史记录,时间长了数据目录会越来越大,加载会话时也会变慢。我设置了一个定期清理策略,超过一定天数的会话自动归档或删除,保持运行轻快。
6.3 安全与访问控制的实际做法
虽然是局域网内部使用,基本的访问控制还是要做。第一层是网络层,通过防火墙规则限制只有特定网段能访问服务端口。第二层是应用层,API 请求需要携带访问密钥,Web 界面登录需要账号密码。第三层是工具层,敏感工具的执行需要二次确认或者限制调用来源。
访问密钥的管理建议定期更换,不要用默认密钥。如果团队人数不多,可以给每个人分配不同的密钥,方便追踪调用来源。Web 界面的账号体系如果框架自带就用自带的,没有的话可以在前面加一层反向代理做认证。
工具的安全边界要划清楚。文件操作类工具限制在特定目录内,命令执行类工具限制可执行的命令白名单,网络请求类工具限制可访问的地址范围。这些限制在工具脚本里实现,不要依赖模型的自觉。
7. 日常维护与后续扩展方向
7.1 日常维护清单
平台跑起来之后,日常维护主要是几件事。定期检查容器状态和资源占用,确认服务没有异常重启或者资源泄漏。定期查看日志,关注错误日志和慢请求日志,提前发现潜在问题。定期备份配置文件和会话数据,虽然容器化部署本身可复现,但配置和数据的备份能省去重建的麻烦。模型有更新时,评估新版本的收益和迁移成本,决定是否升级。
升级流程建议先在测试环境验证,确认新版本兼容现有配置和工具,再切换到生产环境。切换时保留旧版本容器,出问题可以快速回滚。模型权重更新时注意磁盘空间,新旧权重同时存在可能占满磁盘。
7.2 能力扩展的几种思路
这套平台搭好之后,扩展方向其实很多。一个方向是接入更多模型,让 Agent 根据任务类型自动选择模型,简单任务用轻量模型快速响应,复杂任务用大模型深度推理。另一个方向是丰富工具生态,把团队日常用的内部系统都封装成工具,让 Agent 成为统一的操作入口。
还可以做多 Agent 协作,把不同职责的 Agent 组合起来,一个负责理解需求,一个负责执行任务,一个负责检查结果。这种模式在复杂任务上效果更好,但调度逻辑也更复杂,需要框架支持 Agent 之间的通信和任务传递。
对外接口方面,可以把 Agent 平台的能力封装成标准的 API,对接团队现有的聊天工具、工单系统、自动化流程。这样用户不需要专门打开一个界面,在平时用的工具里就能调用 Agent 能力。
7.3 我踩过的几个坑和对应解法
最后分享几个实际踩过的坑。第一个是模型权重下载不完整导致加载失败,排查了很久才想到校验哈希。后来我养成了下载完先校验的习惯,省了很多事。第二个是容器内时区不对,Agent 返回的时间总是差几个小时,解决办法是在启动命令里加时区环境变量。第三个是工具脚本里的依赖库容器内没有,工具调用时报模块找不到,解决办法是把依赖写进工具目录的依赖文件里,或者构建自定义镜像时预装。
还有一个坑是会话上下文过长导致推理变慢。Agent 在多轮对话中会累积历史消息,如果不加控制,上下文会越来越长,显存占用和响应时间都跟着涨。我的做法是设置上下文长度上限,超过后自动截断最早的消息,同时在系统提示词里告诉模型“较早的对话内容可能已被截断”。这样既控制了资源占用,又让模型知道上下文可能不完整。
这套局域网 AI Agent 平台从搭建到稳定运行,前后花了大概两天时间,其中大部分时间用在环境排查和工具调试上。一旦跑通,后续的维护成本很低,团队成员的使用反馈也很好。如果你也在考虑给团队搭一个内部 AI 入口,这套方案值得一试。