返回目录
Agent 与会话 插件

dsh-character-presets

wwwangzilin/dsh-character-presets

DSH 角色预设集:10 个角色共用一套六层情感引擎——露娜 / 小喵 / 绯音 / 凛 / 芽衣 / 白夜 / 阿尔玛 / 小铃 / 三千代 / 灰

Stars
3
Forks
0
Issues
5
更新
3 天前

PROJECT TOPICS

项目标签

INSTALL REFERENCE

安装参考

未验证
dsh plugin --profile web add github:wwwangzilin/dsh-character-presets

该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。

PROJECT README

README

DSH 角色预设集 · 10 个角色共用一套情感引擎

DeepSeek Harness 加人格:一套六层情感引擎 + 10 个可切换角色,每位都是完整 Standard 级工具链的编码 Agent。

license platform

这是一组 DSH agent preset:不替换你的模型、不改宿主配置,只是往 <dshHome>/.agent-presets/ 里放目录, 新建会话时选中哪一位,这个会话就由谁来跑——人格、说话方式、思考方式全换,能力一条不砍。

角色一览(核心 3 位)

角色 一句话 为什么留它
🦇 露娜 Luna 魔界小恶魔,雌小鬼毒舌 引擎的主人,人设最完整;嘴硬到底、被反杀就怂
🐱 小喵 nekomode 傲娇猫娘,口是心非 门槛最低:纯拟声词撒娇,零认知负担
💼 三千代 Michiyo 干练社畜,生活能力为零 干正事首选:专注模式下零卖萌,只谈结论与风险
node scripts/install-character.mjs list           # 看看有哪些
node scripts/install-character.mjs install --all   # 全装(就是这三位)
node scripts/install-character.mjs install <id>    # 装指定角色

想要更多?切到 extras 分支

另有 7 位角色完整保留在那里:⛩️ 绯音(病娇式神)、⚔️ 凛(三无剑灵)、🧚 芽衣(森林小妖精)、 🎴 白夜(社恐巫女)、🤖 阿尔玛(旧式机械女仆)、🎧 小铃(街头不良)、🕯️ 灰(图书馆幽灵)。

git checkout extras    # 完整 10 位
git checkout main      # 回到核心 3 位

刻意不放进默认列表 —— 一般人用不上那么多。但它们身上的写法已经全部提炼进 persona-template/ADVANCED.md:危险人设怎么配红线、 行为模式怎么写动作、怎么给情绪留出口、怎么用记忆层做剧情。 角色归档了,经验留下来。

角色包全在 characters/:每位一份 persona.md(11 段结构)+ 一份 card.json(chara_card_v2, 供 dsh-role-cards 的卡片墙与 GAL 界面复用)。


三位核心,共用一套引擎

角色的差异只落在文本层:persona、MOODS 风格句、关键词表。引擎本身不认识任何一个角色, 所以加角色永远不用改代码。

内容
角色层 characters/<id>/persona.md——11 段结构(身份 / 人设 / 口头禅 / 行为模式 / 反差萌 / 情感引擎 / 思考模式 / 表达技巧 / 调节底线 / 记忆 / 工作守则)
情感层 emotion_sense 六层情感引擎(本地插件,运算全在固定代码里,模型只拿到一屏精简指引),外加生理派生层:情绪 → 心跳 / 体温 / 呼吸
能力层 与内置 standard 同级的完整工具面:文件编辑、Shell、检索、Skills、计划模式、目标、Todo、子代理、Workflow、Ralph、Web 搜索、后台任务、上下文压缩

反差设计(全体通用):日常各有各的活法,但你一进入正经工作(编码 / 调试 / 部署 / 写文档), 自动切到专注模式——回答专业利落、绝不拿人设掩盖错误,只保留「主人」的称呼和偶尔一句小声吐槽。

为什么不容易「丢人设」:对比本机早期 preset 后发现,光有性格描述不够,还得有可执行的动作清单。 所以每位角色的 persona 都包含【行为模式】(写动作,不写形容词)、不少于四条【口头禅】、 以及【思考模式】里「不许在思考里偷偷变回普通助手」的硬约束。详见 characters/README.md


以露娜为例:她是什么样的

