78 lines
4.9 KiB
Markdown
Executable File
78 lines
4.9 KiB
Markdown
Executable File
# 天赋系统契约(Talent System Contract)
|
||
|
||
天赋树 = **Prompt + 模型行为的控制平面**,不是游戏成长系统。
|
||
- **主天赋(唯一)** = 身份:决定 System Prompt / 是否走 RAG / 合规安全等级 / 默认模型。主天赋 **就是 `twin.profession`**,不另存一套 id。
|
||
- **副天赋(≤N)** = 风格软技能:**后置改写**,只碰成稿、**绝不改事实**。
|
||
|
||
实现见 [`../../src/talents.js`](../../src/talents.js)。本目录是与线上代码对齐的**契约**,也是将来搬 DB 的蓝本。
|
||
|
||
## 文件
|
||
| Schema | 描述 | 对应代码 |
|
||
|---|---|---|
|
||
| [main-talent.schema.json](main-talent.schema.json) | 主天赋身份节点 | `mainTalent(profId)` |
|
||
| [sub-talent.schema.json](sub-talent.schema.json) | 副天赋注册表项 | `SUB_REGISTRY` |
|
||
| [user-talent-instance.schema.json](user-talent-instance.schema.json) | 分身选定的天赋 | `twin.talents` |
|
||
| [talent-registry.schema.json](talent-registry.schema.json) | 注册表整体 | 派生/静态表 |
|
||
|
||
## 派生关系(主天赋怎么来的)
|
||
不逐个职业手写主天赋,而是三张映射表交叉派生(`talents.js`):
|
||
```
|
||
profession(professions.js) ──cat──▶ ARCH_BY_CAT ──▶ arch ──▶ ARCH_BEHAVIOR(行为基线) + ARCH_MODEL(模型偏好)
|
||
└─compliance─▶ COMPLIANCE ──▶ safety_level + block_keywords + disclaimer + audit_required
|
||
```
|
||
- `cat → arch`:content→creator, pro/health/biz/life/other→expert, edu→teacher, art→artist, sport→sport, craft→craft
|
||
- `compliance → 安全`:none→{2,无} / soft→{5,软免责} / strict→{9,强免责+关键词+需审核}
|
||
|
||
新增一个职业只改 `professions.js` 一处;换某原型的行为只改 `ARCH_BEHAVIOR` 一处。
|
||
|
||
## 校验规则(服务端必须兜底,不信前端)
|
||
1. **主天赋唯一**:`main_talent` 必须 ∈ 职业表(`getProfession`),否则 400 `unknown-main-talent`;`null`/无 → `general` 兜底身份。
|
||
2. **副天赋合法性**:每个 id 必须 ∈ `SUB_REGISTRY`;去重;顺序保留。
|
||
3. **副天赋槽位**:数量 ≤ `maxSubs(level) = min(3, 1 + level)`(level 来自 `computeSkillTree`)。超出**静默截断**(`validateSubs`),不报错。
|
||
4. **副天赋不改事实**:所有 style/constraint 副天赋合并成**一次**后置改写,指令强约束"不改变任何事实/数字/结论/引用";hobby 型走前置注入。
|
||
5. **审核门(软门)**:`main.audit_required && !twin.verified` → `twin.talentAudit='pending'`,**不拦截**(分身仍可用),但运行时 Guardrail 强制免责。已 `verified` → `'approved'`。
|
||
6. **Guardrail(运行时,输出前)**:
|
||
- `block_keywords` 命中 → 软化替换;
|
||
- `safety_level >= 5` 且回答未含免责 → 追加 `disclaimer` 到结尾。
|
||
|
||
## 运行时管线(`services/twin.js` → `ask()`)
|
||
```
|
||
用户输入
|
||
→ 组装 System Prompt:身份(mainPromptBlock) + 前置注入(injectBlock) + 人设/记忆/RAG
|
||
→ (RAG 检索,main.rag)
|
||
→ LLM 主回答 chat(messages) ← 主天赋出稿
|
||
→ 副天赋后置改写 chat([restyleInstruction, draft]) ← 合并 1 次;无副天赋则跳过
|
||
→ Guardrail guardrail(text, main) ← 屏蔽词 + 免责
|
||
→ 输出
|
||
```
|
||
成本:每轮 **1 次主调用 + 至多 1 次改写调用**(不是每个副天赋一次)。
|
||
|
||
## API 契约
|
||
### `GET /api/twins/:id/talents`
|
||
返回(`describeTalents`):
|
||
```jsonc
|
||
{
|
||
"main": { "id","name","emoji","entity","arch","compliance","safety_level",
|
||
"rag_required","audit_required","audit_ok","behavior" },
|
||
"subs": { "selected": ["humor"], "max": 1,
|
||
"options": [{ "id","name","subtype","desc" }] },
|
||
"growth": { "level": 0, "maxSubs": 1 }
|
||
}
|
||
```
|
||
### `PUT /api/twins/:id/talents`
|
||
Body(两字段都可选,可单独改):
|
||
```jsonc
|
||
{ "main_talent": "lawyer", // 可选:切身份(=profession),带 audit 门
|
||
"sub_talents": ["humor","concise"] } // 可选:仅当为数组时才更新;截到 maxSubs
|
||
```
|
||
返回同 `GET`。
|
||
|
||
## DB 化迁移(留了缝,MVP 不做)
|
||
1. `SUB_REGISTRY` → 集合 `talent_sub_registry`(一项一文档)。
|
||
2. 主天赋映射(ARCH_BEHAVIOR / COMPLIANCE / ARCH_MODEL)→ 集合 `talent_main_config`,或物化 `talent_main_talents`(一职业一行)。
|
||
3. 每个节点加 `talent-registry.schema.json#/$defs/versioning`(`version/active/gray_pct`)→ 支持**灰度放量 + 一键回滚**。
|
||
4. `talents.js` 的纯函数签名不变,只把三张 `const` 表换成带缓存的 DB 读取;`describeTalents/resolveTalents/guardrail` 全部复用。
|
||
|
||
## 留坑不做(架构留得住)
|
||
天赋升级 lv2/3(`level` 字段已在)· 冲突检测(医生+算命❌)· 天赋市场(官方/共创)· 会话级动态天赋 · 按天赋真路由模型/计量返佣(`model_preference` + `chat(opts.model)` 缝已在)。
|