世界书
读取、写入、扫描世界书(lorebook)条目的相关 API。所有函数均通过 Luker.getContext() 暴露;原始 HTTP 路由参见底层端点。
读取世界书
loadWorldInfo
loadWorldInfo(name: string): Promise<WorldInfoData | null>按名称读取单个世界书文件。名称查找忽略大小写和重音符。无匹配文件时返回 null。内部带缓存——重复读取同一文件直接走内存。
const ctx = Luker.getContext();
const book = await ctx.loadWorldInfo('My Lorebook');
console.log(Object.keys(book.entries).length);loadWorldInfoBatch
loadWorldInfoBatch(names: string[]): Promise<Map<string, WorldInfoData | null>>一次 HTTP 往返读取多个世界书文件。返回以解析后名称为键的 Map;不存在的条目映射为 null。已缓存的文件和 in-flight 请求会自动合并。
const books = await ctx.loadWorldInfoBatch(['Book A', 'Book B']);
for (const [name, data] of books) {
if (data) console.log(name, Object.keys(data.entries).length);
}getWorldInfoNames
getWorldInfoNames(): string[]返回编辑器已知的所有世界书文件名的快照副本。可用于填充 UI 下拉框。
getWorldInfoPrompt
getWorldInfoPrompt(
chat: string[],
maxContext: number,
isDryRun: boolean,
globalScanData: WIGlobalScanData,
): Promise<WIPromptResult>底层扫描器,针对给定的聊天切片产出世界书 prompt 片段。大多数插件应优先使用 resolveWorldInfoForMessages——它在此之上封装了归一化和激活后钩子。
WIPromptResult 结构:
| 字段 | 类型 | 说明 |
|---|---|---|
worldInfoString | string | before 与 after 用换行连接 |
worldInfoBeforeEntries | string[] | 注入到聊天前的条目 |
worldInfoAfterEntries | string[] | 注入到聊天后的条目 |
worldInfoExamples | Array<{position, content}> | 对话示例条目 |
worldInfoDepth | Array<{depth, role, entries}> | depth 注入条目 |
anBefore / anAfter | string[] | Author's note 注入 |
outletEntries | Record<string, string[]> | 自定义 outlet 内容 |
当 isDryRun 为 false 时,会触发 event_types.WORLD_INFO_ACTIVATED 事件,携带激活的条目。
写入世界书
saveWorldInfo
saveWorldInfo(
name: string,
data: WorldInfoData,
immediately?: boolean,
options?: object,
): Promise<void>将世界书文件持久化到磁盘。缓存同步更新;网络写入默认走 debounce 队列。
| 参数 | 说明 |
|---|---|
name | 文件名(不含 .json 扩展名) |
data | 完整世界书对象({ entries: Record<number, WIEntry> }) |
immediately | 为 true 时等待真正写入完成而非 debounce |
name 或 data 为 falsy 时直接返回。
updateWorldInfoList
updateWorldInfoList(): Promise<void>从服务器刷新全局 world_names 列表。在编辑器 UI 之外创建、删除、重命名文件后调用。
createWorldBook
createWorldBook(name: string, options?: { interactive?: boolean }): Promise<boolean>创建一个新的空世界书文件。成功返回 true,失败返回 false(例如 interactive 为 false 时遇到重名)。interactive: false(默认)会跳过重名确认弹窗,适合程序化创建。新建文件没有任何条目,通过 saveWorldInfo 或编辑器写入条目。
importEmbeddedWorldInfo
importEmbeddedWorldInfo(skipPopup?: boolean): Promise<void>把 V2/V3 PNG 卡内嵌的 data.character_book 导入为独立的世界书文件并绑定为该角色的 primary world。要导入哪张卡通过 #import_character_info 元素的 chid data 属性读取(角色编辑器在打开含内嵌书的卡时会设置这个值)。skipPopup: true 用来跳过确认弹窗直接导入,适合已经获取过明确确认的工具调用。导入完成后,characters[chid].data.extensions.world 指向新文件,内嵌书不再被提示重新导入。
charUpdatePrimaryWorld
charUpdatePrimaryWorld(name: string): Promise<void>通过名字绑定角色的主世界书(传 '' 解绑)。走角色编辑器的保存路径,因此需要当前有活跃的角色上下文。
getCharacterEmbeddedWorld
getCharacterEmbeddedWorld(charId: number | string): {
present: boolean,
name: string | null,
entryCount: number,
bound: boolean,
}只读地查询某张卡的 V2/V3 内嵌 data.character_book 状态:
present— 卡是否携带内嵌书。name— 内嵌书的name字段。entryCount— 内嵌书包含的条目数。bound— 卡是否已经绑定了一个真实的世界书文件(即data.extensions.world解析得到一个已知的世界书)。present && !bound表示内嵌书还没通过importEmbeddedWorldInfo导入。present && bound表示内嵌书只是绑定世界的过期镜像(导出时留下的良性副产物),运行时应忽略。
reloadWorldInfoEditor
reloadWorldInfoEditor(file: string, loadIfNotSelected?: boolean): void重新渲染 file 对应的世界书编辑器。当 file 不是当前打开的文件时默认无操作;将 loadIfNotSelected 设为 true 可切换到该文件。
激活扫描
simulateWorldInfoActivation
simulateWorldInfoActivation(request: {
coreChat?: ChatMessage[],
maxContext?: number,
dryRun?: boolean,
type?: string,
chatForWI?: string[],
includeNames?: boolean,
globalScanData?: WIGlobalScanData,
}): Promise<WIPromptResult & {
chatForWI: string[],
maxContext: number,
globalScanData: WIGlobalScanData,
}>针对提供的消息执行一次世界书激活扫描,返回激活结果。这是 resolveWorldInfoForMessages 背后的原语;只在需要更精细地控制扫描输入时直接调用。
| 参数 | 说明 |
|---|---|
coreChat | 用于扫描的消息列表({ name, mes, is_user, is_system }) |
maxContext | token 预算;省略或 <= 0 时回退到 getMaxPromptTokens() |
dryRun | 为 true 时抑制 WORLD_INFO_ACTIVATED 事件 |
type | 生成触发标签('normal'、'quiet'、'regenerate' 等) |
chatForWI | 预先构建的扫描输入;提供时会跳过 buildWorldInfoChatInput |
includeNames | 是否在每条扫描行前加上 name: |
globalScanData | 角色卡派生扫描字段的覆盖值 |
返回对象会回显实际使用的 chatForWI、maxContext、globalScanData,便于调用方检视解析后的扫描输入。
buildWorldInfoChatInput
buildWorldInfoChatInput(messages: ChatMessage[], includeNames?: boolean): string[]构建 WI 扫描器期望的反转扫描字符串数组。includeNames 为真时每行格式为 "name: mes",否则只是 mes。当你想给 simulateWorldInfoActivation 喂一份预格式化的切片时有用。
buildWorldInfoGlobalScanData
buildWorldInfoGlobalScanData(type: string, overrides?: Partial<WIGlobalScanData>): WIGlobalScanData为当前角色构建角色卡派生的扫描字段(personaDescription、characterDescription、characterPersonality、characterDepthPrompt、scenario、creatorNotes、trigger)。传入 overrides 可在不重建整个对象的情况下替换单个字段。
getActiveWorldInfoPromptFields
getActiveWorldInfoPromptFields(): {
worldInfoBeforeEntries: string[],
worldInfoAfterEntries: string[],
}返回最近一次实时生成流水线中捕获的世界书 before/after 片段。无活动聊天或快照属于其他聊天时返回空数组。当你想读取上次注入的内容、又不想重跑一次扫描时使用。
角色辅助世界书
角色卡可以在主 world 字段之外声明额外的世界书绑定——例如 CardApp 可以为角色内置工具绑定额外的参考书。
getCharaAuxWorlds
getCharaAuxWorlds(charaFilename: string): string[]返回绑定到角色的辅助世界书名称去重列表。解析规则:
- 角色创建期间(
menu_type === 'create'),从 in-flight 的新角色缓冲区读取。 - 否则从持久化的
world_info.charLore中匹配charaFilename的条目读取。
charaFilename 为 falsy 或没有绑定时返回 []。配合 getCharaFilename 解析当前角色:
const ctx = Luker.getContext();
const auxBooks = ctx.getCharaAuxWorlds(ctx.getCharaFilename());
const datas = await ctx.loadWorldInfoBatch(auxBooks);格式转换
convertCharacterBook
convertCharacterBook(characterBook: V2CharacterBook): {
entries: Record<number, WIEntry>,
originalData: V2CharacterBook,
}把 V2 角色卡的 character_book 负载转换成内部世界书结构。导入角色卡或将 V2 lore 投影到可编辑条目时使用。originalData 保留以便往返序列化。
实战模式
插件读取并编辑一个世界书
const ctx = Luker.getContext();
const book = await ctx.loadWorldInfo('Setting Bible');
if (!book) {
console.warn('Lorebook not found');
return;
}
const newUid = Math.max(0, ...Object.keys(book.entries).map(Number)) + 1;
book.entries[newUid] = {
uid: newUid,
key: ['npc:Tavernkeeper'],
keysecondary: [],
content: 'A burly man with a scar across his cheek.',
comment: 'Auto-added by my-plugin',
constant: false,
selective: true,
order: 100,
position: 0,
disable: false,
excludeRecursion: false,
probability: 100,
useProbability: true,
};
await ctx.saveWorldInfo('Setting Bible', book);
ctx.reloadWorldInfoEditor('Setting Bible', true);插件扫描 WI 而不影响主聊天
const wi = await ctx.simulateWorldInfoActivation({
coreChat: [
{ name: 'User', mes: 'Tell me about the tavernkeeper.', is_user: true },
],
dryRun: true,
});
console.log(wi.worldInfoBeforeEntries);如果想得到能直接传给 buildPresetAwarePromptMessages 的结果,请改用 resolveWorldInfoForMessages。