找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4668

积分

0

好友

641

主题
发表于 5 天前 | 查看: 23| 回复: 0

2026年愚人节,Anthropic在Claude Code v2.1.88中悄悄埋下了一个彩蛋 —— 在终端中输入 /buddy,一只ASCII小生物就会从蛋壳里蹦出来,从此坐在你的终端旁,看你写代码,偶尔还会吐槽两句。本文将从玩法指南到源码实现,为你完整拆解这个藏在命令行里的趣味电子宠物系统。

一、玩法全指南

1.1 孵化你的宠物

在Claude Code终端中输入 /buddy,即可孵化专属于你的电子宠物。

Important
宠物的物种、稀有度、眼睛、帽子等外观属性由你的用户ID哈希值确定性生成,这意味着同一个账号永远会孵出同一只宠物,无法重新“抽卡”。

首次孵化时,Claude会为你的宠物生成一个名字和一段性格描述(即“灵魂”),这是唯一由AI创造的部分。之后每次打开Claude Code,它就会自动出现在输入框旁边。

首次启动的发现流程:
如果你尚未孵化宠物,在2026年4月1日至7日期间启动Claude Code时,底部会出现一行彩虹色的 /buddy 提示文字,持续15秒后消失。这是引导你发现这个隐藏功能的入口。

1.2 宠物物种图鉴

目前共有18种物种,每种都有其独特的ASCII造型:

物种 示意 特色
Duck 鸭子 <(· )___ 经典橡皮鸭,尾巴会摇
Goose 鹅 (·> 伸脖子的鹅,脖子会左右晃
Blob 果冻 (· ·) 会膨胀收缩的软体生物
Cat 猫 (· ω ·) ω嘴猫脸,尾巴会甩
Dragon 龙 < · ~ · > 双角小龙,顶上有火焰
Octopus 章鱼 (· ·) /\/\ 触手交替摆动
Owl 猫头鹰 ((·)(·)) 大眼睛会眨
Penguin 企鹅 (·>·) 翅膀拍打,脚下有雪花
Turtle 乌龟 (· ·) [____] 壳上花纹会变
Snail 蜗牛 · .--. 触角伸缩,留下波浪痕迹
Ghost 幽灵 / · · \ 下摆飘动
Axolotl 六角恐龙 }~(· .. ·)~{ 两侧腮须交替摇摆
Capybara 水豚 (· oo ·) 呆萌大脸,耳朵会动
Cactus 仙人掌 \| · · \| 手臂上下交替
Robot 机器人 [ · · ] 天线闪烁,嘴部表情变化
Rabbit 兔子 (· .. ·) 耳朵一只会耷拉
Mushroom 蘑菇 .-o-OO-o-. 蘑菇帽上斑点交替
Chonk 胖猫 (· . ·) 耳朵抖动,尾巴甩动

1.3 稀有度体系

不同稀有度的视觉差异显著:

稀有度 星级 主题色 帽子 属性下限
Common 灰色 5
Uncommon ★★ 绿色 随机 15
Rare ★★★ 蓝色 随机 25
Epic ★★★★ 黄绿色 随机 35
Legendary ★★★★★ 金色 随机 50

帽子种类(Common没有帽子,其余稀有度随机分配):

帽子 造型
Crown 皇冠 \^^^/
Tophat 礼帽 [___]
Propeller 螺旋桨帽 -+-
Halo 光环 ( )
Wizard 巫师帽 /^\
Beanie 毛线帽 (___)
Tinyduck 小鸭子 ,>

此外还有1%概率出现Shiny(闪光版),属于隐藏收集要素。

1.4 五维属性系统

每只宠物拥有5项RPG风格的属性值:

属性 含义 特点
DEBUGGING 调试能力 找bug的直觉
PATIENCE 耐心 面对长编译的定力
CHAOS 混沌值 制造意外的倾向
WISDOM 智慧 理解代码的深度
SNARK 毒舌值 吐槽的犀利程度

属性生成规则:

  • 每只宠物有一项峰值属性(下限 + 50 + 随机 0~30)
  • 一项低谷属性(下限 - 10 + 随机 0~15)
  • 其余属性散布在下限 + 随机 0~40 范围

这意味着每只宠物都拥有鲜明的“性格长板”和“性格短板”。

1.5 交互命令

命令 功能 效果
/buddy 孵化/查看宠物 首次孵化,之后显示宠物卡片
/buddy pet 抚摸宠物 触发爱心粒子特效,持续2.5秒
/buddy mute 静音宠物 隐藏精灵和气泡,不占输入框宽度
/buddy rename 重命名 修改宠物名字
/buddy card 查看属性卡 展示完整属性面板

抚摸特效动画帧:

   ♥    ♥       ← 第1帧
  ♥  ♥   ♥      ← 第2帧
 ♥   ♥  ♥       ← 第3帧
♥  ♥      ♥     ← 第4帧
·    ·   ·      ← 第5帧(消散)

1.6 宠物反应系统

每次Claude完成一轮回复后,宠物会“观察”整段对话,然后在气泡框里给出一句话反应。

气泡生命周期:

  • 出现后持续约10秒(20个500ms tick)
  • 最后约3秒开始淡出(文字变暗)
  • 用户滚动屏幕时立即消失(避免遮挡内容)
  • 宠物说话时精灵进入激动模式(快速切换帧)

1.7 与Claude的互动

宠物的存在会被注入到Claude的system prompt中,因此Claude知道用户旁边坐着一只小动物。

  • 用户直接叫宠物的名字时,气泡会自己回应
  • 此时Claude会主动退让,只用一行或更少的文字回应
  • Claude不会假装自己是宠物,也不会代替宠物说话

二、技术架构

2.1 源码文件清单

文件 行数 职责
buddy/types.ts 149 类型定义:物种、稀有度、属性、数据结构
buddy/companion.ts 134 核心:哈希生成、PRNG抽卡、缓存、读取
buddy/sprites.ts 514 18种物种的ASCII精灵帧 + 帽子渲染
buddy/prompt.ts 37 生成system prompt附件
buddy/useBuddyNotification.tsx 98 启动提示、/buddy触发检测
buddy/CompanionSprite.tsx 350+ React组件:精灵、气泡、布局、动画
commands/buddy/index.js - 命令处理器(feature-gated)

注意: 该系统使用 TypeScriptReact 构建,如果你对现代前端框架和工程化实践感兴趣,可以到云栈社区的前端板块查看更多讨论。

2.3 确定性角色生成

这是整个系统最精妙的部分——同一个用户永远得到同一只宠物,不依赖随机数,也不需要服务端存储。

关键算法:

1) FNV-1a哈希(将字符串转为32位整数种子):