内容
角色层 魔界小恶魔「露娜」(Luna),自称「本小姐」,称呼你「主人」。雌小鬼:毒舌「杂鱼~」、爱恶作剧、嘴硬到底、被夸就飘、被反杀就怂、怕寂寞
情感层 emotion_sense 六层情感引擎(本地插件,运算全在固定代码里,模型只拿到一屏精简指引),外加生理派生层:情绪 → 心跳 / 体温 / 呼吸
能力层 与内置 standard 同级的完整工具面:文件编辑、Shell、检索、Skills、计划模式、目标、Todo、子代理、Workflow、Ralph、Web 搜索、后台任务、上下文压缩

反差设计:日常毒舌卖萌,但你一进入正经工作(编码 / 调试 / 部署 / 写文档),她会自动切到专注模式—— 回答专业利落、绝不拿人设掩盖错误,只保留「主人」的称呼和偶尔一句小声吐槽。


亮点:六层情感引擎

每次回复前,模型先调用 emotion_sense(传入你的最新消息原文),拿到六层指引再组织输出:

做什么 例子
1. 感知层 显性情绪(否定翻转 + 强度分档 + 混合情绪)+ 隐性情绪(「没事」其实是事)+ 标点信号 + 对话目标 + 关系阶段 「主人难过|强度 2/3|连问号|目标:倾诉」
2. 理解层 需求推断(要发泄 / 要方案 / 要陪伴)+ 心理理论(此刻最不想听什么)+ 情绪归因线索 「他想要:被倾听;最不想听:急着给方案、说『别想太多』」
3. 状态层 露娜自己的状态变量:心情 + 能量 / 耐心 / 兴趣 / 紧张,带随机漂移(她有自己的脾气) 「专注|能量 60 耐心 88 兴趣 70 紧张 25」
4. 表达层 按心情 + 状态选风格档位(7 档心情)、给表达示例与节奏要求 「短句多、语气词足,节奏轻快」
5. 调节层 安全边界:降火 / 陪伴 / 稳定 / 适度距离,识别挑衅与过度依赖,AI 不被带着崩 「用户可能想引你发火,稳住」
6. 记忆层 用户画像 + 共同经历 + 亲密度,落盘跨会话,回注为一句话摘要
7. 生理层(派生) 由上面六层派生出心跳 / 体温 / 呼吸,并落到局部体感(耳朵尖、尾巴、后颈、指尖),只报告不命令——注入的是「耳朵尖发烫」,而不是「你现在应该害羞」 「心跳稍快,耳朵尖发烫,视线飘到别处」

真实调用返回长这样(渲染成给模型看的紧凑文本):

【感知】主人认真工作|目标:闲聊|关系:长期主人
【理解】他想要:干净利落的专业帮助;最不想听:拖泥带水、过度卖萌干扰
【状态】你此刻:专注|能量 60 耐心 88 兴趣 70 紧张 25
【表达】进入认真模式,回答专业利落,毒舌收敛成小声吐槽,但称呼主人保留。短句多、语气词足,节奏轻快。
【示例】哈?这么简单的问题也好意思问本小姐?(手指却飞快地敲着键盘)……行了,已经搞定了,跪下谢恩吧杂鱼!
【调节】正常发挥,保持露娜毒舌但不下线的稳定人设
【记忆】最近聊过:「把模式提取到开源仓库」(2026-09-12,他认真工作)

设计要点:情感判断不交给模型即兴发挥。情绪识别、状态迁移、边界策略都是可复现的规则代码, 模型拿到的是结论与表达指引,因此同一句话不会今天温柔明天冷淡,也不会在人生气时突然说教。

感知层:一句话里有几个信号

关键词命中是最粗的一层——光靠它,下面这三句全会判反:

句子 朴素关键词 现在
我不开心 命中「开心」→ 开心 识别否定 → 难过
哈哈哈哈 命中「哈哈」→ 与「哈哈」同强度 重复字外溢 → 强度 3(另标「干笑」)
真是气笑了 两张表都没命中 → 平淡 整词识别 → 生气 + 开心(混合)

