上个月我在 Ubuntu 服务器上折腾完一件事:把开源 Agent 框架 OpenClaw 跑起来,推理后端接到本地 Ollama,再通过飞书机器人跟它对话。目的很简单——团队已经习惯用飞书协作,我不希望每次想问点事情还要切到终端敲命令行,更不想把内部资料交给云服务商。折腾完回头看,整条链路的难度其实不高,但涉及环境、模型、通道三块,每一块都有几个容易忽略的坑。这篇文章就把我的完整部署过程、配置参数和踩坑记录整理出来,给同样想在 Ubuntu 本地部署 OpenClaw、接入 Ollama 和飞书的人一份可以直接照着做的参考。
1. 这套组合解决什么问题:OpenClaw、Ollama 与飞书的各自角色
1.1 OpenClaw 到底是个什么东西
OpenClaw 是一个开源的 Agent 运行时框架,核心思路是"模型无关 + 通道插件化"。模型无关,意味着它不绑定某一家推理服务,既可以接云端 Claude API,也可以接本地部署的 Ollama、vLLM 这类推理服务;通道插件化,意味着它和人对话的入口可以灵活配置,飞书、Teams、终端、Web 控制台都可以作为交互渠道。
我当时选择它的理由主要是两点:第一,配置是文件化的,整个 Agent 的行为、模型、通道都能用配置文件管理,方便版本控制;第二,它本身提供了会话管理、工具调用、上下文维护这些 Agent 基础设施,不需要我从零写一套状态机。如果你之前在纠结 OpenClaw 和 WorkBuddy 这类产品怎么选,我的观点是:如果你要的是可控部署、自定义通道、接本地模型,OpenClaw 这类开源运行时更合适;如果你要的是开箱即用、图形化管理,那商业产品体验会好很多。
1.2 为什么推理后端要选 Ollama
Ollama 是目前本地部署大模型最省事的引擎之一,一条ollama pull就能把模型权重拉下来,ollama serve就把推理服务跑起来了。它帮我解决了几个实际问题:模型文件放在本地磁盘,对话数据不出服务器,适合处理内部文档、敏感数据;推理成本按电费和硬件折旧算,长期跑比按 token 付费便宜;第三,Ollama 自带 OpenAI 兼容的 API 接口,OpenClaw 接它只需要配一个 base_url,不需要写专门的适配层。
当然本地推理也有代价,模型能力上限取决于服务器显存和内存。我的实践是:16GB 显存的卡可以稳定跑 14B 量级的量化模型,日常问答、文档总结够用;如果只有 CPU,建议选 7B/8B 或者更小的量化版本,并接受响应速度下降的现实。
1.3 飞书通道的价值和外挂方式
飞书作为通道,本质上解决的是"人如何方便地跟 Agent 对话"的问题。终端里跑 Agent 适合开发者,但团队里的人不可能都配好命令行环境。飞书机器人接入后,私聊、群聊里 @ 一下就能触发 Agent,对话记录也顺带留在飞书里,团队协作链路是完整的。
接入方式上,飞书开放平台提供机器人能力和事件订阅机制。OpenClaw 通过飞书 API 接收消息、调用会话逻辑,再把结果通过机器人身份发回聊天窗口。消息的接收方式有两种:一种是用 HTTPS 回调地址,需要公网可达;另一种是飞书支持的长连接模式,OpenClaw 主动建立一个 WebSocket 连接去收事件,这样本地服务器也能用,不需要额外申请公网域名。
2. Ubuntu 环境准备:系统版本、运行时安装与最容易翻车的三个细节
2.1 系统版本和基础软件
我这次部署用的是 Ubuntu 22.04 LTS,内核和软件源都比较成熟。如果你还在用 20.04,问题也不大,但要注意 OpenClaw 如果是最新版本,可能依赖较新的 glibc 和 Node 运行时,越老的系统越容易出现"装不上、启动报错"这类问题。建议直接用 22.04 或 24.04 LTS,省去后续折腾。
开始之前,先确认下面几项:
| 依赖项 | 版本要求 | 检查命令 |
|---|---|---|
| Ubuntu 系统 | 22.04 LTS 或更新 | lsb_release -a |
| Node.js | 18.x 或以上 | node -v |
| Python | 3.10 或以上 | python3 --version |
| Git | 任意较新版本 | git --version |
| 磁盘空间 | 建议剩余 30GB 以上 | df -h |
如果系统里没有 Node.js,我推荐用 nvm 安装,避免直接改系统级目录,后面升级也方便:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 18 nvm use 18这里有个实际经验:安装完 nvm 之后,一定要source ~/.bashrc或者重新打开终端,否则node命令找不到。第一次踩这个坑时我还以为是安装没成功,实际上只是环境变量没刷新。
2.2 磁盘规划:模型比你想象中更占空间
Ollama 拉下来的模型动辄 4GB 到 10GB,如果同时跑好几个模型,磁盘占用会轻松超过 50GB。很多人的服务器根分区只分了 30GB,装完系统和依赖就剩不到一半,再拉模型必然爆盘。
我的做法是单独给模型数据挂一块数据盘,然后把 Ollama 的模型目录指到数据盘上。具体操作是修改 Ollama 服务的环境变量,编辑服务文件或 override 配置:
sudo systemctl edit ollama在打开的编辑器中填入:
[Service] Environment="OLLAMA_MODELS=/data/ollama/models"保存后重载服务:
sudo systemctl daemon-reload sudo systemctl restart ollama这样模型文件全部落到独立数据盘,系统盘只需要装软件本身,压力小很多。这个细节在官方文档里不算醒目,但实际部署中非常关键。
2.3 安装 OpenClaw 的两种途径
OpenClaw 的安装方式,我当时看到的是源码安装和预编译包两种主流方案。源码安装的好处是跟随最新代码,能第一时间体验到新功能;预编译包的好处是稳定,不需要本地编译工具链。
如果你走源码安装,先把代码 clone 下来:
git clone https://github.com/openclaw/openclaw.git cd openclaw npm install npm run build然后把命令行入口放到全局路径,或者干脆做成一个 systemd 服务。注意,源码安装过程对网络依赖比较大,npm install如果中途失败,可以多试几次,大部分情况是网络抖动导致的。预编译包方式更简单,把压缩包解压后,将可执行文件软链到/usr/local/bin/openclaw即可。安装完成后,务必跑一下openclaw version,确认命令可以用,同时也能快速发现 glibc 版本兼容问题。
2.4 环境变量和 PATH 的坑
Ubuntu 环境变量配置错误导致的"命令找不到"问题,我见得太多了。如果你把 OpenClaw 装在自定义目录,记得把它的路径加进~/.bashrc:
export PATH="$HOME/openclaw/bin:$PATH" export OPENCLAW_HOME="$HOME/.openclaw"OPENCLAW_HOME这个变量可以控制 OpenClaw 的配置和数据目录默认位置。默认情况下它会把数据放在用户家目录下,如果你用 root 用户跑,就放在/root/.openclaw,用普通用户跑就在/home/xxx/.openclaw。我建议显式指定,这样多用户共用一台服务器时不会出现数据目录混乱的问题。
配置完之后执行source ~/.bashrc,再打开一个终端窗口验证。不要在一个终端里反复 source,这样容易掩盖环境变量没有写入配置文件的问题。
3. Ollama 推理链路:安装服务、拉取模型、验证接口、接入 OpenClaw
3.1 安装 Ollama 并确认服务状态
Ollama 的安装本身就一条命令:
curl -fsSL https://ollama.com/install.sh | sh安装完成后,服务会以 systemd 方式托管,手动启动和管理可以用:
sudo systemctl start ollama sudo systemctl enable ollama sudo systemctl status ollamaOllama 默认监听本机的 11434 端口,如果你之后有远程调用需求,可以设置OLLAMA_HOST=0.0.0.0:11434,但要先想好访问控制策略。我这边因为 OpenClaw 和 Ollama 都在同一台机器上,保持 127.0.0.1 监听就足够了,减少了暴露风险。
3.2 模型选择和拉取
模型选择是整个链路里最影响体验的一环。我的经验是,先想清楚用途再选模型,而不是一上来就拉最大的参数版本。做代码辅助、结构化输出,7B/8B 模型就能应付;做长文总结、深度分析、多轮对话,14B 或更大的模型明显更稳。参数越大,显存占用和响应延迟都在涨,飞书聊天的体验和终端里完全不同——超过 10 秒没响应,人就会开始怀疑机器人是不是挂了。
我当时先拉了一个 qwen2.5 的 7B 版本验证链路:
ollama pull qwen2.5:7b模型拉到本地后,查看当前有哪些模型:
ollama list拉取过程中如果网速不稳定,Ollama 本身支持断点续传,进度中断后重新执行ollama pull会继续下载。这一步在本地化部署场景里很实用,不用因为一次网络中断就重新开始。
3.3 手动验证推理接口
在把模型接入 OpenClaw 之前,最好先手动验证一次推理接口,避免后面报错时分不清是模型问题还是 Agent 的问题。我一般用 curl 直接请求 Ollama 的 API:
curl http://127.0.0.1:11434/api/chat \ -d '{"model": "qwen2.5:7b", "messages": [{"role": "user", "content": "用一句话介绍你自己"}], "stream": false}'顺利的话,返回 JSON 里会出现"response"字段和模型生成的文本。这一步通过后再去配置 OpenClaw,就能确认推理链路是通的。如果 curl 卡住很久不响应,去看/var/log/ollama.log或者journalctl -u ollama -f,常见的坑是显存不足导致 OOM,或者模型还在首次加载中。
3.4 OpenClaw 侧配置模型 provider
OpenClaw 的模型配置,我是放在~/.openclaw/config.yaml里的,核心思路是把 provider 指到 Ollama 的地址。配置内容大致是这样(具体字段名以你下载版本的文档为准):
agent: default_model: qwen2.5:7b model_provider: type: ollama base_url: http://127.0.0.1:11434 context_window: 8192 temperature: 0.7context_window这个参数值得多说一句。Ollama 默认的上下文长度由模型决定,但 OpenClaw 在组织对话、调用工具、注入系统提示词时,会把整个请求拼到一起,如果上下文窗口设得太小,长对话会报 token 超限。如果你发现"聊着聊着突然不响应了",先查是不是上下文窗口不够,不要急着怀疑模型本身。
还有一点,OpenClaw 会在每次请求时带上系统提示词和工具定义,这部分的 token 也要算进上下文里。所以配置窗口时,给模型默认能力留出余量,比如模型本身支持 32K,但 OpenClaw 里设 24K 或 16K,稳定性和首响应速度都会更好。
4. 飞书通道打通:从开放平台创建应用到长连接接收事件
4.1 创建飞书应用并开启机器人能力
飞书这边的准备工作,需要在飞书开放平台后台完成。登录后进入"开发者后台",创建一个企业自建应用,名字随便起,比如"本地助手"。创建完应用后,在"应用能力"里添加"机器人"能力,这一步会让这个应用获得一个机器人身份,之后所有消息都以这个机器人名义收发。
创建应用这一步本身不难,但很关键的是下一步的权限配置。飞书的权限模型是细粒度的,机器人要能收消息、发消息,必须显式申请对应权限点。我当时申请的是:
im:message—— 读取用户发给机器人的消息im:message:send_as_bot—— 以机器人身份发送消息
权限申请后,需要发布应用版本,管理员审核通过后权限才生效。如果你是在自己的团队内部用,审核一般很快。这里有个容易漏的点:权限版本发布之后,应用要重新上线,否则测试时一直提示无权限。
4.2 获取密钥和配置参数
在开放平台后台的"凭证与基础信息"页面,可以拿到两个关键参数:App ID 和 App Secret。这两个参数就是 OpenClaw 连飞书时要用的身份凭证。把它们复制下来,后续配置里会用到。注意 App Secret 类似密码,别直接提交到公开仓库,建议用环境变量或配置文件权限控制来管理。
另外要确认机器人的名称和头像,因为飞书的消息流里会展示这些信息。我当时把机器人名称改成和内部项目一致,头像也换成了团队 LOGO,这样在群聊里不会和别的 bot 混淆。
4.3 事件订阅:用长连接模式绕开公网回调
飞书接收消息,核心在事件订阅。后台有两种模式可选:一种是"回调地址模式",需要提供一个公网可访问的 HTTPS 地址,飞书服务器会把事件 POST 到那个地址;另一种是"长连接模式",应用主动建立 WebSocket 连接,飞书推送事件过来。
我必须得说,长连接模式对本地部署来说太友好了。不需要公网 IP,不需要配域名证书,不用在 Nginx 里写转发规则,OpenClaw 启动后往飞书开放平台一连,消息就收得到。我当时选的就是这个模式,在事件订阅页面选择"使用长连接接收事件",然后在开放平台后台添加事件:
im.message.receive_v1—— 接收消息事件
添加事件后,页面会提示订阅成功。很多教程默认让你准备公网回调,你要是和我一样在本地或内网服务器上跑,直接选长连接,能省下一整个下午的时间。
4.4 OpenClaw 里的飞书通道配置
回到 OpenClaw,飞书通道的配置文件可以这么写(字段名以你实际版本为准):
channels: feishu: app_id: "cli_xxxxxxxxxxxx" app_secret: "xxxxxxxxxxxxxxxxxxxxxxxx" event_mode: websocket配置里有几个容易踩的点:
app_id和app_secret可以通过环境变量注入,OpenClaw 通常会支持OPENCLAW_FEISHU_APP_ID这种形式的环境变量,比直接写死在配置文件里更安全。- 事件订阅如果配置的是长连接模式,OpenClaw 启动时会自动建立连接,不需要额外设置回调路由。
- 如果飞书后台配置的是回调模式,OpenClaw 这边就要配置回调路径和端口,还要保证网络可达。这属于另一种部署方式,复杂度明显高于长连接。
配置完成后重启 OpenClaw,日志里如果出现"feishu channel connected"之类的信息,基本就是连上了。然后你在飞书里私聊机器人,发一句"你好",机器人应该会在几秒内回你。
5. 实测中的典型报错:agent failed before reply: session file locked 的完整排查链路
5.1 报错现场
链路全部打通后的第二天,群里有人反馈:机器人有时候不回消息,OpenClaw 的日志里出现一行刺眼的报错:
agent failed before reply: session file locked (timeout 60000ms)这个报错表面意思是:会话文件被锁住了,等了 60 秒还没拿到锁,Agent 直接在响应前就失败了。当时我的第一反应是"锁冲突",但到底是谁在持锁、为什么持有这么久,需要一层层往下查。
5.2 理解 session 和锁的机制
OpenClaw 的会话管理,实际操作中会为每个会话维护一个本地状态文件,里面保存对话历史、上下文状态、任务进度。为了保证同一个会话不会被多个进程同时写坏,它会用文件锁来控制访问——这个机制本身是合理的,问题在于锁的粒度和持锁时间。
"timeout 60000ms"说明 OpenClaw 给拿锁设置了一个 60 秒的上限。如果上一个请求持有锁超过 60 秒没释放,后面所有针对同一会话的请求都会直接失败。也就是说,报错不一定是因为锁代码有 bug,很可能是持锁的那一侧跑得太慢或卡死了。
5.3 逐步排查:进程、锁文件、推理耗时
我先看了进程状态:
ps -ef | grep openclaw确认只有一个 OpenClaw 主进程在跑,排除多进程冲突的问题。然后又用lsof查了会话目录下的锁文件被谁占用:
lsof ~/.openclaw/sessions/结果发现持锁进程确实是 OpenClaw 自己,说明是同一个进程在处理并发请求时,前一个任务还没结束,后一个任务就来抢同一会话的锁。这时候再翻对应时间段的日志,看到前一个请求在等 Ollama 返回,而 Ollama 那边模型加载和推理耗时超过了 60 秒,于是锁就一直在那个请求手里,后续请求全部排队超时。
换句话说,根因是:一跳本地推理太慢,锁等待超时设得太短,二跳多个请求撞到了同一个会话。
5.4 修复方案与验证
定位之后,我做了三件事:
第一,把 OpenClaw 的锁超时时间调大。不同版本的配置字段不一样,我的做法是在配置文件里找到类似session_lock_timeout的字段,从 60000 毫秒调到 180000 毫秒。这一步是为了让慢推理也能在锁等待窗口内完成。
第二,给 Ollama 侧减压。确认推理慢是因为模型被频繁冷启动,于是把 Ollama 的 keep_alive 参数调长,让模型常驻显存,避免每次请求都重新加载:
curl http://127.0.0.1:11434/api/generate \ -d '{"model": "qwen2.5:7b", "keep_alive": "30m"}'第三,也是最重要的,在飞书侧和 OpenClaw 侧尽量避免同会话并发。飞书群聊里如果多人同时 @ 机器人,OpenClaw 可能会把多个消息路由到同一个会话,自然触发锁竞争。我的方案是在群聊配置里让不同用户走不同会话,或者干脆减少群内高频调用,重要操作移到私聊里执行。
修改配置后,我复测了三种场景:连续私聊三条消息、群聊里两个人同时 @、以及一个长任务进行中再发一条新消息。前两种都稳定通过了,第三种仍有概率出现排队等待,但已经不会直接报错失败,而是在前一个任务完成后正常处理。这个结果说明锁机制本身没坏,是业务场景中并发和耗时的匹配问题。
5.5 同类报错举一反三
session file locked只是"会话文件锁"这一类问题的表象。和它同族的还有 "session file not found"、"session directory permission denied"、"session file is occupied" 等等。遇到这类问题,我习惯按下面的顺序排查:
- 先确认进程是否异常残留,
ps和lsof能解决大部分持有锁的疑问。 - 再看锁文件的最后修改时间和持有时间,如果是几个小时前的残留锁,且没有活跃进程,可以删除锁文件,但要谨慎操作。
- 然后检查推理耗时,如果模型加载要 30 秒,同时锁超时只有 10 秒,调参是必然的。
- 最后回到业务侧,是不是入口处允许多个请求打到了同一个会话 ID。
这套方法论不仅适用于 OpenClaw,任何有会话管理的 Agent 框架都通用。
6. 直接复现的完整操作清单与两个值得尝试的扩展
6.1 复现操作顺序一览
为了方便你照着操作,我把整条链路的核心顺序整理成一个清单:
- 准备 Ubuntu 22.04 或更新的系统,安装 Node.js 18+ 和 Python 3.10+。
- 规划磁盘,把 Ollama 模型目录指向数据盘。
- 安装 OpenClaw,确认
openclaw version正常。 - 安装 Ollama,
ollama pull拉取目标模型。 - 用 curl 手动验证
/api/chat接口。 - 在 OpenClaw 配置文件中接入 Ollama 作为 model provider。
- 在飞书开放平台创建应用,开启机器人能力,申请权限。
- 配置事件订阅,选择长连接模式,添加消息接收事件。
- 在 OpenClaw 配置飞书通道的 app_id、app_secret。
- 启动 OpenClaw,在飞书里私聊机器人,验证全链路。
这个顺序是我多次部署后觉得最顺的一条路径,每一步都验证通过再进下一步,出问题时定位范围会小很多。
6.2 扩展方向:飞书多维表格回写和多模型路由
链路跑通之后,你可以继续往两个方向扩展。第一个是飞书多维表格回写。OpenClaw 这类 Agent 框架通常支持工具调用,你可以把多维表格的 API 封装成一个工具,让 Agent 把总结、归档、任务跟踪结果直接写入飞书多维表格。比如我后来做了一个简单的日志归档功能:Agent 每完成一次长任务,就会在表格里新增一条记录,包含任务内容、耗时、结果摘要和触发人。这个功能团队反馈非常好用,等于 Agent 从"能聊"升级到了"能干活"。
第二个值得尝试的是多模型路由。Ollama 可以同时管理多个模型,不同任务分给不同模型的策略在 Agent 场景下很实用。简单问答走 7B 小模型省资源,复杂代码或长文总结走 14B 大模型保质量。OpenClaw 如果支持按规则切换模型,就把这个配置做成路由表,飞书消息里的关键词可以触发模型切换,比如消息里出现"总结""分析"就走大模型。这个思路的收益很大,但也会带来一点延迟增加,适合对响应速度要求不极致、但对质量有要求的场景。
6.3 一点更现实的优化建议
最后说一个我在实际使用中的体会:这套部署完成后,最影响体验的不是模型能力,而是模型常驻和并发控制。Ollama 的 keep_alive 一定要调,否则每隔一段时间没人对话,模型就被释放,下一个人触发请求时又要等几十秒加载。OpenClaw 侧的并发限制和锁超时也要提前规划好,宁可排队也别直接失败,飞书用户是不会去看你日志的,他们只会觉得"这个机器人又坏了"。
如果你打算长期跑,建议把 OpenClaw 和 Ollama 都注册成 systemd 服务,设置开机自启和崩溃自动重启。日志用journalctl -u openclaw -u ollama -f统一查看,排查问题时能少走很多弯路。整个项目做到这个程度,就已经具备了在团队内稳定使用的基础。剩下的优化空间,就看你想让它聊得更聪明,还是干更多的活了。