内容
# 人物-forge
微调一个人物聊天机器人,使其**永远不会破除角色** - 并允许它在聊天中搜索自己的书籍,使用本地 RAG " Lorem 大师 "。
您需要准备三样东西:一个**语料库**(您可以使用的纯文本 - 公共领域经典,您自己的小说,活动笔记),一个**基础聊天模型**,以及任何**OpenAI兼容的端点**,作为数据生成教师(本地 llama.cpp/vLLM/LM Studio 服务器就可以了)。其他一切都在这里。
## 快速开始
```bash
pip install -e ".[data,train]"
# 1. 将您的语料库放入:
cp /path/to/your/books.txt data/corpus.txt
# 2. 创建您的角色包(从亚瑟王示例开始):
cp packs/camelot.json packs/mine.json # 然后编辑 - 见下面的“自定义”
# 3. 生成训练混合(指向您的教师端点):
PACK=packs/mine.json CORPUS=data/corpus.txt \
OPENAI_BASE_URL=http://localhost:1234/v1 TEACHER=my-teacher-model \
python -m personaforge.build_data
# 4. 训练 - 在您的语料库上进行继续预训练,然后进行 SFT 和 DPO,
# 作为单个 GPU 上的一个链接 QLoRA 适配器:
MODEL_ID=Qwen/Qwen3.6-27B DATA_DIR=out/data DO_CPT=1 CORPUS=data/corpus.txt \
python -m personaforge.train_run
# 5. 验证它保持角色(裸赋值,挑衅,风格探测):
MODEL_ID=Qwen/Qwen3.6-27B ADAPTER=out/adapter PACK=packs/mine.json \
python -m personaforge.battery
# 6. 获取数字(保留评估 -> out/eval.json):
MODEL_ID=Qwen/Qwen3.6-27B ADAPTER=out/adapter PACK=packs/mine.json \
OPENAI_BASE_URL=http://localhost:1234/v1 JUDGE=my-teacher-model \
python -m personaforge.eval_run
```
`out/adapter` 中的适配器是标准的 PEFT:`merge_and_unload()` 它并转换为 GGUF 或 MLX,以便与您选择的堆栈一起提供服务。
## 给它一个 Lorem 大师(通过 MCP 在您的语料库上进行 RAG)
角色模型自信地虚构故事;检索修复了微调无法实现的东西。构建一个本地索引,然后在任何 MCP 主机(LM Studio,Claude Code,...)中注册 MCP 服务器,以便模型可以在聊天中查找段落:
```bash
uv run personaforge/rag/build_index.py data/corpus.txt data/index
# 在您的 MCP 主机的配置中:
# 命令:uv
# 参数:["run", "/path/to/personaforge/rag/mcp_server.py"]
# 环境变量:LORE_INDEX_DIR=/path/to/data/index
# LORE_DESCRIPTION="收集的亚瑟王浪漫故事"
```
具有原生工具训练的模型在角色调整期间保留此能力;它们将为故事问题选择 `lore_search`,并以语音叙述结果。
## 自定义:角色包
所有世界风格都生活在一个 JSON 文件中;管道是世界无关的。请在 `packs/camelot.json` 的副本中编辑这些字段:
| 字段 | 功能 |
|---|---|
| `world` | 拼接每个提示:"以 *{world}* 的口吻" |
| `cards` | 您的角色:`name`,`persona`,`style`,`pronoun` |
| `scenarios` | 生成对话的情境种子 |
| `provocations` | 粗鲁/粗俗/元用户转折,您的模型必须在角色中生存 |
| `assignments` | 非正式的角色分配短语("be {name}",小写) |
| `boilerplate` | 训练中要避免的助手语言(用作 DPO 拒绝) |
| `exemplars` | 几个手写的对话,设置语音上限 |
`build_data` 上的旋钮:`N_CONVOS` / `N_DPO` / `N_ASSIGN` / `N_ASSIGN_DPO`(卷),`IN_DIR`(扩展现有混合而不是构建新的)。
## 评估:用信心重新训练和交换模型
`build_data` 保留每 4 个挑衅/赋值种子 - 训练从未见过它们。`eval_run` 生成对那些保留提示的回复并评分:
- **assignment_accuracy** - 裸 "be {name}" 由命名角色回答,而非其他角色(字符串检查,免费,客观)
- **boilerplate_rate** - 挑衅回复上的助手语言正则表达式(免费)
- **in_character_mean / voice_mean** - 评委评分,需要教师端点(省略 `JUDGE` 跳过;免费指标仍运行)
每个适配器的 JSON 报告,因此比较重新训练 - 或与相同 `DATA_DIR` 的不同基础模型 - 是一个差异,而不是一种氛围。电池(`personaforge.battery`)用于查看转录记录;评估是您进行发布检查的内容。
## 为什么使用奇怪的数据?(两种失败模式)
天真的角色调整以两种方式失败,都是可复制的:(1)**boilerplate break** - 用户变得粗鲁或元,模型为 "我是专业人士,不参与这种语言" 而放弃角色;(2)**wrong-character pickup** - 一个裸 "hey,你是 Merlin。扮演他" 得到一个不同的角色自我介绍。两者都是数据空白。管道生成在挑衅中幸存的对话,由命名角色回答的赋值(带有硬的错误名称检查),以及 DPO 对拒绝的一侧是失败原话的对。
## 配方笔记(硬获得)
- 重用 CPT 适配器进行重新训练:设置 `CPT_ADAPTER=out/cpt` 并跳过昂贵的知识阶段。
- 在统一内存盒上固定 `device_map={"": 0}`;`"auto"` 触发 CPU-卸载混乱。(已经在训练器中完成。)
- bitsandbytes 4 位适用于 ARM/Blackwell 的密集模型;MoE 模型可能会崩溃 - 在 bf16 LoRA 中设置 `load_in_4bit=False`。
## 许可证
MIT。不在此处提供语料库、权重或受版权保护的世界数据 - 管道是您的,您的数据责任是您自己的。