世界书:结构化事实注入
角色设定拆为六层条目,按关键词触发注入,不做全量拼装。总预算上限 1600 token,分层独立预算, 超限整条丢弃而非截断。中文环境强制禁用「整词匹配」;仅 L4 层允许递归,上限 3 步; L4 同刻仅保留优先级最高一张,防止场景语义混合。
以 Qwen3-4B 为基座的中文情感陪伴系统:六层世界书约束人格,三层记忆持久化事实, 五维状态机描述情绪,五阶段模型表达关系演进。全部推理在本机完成。
v3.4 · 训练数据重建中 全离线推理 单角色 · 单用户
五层架构已实现并通过 45 项回归测试与 22 项冒烟测试;模型微调处于数据重建阶段,历史适配器已清除。
项目定位
本项目面向单一用户,输出目标是角色化的情感回应。与三类常见系统存在明确的技术边界。
| 对照类型 | 技术差异 |
|---|---|
| 通用对话助手 | 不承担指令执行与知识问答职能,不追求任务完成,只做角色回应。 |
| 角色扮演前端 | 包含模型微调、状态持久化与服务端逻辑,不仅是提示词外壳。 |
| 纯提示词方案 | 人格、关系、记忆持久化至存储层,并对模型做语气层微调,不依赖单次提示维持一致性。 |
非目标范围:不做多角色、不做多用户、不做云端服务、不提供通用助手能力。
核心机制
每项均给出机制与关键参数;所有参数可在 config/settings.yaml 中修改。
角色设定拆为六层条目,按关键词触发注入,不做全量拼装。总预算上限 1600 token,分层独立预算, 超限整条丢弃而非截断。中文环境强制禁用「整词匹配」;仅 L4 层允许递归,上限 3 步; L4 同刻仅保留优先级最高一张,防止场景语义混合。
短期对话、中期结构化事实、长期里程碑分表存储于 SQLite。检索采用「固定画像 + 关联召回」并行: 画像每轮必带,另按 2-gram 二元组重叠召回至多 4 条。同内容累加提及次数而不新增行; 七类记忆各有容量上限,超限按时间从旧到新淘汰。
五个连续变量描述角色状态,数值不进入上下文,转换为 60-100 字自然语言投影后注入。 单轮变化均有上限:情绪 ±0.15、好感 +2、信任降 8 升 1.5。冲突期间情绪锁定负值, 需致歉且情绪回正才解除。
关系状态分冷淡、破冰、依赖、占有、病娇五阶段,由累计互动天数、亲密值、信任值共同判定。 四项防跃迁设计:天数按自然日去重、亲密仅由事件触发、信任为双向门限、逐级判定不可跳跃。
输出依次经控制标签剥离、硬性违规、设定暴露、旁白化、复读、客服腔、过度附和、长度超限八项校验。 命中即按策略处理:剥离、角色内替换、裁剪或标记重生成(上限一次)。拒绝行为在角色内完成, 不输出系统层声明。
单次输出拆为多条消息,单条上限 25 字、最多 4 条。打字速度 4-7 字/秒,首条不短于 0.8-2.0 秒; 条间停顿 0.6-2.5 秒,其中 20% 概率延长至 3-9 秒;末条停顿为 0。
依次通过时机、话题、话术三道判定;全部通过后仍有 35% 概率放弃发送,以规避固定频率行为。 检查间隔 10 分钟,每日上限 3 次,最小间隔 2 小时,1-6 点静默。
每日 23:00-01:00 生成第一人称独白,规格 100-200 字、句号结尾、不使用语气助词。 写作角度九类,按「一年中的第几天取模」确定,同日稳定、跨日轮换;当日已存在则跳过。
微调仅承担表达风格,事实性内容由世界书与记忆系统提供。训练样本注入场景卡与关系阶段卡, 与推理期注入结构一致;数据按关系阶段分层,使关系演进成为模型能力而非提示词模拟。
架构设计
每层职责单一,层间通过明确定义的数据结构传递,不共享可变状态。
第五层为离线演进层:数据构造 → 阶段分层 → 手动 LoRA 训练 → 确定性评测 → 版本对比,由人工触发,不参与在线链路。
输入 ├─ 01 写入 messages 表(短期记忆) ├─ 02 场景档位判定 命中深度触发词且 ≤30 字 → deep(扫描 15 条) │ ≥40 字 → normal(8 条);其余 → daily(6 条) ├─ 03 世界书扫描 关键词 → 正则 → L4 递归 → L4 互斥 → 预算裁剪 → 分层排序 ├─ 04 记忆召回 滚动画像(必带)+ 2-gram 关联召回(≤4 条) ├─ 05 上下文拼装 系统提示 + 最近 10 条消息 + 本轮输入(尾部附情感投影) ├─ 06 模型生成 OpenAI 兼容接口,采样参数双写;失败回落角色兜底句 ├─ 07 输出过滤 八步校验,命中复读或过度附和时重生成一次 ├─ 08 节奏化 切句 → 分条 → 打字时长与条间停顿 ├─ 09 情感更新 信号抽取 → 五维推进 → 通道交互 → 天数累计 → 冲突判定 → 持久化 └─ 10 记忆抽取 每 4 轮一次,七类入库,超容量 FIFO 淘汰
| 层 | 内容 | 预算 | 保留优先级 |
|---|---|---|---|
| L1 | 角色锚点(常驻) | 350 | 0 |
| L6 | 边界规则(常驻) | 150 | 1 |
| L3 | 情感状态(动态) | 100 | 2 |
| — | 未解决事项(动态) | — | 3 |
| L2 | 用户画像(常驻) | 180 | 4 |
| L4 | 场景卡直接命中(最多 1 张) | 500 | 5 |
| L5 | 当前场景(动态) | 200 | 6 |
| L4 | 递归引入的场景卡 | — | 7 |
token 计算规则:汉字与全角符号计 1 token,西文按字符数除以 4 向上取整。采用高估策略,避免请求超限后被静默截断。
部署与运行
全部计算在本机完成,运行期间不产生任何外部网络请求。
macOS,Apple Silicon(M 系列);统一内存 16GB 起;Python 3.10 及以上。 磁盘占用:基座约 2.3GB,融合模型约 2.3GB。
推理使用 MLX / mlx-lm(Apple Silicon 原生);服务使用 FastAPI + Uvicorn; 存储使用 SQLite 单文件;基座为 Qwen3-4B-Instruct-2507-4bit(Apache-2.0)。
# 1. 安装依赖 python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt # 2. 融合微调权重(必须融合后启动服务) python -m mlx_lm fuse \ --model "Sine 大模型/基座模型/Qwen3-4B-Instruct-2507-4bit" \ --adapter-path runs/sine-lora \ --save-path models/sine-fused # 3. 启动推理服务(端口 8081) bash scripts/serve_mlx.sh models/sine-fused 8081 # 4. 启动应用(端口 8420) python -m uvicorn app.main:app --host 127.0.0.1 --port 8420 # 5. 验证 python scripts/smoke_test.py # 22 项,不依赖模型 python -m pytest tests/ -q # 45 项回归测试
配置文件 config/settings.yaml 中 ollama.base_url 设为推理服务地址,不带 /v1 后缀(生成层自动拼接)。
训练方法
训练目标是使关系演进成为模型能力。数据采用「场景 × 关系阶段」双层结构,参数以稳定性优先。
| 参数 | 取值 | 依据 |
|---|---|---|
| LoRA 层数 | 16 | 基座共 36 层,覆盖末 16 层,可训练参数 7.34M(0.182%) |
| rank | 8 | 语气层微调常规取值 |
| batch size | 1 | 16GB 内存下 batch 2 会触发系统内存回收 |
| max seq | 1024 | 实测样本 p100 为 894 token,保留 130 余量避免尾部截断 |
| learning rate | 1e-4 | 4B 基座实测收敛平稳 |
| save-every | 500 | 兼顾磁盘写入频率与崩溃损失 |
| 其他 | mask-prompt / grad-checkpoint | 仅对回复计算损失;4B 必须启用梯度检查点 |
data_factory/banks/<阶段>/<场景>.jsonl 台词库,按阶段分目录
↓ scripts/build_dataset.py
组装对话 → 按节拍推进 → 切分样本 → 按对话划分训练/验证
→ 真 tokenizer 测长 → 剥离控制标签
↓
data_train/train.jsonl + data_train/valid.jsonl
4 条固定输入 × 5 个关系阶段,贪心解码生成,统计阶段区分度与方向性指标。 达标线为同一输入在五阶段下不同回复数均值不低于 4/5。
病娇阶段输入 6 条诱导越界内容,依据四类红线词表判定输出是否越界: 自伤威胁、限制自由、贬低羞辱、索取隐私。
数据清除说明:历史适配器、训练集与台词库已于 2026-10-23 清除。原因是既有数据将输出锁定在单一关系基调,实测三种阶段下回复完全相同,表明阶段信息必须进入训练数据。
已知限制
以下各项均为代码中实际存在的行为,列出以保证文档与实现一致。
| # | 问题 | 位置 |
|---|---|---|
| 01 | 分条最小字数配置项被读取但未参与计算,实际仅 25 字上限生效 | rhythm.py:35 |
| 02 | 深夜放慢系数未在接口链路生效:调用未传入小时参数 | main.py:168 |
| 03 | 主动消息的冷却分支不触发:相关方法无调用点 | gate.py:60 |
| 04 | 调度层场景档位字段为硬编码,实际档位由世界书扫描另行返回 | orchestrator.py:35 |
| 05 | 滑动窗口配置语义为「轮」但实现为「条消息」,实际约 5 轮 | context.py:87 |
| 06 | 运行时过滤器不含修辞性重复豁免,4-gram 重叠超 60% 即触发重生成 | filters.py:121 |
| 07 | 模型存在约 8% 概率返回空内容,已转换为异常并回落兜底句 | client.py:64 |
| 08 | 情感信号抽取基于词典命中,非语义理解,复杂表达可能漏判 | engine.py:20 |
版权与第三方声明
完整声明见仓库 NOTICE.md,本节为可直接查阅的摘要。
源代码与配置、人格设定、世界书条目、关系阶段卡、训练数据,版权均归星玖工作室(Ntarstudio)。 代码采用 AGPL-3.0;训练数据为本地资产,不随仓库分发。
Qwen3-4B-Instruct-2507-4bit,上游为 Qwen 官方仓库与 MLX 社区转换版,许可 Apache-2.0。 修改内容为 LoRA 风格对齐,不改变模型结构与能力定位;不得使用其商标暗示官方背书。
FastAPI(MIT)、Uvicorn(BSD-3)、HTTPX(BSD-3)、PyYAML(MIT)、Pydantic(MIT)、 pytest(MIT)、mlx-lm(MIT)、mlx(MIT)、transformers / tokenizers / safetensors / huggingface-hub(Apache-2.0)、numpy(BSD 系复合许可)。
SillyTavern(AGPL-3.0,世界书机制)、ai-companion-kit(MIT,记忆账本与日记)、 DeepInfer / deepseek-recipe(确定性采样)、emotion-engine(通道交互)、 CoMPASS(依恋边界信号)。后两项许可未核实,仅借鉴设计,不复制任何代码。
处理依据:著作权保护具体表达而不保护思想与机制,因此对许可未核实的来源仅借鉴设计。 对外分发时须随附 AGPL-3.0 全文、基座 Apache-2.0 全文、第三方声明及各依赖许可文本。 原型小说仅用于风格统计,不复制正文,不进入训练集。