角色卡是一张普通 PNG 图片,角色数据以 base64 编码的 JSON 藏在图片的 tEXt 块里。图片本身是头像,删掉不影响数据;数据被删掉则只剩一张图。这就是为什么 角色卡能直接当图片传来传去,也是为什么某些图床会把卡「洗」坏。
角色卡的数据存在哪里
PNG 允许在图像数据之外携带若干文本块(tEXt chunk),每块有一个关键字和一段文本。 SillyTavern 用到两个关键字:
| 关键字 | 规范 | 内容 |
|---|---|---|
chara | Character Card V2 | base64 编码的 JSON |
ccv3 | Character Card V3(spec_version 为 3.0) | base64 编码的 JSON |
读取时两个都认,ccv3 优先。写入时 SillyTavern 总会写
chara,并尽量同时写一份 ccv3;如果 JSON 解析失败,就只留 V2 那份。
这套读写逻辑在
src/character-card-parser.js
里,不到一百行,值得一读。
有一个细节常被忽略:SillyTavern 在写卡时会先删掉原有的 chara 和
ccv3 块再写新的,避免同一张图里留下两份互相矛盾的数据。手工用第三方
工具塞数据时如果不做这一步,就可能出现「改了没生效」:读的是另一块。
角色卡有哪些字段,哪些会进提示词
核心字段十来个。真正决定角色表现的是 description、personality、
scenario 和 mes_example 这四个,其余要么是元信息,要么只在特定
场合生效。
| 字段 | 作用 | 是否进提示词 |
|---|---|---|
name | 角色名,同时用于替换 {{char}} | 是 |
description | 角色设定的主体,篇幅通常最长 | 是 |
personality | 性格摘要 | 是 |
scenario | 当前情境/场景设定 | 是 |
first_mes | 开场白,作为第一条消息插入聊天 | 作为消息 |
mes_example | 对话示例,用于示范文风 | 是(可被截断) |
alternate_greetings | 备用开场白,可在开场时切换 | 作为消息 |
system_prompt | 角色自带的系统提示,可覆盖预设 | 是 |
post_history_instructions | 放在历史之后的指令,即通常说的「破限位」 | 是 |
character_book | 随卡携带的世界书 | 按触发 |
creator_notes | 作者留言,给人看的 | 否 |
tags / creator / character_version | 元信息 | 否 |
字段的类型定义在
public/scripts/char-data.js。
注意 creator_notes 不进提示词。很多人把重要设定写在这里,然后
奇怪模型为什么不知道。
description 该写多长
没有硬性上限,但它每轮都会被完整发送。一张 1500 token 的卡,意味着每次回复都先花掉 1500 token 的上下文预算,并且是永久占用。
这带来一个取舍:写得越细,模型越了解角色,但留给对话历史的空间越少,也越早开始遗忘
前文。实践中的常见做法是把「始终成立」的核心特征放进 description,把
「只在特定话题出现时才需要」的细节挪进世界书,
让它按关键词触发。这样上下文只在真正需要时才被占用。
在手机上导入
导入方式和桌面端一致:在角色列表里选择导入,挑选那张 PNG 即可。由于数据就在图片里, 从微信、Discord 或网盘保存下来的卡通常可以直接用——前提是保存的是原图。 经过压缩、转码或截图的图片会丢掉 tEXt 块,剩下的就只是一张普通图片了。
如果导入报错说找不到角色数据,基本可以判定是这个原因。解决办法只有一个:回到来源 重新拿一次原始文件。