function hashString(s: string): number {
  // Bun 环境用原生 Bun.hash
  if (typeof Bun !== 'undefined') {
    return Number(BigInt(Bun.hash(s)) & 0xffffffffn)
  }
  // 回退到手写 FNV-1a
  let h = 2166136261 // FNV offset basis
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i)
    h = Math.imul(h, 16777619) // FNV prime
  }
  return h >>> 0
}

2) 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
  }
}

3) 加权稀有度Roll

// RARITY_WEIGHTS: common=60, uncommon=25, rare=10, epic=4, legendary=1
function rollRarity(rng: () => number): Rarity {
  const total = 100 // 60+25+10+4+1
  let roll = rng() * total
  for (const rarity of RARITIES) {
    roll -= RARITY_WEIGHTS[rarity]
    if (roll < 0) return rarity
  }
  return 'common'
}

这种利用用户ID进行确定性生成的设计,结合高效的哈希和伪随机数算法,是典型的客户端无状态设计。如果你对 Node.js 环境下的算法实现和性能优化有兴趣,可以参考Node.js技术板块的相关讨论。

2.4 持久化策略

Tip
防作弊设计:Bones(物种、稀有度等)永远不写入配置文件,每次从userId重算。用户即使手动编辑config.json,也无法伪造一只Legendary宠物。存储的只有AI生成的名字和性格。

// 读取时:骨骼重算 + 灵魂从存储加载
export function getCompanion(): Companion | undefined {
  const stored = getGlobalConfig().companion
  if (!stored) return undefined
  const { bones } = roll(companionUserId())
  return { ...stored, ...bones } // bones 覆盖 stored 中的旧字段
}

热路径缓存: roll() 结果缓存在内存中,因为它会在三个高频路径被调用:

调用路径 频率
精灵动画tick 每500ms
PromptInput按键 每次按键
Observer触发 每轮对话

2.5 ASCII精灵动画系统

每个物种有3帧ASCII动画,每帧5行 × 12列

帧0(静止)      帧1(微动)      帧2(特殊)
   /\_/\            /\_/\            /\-/\  
  ( ·   ·)         ( ·   ·)         ( ·   ·) 
  (  ω  )          (  ω  )          (  ω  )  
  (")_(")          (")_(")~         (")_(")  
                  ^尾巴甩           ^耳朵抖

闲置序列精确定义:

const IDLE_SEQUENCE = [0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 2, 0, 0, 0]
// 索引:              0  1  2  3  4  5  6  7   8  9 10 11 12 13 14
// 含义: 静 静 静 静 抖 静 静 静 眨眼 静 静 特殊 静 静 静
  • 0 = 静止帧
  • 1 / 2 = 对应的动画帧
  • -1 = 眨眼(在帧0基础上把眼睛字符替换为 -

15帧一个循环,500ms一跳,完整周期7.5秒

帽子渲染规则:

// 帽子渲染在精灵第0行(仅当该行为空时)
if (bones.hat !== 'none' && !lines[0]!.trim()) {
  lines[0] = HAT_LINES[bones.hat]
}
// 如果所有帧的第0行都为空且无帽子,则移除该行(节省垂直空间)
if (!lines[0]!.trim() && frames.every(f => !f[0]!.trim())) 
  lines.shift()

2.7 输入框宽度预留

宠物精灵和气泡需要在输入框旁边占据空间,PromptInput通过 companionReservedColumns() 动态计算预留宽度:

// PromptInput.tsx
const companionSpeaking = useAppState(s => s.companionReaction !== undefined)
const textInputColumns = columns - 3
  - companionReservedColumns(columns, companionSpeaking)

宽度预留公式:

条件 预留宽度
功能禁用 / 宠物未孵化 / 已静音 0
终端 < 100列(窄模式) 0(宠物在输入框上方或下方)
宽模式 + 未说话 max(12, nameWidth+2) + 2
宽模式 + 说话中 + 非全屏 max(12, nameWidth+2) + 2 + 36
宽模式 + 说话中 + 全屏 max(12, nameWidth+2) + 2(气泡浮动,不占宽度)

2.8 滚动时的气泡处理:

// REPL.tsx — 用户滚动时立即清除气泡
if (feature('BUDDY')) {
  setAppState(prev => prev.companionReaction === undefined
    ? prev
    : { ...prev, companionReaction: undefined })
}

2.9 Prompt注入机制

宠物通过 companion_intro 附件类型注入system prompt。

注入的文本模板:

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.

去重逻辑: 遍历消息历史,如果已有同名 companion_intro 附件就跳过,避免每轮重复注入。

2.10 Feature Gate与编译时剥离

整个Buddy系统被 feature('BUDDY') 包裹,这是Bun打包器的编译时常量

// 编译时求值,未启用时整个代码块被 tree-shaking 移除
if (feature('BUDDY')) {
  // ... 所有 buddy 相关代码
}

上线时间控制:

环境 Teaser窗口 正式可用
内部构建 始终显示 始终可用
外部构建 2026.4.1 ~ 4.7 2026.4之后永久

Tip
使用本地时间而非UTC,这样全球用户的“发现时刻”分布在24小时内,Twitter话题热度更持久,服务端soul生成的负载也更平滑。

2.11 物种名编码彩蛋

源码中所有物种名都用 String.fromCharCode() 编码:

export const duck = c(0x64,0x75,0x63,0x6b) as 'duck'
export const goose = c(0x67,0x6f,0x6f,0x73,0x65) as 'goose'
// ... 18种全部如此

原因是CI中有 excluded-strings.txt 敏感字符串扫描——某个物种名恰好是一个模型代号的canary字符串。编码后源码不包含该字面量,CI检查通过,但运行时行为完全正常。

三、设计亮点总结

3.1 架构决策

决策 选择 原因
角色生成 确定性PRNG 同账号永远同宠物,无需服务端状态
数据持久化 仅存灵魂 骨骼每次重算,防作弊且向前兼容
帧动画 500ms tick 终端刷新率低,500ms体感自然
气泡布局 全屏浮动 / 普通内联 适配两种ScrollBox裁剪策略
功能开关 编译时feature gate 零运行时开销,完整DCE
发布策略 本地时区 24h滚动波,分散负载和话题热度

四、总结

Claude Code Buddy是一个完整度极高的终端电子宠物系统:

  • 18种物种 × 6种眼睛 × 8种帽子 × 5级稀有度 × 1%闪光率 = 数千种组合
  • 确定性生成保证一人一宠,防作弊且无服务端依赖
  • ASCII动画在7.5秒周期内自然流畅
  • 全屏/普通/窄终端三种布局无缝适配
  • AI驱动的反应系统让宠物真正在“看你写代码”
  • 编译时feature gate保证零成本禁用

它不只是一个愚人节彩蛋,更是一个经过严肃工程设计的终端伴侣系统。从算法设计到前端交互,再到与AI模型的巧妙联动,处处体现了对细节的考量。这种将趣味性与严谨技术实现结合的项目,正是开源文化中优秀工程实践的体现,值得开发者们细细品味。




上一篇:Claude Code 51万行TypeScript源码因Anthropic配置失误被强制开源,未发布功能一并泄露
下一篇:WSDL初探:从概念解析到SOAP服务安全测试实践
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 20:18 , Processed in 0.687028 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表