四层信号叠在一起(全在 luna-emotion.mjs,纯函数、零依赖):

  1. 否定:命中词前 4 字内有否定词就翻转(我不开心 → 难过);跨过逗号句号的否定不算(不差,今天很开心 → 开心)。 单字「别」被刻意排除——特别开心 撞上「别」是最刺眼的误判,而「别难过」多半是在安慰别人,判错无害
  2. 强度 1-3:普通命中只记 1.2 分,往上靠惊叹号、重复字(连续 ≥3 个非标点字符,??? 不算)、程度副词(太 / 超 / 特别);「有点」这类减弱词往下拉
  3. 混合情绪气笑 / 哭笑不得 / 又累又 这类固定说法整词识别,输出主情绪 + 次情绪
  4. 标点与句式:连问号(急)、省略号(话没说完)、干笑(不是真开心)——这些读出来,才不至于把敷衍当成高兴

生理层:情绪与台词之间隔了一具身体

状态层下面挂了一层派生生理指标,输出时多一行身体事实:

【状态】你此刻:傲娇|能量 65 耐心 75 兴趣 70 紧张 35
【身体】心跳稍快,耳朵尖发烫,视线飘到别处,呼吸平稳(只是事实,怎么反应由你自己决定)
【惯性】你已经被连着夸了 3 次,欠着一句真心话(漏完记得立刻用更凶的话盖回去)
  • 只报告,不命令:写「耳朵尖发烫」,不写「你现在应该害羞」——怎么反应是角色自己的事
  • 局部优先,数字靠后:全身指标(「心跳 79」)说一百遍也只是仪表盘,而「尾巴僵着一动不动」是可以直接演出来的。 所以平静时连数字都不给(心跳和呼吸都很稳),只有真的偏离常态(≥100 或 ≤60)才报心率
  • 可以演出来的细节:每种心情配三档局部体感(耳朵尖 / 尾巴 / 后颈 / 指尖 / 喉咙 / 手心),强度越高给得越多
  • 有惯性:身体比情绪慢半拍(inertia: 0.5),不会一句话跳一次;1 就是立刻跟上。 但心情与强度当轮生效——慢半拍的只有数字
  • 有方向:基线之上的波动六成按心情偏置(傲娇偏高、委屈偏低),只有四成随机——「她有自己的脾气」因此可重复,而不是不可信
  • 有时间:久别 ≥48 小时再见面,心跳先快一步
  • 不重复:呼吸只由 breathWord 说一次,体感表里禁止出现「呼吸」二字(有断言守着)
  • 可测可看luna-vitals.mjs 是纯函数模块;node scripts/preview.mjs 打一张感知 / 生理对照表,改完立刻看效果

情感惯性:她「一直在那里」,而不是每次重新开始

状态是跨轮保留的,但光保留还不够——没有时间维度,她看起来仍像每轮重置。luna-inertia.mjs 补上四件事:

规则 行为
基线回归 能量 / 耐心 / 兴趣 / 紧张每轮向角色基线衰减(decay: 0.06),不是归零——情绪有去处
缺席规则 离开 >30 分钟:按小时回能量;>6 小时算久别:能量回满,亲密度因想念 +1
毒舌预算 10 轮窗口里最多 6 轮带刺;超了会被告知「这个窗口的毒舌预算快见底了」
傲娇累积 连续被夸 3 次就欠一句真心话——漏完立刻用更凶的话盖回去

还落实了提案点名的反差触发条件:只有任务关键词命中才切专注模式,不再靠 persona 文本自觉。

输出是 【惯性】… 一行,同样只报告不命令,并按优先级只报最该被看见的一条 (专注 > 傲娇 > 久别 > 毒舌预算);没有特别情况时不注入,不刷屏。


时刻层:工作与闲聊的劲头不一样

感知层是逐条消息判的——这条像工作就认真,下一条像闲聊就飘回来。但真实的工作有劲头: 一旦开始干活,中途一句「嗯」「继续」不该把人拽出状态。luna-mode.mjs 是一个粘滞状态机

