1. 项目概述:在聊天机器人里养一只AI驱动的电子宠物
如果你和我一样,对90年代的电子宠物(Tamagotchi)念念不忘,同时又对现在AI能做的事情充满好奇,那么ClawPet这个项目绝对会让你眼前一亮。简单来说,ClawPet是一个运行在OpenClaw框架下的AI虚拟宠物技能。它不是一个独立的App,而是直接“住”在你的WhatsApp、Telegram或者Discord里。你可以像和朋友聊天一样,通过自然语言来喂养、玩耍、照顾它,看着它从一颗蛋孵化,经历五个成长阶段,最终进化成独一无二的传奇形态。这一切的核心,是一个由fal.ai驱动的图像生成引擎,确保你的每一只宠物在每个阶段都拥有专属的AI生成肖像。
这个项目的魅力在于,它将怀旧的养成乐趣与现代的AI对话和生成能力无缝结合。你不再需要盯着一个小小的黑白屏幕按按钮,而是可以随时随地通过你日常使用的聊天软件,用一句“我的宠物怎么样了?”来关心它,或者用“带Luna去冒险”来开启一段奇遇。对于开发者而言,它展示了如何将一个复杂的、有状态的生命模拟系统,优雅地封装成一个聊天机器人技能,这其中的架构设计和状态管理思路,非常值得借鉴。无论你是想重温儿时记忆的普通用户,还是对AI Agent应用开发感兴趣的开发者,ClawPet都提供了一个既有趣又富有技术深度的 playground。
2. 核心设计思路与架构解析
2.1 核心理念:将复杂模拟封装为无状态技能
ClawPet最精妙的设计在于,它本质上是一个运行在OpenClaw框架内的“技能”(Skill)。OpenClaw本身是一个AI Agent框架,负责处理消息路由、意图识别和与各大聊天平台的对接。而ClawPet作为技能,其核心挑战在于:如何在一个本质上是无状态、按请求处理的HTTP服务中,维护一个有状态的、随时间变化的虚拟生命?
传统的Web服务是无状态的,每个请求相互独立。但一个宠物有饥饿度、心情、能量等属性,这些属性必须随时间衰减,并且在不同用户的对话间保持连续。ClawPet的解决方案非常经典:将状态持久化到本地文件系统。具体来说,每个用户的宠物数据(包括所有属性、物种、进化阶段等)都以JSON文件的形式,存储在运行OpenClaw的服务器的特定目录下。当用户通过聊天软件发送指令时,OpenClaw将请求转发给ClawPet的技能处理脚本,脚本首先根据用户ID加载对应的宠物数据文件,然后根据指令和经过的时间(通过时间戳计算)更新宠物状态,最后将结果保存回文件,并生成回复消息。
这种设计的好处是简单、可靠,且不依赖外部数据库,非常适合个人或小规模部署。但它也带来了一个关键问题:状态衰减的触发时机。宠物不会在用户不聊天时自动变饿,这需要另一个机制。
2.2 双引擎驱动:即时交互与后台巡检
为了解决状态实时更新的问题,ClawPet采用了“双引擎”设计:
即时交互引擎:这是主处理逻辑,位于
skill/scripts/pet-action.sh。当用户主动发送“喂养”、“玩耍”等指令时触发。它的工作流程是:加载数据 -> 计算从上一次保存到现在的耗时 -> 按小时衰减率更新饥饿、心情等属性 -> 处理用户指令(如增加饱腹感)-> 检查进化条件 -> 保存数据并生成回复。后台巡检引擎:由
skill/scripts/pet-check.sh实现,通常通过系统的cron定时任务(例如每天运行一次)来触发。这个脚本会遍历所有用户的宠物数据文件,统一根据时间差更新状态,并检查是否有宠物因饥饿过度而健康值归零。它确保了即使主人几天没来,宠物的世界时间依然在流动,模拟了真实的“照料”压力。
注意:这种文件存储+定时任务的模式,在用户量极大(例如上万)时,遍历所有文件可能会成为性能瓶颈。对于大规模应用,应考虑将状态迁移到Redis或数据库,并改用消息队列或事件驱动的方式来处理状态衰减。但对于ClawPet定位的个人或社区项目,当前设计在简洁性和功能性上取得了完美平衡。
2.3 基于fal.ai的动态肖像生成
宠物的视觉表现是沉浸感的关键。ClawPet没有使用静态的精灵图,而是为每个物种的每个进化阶段,都设计了对应的文本提示词(Prompt),并调用fal.ai的API来生成独一无二的肖像。例如,“月影猫”(Luna)在“成年体”阶段的提示词可能包含“a majestic celestial cat with starry fur, glowing moon-like eyes, in a serene night setting, digital art”。
这个设计的优势在于无限的可能性与独特性。每个用户的同种宠物,由于AI生成的随机性,都会有些许不同,真正成为“你的”宠物。实现上,生成操作通常不是每次查询都触发,而是在宠物进化到新阶段的瞬间,或用户首次使用“查看宠物”指令时触发,并将生成的图片URL保存到宠物数据中,后续直接展示,以节省API调用成本和等待时间。
3. 核心模块深度剖析与实操
3.1 数据模型:宠物的数字基因
所有宠物的核心都定义在data/species.json文件中。理解这个文件的结构,就掌握了创造新物种的钥匙。
{ "species": { "luna": { "name": "Luna", "emoji": "🐱", "type": "Celestial Cat", "element": "Moon", "personality": "Calm, wise, fiercely loyal", "evolution_stages": { "egg": { "display": "🥚 Egg", "level_required": 0, "prompt_template": "A mysterious glowing egg with lunar patterns..." }, "baby": { "display": "🐣 Baby Luna", "level_required": 1, "prompt_template": "A tiny kitten with silvery fur and curious blue eyes..." } // ... 其他阶段 } } // ... 其他物种 }, "foods": [ { "emoji": "🍖", "name": "Basic Kibble", "rarity": "common", "hunger": 15, "happiness": 2, "energy": 5 }, // ... 其他食物 ], "activities": [ { "emoji": "🤗", "name": "Cuddle", "xp": 5, "energy_cost": 3, "happiness_effect": 12 }, // ... 其他活动 ] }实操要点:
- 添加新物种:你完全可以在这个文件中新增一个物种,比如“Phoenix”(凤凰)。关键是为其设计好从蛋到传奇的5个进化阶段的
prompt_template,这直接决定了fal.ai生成图像的质量和风格一致性。建议使用Midjourney或Stable Diffusion等工具先进行提示词调试,再将成功的提示词模板化后放入。 - 平衡性调整:
foods和activities中的数值直接影响游戏节奏。如果觉得宠物升级太快或太慢,可以调整活动的XP奖励;如果觉得宠物太难养活,可以降低饥饿衰减率(在pet-engine.js中)或提高基础食物的饱腹感。
3.2 状态引擎:时间的魔法
data/pet-engine.js是项目的大脑,它包含了所有核心逻辑。我们重点看状态更新函数:
function updatePetStats(petData, hoursPassed) { // 基础衰减 petData.hunger -= HUNGER_DECAY_RATE * hoursPassed; // 例如 -3/小时 petData.happiness -= HAPPINESS_DECAY_RATE * hoursPassed; // -2/小时 petData.energy -= ENERGY_DECAY_RATE * hoursPassed; // -1/小时 // 边界检查与连锁反应 petData.hunger = Math.max(0, Math.min(100, petData.hunger)); // ... 其他属性边界检查 // 健康度逻辑:饥饿低于20%时开始扣减健康 if (petData.hunger < 20) { let healthPenalty = (20 - petData.hunger) * 0.5; // 自定义惩罚系数 petData.health -= healthPenalty * hoursPassed; } else if (petData.health < 100) { // 缓慢自然恢复 petData.health += NATURAL_HEALTH_REGEN * hoursPassed; } petData.health = Math.max(0, Math.min(100, petData.health)); // 检查死亡 if (petData.health <= 0) { petData.alive = false; } return petData; }经验心得:
- 时间计算精度:在
pet-action.sh中,计算hoursPassed时,务必使用高精度的时间库(如JavaScript的Date),并考虑时区问题。一个常见的坑是服务器时间与用户所在时区不一致,导致“时间跳跃”。建议所有时间戳都存储为UTC时间,并在计算时统一转换。 - 状态保存的原子性:当并发请求发生时(虽然对于个人机器人概率低),可能会出现多个进程同时读写同一个宠物文件,导致数据损坏。一个简单的改进是使用文件锁(
flock命令)或将读写操作设计为原子化的。
3.3 技能集成:与OpenClaw的握手
ClawPet作为OpenClaw的技能,其入口是skill/SKILL.md文件。这个文件定义了技能如何被触发。
# ClawPet Skill trigger: - “pet” - “luna” - “feed” - “play” - “how is my” response: | {{#script}}./scripts/pet-action.sh{{/script}} schedule: - “0 9 * * *” # 每天上午9点运行宠物检查 action: “{{#script}}./scripts/pet-check.sh{{/script}}”关键解析:
- 触发词设计:触发词列表要兼顾精确性和模糊性。“pet”、“luna”是精确触发;“how is my”则能匹配“how is my pet”、“how is my cat”等多种自然问法。这是保证用户体验流畅的关键。
- 响应模板:
{{#script}}...{{/script}}是OpenClaw的模板语法,它告诉框架去执行指定的shell脚本。脚本的stdout输出会自动作为消息回复给用户。 - 定时任务:
schedule部分定义了后台巡检。“0 9 * * *”是cron表达式,表示每天9:00 AM执行。确保你的服务器cron服务正常运行,并且OpenClaw的调度器已启用。
4. 从零开始部署与深度定制指南
4.1 环境准备与安装
假设你已经在云服务器或本地电脑上部署好了OpenClaw(这是一个前提)。接下来的步骤是集成ClawPet。
# 1. 通过npx直接运行交互式安装器(推荐) npx clawpet # 安装器会引导你完成所有步骤 # 2. 或者,手动克隆并安装 git clone https://github.com/94090397/clawpet.git cd clawpet # 将skill目录链接或复制到OpenClaw的skills目录下 ln -s $(pwd)/skill /path/to/your/openclaw/skills/clawpet # 安装Node.js依赖(如果有) npm install安装过程中的关键决策点:
- fal.ai API Key:安装器会向你索要。你需要去fal.ai官网注册并获取一个API密钥。这个密钥会被安全地存储在OpenClaw的配置环境中,而不是代码里。请务必注意该服务的计费方式,虽然宠物图片生成次数有限,但调用量极大时也可能产生费用。
- 每日提醒设置:安装器会询问是否设置每日宠物状态检查提醒。如果选择“是”,它会在你的OpenClaw配置中创建一条定时任务,在每天你指定的时间,主动向所有有宠物的用户发送一条状态摘要(如“你的Luna有点饿了哦!”)。这是一个提升用户留存的功能,但如果你不喜欢被打扰,可以关闭。
4.2 本地测试与调试
在将技能部署到正式的聊天机器人之前,强烈建议使用项目自带的本地测试工具进行完整流程的沙盒测试。
cd /path/to/clawpet node test-local.js这个测试脚本提供了一个完整的终端交互界面。它的价值在于:
- 隔离环境:完全不依赖OpenClaw和聊天平台,你可以快速测试宠物的核心逻辑:孵化、喂养、玩耍、进化。
- 时间旅行:这是最强大的调试功能。你可以模拟“跳过”若干小时甚至几天,瞬间看到宠物状态的变化,验证衰减逻辑和死亡条件是否正确。
- 可视化状态:在终端中用彩色进度条实时显示饥饿、快乐等数值,比看原始JSON数据直观得多。
调试案例:假设你修改了pet-engine.js中的饥饿衰减率,从-3/小时改为-5/小时。你可以通过本地测试,快速观察到宠物饿得更快,从而判断这个改动是否过于严苛。
4.3 自定义与扩展实战
ClawPet的架构鼓励扩展。以下是一些可行的自定义方向:
方向一:增加新的互动类型比如增加一个“学习”活动,消耗高能量,获得大量XP,但可能降低快乐度。
- 在
data/species.json的activities数组中添加新项目。 - 在
pet-engine.js中,找到处理活动逻辑的函数(通常是performActivity),添加对新活动名称的判断,并实现对应的数值变化逻辑。 - 在
skill/scripts/pet-action.sh中,确保自然语言理解能匹配到新活动(例如,用户说“让Luna学习”,能映射到“study”这个活动键)。
方向二:实现社交功能(多宠物互动)这是一个高级特性。思路是修改数据模型,允许一个用户拥有多只宠物,并增加一个“关系”属性。当用户发出“让Luna和Ember一起玩”的指令时,脚本需要同时加载两只宠物的数据,更新它们的状态(例如,快乐度大幅增加,消耗一些能量),并生成一个联合的、描述性的回复(“Luna和Ember在草地上追逐打闹,它们看起来开心极了!”)。这需要更复杂的自然语言解析和状态管理。
方向三:接入其他AI图像服务如果你不想用fal.ai,或者想用Stable Diffusion本地部署,可以修改图像生成模块。
- 定位到生成图片的函数(可能在
pet-engine.js或一个单独的image-generator.js中)。 - 将调用fal.ai API的代码,替换为调用其他服务的代码(如Automatic1111的WebUI API)。
- 注意调整请求参数和响应处理逻辑,因为每个服务的API格式都不同。
5. 常见问题排查与运维心得
在实际运行中,你可能会遇到以下问题。这里记录了我的踩坑实录和解决方案。
5.1 宠物状态不更新或更新异常
症状:宠物好像永远不饿,或者饥饿度骤降。排查步骤:
- 检查时间戳:首先查看宠物数据文件(通常位于
openclaw/data/pets/user_123.json)。找到last_updated字段。它的值应该是一个ISO时间字符串。计算当前时间与它的差值,看是否合理。 - 检查脚本时区:在
pet-action.sh和pet-check.sh中,打印出服务器当前时间和计算出的时间差。确保脚本使用的是UTC或与你预期一致的时区。一个经典错误是服务器使用UTC,而开发者按本地时间思考。 - 检查cron任务:执行
crontab -l查看定时任务是否正常添加。检查系统日志grep cron /var/log/syslog查看pet-check.sh是否有执行记录和报错。
根本原因与修复:
最常见的原因是时间格式不一致或解析错误。确保在JavaScript中使用
Date.parse()或new Date().getTime()处理时间,在Shell中使用date +%s获取时间戳进行计算。统一使用Unix时间戳(秒数)进行计算是最稳妥的。
5.2 AI图片生成失败或缓慢
症状:用户请求看宠物图片时,长时间无响应或返回错误。排查步骤:
- 测试API连通性:在服务器上直接用curl命令测试fal.ai API。
curl -X POST -H "Authorization: Key YOUR_API_KEY" -H "Content-Type: application/json" -d '{"prompt":"a cat"}' https://fal.ai/models/...。检查返回状态码和错误信息。 - 检查配额与计费:登录fal.ai控制台,确认API密钥有效,且没有超出用量配额或欠费。
- 查看脚本日志:OpenClaw通常有运行日志。找到ClawPet技能执行时的日志,看是否有图片生成函数的报错信息。
优化建议:
- 实现缓存:绝对不要每次查询都生成图片。在宠物进化时生成一次,然后将图片URL或Base64编码存储在宠物数据中。查询时直接返回已存储的图片。
- 设置超时与降级:在调用生成API时,设置一个合理的超时(如10秒)。如果超时或失败,则返回一个预设的、表示“图片加载中”的占位图,或者描述宠物外观的文字。
- 使用更轻量的模型:如果速度是首要考虑,可以在fal.ai上选择推理速度更快的模型,虽然可能在细节上略有牺牲。
5.3 自然语言理解(NLU)不准确
症状:用户说“我宝贝饿不饿”,机器人无法识别;或者说“喂食”,机器人却理解为“玩耍”。排查思路: ClawPet本身不包含复杂的NLU,它依赖OpenClaw的触发词匹配和简单的关键词提取。提升理解能力有两个层面:
- 扩充触发词:在
skill/SKILL.md的trigger列表里,加入更多同义词和常见口语表达。例如,除了“feed”,还可以加入“喂”、“吃饭”、“投喂”、“给它吃点东西”。 - 增强意图解析:在
pet-action.sh脚本中,可以对OpenClaw传递过来的用户消息进行更细致的处理。例如,使用简单的正则表达式或关键词列表:
这能显著提升在简单场景下的识别率。对于更复杂的需求,则需要考虑在OpenClaw上层集成一个真正的NLU引擎(如Rasa或Dialogflow)。user_message="$1" # OpenClaw传递的用户消息 if [[ $user_message =~ (喂|feed|吃饭|hungry) ]]; then action="feed" elif [[ $user_message =~ (玩|play|游戏|ball) ]]; then action="play" fi
5.4 数据安全与备份
重要提醒:宠物的所有数据都保存在服务器的文件里。一旦服务器磁盘损坏或误删,所有用户的宠物都将“死亡”。必须实施的备份方案:
- 定期备份:编写一个简单的脚本,将整个
openclaw/data/pets/目录打包压缩,并上传到云存储(如AWS S3、Backblaze B2)或另一台服务器。使用cron每天执行一次。# 示例备份脚本 backup_pets.sh tar -czf /backup/pets_$(date +%Y%m%d).tar.gz /path/to/openclaw/data/pets/ # 然后使用rclone或aws cli上传到云 - 版本控制考虑:虽然不适合直接git跟踪频繁变化的JSON数据文件,但你可以将
data/species.json这类定义文件纳入git管理,确保配置变更可追溯。
运行这样一个AI宠物项目,最大的成就感来自于看到社区里的用户分享他们宠物的成长截图和故事。技术上的挑战,如状态管理、API集成和自然语言交互,都是非常实用的学习场景。而最重要的经验是,在项目初期就建立好清晰的日志系统和简单的监控(比如一个能查看所有宠物健康状态的仪表盘),这能在出现问题时为你节省大量排查时间。最后,保持游戏的平衡性和趣味性需要反复测试和调整,不妨邀请几个朋友作为第一批测试用户,他们的反馈往往是最宝贵的。