1. 从“渴了才喝水”到“定时提醒”一个程序员的健康自救不知道你有没有过这样的体验早上九点坐到工位前打开IDE开始沉浸式coding。等回过神来一看时间已经下午两点了。不仅午饭忘了吃更可怕的是你发现自己从坐下到现在一口水都没喝。喉咙干得像撒哈拉沙漠身体已经在发出警报但你的大脑还沉浸在解决那个该死的Bug的兴奋感里。这种状态我们程序员太熟悉了。长期如此什么肾结石、尿酸高、皮肤干燥各种毛病都找上门来。我以前也这样直到有一次体检医生看着我的报告直摇头说你这饮水量严重不足。我才意识到靠“渴了再喝”这种原始生理信号在高度专注的编程状态下是完全失效的。我们需要的是外部干预一个温和但坚定的“打断”。市面上有很多喝水提醒软件功能花里胡哨有的甚至要付费订阅。但作为一个程序员我的第一反应是这东西我自己能不能搓一个要足够轻量、完全可控、不打扰核心工作流还能有点趣味性。这就是“OpenClaw”进入我视野的原因。最近围绕OpenClaw的讨论非常火热从安装部署到接入飞书、微信玩法很多。但在我看来它最吸引我的核心价值是一个高度可编程、可通过自然语言驱动的自动化智能体框架。这意味着我不需要去写复杂的GUI或者研究系统通知API我只需要用人类语言告诉它“每隔一小时提醒我站起来活动一下喝口水”它就能自己去理解、规划并执行这个任务。这简直就是为我们这种“懒”但又追求极致的程序员量身定做的。所以我决定用OpenClaw花一分钟时间手搓一个专属于我的、AI驱动的喝水提醒助手。这个项目没有复杂的业务逻辑没有庞大的代码库它的全部意义就在于用最小的代价解决一个真实、高频的痛点并享受“自己动手丰衣足食”的乐趣。下面我就带你完整走一遍这个“一分钟手搓”的过程并分享其中我踩过的坑和总结的经验。2. OpenClaw极速部署绕过那些复杂的配置陷阱在开始我们的“喝水提醒器”之前得先让OpenClaw跑起来。网络上关于OpenClaw安装的教程很多但信息也相当零碎和矛盾特别是对于只是想快速体验一下的新手。有人推荐Docker有人推荐源码还有各种关于ollama_base_url、default_model的配置错误。我的目标是极简、快速、一次成功所以我会带你走一条最稳妥的路径。2.1 环境选择与准备为什么是Docker部署OpenClaw主要有三种方式1. 本地Python环境直接安装2. 使用Docker Compose3. 结合Ollama部署本地大模型。对于我们的“一分钟手搓”目标我强烈推荐Docker Compose方案。原因如下环境隔离避免污染OpenClaw依赖的Python包可能和你本地开发环境冲突用Docker可以完美避开“装了这个那个坏了”的经典困境。一键启动复杂度低相比源码安装需要处理虚拟环境、依赖版本、系统服务等问题Docker Compose通常只需要一个配置文件和一个启动命令。网络配置清晰Docker容器内的网络和端口映射是明确的排错时思路更清晰。所以请确保你的机器上已经安装了Docker和Docker Compose。如果没有去Docker官网下载安装这是基础此处不赘述。2.2 获取并调整docker-compose.yml关键参数解析OpenClaw官方或社区通常会提供一个docker-compose.yml模板。但直接使用很可能失败我们需要进行关键调整。以下是一个经过我实测可用的精简版本我将其保存为docker-compose.yml文件version: 3.8 services: openclaw: image: crestodian/openclaw:latest # 使用稳定的镜像标签而非latest有时更安全 container_name: openclaw restart: unless-stopped ports: - 3000:3000 # 将容器内的3000端口映射到宿主机的3000端口 environment: - OPENCLAW_LOG_LEVELINFO - OPENCLAW_DATABASE_URLsqlite:///./data/openclaw.db # 使用SQLite简单 - OPENCLAW_SERVER_HOST0.0.0.0 # 重要允许外部访问 volumes: - ./data:/app/data # 持久化数据避免容器重启后数据丢失 - ./skills:/app/skills # 挂载本地skills目录方便自定义技能 command: uvicorn openclaw.main:app --host 0.0.0.0 --port 3000 --reload关键点与避坑指南镜像选择crestodian/openclaw:latest是一个常见的社区镜像。如果latest拉取失败或启动有问题可以尝试指定一个具体的版本号如crestodian/openclaw:2.7.9。网络上搜索“openclaw 2.7.9免费版”提到的就是这个版本。端口映射“3000:3000”左边是宿主机端口右边是容器内端口。你可以把左边的3000改成任何你喜欢的、未被占用的端口如8080。OPENCLAW_SERVER_HOST这是最大的坑很多教程忽略了这一点。如果不设置为0.0.0.0容器内的服务只会监听本地回环地址你从宿主机浏览器通过localhost:3000或通过IP访问都会失败。务必检查** volumes卷挂载**挂载./data是为了持久化数据库和配置挂载./skills是我们后续自定义“喝水提醒”技能的关键它让你可以在宿主机上编辑文件直接影响容器内的技能库。** command**这里的命令直接启动了OpenClaw的FastAPI应用。--reload参数在开发时很有用但生产环境建议去掉。2.3 启动与验证看到界面才算成功在包含docker-compose.yml文件的目录下打开终端执行一条命令docker-compose up -d-d参数代表后台运行。如果一切顺利你会看到Docker拉取镜像并启动容器。接下来验证服务是否正常查看容器状态docker-compose ps。应该看到openclaw服务的状态是Up。查看日志docker-compose logs -f openclaw。观察启动日志有没有ERROR报错。常见的错误包括端口被占用换端口、环境变量配置错误、镜像拉取失败等。如果看到类似Application startup complete.的日志基本就成功了。访问Web界面打开浏览器访问http://localhost:3000如果你改了端口就换成对应的如http://localhost:8080。如果能看到OpenClaw的Web UI界面可能是一个简单的聊天窗口或管理面板恭喜你部署成功注意如果访问不到请按以下顺序排查① 确认容器状态是Up② 确认宿主机防火墙是否放行了对应端口③ 检查docker-compose logs是否有明确错误④ 确认OPENCLAW_SERVER_HOST环境变量已设置为0.0.0.0。至此OpenClaw的运行环境已经就绪。它现在就像一个空有大脑框架但没有技能具体能力的智能体。接下来我们要教给它第一个也是最重要的一个技能提醒我喝水。3. 手搓核心技能让OpenClaw学会“定时提醒”OpenClaw的能力扩展依赖于“Skill”技能。一个Skill本质上就是一个Python文件里面定义了这个技能能做什么、需要什么参数、以及具体的执行逻辑。我们的目标是创建一个“定时提醒”技能。网络上有很多复杂的Skill例子但对于喝水提醒我们要追求极致简单。3.1 创建技能文件结构比想象中简单首先在宿主机上进入与docker-compose.yml同级的目录如果你按照上面挂载了./skills。创建一个名为drink_reminder_skill的文件夹并在里面创建__init__.py和skill.py两个文件。结构如下你的项目目录/ ├── docker-compose.yml ├── data/ 自动生成 └── skills/ └── drink_reminder_skill/ ├── __init__.py └── skill.py__init__.py可以是空文件它的存在是为了让Python将这个目录识别为一个包。核心全部在skill.py中。3.2 编写skill.py详解每一个参数与装饰器打开skill.py开始编写代码。我将逐段解释让你不仅会抄更懂为什么这么写。import asyncio from datetime import datetime from typing import Any, Dict from openclaw.skills import skill, BaseSkill skill( namedrink_water_reminder, description设置一个周期性喝水提醒。, inputs{ interval_minutes: { type: integer, description: 提醒间隔时间分钟。, required: True, default: 60 }, reminder_message: { type: string, description: 提醒时显示的消息。, required: False, default: 时间到该站起来活动一下喝口水啦 } } ) class DrinkWaterReminderSkill(BaseSkill): 一个简单的周期性喝水提醒技能。 async def execute(self, inputs: Dict[str, Any]) - Dict[str, Any]: 执行技能启动一个后台异步任务每隔指定分钟发送一次提醒。 interval inputs[interval_minutes] message inputs.get(reminder_message, 时间到该喝水啦) # 获取技能上下文中的消息发送函数这通常是OpenClaw框架注入的 # 这里假设框架提供了一个 send_message 方法来发送提醒。 # 实际方法名可能需要根据OpenClaw版本调整例如可能是 notify_user 或 send_notification。 send_notification self.context.get(send_message) if not send_notification: return { success: False, message: 无法发送通知未找到消息发送接口。 } self.logger.info(f开始喝水提醒任务间隔 {interval} 分钟。) # 启动一个后台任务不阻塞当前执行 asyncio.create_task(self._reminder_loop(interval, message, send_notification)) return { success: True, message: f喝水提醒已启动每隔 {interval} 分钟提醒一次。 } async def _reminder_loop(self, interval_minutes: int, message: str, sender): 后台循环任务负责定时发送提醒。 while True: await asyncio.sleep(interval_minutes * 60) # 转换为秒 try: # 这里调用发送函数。在实际OpenClaw中这可能会触发桌面通知、发送聊天消息等。 # 例如await sender(textmessage) # 由于我们是在极简演示这里先打印日志并假设sender是一个可调用的异步函数。 self.logger.info(f发送提醒{message}) # 模拟发送动作。在实际集成中你需要根据OpenClaw的API替换这里。 if callable(sender): await sender(message) except Exception as e: self.logger.error(f发送提醒时出错{e})代码深度解析与避坑点skill装饰器这是OpenClaw框架识别一个技能的关键。它定义了技能的元数据。name: 技能的唯一标识符后续我们通过这个名字来调用技能。description: 对技能的自然语言描述这很重要因为OpenClaw的LLM大语言模型会通过这个描述来理解何时该调用这个技能。inputs: 定义技能需要的输入参数。这里我们定义了两个interval_minutes: 整数类型必须提供默认60分钟。这个设计很关键它让技能变得可配置。reminder_message: 字符串类型非必须有一个友好的默认值。这增加了技能的灵活性。BaseSkill类与execute方法所有技能都必须继承BaseSkill并实现execute方法。这个方法是技能的入口点当OpenClaw决定调用这个技能时就会运行它。inputs参数接收到的就是用户在调用时提供的参数会匹配skill装饰器中定义的inputs结构。异步asyncexecute方法是异步的。这是因为OpenClaw本身是异步框架为了不阻塞主线程特别是在执行定时、IO等操作时。核心逻辑_reminder_loop这是实现定时功能的关键。asyncio.create_task(): 我们将循环任务创建为一个独立的“后台任务”。这样execute方法就能立即返回响应“提醒已启动”而不会一直卡在那里等待循环结束。这是编写友好技能的重要技巧。asyncio.sleep(): 用于实现等待。注意这里乘以60将分钟转换为秒。sender函数这是最大的假设和需要适配的点代码中的sender是我假设的一个由OpenClaw框架提供的、用于发送通知的函数。在实际的OpenClaw版本中这个函数可能叫send_message、notify或者需要通过特定的API通道如WebSocket来发送。你需要根据你部署的OpenClaw版本的实际API来修改这部分。我们的初版目标只是让技能被成功加载和调用所以先用logger.info打印日志来验证循环是工作的。错误处理在循环中使用了try...except来捕获发送消息时可能出现的异常并用logger.error记录。这能防止因为一次发送失败导致整个提醒任务崩溃。3.3 加载技能让OpenClaw发现它创建好技能文件后我们需要让OpenClaw容器知道这个技能的存在。因为之前我们在docker-compose.yml中已经将本地的./skills目录挂载到了容器的/app/skills所以现在只需要重启OpenClaw容器它就会自动扫描并加载新技能。docker-compose restart openclaw重启后再次查看日志docker-compose logs -f openclaw。你应该能在日志中看到类似Loaded skill: drink_water_reminder的信息这表明你的技能已经被成功加载。如果没看到加载信息请检查① 技能文件夹和文件的路径、命名是否正确② 技能类是否正确定义并使用了skill装饰器③ 挂载的卷是否生效可以进入容器查看docker exec -it openclaw ls /app/skills。4. 与智能体对话用自然语言激活你的提醒技能加载成功后它还是一个静止的工具。我们需要通过OpenClaw的“大脑”——即集成的大语言模型LLM——来理解和调用这个技能。这就是OpenClaw最有趣的地方你不需要写调用代码只需要用自然语言告诉它。4.1 配置LLM连接给OpenClaw装上“大脑”默认情况下OpenClaw可能没有配置LLM或者配置的是需要API Key的在线模型如OpenAI。为了快速测试我推荐使用Ollama在本地运行一个开源模型完全免费且隐私安全。安装并启动Ollama前往Ollama官网下载安装。安装后在终端运行ollama run qwen2.5:7b这是一个不错的轻量级中文模型。这会拉取镜像并运行模型服务默认API端口是11434。配置OpenClaw连接Ollama这通常需要修改OpenClaw的配置文件。由于我们使用Docker可以通过环境变量或配置文件挂载来设置。最直接的方法是修改docker-compose.yml添加Ollama相关的环境变量environment: - OPENCLAW_LOG_LEVELINFO - OPENCLAW_DATABASE_URLsqlite:///./data/openclaw.db - OPENCLAW_SERVER_HOST0.0.0.0 # 新增Ollama配置 - OPENCLAW_LLM_PROVIDERollama - OPENCLAW_OLLAMA_BASE_URLhttp://host.docker.internal:11434 # 关键从容器内访问宿主机的服务 - OPENCLAW_OLLAMA_MODELqwen2.5:7b关键点host.docker.internal是一个特殊的DNS名称指向宿主机允许容器内部访问宿主机上运行的服务这里是Ollama。如果你的OpenClaw和Ollama不在同一台机器则需要使用宿主机的真实IP。重启并验证docker-compose down然后docker-compose up -d。查看日志应该能看到OpenClaw成功连接Ollama模型的提示。4.2 自然语言触发像和人说话一样下指令现在打开浏览器访问OpenClaw的Web UI通常是localhost:3000。你应该能看到一个聊天界面。试着输入“请帮我设置一个每隔45分钟提醒我喝水休息一下的提醒消息就说‘起来动动看看远方’。”接下来神奇的事情会发生如果一切配置正确意图理解OpenClaw的LLM会分析你的这句话识别出你的意图是“设置一个周期性提醒”。技能匹配LLM会根据已加载技能的描述就是我们写在skill装饰器里的description发现drink_water_reminder技能的描述“设置一个周期性喝水提醒”与当前用户意图高度匹配。参数提取LLM会从你的自然语言中提取出关键参数interval_minutes45reminder_message“起来动动看看远方”。技能调用与执行OpenClaw框架会调用drink_water_reminder技能的execute方法并传入提取到的参数。于是我们的后台循环任务就启动了。结果反馈你会在聊天窗口收到技能execute方法返回的消息“喝水提醒已启动每隔 45 分钟提醒一次。”此时查看OpenClaw容器的日志(docker-compose logs -f openclaw)你应该会看到INFO: Loaded skill: drink_water_reminder ... (用户请求日志) INFO: 开始喝水提醒任务间隔 45 分钟。 INFO: 发送提醒起来动动看看远方。每过45分钟就会重复出现“发送提醒”的日志。4.3 从日志到真实通知打通“最后一公里”到目前为止我们的提醒还只是停留在服务器日志里。如何让它真正弹到你的电脑桌面上这就需要根据你的操作系统和偏好修改技能中的sender函数实现。方案一集成系统通知跨平台你可以使用Python的plyer库。首先需要在技能容器内安装这个包。修改docker-compose.yml在openclaw服务下添加volumes挂载一个自定义的requirements.txt文件并在启动命令中安装。创建requirements.txt内容为plyer。修改docker-compose.yml:volumes: - ./data:/app/data - ./skills:/app/skills - ./requirements.txt:/app/requirements.txt # 挂载依赖文件 command: sh -c pip install -r /app/requirements.txt uvicorn openclaw.main:app --host 0.0.0.0 --port 3000 --reload修改skill.py中的_reminder_loop方法内的发送部分from plyer import notification # ... 在 _reminder_loop 函数内 ... try: # 发送系统通知 notification.notify( title喝水提醒, messagemessage, app_nameOpenClaw健康助手, timeout10 # 通知显示10秒 ) self.logger.info(f系统通知已发送{message})这种方式会在你的电脑上弹出原生系统通知。方案二发送消息到即时通讯工具如飞书、微信这就是网络热词中“openclaw接入飞书/微信”所做的事情。这需要更复杂的配置例如在飞书开放平台创建应用、获取权限、配置Webhook等。你需要编写另一个技能或修改现有技能调用飞书或企业微信的API来发送消息。这超出了“一分钟手搓”的范围但却是OpenClaw更强大的应用场景。对于我们最简单的需求方案一已经足够。它实现了从“自然语言指令”到“桌面物理通知”的完整闭环。你现在拥有了一个完全由自己控制、通过AI对话即可管理的智能喝水提醒器。5. 优化、扩展与日常使用心得一个能跑通的demo只是开始要让这个小工具真正融入日常工作流变得可靠、好用还需要一些优化和深度思考。5.1 技能功能的增强与健壮性提升最初的技能版本还有很多可以改进的地方单例控制目前的技能每次对话说“设置提醒”都会启动一个新的后台循环任务。如果你不小心说了两次就会有两个重复的提醒任务在跑。我们需要在技能类里加一个类变量或利用上下文来标记任务状态确保同一技能只运行一个实例。class DrinkWaterReminderSkill(BaseSkill): _task None # 类变量存储后台任务引用 async def execute(self, inputs): if self._task and not self._task.done(): return {success: False, message: 喝水提醒任务已经在运行中。} # ... 其他逻辑 ... self._task asyncio.create_task(self._reminder_loop(...))提供停止命令一个好的技能应该能随时被终止。我们可以创建另一个技能或者扩展当前技能通过一个特定的输入参数如action: “stop”来取消_task。# 在 inputs 中增加一个 action 参数 inputs{ action: { type: string, description: 执行的操作start 或 stop。, required: False, default: start }, # ... 其他参数 ... } # 在 execute 方法中根据 action 判断更丰富的提醒消息可以让提醒消息不那么枯燥比如随机从一句鼓励的话列表中选取一条或者附带一些健康小贴士。5.2 与现有工作流的无缝集成VS Code插件如果你主要使用VS Code可以探索是否有OpenClaw的VS Code插件或者自己写一个简单的插件在IDE内直接与OpenClaw对话设置提醒体验更沉浸。命令行调用除了Web UIOpenClaw通常也提供REST API。你可以写一个简单的Shell脚本remind_me.sh内容是用curl调用OpenClaw的API来触发技能然后给这个脚本设置一个键盘快捷键如CmdShiftH一键启动喝水提醒。作息结合将技能升级为“健康守护者”。除了喝水还可以增加“久坐提醒”、“眼保健操提醒”、“颈椎活动提醒”。只需要修改循环逻辑或者创建多个类似的技能。5.3 我踩过的坑与核心经验Docker网络是首要难题OPENCLAW_SERVER_HOST0.0.0.0和host.docker.internal这两个配置是解决大部分“连接不上”问题的关键。前者解决“外面访问不了容器”后者解决“容器访问不了宿主机本地服务”。技能加载失败静默无声如果技能文件有语法错误OpenClaw可能在启动时不会明确报错只是默默跳过。一定要养成查看完整启动日志的习惯搜索你的技能名是否出现在“Loaded skill”列表中。LLM的理解偏差有时你对AI说“提醒我喝水”它可能不会调用你的技能而是自顾自地生成一段提醒文字。这说明你的技能描述description不够精准或者LLM的“技能调用”功能未正确启用。优化技能描述确保其清晰、唯一地定义了功能边界例如“设置一个周期性的喝水提醒任务”。异步任务的资源管理我们的技能创建了asyncio.create_task这是一个“fire and forget”的后台任务。如果OpenClaw服务重启这个任务就消失了。对于需要持久化的定时任务更好的做法是利用系统的定时任务如cron来定期调用OpenClaw的API或者使用更专业的后台任务队列如Celery但这复杂度就上去了。对于喝水提醒这种轻量级、可接受中断的场景当前方案是性价比最高的。回过头看从“渴了才喝水”到拥有一个AI智能体管家整个过程的核心并不是多高深的技术而是一种思维转变将重复性的、容易被遗忘的自我管理需求抽象成一个可编程、可对话的自动化技能。OpenClaw这样的框架降低了实现这一转变的门槛。你不需要是全栈工程师只需要一点Python基础加上清晰的思路就能打造贴合自己习惯的数字助手。这个“一分钟手搓”的喝水提醒器就是一个最好的起点。它简单但完整地走通了从想法、到部署、到技能开发、再到自然语言交互的整个链路。当你看到第一个由你自己描述、自己实现、自己触发的AI提醒出现时那种感觉比解决一个生产Bug还要美妙。因为它真正服务于你服务于你的健康。