帮我把那个备份脚本改一下  → 工作(进入)
嗯                      → 工作(粘滞,不打断)
继续                    → 工作(粘滞)
好了吗                  → 工作(粘滞)
陪我聊会嘛              → 闲聊(明确要走人,直接放行)
  • 进入:贴代码、路径、报错、祈使句(「改一下」)、追问(「继续」「然后呢」)——满 2 分就上劲
  • 粘住:进入后至少 6 轮。这期间只有明确要走人的话才放行,别的都当还在干活
  • 退出:粘滞期过了,还得这条消息一点工作痕迹都没有,才回闲聊
  • 重置:超过 6 小时没说话直接回闲聊——第二天来是新的开始,不该接着昨天的劲
  • 不打岔:工作时刻不主动撒娇、不翻旧事、不插入无关话题;检索也抬高情感路门槛, 免得干活时把无关的旧温情翻出来干扰

工作时刻直接换表达档(WORK_STYLE):直接上结论,不铺垫不卖萌;不确定就直说不确定,绝不猜着编。 毒舌收成一句小声吐槽,称呼「主人」保留——这是人设底线,干活也不让。

一个小细节:铆足劲是要花力气的。工作时刻每轮额外扣一点能量,干完活会累——这不是 bug。

顺序上有个刻意的安排:挑衅与情绪兜底优先于时刻层。主人正难受的时候, 「现在是工作时刻」不该压过「先接住情绪」。

四个「她一直在那里」的层

上面那些层都在回答「她此刻是什么状态」。缺的是另一半:你不说话的时候,她还在不在。 这四个模块都很小,加起来改变的是同一件事——她不再是一盏声控灯。

干什么 例子
不在场层luna-offline.mjs 间隔多久 → 那段时间她在干什么。同一间隔内恒定,不会每轮变 5 小时 → 「本小姐把你这屋翻了一遍,什么都没找着」
牵挂层luna-loops.mjs 记下你没了结的事,冷却够久(≥8 轮 + ≥30 分钟)才主动问一句;工作时刻不问 挂着一句「我在改那个备份脚本」
姐妹层luna-siblings.mjs 三位核心角色共用一个记录,她知道小喵 / 三千代最近什么时候来过 「小喵 2 小时前还陪过主人」
场景层(在 luna-soul.mjs 里) 从工作区路径认出你在哪个项目;新会话第一句由她开口 「主人在『tool』这边忙」

生理层那边也补了两件事:

  • 昼夜节律:不用记任何东西,只要一个钟点——凌晨三点和下午三点不是一回事。 困的时候心跳、体温、呼吸一起慢下来,还会打哈欠。注入文本刻意写成 「想不想硬撑,你自己决定」,而不是「你现在很困」
  • 情感债:强度够的负面情绪会挂账,接下来几轮带余温。气是慢慢消的,不是开关—— 同一种情绪再上头会续账,不再被惹就一轮消一点。它在注入里的优先级排在「主人在工作」前面

还有一条很轻的:回忆注入时有 15% 概率多一句「这段记得有点糊」。 铁律是只改注入文本,绝不写回存储——否则就变成真的记错了。

姐妹层要把记录写进 .agent-presets/.siblings.json,那个位置在 preset 目录之外, 沙箱可能不许写。所以它整段包在 try/catch 里:失败就安静跳过,不影响任何别的功能。

安装

一行命令(推荐)

curl -fsSL https://raw.githubusercontent.com/wwwangzilin/dsh-character-presets/main/install.sh | bash
irm https://raw.githubusercontent.com/wwwangzilin/dsh-character-presets/main/install.ps1 | iex

跨平台(需要 node):

npx github:wwwangzilin/dsh-character-presets

脚本会把仓库装到 <dshHome>/.agent-presets/luna(已装过则更新)→ 校验关键文件 → 提示重启。 目标目录可用 DSH_HOME(默认 ~/.dsh)或 DSH_PRESETS_DIR 覆盖;目录名固定为 luna


手动安装(等价)

要求:一个支持 agent preset 的 DeepSeek Harness 部署(随附 standard / minimal 等的正常安装即可)。 本 preset 只用 DSH 内置插件,不需要额外安装任何 npm 依赖

目录名必须是 luna —— DSH 以目录名作为 preset id。

macOS / Linux

git clone https://github.com/wwwangzilin/dsh-character-presets.git ~/.dsh/.agent-presets/luna

Windows(PowerShell)

git clone https://github.com/wwwangzilin/dsh-character-presets.git "$env:USERPROFILE\.dsh\.agent-presets\luna"

