Claude Code Buddy 伴侣系统解析
重置buddy脚本:https://github.com/Fzuim/reset-buddy.git
1. 功能概述
Buddy 是 Claude Code 内置的虚拟宠物系统。用户通过 /buddy 命令"孵化"一只 ASCII 小宠物,它会蹲在终端输入框旁边,偶尔通过气泡发表评论,为编程过程增添趣味。
系统具有以下特征:
- 18 种物种:duck、goose、blob、cat、dragon、octopus、owl、penguin、turtle、snail、ghost、axolotl、capybara、cactus、robot、rabbit、mushroom、chonk
- 5 个稀有度等级:common(60%)、uncommon(25%)、rare(10%)、epic(4%)、legendary(1%)
- 外观自定义:6 种眼睛样式、8 种帽子(common 不戴帽)、1% 概率闪光(shiny)
- 属性系统:DEBUGGING、PATIENCE、CHAOS、WISDOM、SNARK,每个宠物有峰值属性和谷值属性
- AI 生成灵魂:名字和性格由 AI 在首次孵化时生成,作为"灵魂"持久化存储
2. 文件结构
src/buddy/
├── types.ts # 类型定义(物种、稀有度、属性等)
├── companion.ts # 核心生成逻辑(PRNG、确定性孵化)
├── sprites.ts # ASCII 精灵渲染(每物种 3 帧动画 + 帽子叠加)
├── CompanionSprite.tsx # React 组件(精灵动画、气泡、爱心特效)
├── useBuddyNotification.tsx # 启动提示(彩虹 /buddy 广告)
└── prompt.ts # 系统提示词注入(让 AI 不冒充宠物)
3. 确定性生成原理
这是整个系统最核心的设计——同用户永远孵出同一只宠物。
3.1 用户 ID 的来源
// companion.ts
export function companionUserId(): string {
const config = getGlobalConfig()
return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon'
}
优先级:
| 来源 | 场景 | 稳定性 |
|---|---|---|
oauthAccount.accountUuid |
已登录 OAuth 账号 | 跨设备不变 |
userID |
未登录,本地随机生成 | 本机不变 |
'anon' |
都没有(极端情况) | 每次不同 |
未登录时,userID 在首次使用时由 crypto.randomBytes(32).toString('hex') 生成一个 64 字符的十六进制字符串,保存在 ~/.claude.json 中:
// config.ts
export function getOrCreateUserID(): string {
const config = getGlobalConfig()
if (config.userID) return config.userID
const userID = randomBytes(32).toString('hex')
saveGlobalConfig(current => ({ ...current, userID }))
return userID
}
3.2 种子化 PRNG
宠物属性由确定性伪随机数生成器(PRNG)决定:
用户ID + 固定盐值 "friend-2026-401"
↓ hashString()
↓ mulberry32()
↓ 确定性随机序列
↓ 依次抽取:稀有度 → 物种 → 眼睛 → 帽子 → 闪光 → 属性
关键实现:
// Mulberry32 — 轻量级种子化 PRNG
function mulberry32(seed: number): () => number {
let a = seed >>> 0
return function () {
a |= 0
a = (a + 0x6d2b79f5) | 0
let t = Math.imul(a ^ (a >>> 15), 1 | a)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
hashString 将用户 ID 字符串转换为数字种子,支持 Bun 原生哈希和 FNV-1a 回退两种实现。
3.3 完整生成流程
rollFrom() 函数按固定顺序依次从 PRNG 序列中抽取所有属性:
function rollFrom(rng: () => number): Roll {
const rarity = rollRarity(rng) // 1. 稀有度
const bones: CompanionBones = {
rarity,
species: pick(rng, SPECIES), // 2. 物种(18选1)
eye: pick(rng, EYES), // 3. 眼睛(6选1)
hat: rarity === 'common' // 4. 帽子
? 'none' // common 强制无帽
: pick(rng, HATS), // 其他稀有度 8选1
shiny: rng() < 0.01, // 5. 闪光(1%)
stats: rollStats(rng, rarity), // 6. 属性
}
return { bones, inspirationSeed: Math.floor(rng() * 1e9) } // 7. AI种子
}
3.4 稀有度抽卡
稀有度通过加权随机轮盘抽取:
const RARITY_WEIGHTS = {
common: 60, // 60%
uncommon: 25, // 25%
rare: 10, // 10%
epic: 4, // 4%
legendary: 1, // 1%
}
实现原理:累加权重,用随机数落点确定稀有度。总权重 100,随机数 [0, 100) 从高到低逐级扣除权重,落为负数时返回对应等级。
3.5 属性随机算法
5 个属性:DEBUGGING、PATIENCE、CHAOS、WISDOM、SNARK。
每个稀有度设定一个属性下限(floor):
| 稀有度 | floor | 影响 |
|---|---|---|
| common | 5 | 所有属性的基础值极低 |
| uncommon | 15 | 有一定基础 |
| rare | 25 | 中等偏上 |
| epic | 35 | 较高基础 |
| legendary | 50 | 所有属性都不低 |
生成过程分三步:
步骤一:随机选定 Peak(巅峰属性)和 Dump(废物属性)
从 5 个属性中随机选 1 个 Peak,再随机选 1 个 Dump(确保不与 Peak 重复)。
步骤二:按公式计算各属性值
// 峰值属性
stats[peak] = Math.min(100, floor + 50 + Math.floor(rng() * 30))
// 范围:floor+50 ~ floor+79,上限截断到 100
// 废物属性
stats[dump] = Math.max(1, floor - 10 + Math.floor(rng() * 15))
// 范围:floor-10 ~ floor+4,下限保护到 1
// 其余 3 个普通属性
stats[name] = floor + Math.floor(rng() * 40)
// 范围:floor ~ floor+39
步骤三:各稀有度属性区间一览
| 稀有度 | Peak 范围 | Dump 范围 | 普通属性范围 |
|---|---|---|---|
| common | 55~84 | 1~19 | 5~44 |
| uncommon | 65~94 | 5~29 | 15~54 |
| rare | 75~100 | 15~39 | 25~64 |
| epic | 85~100 | 25~49 | 35~74 |
| legendary | 100 | 40~54 | 50~89 |
注:legendary 的 Peak 理论为 100~129,但被
Math.min(100, ...)截断为固定 100。
3.6 外观与特殊属性
物种(18 种):均匀随机,pick(rng, SPECIES)。
眼睛(6 种):· ✦ × ◉ @ °,均匀随机。
帽子(8 种):none / crown / tophat / propeller / halo / wizard / beanie / tinyduck。
common稀有度强制无帽(hat: 'none')- 其他稀有度从全部 8 种中均匀随机
闪光(Shiny):rng() < 0.01,1% 概率触发,纯视觉特效标识。
灵感种子:Math.floor(rng() * 1e9),用于后续 AI 生成名字和性格时提供确定性随机性。
4. 数据存储模型
宠物的属性被拆分为两部分:
4.1 Bones(骨骼)— 不持久化
type CompanionBones = {
rarity: Rarity // 稀有度
species: Species // 物种
eye: Eye // 眼睛样式
hat: Hat // 帽子
shiny: boolean // 是否闪光
stats: Record<StatName, number> // 属性值
}
Bones 每次从用户 ID 重新计算,不写入配置文件。这样设计的原因:
- 物种重命名不会破坏已有宠物
- 用户无法通过编辑配置伪造稀有度
- PRNG 参数调整会自动生效
4.2 Soul(灵魂)— 持久化
type CompanionSoul = {
name: string // AI 生成的名字
personality: string // AI 生成的性格描述
}
type StoredCompanion = CompanionSoul & { hatchedAt: number }
存储在 ~/.claude.json 的 companion 字段中:
{
"companion": {
"name": "Crumpet",
"personality": "A common cat of few words.",
"hatchedAt": 1775005790230
}
}
4.3 完整宠物对象
运行时通过合并得到完整宠物:
export function getCompanion(): Companion | undefined {
const stored = getGlobalConfig().companion // 从配置读 soul
if (!stored) return undefined
const { bones } = roll(companionUserId()) // 从 ID 算 bones
return { ...stored, ...bones } // bones 覆盖,保证一致性
}
5. 渲染系统
5.1 ASCII 精灵
每个物种有 3 帧动画用于闲置状态:
- 帧 0:静止
- 帧 1:轻微晃动
- 帧 2:特殊效果(喷火、冒泡、天线等)
精灵高度 5 行、宽度 12 字符,{E} 占位符在运行时替换为实际眼睛样式。
帽子系统通过替换第 0 行实现(仅在该行为空时叠加),可选:crown、tophat、propeller、halo、wizard、beanie、tinyduck。
5.2 动画序列
500ms tick 驱动的闲置序列:
const IDLE_SEQUENCE = [0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 2, 0, 0, 0]
// 0=静止, 1=晃动, -1=眨眼, 2=特效
说话或被 pet 时进入兴奋状态,快速循环所有帧。
5.3 自适应布局
| 终端宽度 | 渲染模式 |
|---|---|
| ≥ 100 列 | 完整精灵 + 气泡(非全屏时内联,全屏时浮动叠加) |
| < 100 列 | 单行表情如 (✦ω✦) Crumpet,说话时替换为截断气泡文本 |
5.4 气泡机制
- 说话后显示约 10 秒(20 个 tick)
- 最后 3 秒进入淡出状态(颜色变灰)
- 全屏模式下气泡浮动在 scrollback 上方
- 非全屏模式气泡内联在精灵左侧(输入框会相应缩窄)
6. 系统提示词
当宠物存在时,系统会注入额外的提示词:
A small {species} named {name} sits beside the user's input box and
occasionally comments in a speech bubble. You're not {name} — it's
a separate watcher.
When the user addresses {name} directly (by name), its bubble will
answer. Your job in that moment is to stay out of the way: respond
in ONE line or less, or just answer any part of the message meant
for you.
这确保 AI 不会冒充宠物,在用户直接叫宠物名字时让出舞台。
7. 时限与特性开关
7.1 触发窗口
// 提示窗口:2026 年 4 月 1-7 日(本地时间,非 UTC)
function isBuddyTeaserWindow(): boolean {
const d = new Date()
return d.getFullYear() === 2026 && d.getMonth() === 3 && d.getDate() <= 7
}
// 命令存活:2026 年 4 月起永久可用
function isBuddyLive(): boolean {
const d = new Date()
return d.getFullYear() > 2026 || (d.getFullYear() === 2026 && d.getMonth() >= 3)
}
使用本地时间而非 UTC,使得"彩虹 /buddy"提示在全球 24 小时滚动出现,避免单一时间点的流量尖峰。
7.2 特性开关
整个系统受 feature('BUDDY') 门控,可在构建层面完全关闭。
8. 反作弊设计
系统在多处防止用户伪造稀有宠物:
| 攻击向量 | 防御措施 |
|---|---|
| 编辑配置中的稀有度 | Bones 不持久化,每次从 ID 重算 |
| 伪造 species/eye/hat | 同上 |
| 修改 userID | 需要同时重新孵化,但新 ID 可能孵出更差的宠物 |
| 物种名冲突 | 用 String.fromCharCode 编码物种名,绕过构建系统的代码名检查 |
9. 总结
Buddy 系统是一个精心设计的彩蛋功能,核心理念是确定性生成 + 属性/灵魂分离:
- 生成流程:
userId + salt → hash → PRNG → 稀有度(加权) → 物种(均匀) → 眼睛(均匀) → 帽子(common=none) → 闪光(1%) → 属性(peak/dump/normal) → 灵感种子 - 稀有度:60/25/10/4/1 加权抽卡,决定帽子有无和属性下限(common=5, legendary=50)
- 属性:1 个 peak(floor+50~79)、1 个 dump(floor-10~+4)、3 个 normal(floor~+39),legendary peak 固定 100
- 外观:18 物种 × 6 眼睛 × 8 帽子(common 除外)× 1% shiny,纯确定性随机
- 外属性(Bones) 完全由用户 ID 决定,不持久化,不可篡改、不可重抽
- 灵魂(Soul) 由 AI 一次性生成,持久存储在
~/.claude.json - 渲染 根据终端宽度自适应,窄终端降级为单行表情
- 交互 通过气泡评论、pet 爱心、闲置动画增添趣味性
这种设计既保证了公平性(稀有度与用户 ID 绑定,属性由算法确定),又留出了个性化的空间(AI 生成的名字和性格),同时通过不持久化 Bones 的机制杜绝了配置篡改。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)