feat: W7 功能標籤分類、對白檢索與注入 (#8)

用 $.model.complete(Haiku)判斷回應的功能標籤,從角色索引取同標籤對白,
以只增不改的訊息附在使用者訊息之前;失敗一律退回不注入。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jeffery (jiantw83)
2026-10-14 17:44:09 +00:00
co-authored by Claude Sonnet 5.5
parent 1c4e0c5d98
commit d9e50d196b
12 changed files with 385 additions and 4 deletions
@@ -0,0 +1,61 @@
import { describe, expect, test } from 'claude-code/testing'
import { formatExamples } from './examples'
import { retrieve, type IndexEntry } from './retrieval'
const e = (id: string, fn: IndexEntry['function'], mode: string | null = null): IndexEntry => ({
id,
function: fn,
emotion: 'neutral',
mode,
text: `句${id}`,
})
const INDEX = [e('c', 'comfort'), e('a', 'comfort'), e('b', 'comfort'), e('d', 'tease'), e('x', 'comfort', 'leafa')]
describe('retrieve', () => {
test('只取同功能標籤,依 id 排序', () => {
expect(retrieve(INDEX, 'comfort', { k: 10 }).map(r => r.id)).toEqual(['a', 'b', 'c', 'x'])
})
test('預設取 3 句', () => {
expect(retrieve(INDEX, 'comfort')).toHaveLength(3)
})
test('沒有同標籤的對白回傳空陣列', () => {
expect(retrieve(INDEX, 'greet')).toEqual([])
})
test('依模式過濾', () => {
expect(retrieve(INDEX, 'comfort', { mode: 'leafa' }).map(r => r.id)).toEqual(['x'])
})
test('同一輪結果固定,不同輪輪流取', () => {
const ids = (turn: number) => retrieve(INDEX, 'comfort', { k: 2, turn }).map(r => r.id)
expect(ids(0)).toEqual(ids(0))
expect(ids(0)).toEqual(['a', 'b'])
expect(ids(1)).toEqual(['b', 'c'])
expect(ids(3)).toEqual(['x', 'a'])
})
test('k 大於池子時不重複', () => {
expect(new Set(retrieve(INDEX, 'tease', { k: 5 }).map(r => r.id)).size).toBe(1)
})
})
describe('formatExamples', () => {
test('沒有對白不注入', () => {
expect(formatExamples('亞絲娜', [])).toBeNull()
})
test('列出對白並說明只學語氣', () => {
const text = formatExamples('亞絲娜', [e('a', 'comfort')])!
expect(text).toContain('句a')
expect(text).toContain('不要照抄')
expect(text).toContain('亞絲娜')
})
test('過長的句子截斷', () => {
const long = { ...e('a', 'comfort'), text: 'あ'.repeat(200) }
expect(formatExamples('亞絲娜', [long])!.length).toBeLessThan(300)
})
})