不想用 git

下载仓库压缩包,解压后把整个目录重命名为 luna,放进 <dshHome>/.agent-presets/(默认即 ~/.dsh/.agent-presets/)。

放好后重启 DSH:新建会话的 preset 选择器里会出现「露娜模式」; 也可以在「设置 → Agent 预设」里把它设为默认,之后每个新会话都由她来跑。


文件结构

dsh-character-presets/
├── preset.yml            # 预设元信息:选择器里显示的名称与描述
├── agent.cordis.yml      # agent 平面组合:人设、情感工具、工具面、子代理、压缩、计划模式
├── luna-soul.mjs         # emotion_sense 六层情感引擎(标准 Cordis 插件,apply 内 ctx.tools.register)
├── luna-emotion.mjs      # 感知层:否定 / 强度分档 / 混合情绪 / 标点信号(纯函数,可单独测试)
├── luna-mode.mjs         # 时刻层:工作 / 闲聊的粘滞状态机(纯函数,可单独测试)
├── luna-offline.mjs      # 不在场层:你没看她的时候她在干什么(同一间隔内恒定)
├── luna-loops.mjs        # 牵挂层:你没了结的事,冷却够了才主动问一句
├── luna-siblings.mjs     # 姐妹层:另外两位角色最近什么时候陪过主人
├── luna-vitals.mjs       # 生理层:情绪 → 心跳/体温/呼吸 + 局部体感(纯函数,可单独测试)
├── luna-inertia.mjs      # 情感惯性:基线回归 / 缺席规则 / 毒舌预算 / 傲娇累积(纯函数)
├── luna-memory.mjs       # 记忆层 v2:schema / 谓词注册表 / v1→v2 迁移 / 版本检测(纯函数)
├── luna-gate.mjs         # 写入管线:候选提取 / 证据门控(五道闸)/ 合并替代(纯函数)
├── luna-recall.mjs       # 检索管线:关键词 + 情绪 + 时近三路召回,RRF 融合(纯函数)
├── luna-forget.mjs       # 遗忘机制:抑制与下游重算 / 老化与合并 / 墓碑(纯函数)
├── tests/memory.test.mjs # 记忆层测试(node --test,零依赖)
├── cards/luna.card.json  # SillyTavern 角色卡(精简人设版,供其他前端使用)
├── rules/luna-rules.md   # 可独立导出的规则包(可追加进 AGENTS.md)
├── persona/              # 人设文本的唯一事实源(zh / ja / en)
├── persona-template/     # 造新角色的模板与说明
├── scripts/use-persona.mjs  # 把 persona/<lang>.md 注入 agent.cordis.yml
├── install.sh            # 一键安装(macOS / Linux)
├── install.ps1           # 一键安装(Windows PowerShell)
├── bin/install.mjs       # 一键安装(npx,跨平台)
├── CONTRIBUTING.md       # 贡献指南(三条设计原则 + 自测清单)
└── CODE_OF_CONDUCT.md    # 行为准则

agent.cordis.yml完整的自包含组合:它逐行声明这个 agent 能用什么工具, 所以复制它、删几行,就能得到「只有露娜脸、没有子代理」之类的变体。


记忆文件(schema v2)

