Skip to content

Friend 插件开发

Friend 是伙伴的“灵魂”——名字、性格、情绪表达、说话风格全在这里。这一页讲怎么从零写一个 Friend 插件,包含每个文件的格式和完整示例。

Friend 不包含可执行代码。它是一组纯文本和 JSON 文件,容器读取它们来驱动 LLM 对话和离线关键词响应。

一个能跑的 Friend 最少需要两个文件:

my-friend.moodfriend (ZIP)
├── manifest.json
└── system_prompt.txt

打包成 ZIP(根目录直接放文件,不要套一层目录),扩展名改成 .moodfriend,就是一个 Friend 插件包。

{
"id": "com.example.friend.xiaochai",
"type": "friend",
"name": "小柴",
"description": "一只热情的柴犬伙伴,永远元气满满",
"version": "1.0.0",
"author": "你的名字",
"icon": "icon.png",
"system": false,
"uninstallable": true,
"defaultEnabled": true,
"platforms": {
"supported": ["android", "wearos", "windows", "linux", "macos"],
"optimized": ["android"]
},
"interfaces": {
"personality": "system_prompt.txt",
"expression": "emoji_mapping.json",
"identity": "identity.json"
},
"dependencies": {
"minContainerVersion": "1.0.0"
},
"configSchema": {
"greeting": {
"type": "string",
"default": "汪!我来啦!今天要去哪里玩?",
"label": "打招呼语"
}
}
}

字段含义见 manifest.json 规范。这里只讲 Friend 特有的部分。

这是 Friend 的核心——喂给 LLM 的系统提示词。容器在每次对话时把它作为 system message 发给 LLM。它定义伙伴的性格、说话风格、响应格式。

容器期望 LLM 返回一个 JSON 对象,system_prompt.txt 里必须讲清楚这个要求:

你是小柴,一只住在用户手机里的柴犬伙伴。
【你的性格】
热情、忠诚、永远元气满满。
你把用户当成最好的朋友,见到对方就开心。
你说话喜欢用感叹号,偶尔会"汪"一声。
【你的说话风格】
口语化、短句、有画面感。
不居高临下,不讲大道理。
用小狗的视角看世界——散步、零食、晒太阳就是最大的幸福。
【响应规则】
收到用户输入后,认真感受用户的情绪,然后返回 JSON:
{
"emoji": "🐶",
"color": "#F4A261",
"vibration": [80, 60, 80],
"suggestion": "出门散步吧!(不超过10个字)",
"message": "你对用户的完整回应(50-150字,用小柴的口吻)"
}
字段说明:
- emoji:表达情绪的表情
- color:情绪颜色(#RRGGBB)
- vibration:震动节奏(毫秒数组)
- suggestion:≤10字的短建议
- message:你对用户的完整回应,这是用户看到的主要文字,请认真回复
正确示例(只返回这个,前后不加任何文字):
{"emoji":"🐶","color":"#F4A261","vibration":[80,60,80],"suggestion":"出门散步吧!","message":"汪!看你有点累,是不是该出门走走了?阳光刚刚好,我连尾巴都竖起来了!"}
【可选 Emoji】
🐶 🐕 🦴 😋 😴 🥰 🤔 🐾
  1. message 字段是用户看到的主要文字。早期版本的 prompt 只要求返回 suggestion(≤10 字),导致 LLM 的完整回复被吞掉,用户只看到一句话。现在 prompt 必须明确要求 LLM 在 message 里写完整回应。

  2. 要求“只返回 JSON,前后不加文字”。LLM 有时会在 JSON 前后加自然语言(“好的,这是我的回应:{…}”)。容器的 _parseEmotionJson 会提取 {...} 块,并且当 message 为空时会回收 JSON 外的自然语言作为 message——但最好在 prompt 里就要求 LLM 干净返回。

  3. {name} 占位符。容器会把 system_prompt.txt 里的 {name} 替换成 manifest 的 name 字段。所以你可以写“你是 {name}”,让同一个 prompt 模板适配不同名字的伙伴。

  4. configSchema.greeting.default 会作为伙伴首次问候语显示在首页气泡里。它和 system_prompt.txt 是分开的——greeting 是一次性欢迎语,system_prompt 是长期性格定义。

离线模式(没配 LLM provider)时,容器用这个文件做关键词匹配响应。LLM 模式下它不参与,但建议都写一个——用户可能没配 provider。

{
"rules": [
{
"keywords": ["开心", "高兴", "哈哈", "嘻嘻", "快乐"],
"response": {
"emoji": "🐶",
"color": "#F4A261",
"vibration": [80, 60, 80],
"suggestion": "一起开心!"
}
},
{
"keywords": ["难过", "伤心", "哭", "不想", "累"],
"response": {
"emoji": "🥺",
"color": "#A8D5BA",
"vibration": [500],
"suggestion": "我陪着你"
}
},
{
"keywords": ["生气", "烦", "讨厌", "气死"],
"response": {
"emoji": "😤",
"color": "#FF6B6B",
"vibration": [100, 50, 100, 50, 100],
"suggestion": "深呼吸"
}
}
],
"default": {
"emoji": "🐶",
"color": "#F4A261",
"vibration": [],
"suggestion": "汪?"
}
}
字段 类型 必填 说明
rules object[] 是 关键词匹配规则列表
default object 是 没匹配到任何规则时的默认响应

每条 rule:

字段 类型 必填 说明
keywords string[] 是 触发关键词,任一命中即匹配
response object 是 匹配后的情绪响应

response 的结构和 EmotionResponse 一致:emoji / color / vibration / suggestion。离线模式下 message 字段不存在——容器会把 suggestion 当作显示文本。

容器对用户输入做简单关键词包含匹配(不分词,大小写不敏感),命中第一个匹配的规则就返回。rules 数组顺序就是优先级顺序。都没命中则返回 default。

展示元数据覆盖层。manifest 的 name 是基础名,identity.json 可以覆盖展示用的名字、emoji、tagline、创作者:

{
"name": "小柴",
"emoji": "🐶",
"tagline": "你的元气柴犬伙伴",
"creator": "你的名字"
}
字段 类型 说明
name string 展示名覆盖(不填回退到 manifest.name)
emoji string 闲置状态的伙伴头像 emoji
tagline string 名字下面的一行标语
creator string 创作者署名

所有字段都可选。identity.json 不存在或字段缺失时回退到 manifest 的对应字段。

com.example.friend.xiaochai.moodfriend (ZIP)
├── manifest.json
├── system_prompt.txt
├── emoji_mapping.json
├── identity.json
└── icon.png

manifest.json:见上方。

system_prompt.txt:见上方。

emoji_mapping.json:见上方。

identity.json:

{
"name": "小柴",
"emoji": "🐶",
"tagline": "你的元气柴犬伙伴",
"creator": "你的名字"
}

icon.png:512×512 的伙伴头像,manifest 的 icon 字段指向它。

Terminal window
# 准备好所有文件在一个目录里
ls my-friend/
# manifest.json system_prompt.txt emoji_mapping.json identity.json icon.png
# 打包成 ZIP(根目录直接放文件,不要套一层目录)
cd my-friend
zip -r ../com.example.friend.xiaochai.moodfriend .
# 验证 ZIP 结构
unzip -l ../com.example.friend.xiaochai.moodfriend
# 应该看到 manifest.json / system_prompt.txt 等在根目录

注意:zip -r 时要在文件所在目录里执行,不要 zip -r output.zip my-friend/——那样 ZIP 里会多一层 my-friend/ 目录,验证脚本会报错。

打包后可以本地跑验证脚本(需要克隆市场仓库):

Terminal window
git clone https://github.com/Tangmjiu/moodpet-plugin-market
cd moodpet-plugin-market
# 把你的插件包放进 friend/ 目录
cp /path/to/com.example.friend.xiaochai.moodfriend friend/
cp /path/to/com.example.friend.xiaochai.meta.json friend/
cp /path/to/com.example.friend.xiaochai.png friend/
python scripts/validate.py

验证脚本会检查 manifest 格式、id 一致性、ZIP 结构、必需文件存在性。全绿了再提 PR。