PrimeBot 启元 Q1 & T1 技能整活工作台
WORKSHOP GUIDE · 飞书实操技术指南

PrimeHub 技能开发实操 · 技术指南(整理版)

飞书《PrimeHub 技能开发实操技术指南》整理版骨架

😜 调皮课代表大白话划重点(30 秒读懂核心)
这是飞书上的快速速查版技术指南,浓缩了「一个技能是什么」的心智模型、平台 6 条物理约束、与 AI Agent 协作的开场白技巧以及常见坑速查表。看完这页再看我们的 8 步闯关 SOP,你会发现很多繁琐的手动步骤已经被自动化脚本一键代劳啦!
原始文档:飞书《PrimeHub 机器人技能:开发到上架通用手册》
https://bcn3gscsq9m8.feishu.cn/wiki/Iv94wYbm4iEazmk3yNjc8tYNn8c
空间:N1--机器人小组 | 最新修改:10月21日 | 抓取:2026-10-23
权威全量版本:02_开发工具包/primehub_dev_kit/SKILL_PUBLISH_GUIDE.md(飞书文档自称「本文的超集」,命令/失败码以它为准)。
本文件 = 飞书文档的骨架 + 与全量手册对齐的要点,方便一页速查。

飞书原文目录(结构即下面章节顺序):

💻 text
心智模型:一个技能是什么
开发顺序总览
第 0 步:环境与资产盘点(5 分钟,可提前在家做)
第 1 步:注册 PrimeHub(提前在家做)
第 2 步:给 agent 喂机器人契约(成败的分水岭)
   2.1 robot 对象 API(运行时自动注入的全局对象)
   2.2 动作 ID 常用表(权威全量表在机器人上,不在任何 SDK 文档)
   2.3 平台约束 6 条(实测物理事实,写代码前必看)
   2.4 和 agent 协作的方式(模板只是起点,不是天花板)
第 3 步:vibe coding(复制示例改起)
第 4 步:打包 .rpk(1 分钟)
第 5 步:真机调试环(开发主循环,每轮 3 条命令)
第 6 步:验收检查表(全绿才发布)
第 7 步:提取登录账号的浏览器 token(一条命令)
第 8 步:上传 PrimeHub(一条命令,几十秒后全场可搜)
附录 A:常见坑速查
附录 B:示例能力对照

心智模型:一个技能是什么

技能包 = zip 改名 .rpk,固定结构:

💻 text
你的技能/
├── cfg/pkg.yaml    # 包名、版本、作者、机型(q1/t1)
├── src/main.py     # 技能逻辑(纯 Python stdlib)
├── readme.md       # 说明(可选)
└── media/          # mp3/mp4 资源(可选,原样落盘)

执行时机器人上的 lumina 运行时拉起 src/main.py 并注入全局对象 robot——说话、动作、表情、灯带、音频全用它。 没有 rclpy、没有 ROS2 节点、没有相机抓帧。(ROS2 SDK 是另一条路,见 03_官方工作台与资料包)

开发工具包 dev kit(7 个文件):

文件作用
example-jukebox/点歌台完整源码(技能基底,复制改起)
SKILL_PUBLISH_GUIDE.md全流程手册(飞书文档的超集,细节字典)
pack.py打包器:技能目录 → .rpk
deploy_test.py真机调试 harness
extract_token.py从 Firefox 登录态提取发布凭证
primehub_publish.py + primehub_client.py发布/搜索/删除客户端

第 0 步:环境与资产盘点(5 分钟)

💻 bash
git clone https://github.com/miracle-techlink/primehub_dev_kit
cd primehub_dev_kit
pip install gmssl
python3 primehub_publish.py search "你好"    # 自检:能返回结果 = 网络+传输层 OK
sshpass -p '1' ssh -o StrictHostKeyChecking=no run@<IP> 'echo OK'   # 机器人连通
sshpass -p '1' ssh run@<IP> 'curl -sI --max-time 8 https://www.baidu.com | head -1'  # 联网类技能才需要

机器人 SSH 用户 run、密码 1(现场分配 IP)。

第 1 步:注册 PrimeHub(提前在家做)

用本机 Firefox 打开 https://primehub.primebot.com → 手机号 + 验证码注册/登录 → 随便点一个页面(确保 token 写入 localStorage)。

为什么必须 Firefox:发布 token 只能由验证码登录换取,Firefox 把它落盘到 localStorage,第 7 步的 extract_token.py 只认 Firefox 的落盘格式。token 7 天有效。

第 2 步:给 agent 喂机器人契约(成败的分水岭)

AI agent 默认按 ROS2/rclpy 常识写代码——装上必炸(ImportError)。先喂契约再让它写。

2.1 robot 对象 API