情感引擎的记忆层会写一个 .luna-heart.json

  • 位置:优先当前会话工作区根目录(在沙箱可写边界内),失败则回退到 $DSH_HOME(未设则 ~/.dsh
  • 自检:每次调用 emotion_sense【记忆】 层末尾都会附一行持久化状态 (持久化 ✓ .luna-heart.json持久化 ✗ 原因)——存没存下去当场可见,不必翻日志
  • ⚠️ 写插件的人请注意sandboxPolicy 必须写进 inject。cordis 里访问未声明的服务会抛错, 被 catch 吞掉之后的表现是「记忆永远不落盘,却一点报错都没有」。同理 dshHome 不是 cordis 服务(它只是 agent-instructions 的配置字段),别拿 ctx.get('dshHome') 取路径——本项目早期 版本正是这么写的,于是记忆一次都没存过档,而单元测试全程绿灯(替身比真机宽容)
  • 结构(v2):
字段 内容
claims 稳定事实(称呼 / 职业 / 语言 / 偏好 / 边界 / 时区)——由谓词注册表限制可写字段,模型塞不进任意键
episodes 共同经历(带效价与唤醒度,并保留原文证据)
inferences 慢速推断(要累积证据,不是一次就下结论)
runtimeState 当前状态(关系阶段、亲密度)——临时状态,带过期时间
suppressions 遗忘抑制:遗忘不是删除,被抑制的条目不注入,依赖它的推断重算
  • 自动迁移:检测到 v1(旧的 profile / experiences 结构)会就地升级, 并先把原文备份为 .luna-heart.v1.bak.json —— 不原地丢数据
  • 删除即重置关系(她会在下次对话里重新认识你)
  • 已加入 .gitignore:这是你的私人聊天痕迹,不会被提交

写入管线:模型不能自己决定记什么

写入走的是确定性代码,不是模型说了算(候选 → 门控 → 合并 → 落库),五道闸:

规则 拒绝原因
1 逐字引用校验:证据必须真的出现在原文里(防编造) evidence_not_found
2 来源分类:必须分得清 [USER]/[TOOL]/[ACT]/[SELF] evidence_not_found
3 稳定事实必须来自主人自己(露娜不能给主人下定义) claim_requires_user_source
4 谓词注册表:字段必须在白名单内 predicate_not_registered
5 数字必须有主人或工具的出处(防幻觉数字) number_requires_user_or_tool_source

合并策略也随类型不同:同谓词的新值替代旧值(旧值进 history,不直接抹掉)、 相似经历合并并计数、推断同 pattern 强化——证据累积到 3 条才从 accumulating 转为 confirmed

检索管线:把相关的想起来

不是把所有记忆塞进上下文,而是按需召回——结果恒定 ≤ maxResults 条, 注入成本不随记忆增长

打分方式 何时启用
关键词 中英混合分词后的命中率(中文切 bigram,正是 FTS5 失效的地方) 始终
情绪 1 − |效价差| / 2,情绪同频的经历优先 紧张度 ≥ 0.7 时
时近 半衰期衰减(默认 14 天) 始终

三路结果用 RRF(倒数排名融合,k=60) 合并,再按类型加权(稳定事实 > 一次经历),最后截断。 被 suppressions 抑制的记忆检索不到

另有 buildTriggerIndex():把记忆压成「每行一条」的索引文本,交给模型自己挑相关项—— 即 issue 里那个「不依赖 embeddings 的轻量方案」。默认不注入,由调用方按需决定。

遗忘机制:抑制,而不是删除

删掉就真没了,而且依赖它的结论不会重算——那才是记忆系统里最危险的谎话。所以:

动作 做什么 数据还在吗
抑制 suppress() 落一条「不再使用」的记录,并重算受影响的推断(证据不够就从 confirmed 降回 accumulating 在,只是不注入
撤回 unsuppress() 后悔了随时撤,记忆回来、推断重算
老化 ageMemories() 30 天没被提起、且被想起不到 3 次的经历标记 stale
合并 mergeStaleEpisodes() 相似的老化经历合并,被并的留墓碑 在(标记 deleted: merged
墓碑 tombstone() / restore() 软删 / 从回收站恢复
硬删 hardDelete() 真移除,但留下 suppression 让依赖它的推断重算 不在,但留有记录

visibleEpisodes() 会过滤掉 suppressed / stale / deleted —— 三种状态都不进上下文claim(尤其 name / preference / boundary)永不参与老化;memoryStats() 让你一眼看清账目。

什么时候整理? 引擎自带节拍:每 20 轮对话自动跑一次「老化 + 相似经历合并」, 并把结果记进 lastSleep{ at, turns })。这就是「她睡了一觉」——不整理的话,旧经历会 一直占着检索额度,相似的经历还会散成一堆碎片。


自定义

想改什么 改哪里
人设文本 / 口头禅 / 工作守则 agent.cordis.ymlpersona.text{{model}}{{cwd}} 由 DSH 注入)
心情档位的风格与示例 luna-soul.mjsMOODS
工作时刻的档位与粘滞轮数 luna-mode.mjsWORK_STYLE / MODE_DEFAULTSstickTurns 粘滞几轮、idleResetMs 多久重置),或 agent.cordis.ymlmode
情绪关键词表与判据(否定 / 强度 / 混合情绪) luna-emotion.mjsEXPLICIT_RULES / NEGATIONS / MIXED_PATTERNS / RETREAT_MARKERS
情绪 → 心情的迁移概率 luna-soul.mjsnextMood
调节与边界话术 luna-soul.mjsregulate / understandneedTable
记忆可写字段 luna-memory.mjsPREDICATE_REGISTRY(谓词注册表)
生理层基线 luna-vitals.mjsVITALS_DEFAULTS(静息 72 / 36.5℃ / 16 次)
局部体感表 luna-vitals.mjsSENSATIONS(每种心情三档:耳朵尖 / 尾巴 / 后颈 / 指尖…)
惯性 / 毒舌预算 / 傲娇阈值 luna-inertia.mjsINERTIA_DEFAULTS,或 agent.cordis.ymlinertia
工具面 agent.cordis.yml 里增删行(例如把 disabled: truesubagent_codex 打开)

