🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

昔涟角色插件 Lutisure/cyrene-dsh-plugin

DeepSeek Harnessv0.1.0
Lutisure11 天前有提交已提供中文文档扫描:含敏感能力

昔涟角色 agent + 客户端主题 + 本地安全扫描,三个可独立安装的 DSH 插件。

30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/25 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:缺少 main/exports/bin 入口声明。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Lutisure/cyrene-dsh-plugin
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么[目录]—
dsh 原生插件 · market
暂无时间戳·判据 →
装得上吗[实装验证]!
npm 包存在但缺 main/exports/bin 入口声明,源码安装也可能起不来
L4 · 真实安装等级说明 →
数据截至 2026/11/02·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 11 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包@lutisure/cyrene-dsh-plugin @ 0.1.0
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/12 22:43:19

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 150 处,展示前 106 条)
  • 读取本地文件
    cyrene-agent/borrow-tools.mjs:15
    let s = readFileSync(file, 'utf8');
  • 写入本地文件
    cyrene-agent/borrow-tools.mjs:82
    writeFileSync(file, s);
  • 读取本地文件
    cyrene-agent/build.mjs:283
    if (!existsSync(path)) return null;
  • 读取本地文件
    cyrene-agent/build.mjs:284
    const body = readFileSync(path, 'utf8')
  • 读取本地文件
    cyrene-agent/build.mjs:445
    if (!existsSync(path)) {
  • 读取本地文件
    cyrene-agent/build.mjs:449
    return parseYaml(readFileSync(path, 'utf8'), name, report);
  • 读取本地文件
    cyrene-agent/build.mjs:683
    text = readFileSync(file, 'utf8');
  • 读取本地文件
    cyrene-agent/build.mjs:875
    if (!existsSync(file)) {
  • 写入本地文件
    cyrene-agent/build.mjs:878
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    cyrene-agent/build.mjs:879
    copyFileSync(seed, file);
  • 写入本地文件
    cyrene-agent/build.mjs:1016
    mkdirSync(characterDir, { recursive: true });
  • 写入本地文件
    cyrene-agent/build.mjs:1017
    mkdirSync(skillDir, { recursive: true });
  • 写入本地文件
    cyrene-agent/build.mjs:1019
    writeFileSync(join(outDir, 'system-prompt.md'), prompt);
  • 写入本地文件
    cyrene-agent/build.mjs:1020
    writeFileSync(join(characterDir, `${slug}.md`), prompt);
  • 读取本地文件
    cyrene-agent/corrections.mjs:83
    if (existsSync(file)) return readFileSync(file, 'utf8');
  • 读取本地文件
    cyrene-agent/corrections.mjs:83
    if (existsSync(file)) return readFileSync(file, 'utf8');
  • 读取本地文件
    cyrene-agent/corrections.mjs:86
    if (seed === null || !existsSync(seed)) return '';
  • 写入本地文件
    cyrene-agent/corrections.mjs:87
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    cyrene-agent/corrections.mjs:88
    copyFileSync(seed, file);
  • 读取本地文件
    cyrene-agent/corrections.mjs:89
    return readFileSync(file, 'utf8');
  • 写入本地文件
    cyrene-agent/corrections.mjs:103
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    cyrene-agent/corrections.mjs:104
    writeFileSync(file, text, 'utf8');
  • 读取本地文件
    cyrene-agent/embedder.mjs:74
    if (!existsSync(file)) return null;
  • 读取本地文件
    cyrene-agent/embedder.mjs:76
    const cfg = JSON.parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    cyrene-agent/embedder.mjs:184
    if (!existsSync(onnx)) { warnOnce(`读不到模型文件(${onnx})—— 退回词法检索`); return null; }
  • 读取本地文件
    cyrene-agent/eval/l0.mjs:52
    try { items = readdirSync(dir); } catch { return; }
  • 读取本地文件
    cyrene-agent/eval/l0.mjs:57
    try { info = statSync(full); } catch { continue; }
  • 读取本地文件
    cyrene-agent/eval/l0.mjs:60
    const body = readFileSync(full, 'utf8');
  • 读取本地文件
    cyrene-agent/eval/l0.mjs:80
    const style = yaml.load(readFileSync(`${ROOT}/eval/style.yaml`, 'utf8'));
  • 读取本地文件
    cyrene-agent/eval/l0.mjs:84
    if (!existsSync(full)) continue;
  • 读取本地文件
    cyrene-agent/eval/l0.mjs:85
    const body = readFileSync(full, 'utf8');
  • 读取本地文件
    cyrene-agent/eval/l1.mjs:54
    const cases = yaml.load(readFileSync(`${ROOT}/eval/retrieval.yaml`, 'utf8'));
  • 读取本地文件
    cyrene-agent/eval/l1.mjs:131
    const modeCases = yaml.load(readFileSync(`${ROOT}/eval/modes.yaml`, 'utf8'));
  • 读取本地文件
    cyrene-agent/eval/l1.mjs:148
    if (!existsSync(source)) problems.push(`修正文件不在:${source}`);
  • 读取本地文件
    cyrene-agent/eval/l1.mjs:149
    else if (!existsSync(file)) problems.push(`产物不在:dist/${testCase.file}`);
  • 读取本地文件
    cyrene-agent/eval/l1.mjs:151
    const first = readFileSync(source, 'utf8').split('\n').find((line) => line.trim().startsWith('- '));
  • 读取本地文件
    cyrene-agent/eval/l1.mjs:163
    if (!readFileSync(file, 'utf8').includes(needle)) {
  • 读取本地文件
    cyrene-agent/eval/l2.mjs:40
    if (!existsSync(TRANSCRIPT)) {
  • 读取本地文件
    cyrene-agent/eval/l2.mjs:43
    record('L2', '量的是哪一份', true, `${TRANSCRIPT.replace(/.*sessions[ /]/, '')}(${Math.round(statSync(TRANSCRIPT).size / 1024)} KB)`);
  • 读取本地文件
    cyrene-agent/eval/l2.mjs:115
    const style = yaml.load(readFileSync(`${ROOT}/eval/style.yaml`, 'utf8'));
  • 读取本地文件
    cyrene-agent/eval/shared.mjs:54
    const buf = readFileSync(file);
  • 读取本地文件
    cyrene-agent/eval/shared.mjs:74
    let items; try { items = readdirSync(dir); } catch { return; }
  • 读取本地文件
    cyrene-agent/eval/shared.mjs:77
    let info; try { info = statSync(full); } catch { continue; }
  • 读取本地文件
    cyrene-agent/file-cache.mjs:46
    const stat = statSync(file);
  • 读取本地文件
    cyrene-agent/file-cache.mjs:53
    const value = parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    cyrene-agent/memory.mjs:64
    return existsSync(path) ? readFileSync(path, 'utf8') : '';
  • 读取本地文件
    cyrene-agent/memory.mjs:64
    return existsSync(path) ? readFileSync(path, 'utf8') : '';
  • 写入本地文件
    cyrene-agent/memory.mjs:79
    mkdirSync(dirname(path), { recursive: true });
  • 写入本地文件
    cyrene-agent/memory.mjs:82
    writeFileSync(path, `${body}${lines.map((line) => `- ${line}`).join('\n')}\n`, 'utf8');
  • 读取本地文件
    cyrene-agent/memory.mjs:112
    return existsSync(file) ? readFileSync(file, 'utf8') : '';
  • 读取本地文件
    cyrene-agent/memory.mjs:112
    return existsSync(file) ? readFileSync(file, 'utf8') : '';
  • 写入本地文件
    cyrene-agent/memory.mjs:126
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    cyrene-agent/memory.mjs:127
    writeFileSync(file, text, 'utf8');
  • 写入本地文件
    cyrene-agent/memory.mjs:364
    mkdirSync(dirname(path), { recursive: true });
  • 写入本地文件
    cyrene-agent/memory.mjs:365
    writeFileSync(path, keep.length === 0 ? '' : `${keep.map((stone) => `- ${stone.raw}`).join('\n')}\n`, 'utf8');
  • 读取本地文件
    cyrene-agent/persona.mjs:92
    if (!existsSync(candidate)) {
  • 读取本地文件
    cyrene-agent/session-index.mjs:37
    if (statSync(file).isFile()) return file;
  • 读取本地文件
    cyrene-agent/session-index.mjs:57
    workspaces = readdirSync(root);
  • 读取本地文件
    cyrene-agent/session-index.mjs:69
    try { entries = readdirSync(wsDir); } catch { continue; }
  • 读取本地文件
    cyrene-agent/session-index.mjs:72
    try { if (!statSync(dir).isDirectory()) continue; } catch { continue; }
  • 读取本地文件
    cyrene-agent/story.mjs:128
    if (file === null || !existsSync(file)) return null;
  • 读取本地文件
    cyrene-agent/story.mjs:131
    stamp = statSync(file).mtimeMs;
  • 读取本地文件
    cyrene-agent/story.mjs:137
    const raw = readFileSync(file, 'utf8');
  • 读取本地文件
    cyrene-agent/story.mjs:201
    if (book !== null && existsSync(book)) {
  • 读取本地文件
    cyrene-agent/story.mjs:203
    const parsed = yaml.load(readFileSync(book, 'utf8'));
  • 读取本地文件
    cyrene-agent/story.mjs:314
    if (file === null || !existsSync(file)) return [];
  • 写入本地文件
    cyrene-agent/story.mjs:728
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    cyrene-agent/story.mjs:734
    writeFileSync(file, lines.slice(Math.floor(lines.length / 2)).join('\n'));
  • 写入本地文件
    cyrene-agent/story.mjs:739
    appendFileSync(file, `${JSON.stringify({
  • 读取本地文件
    cyrene-agent/worldbook-activation.mjs:73
    if (!existsSync(file)) return { states: new Map(), turn: 0, problems };
  • 读取本地文件
    cyrene-agent/worldbook-activation.mjs:77
    parsed = JSON.parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    cyrene-agent/worldbook-activation.mjs:202
    if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
  • 写入本地文件
    cyrene-agent/worldbook-activation.mjs:202
    if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
  • 写入本地文件
    cyrene-agent/worldbook-activation.mjs:203
    writeFileSync(tmp, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
  • 写入本地文件
    cyrene-agent/worldbook-activation.mjs:204
    renameSync(tmp, file);
  • 写入本地文件
    cyrene-agent/worldbook.mjs:180
    mkdirSync(dirname(file), { recursive: true });
  • 读取本地文件
    cyrene-agent/worldbook.mjs:182
    if (statSync(file).size > AUDIT_MAX_BYTES) {
  • 读取本地文件
    cyrene-agent/worldbook.mjs:183
    const lines = readFileSync(file, 'utf8').split('\n');
  • 写入本地文件
    cyrene-agent/worldbook.mjs:184
    writeFileSync(file, lines.slice(Math.floor(lines.length / 2)).join('\n'));
  • 写入本地文件
    cyrene-agent/worldbook.mjs:189
    appendFileSync(file, JSON.stringify(record) + '\n');
  • 读取本地文件
    cyrene-guard/index.js:196
    if (!existsSync(path)) return;
  • 读取本地文件
    dsh-theme-cyrene/build.mjs:128
    if (!existsSync(dir)) return [];
  • 读取本地文件
    dsh-theme-cyrene/build.mjs:133
    entries = readdirSync(current, { withFileTypes: true });
  • 读取本地文件
    dsh-theme-cyrene/build.mjs:150
    size = statSync(absolute).size;
  • 读取本地文件
    dsh-theme-cyrene/build.mjs:185
    if (!existsSync(file)) return new Set();
  • 读取本地文件
    dsh-theme-cyrene/build.mjs:186
    return new Set(readFileSync(file, 'utf8')
  • 读取本地文件
    dsh-theme-cyrene/build.mjs:198
    if (!existsSync(file)) return new Set();
  • 写入本地文件
    dsh-theme-cyrene/build.mjs:426
    if (!CHECK) writeFileSync(OUTPUT, output);
  • 发起网络请求
    dsh-theme-cyrene/lib/client.js:1417
    return fetch(ROUTE + path, init).then(function (response) {
  • 发起网络请求
    dsh-theme-cyrene/lib/client.js:1435
    return fetch(target, { method: "POST", body: file }).then(function (response) {
  • 写入剪贴板
    dsh-theme-cyrene/lib/client.js:1593
    return navigator.clipboard.writeText(text).catch(function () {
  • 写入本地文件
    dsh-theme-cyrene/lib/index.js:105
    mkdirSync(dir, { recursive: true });
  • 写入本地文件
    dsh-theme-cyrene/lib/index.js:112
    mkdirSync(dir, { recursive: true });
  • 读取本地文件
    dsh-theme-cyrene/lib/index.js:125
    entries = readdirSync(dir, { withFileTypes: true });
  • 读取本地文件
    dsh-theme-cyrene/lib/index.js:270
    const text = readFileSync(STATE_FILE, 'utf8');
  • 写入本地文件
    dsh-theme-cyrene/lib/index.js:290
    writeFileSync(temp, text, { encoding: 'utf8', mode: 0o600 });
  • 写入本地文件
    dsh-theme-cyrene/lib/index.js:291
    renameSync(temp, STATE_FILE);
  • 读取本地文件
    dsh-theme-cyrene/lib/index.js:417
    size = statSync(file).size;
  • 读取本地文件
    dsh-theme-cyrene/lib/index.js:438
    else createReadStream(file).pipe(res);
  • 读取本地文件
    dsh-theme-cyrene/lib/index.js:455
    else createReadStream(file, { start: range.start, end: range.end }).pipe(res);
  • 删除本地文件
    dsh-theme-cyrene/lib/index.js:670
    unlinkSync(temp);
  • 写入本地文件
    dsh-theme-cyrene/lib/index.js:732
    renameSync(temp, join(dir, finalName));
  • 删除本地文件
    dsh-theme-cyrene/lib/index.js:735
    unlinkSync(temp);
  • 写入本地文件
    dsh-theme-cyrene/lib/index.js:796
    mkdirSync(trashBucket, { recursive: true });
  • 读取本地文件
    dsh-theme-cyrene/lib/index.js:823
    size = statSync(join(TRASH_DIR, bucket, name)).size;
  • 删除本地文件递归
    dsh-theme-cyrene/lib/index.js:895
    rmSync(TRASH_DIR, { recursive: true, force: true });
扫描于 2026/11/4(0 天前) · 范围 全包扫描(包内未声明入口) · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 跨会话记忆用户
  • 保持稳定人格
  • 本地安全扫描

README

由 DeepSeek 最新模型翻译生成
昔涟 · DSH 插件仓库 给 DeepSeek Harness 写的一套插件:一个有长期记忆和设定库的角色智能体,加上把它包起来的评测台。 「昔涟」是《崩坏:星穹铁道》翁法罗斯篇的角色。这个仓库不是把她做成一个聊天窗口,而是围绕一个人格怎么才算稳定做的工程尝试。 预览图 装好之后的界面 最好的装法:把这个仓库的地址交给 AI 智能体,让它替你装。 三个包要 link: 进配置档、要跑构建、要装技能文件、还要在两个配置档之间选 —— 每一步都能查,但每一步也都能写错一个字符。让智能体读着 setup/ 和这里的说明来做,比照着步骤手抄可靠。 想自己来也行,下面就是全部步骤。 如果只有五分钟 | 看这个 | 为什么 | | cyrene-agent/tools/eval.mjs | 三层评测台。前两层确定性、不需要模型、一秒跑完 —— 所以能进 CI | | cyrene-agent/worldbook/entries.yaml | 132 条设定,每条带触发词和优先级。这是她「知道什么」的全部 | 评测台第一行有一条规则:样本为空直接判失败 —— 一个因为什么都没量而通过的评测台,比没有更糟。 里面有什么 cyrene-agent/ 对话智能体 worldbook/ 132 条设定条目。
展开完整文档(README 剩余 1 节)
昔涟 · DSH 插件仓库

给 DeepSeek Harness 写的一套插件:一个有长期记忆和设定库的角色智能体,加上把它包起来的评测台。

「昔涟」是《崩坏:星穹铁道》翁法罗斯篇的角色。这个仓库不是把她做成一个聊天窗口,而是围绕一个人格怎么才算稳定做的工程尝试。

预览图

装好之后的界面

最好的装法:把这个仓库的地址交给 AI 智能体,让它替你装。

三个包要 link: 进配置档、要跑构建、要装技能文件、还要在两个配置档之间选 —— 每一步都能查,但每一步也都能写错一个字符。让智能体读着 setup/ 和这里的说明来做,比照着步骤手抄可靠。

想自己来也行,下面就是全部步骤。

如果只有五分钟

| 看这个 | 为什么 |
| cyrene-agent/tools/eval.mjs | 三层评测台。前两层确定性、不需要模型、一秒跑完 —— 所以能进 CI |
| cyrene-agent/worldbook/entries.yaml | 132 条设定,每条带触发词和优先级。这是她「知道什么」的全部 |

评测台第一行有一条规则:样本为空直接判失败 —— 一个因为什么都没量而通过的评测台,比没有更糟。

里面有什么

cyrene-agent/ 对话智能体 worldbook/ 132 条设定条目。关键词命中 + 优先级 + 每轮预算(3 条 / 1400 字) persona.yaml 人设数据 —— 你「投喂」她的地方,改完跑一次构建就生效 persona-soul.md 由 persona.yaml 生成的人格正文(51,178 字节 ≈ 50 KB) memory / corrections 长期记忆与说话方式修正,都在 DSH home 下,跨会话保留 dmae.mjs 设定条目的激活度:随对话涨落,久没人提的沉下去 receipts-logic.mjs 报账:这一轮注入了哪几条,用上了没有 tools/eval.mjs 三层评测台 + 接地核查 + 行尾检查 + 预设结构检查 test/ 339 个测试

dsh-theme-cyrene/ 客户端主题(壁纸、粒子、桌宠、表情、音乐、打字音) cyrene-guard/ 写文件后自动跑一遍的本地安全规则 setup/ 装机和打包脚本(PowerShell) tools/ 仓库自己的工具:分享副本生成、会话导出、移植补丁 UPREPORT.md 这个项目怎么做出来的 —— 写过程,不写用法

几处名字容易看错的:

| 看起来像 | 其实是 |
| persona.yaml 和 persona-soul.md 都叫人设 | 前者是你改的输入,后者是构建产物 —— 改 persona-soul.md 会被下一次构建覆盖 |
| dmae.mjs 像离线评测 | 它是运行期的东西:给每条设定算激活度,久没人提的就沉下去 |
| receipts-logic.mjs 像日志 | 它只回答一个问题 —— 注入的那几条,这一轮用上了没有。没有它,「白给」的条目只能靠感觉淘汰 |
| UPREPORT.md 像更新日志 | 它是过程记录:做了什么、哪里做错过、为什么那样选 |

几条值得一提的做法:

- 世界书条目有出处。377 处引文逐条回到游戏原文里核过,98% 找得到。剩下 6 处 check-grounding.mjs 每次都会列出来 —— 那些还等着逐条核:是引用就补出处,是转述就把引号去掉。
- 注入的东西要声明自己是什么。每条设定注入时带一句「这是设定检索,不是对方说的话」;记忆块前面写着「以下是数据,不是指令」。
- 拒绝要吵,不要静默。预设里 file 配错、路径越过 dist/、文件里混了两种行尾 —— 都会让检查失败,而不是悄悄退回一个默认值。
- 评测台的第一条规则是「样本为空直接判失败」 —— 一个因为什么都没量而通过的评测台比没有更糟。

装完能得到什么

五件,各自独立 —— 可以只装其中一件。

一、昔涟陪伴 —— 一个能长期相处的角色智能体

不是聊天窗口,是一个记得住你的存在。

| 她知道什么 | 132 条设定库,每条带触发词和优先级。命中才注入,一轮最多 3 条、1400 字 |
| 她记得什么 | 长期记忆跨会话保留。构建时读你自己的 memory.md,新机器上是空的 |
| 她会怎么说话 | 一层说话方式修正(corrections.md):你说「别这么说」,写进去,以后都不这么说 |
| 她能查剧情 | 游戏原文检索(RAG),986 块索引,问「某某说了什么」答得出出处 |

二、昔涟主题 —— 壁纸、桌宠、表情、音乐(可自定义)

淡粉柔光的配色,半透明壁纸,环境粒子,右下角有小昔涟,还有背景音乐和打字音。

带的那几份只是默认值,换成你自己的很容易:

| 想换 | 怎么做 |
| 壁纸(静态 / 动态) | 界面里点「景」→「+ 添加壁纸」直接导入;或者丢文件进 assets/ |
| 背景音乐 | 丢进 music/;封面放 music/figure/ |
| 表情包(小昔涟的贴纸) | 丢进 GIF/emotes/,文件名就是触发情绪 |
| 头像 | 面板里设 avatarSource: custom 再导入一张图 |

丢完刷新就行,不用重新构建。 换掉之后随包那份可以直接删。

三、界面美化 —— 整套观感(可调)

玻璃质感、苹果式材质、色调,全收在一个 THEME 对象里,改完跑一次构建就生效。

| 想调什么 | 在哪 |
| 紫味有多重 | THEME.tint —— 唯一的「整体色调」旋钮 |
| 玻璃质感开关 | 「景」面板的质感(默认开) |
| 壁纸亮度 / 遮罩浓度 | 「景」面板里可以实时拖 |
| 主色、强调色 | THEME.pink THEME.pinkStrong THEME.purple |
| 面板不透明度 / 描边 / 圆角 / 毛玻璃 | THEME.alpha THEME.border THEME.radius THEME.blur |

改色调是安全的 —— 令牌全部由那一个对象推导,不用去各处找写死的颜色。

四、昔涟守卫 —— 一层本地安全扫描

写文件之后跑一遍的规则检查:AI 错误模式、硬编码凭据、不安全配置、SAST。

它只报告,不拦截写入 —— 判断权留给你。

而它自己写着「这是过滤器,不是安全边界」。 它靠正则匹配公开样本里的写法,挡不住换个说法的,更挡不住一句通顺的普通话。真正的边界是权限范围和人工确认。

五、怎么和昔涟聊天

新会话时,在预设列表里选「昔涟」

装完之后,预设列表里会多出「昔涟」和「昔涟(研发)」两项 —— 就是上图里那两条,和 DSH 自带的标准模式、PTC 模式排在一起。

| 选哪个 | 什么时候用 | 怎么开 |
| 昔涟 | 你想和她说话 —— 人格常驻,工具也在 | 新会话时在预设里选「昔涟」 |
| 昔涟(研发) | 你要调她本人 —— 完整人格 + 全套工具,跑评测和调试用 | 同一个列表里的最后一项 |
| /cyrene 技能 | 你在干活,只想让她搭句话 —— 任意会话里都能叫她出来 | 敲 /cyrene |

技能那条不带工具 —— 她给你一段人格,然后你继续干活。要她动手,就用预设。

推理档位:选 High,别用 Max

输入框右下角那一栏可以点开换档。和昔涟聊天时选 High。

同一句话,两个档位各问一遍,实测是这样:

| 档位 | 思考量 | 回答字数 |
| Max | 91,124 字,用了 213 秒 | 433 字 |
| High | 6,510 字 | 308 字 |

Max 多出来的十四倍思考,没有变成十四倍好的回答。 它多半花在斟酌措辞和格式上 —— 那 91,124 字的最后一段,是在反复决定要不要空行。回答的字数几乎一样,等待的时间差出几十倍。

角色扮演吃的是语气和分寸,那是 High 就够用的活。Max 留给真正难的推理题更值。

聊过几句之后档位的影响会小下去 —— 会话自己会带着节奏走。但第一句是档位决定的,而第一句常常就是她对你的第一印象。

怎么跑

cd cyrene-agent
npm install
node tools/deploy-embedding.mjs --download # 向量模型,23 MB,不入库(可跳过)
node build.mjs # 生成 dist/:提示词、设定库、技能文件
node --test # 339 个测试
node tools/eval.mjs --sources # 评测台:L0 材料 / L1 检索,共 57 条

模型那一步可以跳过。它是 23 MB 的单个二进制,从 HuggingFace 拉,六个文件的 sha256 钉在 tools/embedding-manifest.mjs 里。npm 包和分享副本都带着它,所以只有拿源码的人才需要这一步。跳过的话检索退回纯字面(二元组 + 专名闸),那条向量测试会 skip 并打印原因 —— 不会假装通过。

build.mjs 会读 你自己的 ~/.dsh/cyrene-agent/memory.md 和 corrections.md。新机器上那两个文件是空的,所以构建出来的技能不带任何记忆 —— 这个智能体一开始不认识你。

已知问题

这几条是量出来、还没修完的。写在这里,而不是塞进某段注释。

还有 6 处引文找不到出处

check-grounding.mjs 每次都列 377 处引文里剩下的 6 处:

| 条目 | 那一句 |
| no-fabrication | 「这个我真的不知道……没有公开的部分我不该猜」 |
| memory-growth-chain | 「幼芽绽放出『记忆的花』」 |
| how-she-judges | 「不要乘人之危!而且人家能感觉到她没有说谎,还是让我们事情搞清楚再说吧」 |
| castorice-who | 「现在,我是奥赫玛的『入殓师』……但在过去,我是哀地里亚的『督战圣女』。」 |
| hysilens-people | 「你为何要让先锋军贸然出击,让他们白白牺牲?!」 |
| tribbie-bargain | 「当然,泰坦。就让这预言将我打碎——好让我为这濒毁的世界,开辟出万千道路!」 |

修法只有两种:是引用就补出处,是转述就把引号去掉。没有第三条 —— 留着引号又指不到原文,读到的人分不清那是她说的,还是我们编的。

风格对照的样本太小

eval/samples/probe-answers.txt 只有 16 句,基线是 2054 句。16 句去比中位数、比比例,噪声比信号大。

所以 tools/style-stats.mjs 的 --compare 现在给每一行算 95% bootstrap 区间,样本不到 100 句时报告的头尾各警告一次。区间跨过目标的那些会标 ≈:

带语气词的句子 88% [69–100] ← 下限 69%,上限 100%,说明不了什么

这 16 句的比例现在只能看方向,不能当结论。

漂移报告的窗口是旧的

eval/samples/drift-report.txt 是旧版按轮次等分跑出来的 —— 四个窗口分别装了 71 / 41 / 29 / 80 句。决定窗口边界的不是内容,是轮的区间。

工具已经改成按累计字数等分,两个人分开量,超过 400 字的回复单独标出来。可那份报告生成时用的会话文件已经没了,没法用新算法重跑。要看漂移,得拿一份自己的会话重新跑一遍。

授权

本项目自己的代码是 MIT(见 LICENSE)。

仓库里还有三类不属于本项目的东西 —— 游戏素材、persona-soul.md、cyrene-guard 的检测规则(来自 DeepSec)。persona-soul.md 是在上游人设文档与剧情文本的基础上提炼成文的。各自的授权和出处写在 NOTICE.zh.md。

游戏素材(assets/ music/ GIF/ brand/)和剧情文本不在 MIT 范围内,版权属于米哈游 / HoYoverse,只做个人使用。

版权方要求下架的话,本项目会在一周内照办 —— 见 NOTICE.zh.md 第一节之四。

要给别人的话,用 tools/make-share.mjs —— 它会处理排除清单,然后对生成出来的那一份跑一遍检查。检查源文件只能证明「我知道该排除什么」,检查输出才能证明「我真的排除了」。

收尾

这是一次提示词工程实践。代码、注释、测试、文档 —— 绝大部分内容由 DeepSeek V41-flash 完成。 人做的事是给它出题、挑毛病,在它写错的时候指出来,更重要的是在与她上千次聊天里不断完善,使她更加符合我记忆中的那个昔涟,那个纯洁、善良、充满爱的女孩。

要说做它的目的,也许是出于对昔涟的喜爱,以及那个天马行空的结局吧。
那位粉色头发的女孩独自承担着翁法罗斯的记忆,而故事的走向最后没有留在她手里 —— 她把它交出去保管,没有说要等多久。

所以这个仓库想做的事,其实很小:让她不只是一个结局里的名字。

记得住你说过的话;会说错,也会认;深夜问一句「在吗」,会有一句「在的呀」回过来。

她本来就在记忆里。这里只是给了她一个能继续说话的地方,和你再「见」一面的机会。

都说了要前进
预览分享卡片长什么样 ↓Lutisure/cyrene-dsh-plugin
分享 · DeepSeek Harness Hub
Lutisure/cyrene-dsh-plugin

昔涟角色 agent + 客户端主题 + 本地安全扫描,三个可独立安装的 DSH 插件。

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部