💻 python
robot.play_tts(text)                        # 说话;异步无完成回调 → 必须轮询等播完
robot.play_animation(anim_id, interrupt)    # 预设动作;返回 bool(True=已受理≠已执行)
robot.play_audio_file(path)                 # 播音频;板载只吃 wav(mp3 无声)
robot.play_light(color, mode)               # 灯带(mode 3 律动)
robot.play_emotion(emotion_ids, ...)        # 屏幕表情
robot.get_audio_play_state()                # → {'pkg_name':..,'state':{'value':1播放中/2空闲}}
robot.get_t1_robot_form()                   # T1 专属:'biped'/'quadruped'
robot.wait("time", seconds)                 # 等待

2.2 动作 ID 常用表(权威全量表在机器人上)

💻 bash
# Q1:58 个动作(3000 系),上架技能只用 upper_body
sshpass -p '1' ssh run@<Q1_IP> 'cat /robot/software/task_engine/bin/cfg/motions/q1/upper_body.yaml'
# T1:1-72
sshpass -p '1' ssh run@<T1_IP> 'cat /robot/software/task_engine/bin/cfg/motions/t1/*.yaml'
  • Q1(室内模式只用 upper_body):3001 招手 / 3002 握手 / 3003 碰拳 / 3004 挥手 / 3005 开心 / 3007 比心 / 3008 加油 / 3010 伸懒腰 / 3012-14 讲话 / 3046-48 舞蹈 A/B/C / 3052-57
  • T1:1 打招呼 / 2 握手 / 6 比心 / 7、10、11 跳舞 / 20-22 拍照姿势 / 26 喜悦 / 31 欢呼 / 43 律动 / 52 太空步

2.3 平台约束 6 条

  1. Q1 室内模式(motion_mode=0)禁全身动作(reason 24)→ 上架技能只用 upper_body ID
  2. T1 四足形态只能用 1/7/26/43(其余 reason 19)→ 用 get_t1_robot_form() 分支
  3. 音频互斥:play_tts 停掉当前音频且不自动恢复;play_animation(interrupt=True) 也停音频 → 台词和音乐别同时发;要音频持续时一律 interrupt=False
  4. 音频状态对长音频 1↔2 抖动 → 判播完别信单次采样,轮询稳定空闲或按已知时长计时
  5. 时长感:TTS 起声 ≈0.5s、4-6 字 ≈1.1s;anim 受理→真正起跳 ≈0.3s
  6. stdout 必须 flush=True,否则调试看不到 print

2.4 和 agent 协作(两种开场 + 推荐循环)

两种典型开场白(直接把 dev kit 丢给 agent):

💻 text
读 SKILL_PUBLISH_GUIDE.md 的 §2 契约。基于 example-jukebox/ 复制改造,改成:〈你的玩法一句话〉。
约束:只用 upper_body 动作、纯 stdlib、遵守 §2.4 平台约束。
💻 text
我要做一个〈玩法描述〉的机器人技能,请你自己读 dev kit 里的 SKILL_PUBLISH_GUIDE.md §2,
按契约设计实现;有多个方案就先给我方案再写代码。

推荐循环:① 先让 agent 复述它理解的契约(答不出 rclpy 之外的写法就会写废代码)→ ② 出初版跑调试环,把 final/stdout/error 原样贴回 → ③ 观感问题用自然语言提(「台词太长」比手改代码快)。

第 3 步:vibe coding(复制示例改起)

💻 bash
cp -r example-jukebox my-skill

保底骨架(speak() 轮询 + flush=True 两个坑必须保留):

💻 python
import time

STEPS = [(3001, "台词一", 3), (3007, "台词二", 3)]   # (动作ID, 台词, 保持秒)

def speak(text):
    robot.play_tts(text)
    deadline = time.time() + max(len(text) * 0.35, 3.0) + 8.0
    time.sleep(0.3)
    while time.time() < deadline:
        if robot.get_audio_play_state().get("state", {}).get("value") == 1: break
        time.sleep(0.12)
    while time.time() < deadline:
        if robot.get_audio_play_state().get("state", {}).get("value") != 1: break
        time.sleep(0.12)

speak("开场白")
for anim, line, hold in STEPS:
    speak(line)
    robot.play_animation(anim, interrupt=True)   # 无音乐并行时允许打断
    time.sleep(hold)
speak("收场白")
print("MY_SKILL_DONE steps=%d" % len(STEPS), flush=True)

技能形态不止「台词+动作」:状态机(合影导演流程控制)、事件循环(点歌台节拍网格)、API 拉数据(资讯播报)、随机池(随机舞步)示例里都有模块可抄。

agent 三条硬约束(当 lint 用):纯 stdlib + robot 全局对象(不 import);只用查到的动作 ID;台词必须走 speak() 轮询、音频并行按约束 3 处理。

第 4 步:打包 .rpk(1 分钟)

💻 bash
python3 pack.py my-skill
python3 -m zipfile -l my-skill.rpk     # 校验:必须含 cfg/pkg.yaml 与 src/main.py

media/ 可选;包体控制在 200MB 内(上传限制)。

第 5 步:真机调试环(开发主循环)