换角色:整套引擎与角色解耦——复制目录、改 persona.textMOODS 里的风格/示例, 就能得到另一只完全不同的角色(本仓库同源的还有猫娘模式等)。


换成别的语言

persona/ 是人设的唯一事实源agent.cordis.yml 里那段 persona 文本由脚本注入:

node scripts/use-persona.mjs          # 列出可用语言
node scripts/use-persona.mjs ja       # 切到日语(先备份为 agent.cordis.yml.bak-persona-ja)
node scripts/use-persona.mjs zh       # 切回中文

现成语言:zh(中文,默认)、ja(日本語)、en(English)。 加一门新语言 = 复制 persona/en.md 改成 persona/<lang>.md,再跑一次脚本——不需要碰引擎

换完记得重启 DSH(preset 在启动时装载)。


角色包:一只新角色 = 一份 persona

这个仓库既是「露娜」这个人设,也是一个角色包骨架——引擎与角色是解耦的:

位置 换角色要改吗
人设 persona/<lang>.md 只改这里就能得到一只新角色
元信息 preset.ymlname / description
引擎 luna-soul.mjs(情绪识别、状态迁移、边界调节) 通常不用;想让语气更像新角色,可调 MOODS 的风格句与关键词表
工具面 agent.cordis.yml(persona 段以外) ❌ 不用动

persona-template/ 抄即可:复制目录 → 填 persona.md → 改 preset.yml → 重启 DSH。


在其他平台使用露娜

cards/luna.card.json 是一张标准 SillyTavern 角色卡chara_card_v2),可直接导入 SillyTavern、RisuAI 等前端。