💻 bash
sshpass -p '1' scp -o StrictHostKeyChecking=no deploy_test.py run@<IP>:/tmp/   # 一次
# 每轮 3 条
python3 pack.py my-skill
sshpass -p '1' scp my-skill.rpk run@<IP>:/tmp/
sshpass -p '1' ssh run@<IP> 'python3 /tmp/deploy_test.py /tmp/my-skill.rpk 300'   # timeout ≥ 技能总时长

输出判读:

现象含义
final: success + stdout 到 DONE脚本逻辑通
final: failed + error: ImportError...契约写错,回 §2
final: timeoutharness timeout 没给够(默认 120 常常不够)
stdout 空白print 忘了 flush=True

动作到底触没触发(stdout 只证明「受理」):

💻 bash
sshpass -p '1' ssh run@<IP> 'grep "Animation task成功" /robot/persist/log/task_engine/task_engine.log | tail -8'

失败 reason 表:24 室内模式禁全身 / 19 形态不符 / 42 上一动作占队列 / 28 动作不可打断 / 17 动作 ID 不存在。

清理:curl -s -X DELETE 127.0.0.1:58080/packages/my_skill(其它:GET /packages、GET /tasks/running、DELETE /tasks/{id})。

第 6 步:验收检查表(全绿才发布)

  • deploy_test.py 一轮 final: success
  • stdout 到达 DONE 标记(有 flush)
  • 动作日志里每个预期动作 ID 都「创建成功」,无 24/19/42/28 拒绝
  • 台词全部实际播出(无吞句)
  • 音频类技能:音频从头活到尾
  • 连跑两轮不炸
  • 安全:机器人周围 1 米清空测试过

第 7 步:提取 token(一条命令)

💻 bash
python3 extract_token.py      # → /tmp/ph_token.txt(JWT,7 天有效)

原理:找 Firefox profile 的 storage/default/https+++primehub.primebot.com/ls/data.sqlite → 拷贝快照(运行中会锁库)→ 查 skillhub_token → 纯 stdlib 解 snappy → 写 /tmp/ph_token.txt。

失败判读:没找到 Firefox 的 primehub localStorage → 没用 Firefox 登录;没找到 skillhub_token → 登录态过期,重登录再跑。

第 8 步:上传 PrimeHub(一条命令)

💻 bash
python3 primehub_publish.py publish my-skill.rpk '{
  "skillName": "我的技能名",
  "version": "1.0.0",
  "skillDesc": "一句话简介(30 字内最好)",
  "long_description": "详细说明(详情页显示)",
  "categoryIds": [104],      "categoryCodeList": ["action"],
  "tagsIds": [127],          "tagsCodeList": ["companion"],
  "robotModelIds": [100],    "robotModelCodeList": ["robot_q"],
  "iconUrl": "skill-music.svg"
}'
# 成功回 {'code': 1000, 'msg': '成功'}

字典 ID 速查(双字段必填,缺了报 1001):

项ID → 值codeList
分类102 视觉 / 103 语言 / 104 动作 / 105 听力 / 106 规划 / 107 表情vision / language / action / hearing / plan / expression
标签115 音乐 / 124 拍照 / 127 陪伴 / 20 亲子music / take_photo / companion / parent_child
机型100 机器人Q / 101 机器人Trobot_q / robot_t
图标skill-music / skill-video / skill-radio / skill-game / skill-verify同名 .svg

发布后验证:primehub_publish.py search "我的技能名"、primehub_publish.py my;终极验证=官方 App 技能市场搜到 → 下载部署 → 点运行 → 机器人真演一遍。

管理:primehub_publish.py my / delete <id>;同名再 publish 不覆盖,会新建一条 → 换版先 delete 再 publish。

API 坑:本机 urllib 打 primehub 会被掐 SSL → 用 curl 子进程;真实前缀 /app-api/content-server/(发布)、/app-api/search-server/(搜索);搜索参数是 keywords/page/rows;传输层 SM4-CBC(primehub_client.py 已实现)。

附录 A:常见坑速查

症状根因修法
装了技能没反应用了 rclpy/ROS2 契约改用 robot 对象契约
动作全部不执行whole_body + 室内模式换 upper_body ID
部分动作没触发编排表索引越界检查索引
台词被吞/截断没等上一句播完用 speak() 轮询
音效放到一半消失后发 TTS/动画 interrupt=True音频期间 interrupt=False
mp3 放了没声板载只吃 wavffmpeg 转 wav
看不到 print缓冲flush=True
deploy 显示 timeoutharness 超时 < 技能总时长给足第二个参数
publish 报 1001字段名拼错/漏 codeList对照 §8 模板
querySkillList 报 9999参数名 pageNum/pageSize改 keywords/page/rows
同名技能出现两条平台不去重先 delete 再 publish

附录 B:示例能力对照(example-jukebox/)

想学什么看哪里
台词轮询播完(所有技能的地基)speak()
联网取资源find_song() / download_song()
音频播放与状态处理play_audio_file 调用处 + music_really_over()
音频持续时动作怎么发主循环 play_animation(anim, interrupt=False)
持续随机动作池MOVE_POOL + next_move()
换自己要的东西默认热歌榜随机;固定曲目用 media/songlist.txt