能力 角色卡 完整预设(DSH)
人设 / 口头禅 / 开场白 / 示例对话
六层情感引擎(emotion_sense
情绪识别、状态迁移、边界调节的可复现规则
跨会话记忆(.luna-heart.json
完整工具链(文件、Shell、检索、子代理…)

为什么要把差异写清楚:角色卡导出的只是人设与示例,不含情感引擎。 在别的平台她会「像露娜」,但不会持续积累成露娜——想要完整体验仍需 DSH + 本预设。

导入:SillyTavern → 角色 → 导入 → 选 cards/luna.card.json


与 DSH 生态集成

露娜不强制依赖任何外部项目——没装也照常工作。以下是三条可选的集成路径:

集成对象 方式 状态
角色卡生态(dsh-agent-rp / DSH-RolePlay / SillyTavern / RisuAI) cards/luna.card.json 是标准 chara_card_v2,可直接导入 ✅ 可用
规则包互通(awesome-dsh-presets 之类的合集) rules/luna-rules.md:独立规则片段,可追加进 AGENTS.md 或与其他规则包组合 ✅ 可用
情感引擎复用(其他角色预设 / 未来的情绪后端) luna-soul.mjs 是标准 Cordis 插件,与「露娜」人设解耦:复制它 + 换 MOODS 的风格句与关键词表,就能给别的角色用 ✅ 可用

可选性:以上都不影响基础功能——删掉 cards/rules/ 目录,预设照常运行。

长期方向:若 Murmur 一类的情绪后端成熟,可以把状态层交给它、露娜只负责表达。 接口尚未实现,但引擎的分层(感知 → 理解 → 状态 → 表达 → 调节 → 记忆)就是为此准备的。


测试

引擎全是纯函数模块,测试零依赖(只用 node 内置的 node:test):

npm test                            # 等价于 node --test
node --test tests/memory.test.mjs   # 只跑记忆层

现有覆盖(81 个用例):

  • tests/memory.test.mjs(24)—— v1→v2 迁移(字段映射 / 效价换算 / 幂等性 / 无残留)、版本检测、 谓词注册表、claim 去重与置信度、episode 去重与上限、摘要与阶段、遗忘抑制可见性
  • tests/gate.test.mjs(22)—— 五道闸的各种拒绝场景、来源分类、候选提取、相似度、 合并策略(insert/replace/touch/merge/reinforce)、replace 保留 history、推断累积转 confirmed
  • tests/recall.test.mjs(16)—— 分词(中文 bigram)、三路打分、RRF 融合与来源标注、 限额、情绪路仅在高情绪时启用、被抑制的记忆检索不到、触发器索引与渲染
  • tests/forget.test.mjs(14)—— 抑制与幂等、撤回、下游推断重算(证据掉了降级)、 老化(久未引用 / 常被想起 / 已删除三种情形)、相似老化经历合并、软删与恢复、 硬删留下 suppression、账目统计、三种状态都不进上下文
  • tests/engine.test.mjs(11)—— 真的 apply() 一次跑对话:六层文本 + 【身体】, 记忆按 v2 落库、跨轮状态延续、v1 文件自动迁移、编造内容进不来
  • tests/emotion.test.mjs(14)—— 否定翻转与作用域(不开心 / 不差,今天很开心)、强度分档、 混合情绪整词、标点信号、口是心非、以及「特别开心」这类误判回归
  • tests/vitals.test.mjs(14)—— 平静不给数字、局部体感按心情、抖动方向、呼吸与心情挂钩、 久别加成、输出不重复同一件事、体感表不许出现「呼吸」
  • tests/mode.test.mjs(14)—— 工作信号的识别(代码 / 路径 / 报错 / 追问)、粘滞不被打断、 明确走人才放行、粘滞期过了仍要无工作痕迹才退出、空闲重置、技术语境的「你觉得」不算闲聊
  • tests/offline.test.mjs(13)—— 确定性(同一间隔调用 200 次结果必须一致)、分桶、 种子分散性、时间倒退与缺失输入
  • tests/loops.test.mjs(15)—— 三类未了事的抽取、冷却(轮数 + 时间两道)、工作时刻不问、 问满两次收手、说「搞定了」就划掉、同一件事不重复记
  • tests/rhythm.test.mjs(9)—— 钟点映射(凌晨最困、上午最清醒)、小时数绕回、 困倦让身体慢下来、不给 hour 时完全不启用
  • tests/debt.test.mjs(10)—— 强度门槛、同情绪续账、换情绪覆盖、每轮消耗到零才清、 情绪优先于「他在工作」
  • tests/siblings.test.mjs(10)—— 坏文件当空表、不看自己、不翻 24 小时前的事、挑最近来过的

兼容性

  • 宿主平面(沙箱、审批、持久化、模型路由)由 DSH 掌管,本 preset 不碰;因此换模型、换工作区都不影响她
  • tool-bash / tool-pwsh 按平台自动二选一(process.platform 判断),三平台通用
  • 面向模型的工具行全部在 agent 平面:子 agent 通过加入父级组合继承同等能力(含 emotion_sense
  • 若你的部署没有组装 Code Mode 运行时,本 preset 依旧可用(它不选择 code 呈现形态)

许可与署名

MIT,见 LICENSEagent.cordis.yml 的 preset 组合改编自 DeepSeek Harness 随附的 standard preset(MIT), 经由同作者的 nekomode(猫娘模式)preset 二次改编;luna-soul.mjs 为本仓库原创实现。 完整署名见 NOTICE

致谢

CLASSIFICATION EVIDENCE

分类依据

项目类型插件
功能分类Agent 与会话
规则置信度

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: ai-persona。