扩展 API 参考
本文档是 Luker 扩展 API 的完整参考,面向插件开发者。所有 API 均通过 Luker.getContext() 暴露。
全局入口
const context = Luker.getContext();| 别名 | 说明 |
|---|---|
Luker.getContext() | 推荐使用 |
SillyTavern.getContext() | 兼容别名 |
st.getContext() | 兼容别名 |
新插件应统一使用 Luker.getContext()。兼容别名仅为迁移期保留。
与 SillyTavern 的 API 差异
Luker 基于 SillyTavern 构建,但在 API 层面有以下主要差异:
| 领域 | SillyTavern | Luker |
|---|---|---|
| 聊天持久化 | 整文件覆写 | Patch-first(RFC 6902 增量更新) |
| 聊天绑定状态 | 仅 chat_metadata | 新增聊天状态机制 |
| 预设管理 | 直接导入内部模块 | context.presets.* 统一 API |
| 提示词组装 | 需要手动拼接 | buildPresetAwarePromptMessages() |
| 世界书模拟 | 无 | simulateWorldInfoActivation() |
| 生成钩子 | 基础事件 | 新增 GENERATION_CONTEXT_READY、GENERATION_BEFORE_WORLD_INFO_SCAN 等细粒度钩子 |
| 事件排序 | 注册顺序 | 支持 priority、pluginOrder、makeFirst/makeLast |
| 正则运行时 | 无插件 API | registerManagedRegexProvider() |
| 搜索工具 | 无插件 API | Luker.searchTools 全局 API |
| 函数调用 | 基础 ToolManager | 纯文本模式支持 + 连接级独立开关 + sendOpenAIRequest 预设覆盖 |
| 连接配置 | 全局单一 | context.presets.resolve() 支持按预设解析连接配置 |
IMPORTANT
优先使用 Luker.getContext() 提供的 API,而非直接调用底层 HTTP 端点。Context API 封装了 patch-first 语义、冲突处理和重试逻辑,直接调用端点需要自行处理这些细节。
聊天数据(只读)
以下属性提供当前聊天的只读访问:
| 属性 | 类型 | 说明 |
|---|---|---|
context.chat | ChatMessage[] | 当前聊天消息数组 |
context.characters | Character[] | 角色列表 |
context.groups | Group[] | 群组列表 |
context.name1 | string | 用户名 |
context.name2 | string | 角色名 |
context.characterId | number | 当前角色 ID |
context.groupId | string | 当前群组 ID |
context.chat_metadata | object | 当前聊天的元数据 |
context.online_status | string | API 连接状态 |
消息 API
Luker 提供了统一的高层消息操作 API。每个操作都是完整的一条龙流程:内存更新 + DOM 渲染 + 事件触发 + 持久化。
addMessages
addMessages(
messages: ChatMessage | ChatMessage[],
options?: { scroll?: boolean, silent?: boolean }
): Promise<number | number[]>添加一条或多条消息到聊天中。
- 自动 push 到
chat[]、渲染 DOM、触发MESSAGE_SENT/MESSAGE_RECEIVED和MESSAGE_RENDERED事件、持久化到后端 - 传入数组时批量操作,只触发一次持久化
- 返回新消息的索引(单条返回
number,批量返回number[])
// 添加单条消息
const index = await context.addMessages({
name: 'System',
mes: '这是一条系统消息',
is_system: true,
});
// 批量添加
const indices = await context.addMessages([
{ name: 'User', mes: '你好', is_user: true },
{ name: 'Assistant', mes: '你好!有什么可以帮你的?', is_user: false },
]);updateMessages
updateMessages(
updates: { index: number, patch: object } | { index: number, patch: object }[],
options?: { rerender?: boolean, silent?: boolean }
): Promise<void>更新一条或多条消息的内容并持久化。
patch对象的字段会合并到chat[index]中- 自动重渲染 DOM、触发
MESSAGE_EDITED和MESSAGE_UPDATED事件、通过 RFC 6902 增量持久化 - 批量操作时合并为一次持久化调用
// 更新单条消息
await context.updateMessages({
index: 4,
patch: { mes: '修改后的内容' },
});
// 批量更新
await context.updateMessages([
{ index: 3, patch: { mes: '新内容 A' } },
{ index: 5, patch: { mes: '新内容 B', extra: { model: 'gpt-4o' } } },
]);deleteMessages
deleteMessages(
index: number | number[],
options?: { swipe?: number, silent?: boolean }
): Promise<ChatMessage | ChatMessage[]>删除一条或多条消息。
- 自动从
chat[]移除、清理 DOM、触发MESSAGE_DELETED事件、通过 RFC 6902 增量持久化 - 批量删除时自动处理索引偏移
- 指定
swipe选项时,只删除该消息的特定 swipe 而非整条消息 - 返回被删除的消息对象
// 删除单条消息
const deleted = await context.deleteMessages(5);
// 批量删除
const deletedList = await context.deleteMessages([3, 5, 7]);
// 只删除特定 swipe
await context.deleteMessages(5, { swipe: 2 });getMessage
getMessage(index: number): Readonly<ChatMessage> | null获取指定索引的消息(只读)。返回一个 Proxy 对象,尝试修改属性会抛出错误并引导使用 updateMessages()。
getMessageCount
getMessageCount(): number返回当前聊天的消息总数。
已弃用的底层 API
以下函数仍然可用但已标记为 deprecated,插件开发者应使用上述统一 API:
addOneMessage()→ 使用addMessages()deleteLastMessage()→ 使用deleteMessages(chat.length - 1)deleteMessage()→ 使用deleteMessages()updateMessageBlock()→ 使用updateMessages()patchChatMessages()→ 底层 RFC 6902 传输层,使用updateMessages()/deleteMessages()appendChatMessages()→ 底层追加传输层,使用addMessages()
聊天持久化
saveChatMetadata
saveChatMetadata(withMetadata?: object): Promise<boolean>保存聊天元数据。如果传入 withMetadata,会先合并到 chat_metadata 再保存。
聊天状态
聊天状态是 Luker 新增的聊天绑定状态机制,让插件可以将结构化数据绑定到特定聊天,而不是塞进 chat_metadata。
getChatState
getChatState(
namespace: string,
options?: { target?: ChatTarget }
): Promise<any | null>读取指定命名空间的聊天状态。返回 null 表示该命名空间无数据。
namespace:插件的唯一标识符,建议使用插件名target:可选,指定目标聊天(用于跨聊天读取,如分支场景)
getChatStateBatch
getChatStateBatch(
namespaces: string[],
options?: { target?: ChatTarget }
): Promise<Record<string, any>>批量读取多个命名空间的聊天状态。返回一个以命名空间为键的对象。
updateChatState
updateChatState(
namespace: string,
updater: (current: any) => any,
options?: { target?: ChatTarget }
): Promise<{ ok: boolean }>推荐的读-改-写方式。 updater 函数接收当前状态,返回新状态。系统会自动处理并发冲突。
await context.updateChatState('my-plugin', (current = {}) => ({
...current,
counter: (current.counter || 0) + 1,
lastUpdated: Date.now(),
}));deleteChatState
deleteChatState(
namespace: string,
options?: { target?: ChatTarget }
): Promise<{ ok: boolean }>删除指定命名空间的聊天状态。
最佳实践
- 使用
updateChatState()进行读-改-写,而非手动链式调用getChatState()+patchChatState() - 保持 payload 为可 JSON 序列化的纯对象
- 处理
ok: false返回值,保持插件 UI 的弹性 - 对于大型插件数据,优先使用聊天状态而非
chat_metadata
预设 API
context.presets 提供了统一的预设管理接口,替代直接导入 PresetManager 内部模块。
presets.list
presets.list(collection?: string): Array<PresetRef>列出指定集合的所有已保存预设。collection 为预设集合名(如 'openai')。
presets.getSelected
presets.getSelected(collection?: string): PresetRef | null获取当前选中的预设引用。如果当前选中的是角色卡绑定的运行时预设,返回 null。
presets.getLive
presets.getLive(collection?: string): PresetBody | null获取当前 UI 中正在编辑的预设内容(包括未保存的修改)。适合需要读取当前实际生效配置的场景。
presets.getStored
presets.getStored(ref: { collection: string, name: string }): PresetBody | null获取指定预设的已保存内容。适合跨预设比较或复制内容。
presets.save
presets.save(
ref: { collection: string, name: string },
body: PresetBody
): Promise<void>保存预设内容。
presets.resolve
presets.resolve(
target?: PresetRef,
options?: object
): ConnectionProfile解析预设对应的连接配置(API 端点、模型、密钥等)。这是插件进行独立 API 调用时获取连接信息的推荐方式。
返回的 ConnectionProfile 包含:
| 字段 | 说明 |
|---|---|
requestApi | 规范化的 API 类型(如 'openai') |
requestModel | 模型名称 |
requestUrl | API 端点 URL |
secretId | 密钥标识符 |
presets.state
presets.state.update(
namespace: string,
updater: (current: any) => any,
options?: { target: PresetRef }
): Promise<void>管理绑定到预设的插件运行时/会话数据。这些数据不会随预设导出,仅用于插件的运行时状态。
使用规则
list()和getSelected()只返回已保存的预设- 编辑中的预设用
getLive() - 角色卡绑定的运行时预设不算「已保存」,
getSelected()返回null,但getLive()仍可读取 - 不要将插件运行时数据塞进预设 body,使用
presets.state.*
提示词与世界书组装
buildPresetAwarePromptMessages
buildPresetAwarePromptMessages(options: {
messages: Array<{ role: string, content: string }>,
envelopeOptions?: {
includeCharacterCard?: boolean,
api?: string,
promptPresetName?: string,
},
promptPresetName?: string,
runtimeWorldInfo?: object,
}): PromptMessage[]基于当前预设配置,将插件的消息按照 prompt 预设的排列顺序组装为可发送给 API 的提示词消息列表。这是一个可选的组装工具——简单的 LLM 调用不需要它,只有当你需要复用角色卡、世界书或 prompt 模板时才需要使用。
参数说明:
| 参数 | 说明 |
|---|---|
messages | 要发送的消息数组,每条消息包含 role('system'/'user'/'assistant')和 content |
envelopeOptions.includeCharacterCard | 是否在提示词中包含当前角色卡的设定(默认 true) |
envelopeOptions.api | 指定使用的 API 类型(如 'openai'),不指定则使用当前连接 |
envelopeOptions.promptPresetName | 指定使用的预设名称,不指定则使用当前预设 |
promptPresetName | 与 envelopeOptions.promptPresetName 相同,顶层快捷方式 |
runtimeWorldInfo | 预先解析好的世界书激活结果(通过 resolveWorldInfoForMessages 获取) |
关键行为:
- 保留活跃预设中聊天历史以外的内容(系统提示、角色描述等)
- 仅替换聊天历史部分为你提供的
messages - 如果提供了
runtimeWorldInfo,世界书条目会被注入到对应位置 - 如果指定了
promptPresetName,会使用该预设的提示词模板而非当前预设
实际使用示例(参考记忆图插件的召回流程):
// 1. 先解析世界书激活结果
const runtimeWorldInfo = await context.resolveWorldInfoForMessages(
resolverMessages,
{
type: 'quiet',
fallbackToCurrentChat: false,
postActivationHook: rewriteDepthWorldInfoToAfter, // 重写指令:将 depth 类型的世界书条目移到 after 位置
}
);
// 2. 组装提示词
const promptMessages = context.buildPresetAwarePromptMessages({
messages: [
{ role: 'system', content: '你是一个记忆分析助手...' },
{ role: 'user', content: '请分析以下对话中的关键信息...' },
],
envelopeOptions: {
includeCharacterCard: true,
api: envelopeApi,
promptPresetName: selectedPromptPresetName,
},
promptPresetName: selectedPromptPresetName,
runtimeWorldInfo: runtimeWorldInfo,
});
// 3. 发送给 LLM
import { sendOpenAIRequest } from '../../../openai.js';
const response = await sendOpenAIRequest('quiet', promptMessages, signal, {
requestScope: 'extension_internal',
});关于后处理钩子(postActivationHook)
resolveWorldInfoForMessages 的 postActivationHook 参数允许你在世界书激活后、注入前对条目进行任意修改——包括修改内容、调整注入位置和深度、甚至增删条目。hook 接收归一化后的完整世界书 payload 并返回修改后的版本。例如记忆图插件利用此钩子将 depth 类型的世界书条目重写到 after 位置,避免插入到聊天深度中干扰插件自己的指令。
resolveWorldInfoForMessages
resolveWorldInfoForMessages(
messages: Array<{ role: string, content: string }>,
options?: {
type?: string,
fallbackToCurrentChat?: boolean,
postActivationHook?: (entries: object) => object,
}
): Promise<object>对指定消息执行世界书激活扫描,返回激活结果。这相当于对自定义消息进行一次世界书「重扫」。
参数说明:
| 参数 | 说明 |
|---|---|
messages | 用于触发世界书关键词匹配的消息列表 |
options.type | 激活类型(如 'quiet' 表示静默扫描,不影响主对话) |
options.fallbackToCurrentChat | 如果 messages 为空,是否回退到当前聊天消息 |
options.postActivationHook | 激活后的钩子函数,接收完整的世界书 payload,可以修改条目的内容、位置、深度,或增删条目 |
返回的对象包含 worldInfoBeforeEntries、worldInfoAfterEntries、worldInfoDepth 等字段,可以直接传给 buildPresetAwarePromptMessages 的 runtimeWorldInfo 参数。
世界书重扫
resolveWorldInfoForMessages 本质上就是对自定义消息进行世界书重扫。插件可以用它来:
- 为独立的 LLM 调用获取相关的世界书条目
- 测试特定消息会触发哪些世界书条目
- 在不影响主对话的情况下进行世界书激活模拟
推荐的独立 LLM 调用模式
当插件需要进行独立的 LLM 调用(如弹窗中的 AI 辅助功能)时,推荐以下模式:
import { sendOpenAIRequest } from '../../../openai.js';
const context = Luker.getContext();
// 1. 解析世界书激活结果
const wi = await context.resolveWorldInfoForMessages(myCustomMessages, {
type: 'quiet',
fallbackToCurrentChat: false,
});
// 2. 组装提示词(注入角色卡、世界书、按 prompt_order 排列)
const requestMessages = context.buildPresetAwarePromptMessages({
messages: myCustomMessages,
runtimeWorldInfo: wi,
});
// 3. 发送请求
const result = await sendOpenAIRequest('quiet', requestMessages, signal, {
requestScope: 'extension_internal',
});如果不需要角色卡和世界书,可以跳过步骤 1-2,直接传 messages 给 sendOpenAIRequest。
正则运行时 API
插件可以通过 registerManagedRegexProvider() 注册托管的正则处理器,参与 Luker 的正则处理流程。该函数从正则引擎模块导出:
import { registerManagedRegexProvider } from '../../extensions/regex/engine.js';
const handle = registerManagedRegexProvider('my-plugin', {
reloadOnChange: true,
});
// 添加正则脚本
handle.upsertScript({
id: 'my-rule-1',
scriptName: 'My Regex Rule',
findRegex: 'foo',
replaceString: 'bar',
// ...其他正则脚本字段
});
// 卸载时取消注册
handle.unregister();registerManagedRegexProvider 返回的句柄提供 upsertScript、removeScript、setScripts、clearScripts 和 unregister 方法。
搜索工具 API
搜索插件通过 Luker.searchTools 全局对象暴露 API,供其他插件调用搜索能力:
// 检查搜索插件是否可用
if (globalThis?.Luker?.searchTools) {
// 获取可用的搜索工具名称列表
const toolNames = Luker.searchTools.toolNames;
// 获取工具定义(用于函数调用)
const toolDefs = Luker.searchTools.getToolDefs();
// 检查某个工具名是否属于搜索工具
const isSearchTool = Luker.searchTools.isToolName('web_search');
}Luker.searchTools 暴露的是工具定义元数据,实际的搜索执行通过内部的工具调用循环完成。详见搜索插件。
发送 LLM 请求
插件可以使用 sendOpenAIRequest 发送独立的 LLM 请求,这是核心的生成函数。
基本用法
对于不需要角色卡或世界书的简单 LLM 调用:
import { sendOpenAIRequest } from '../../../openai.js';
const result = await sendOpenAIRequest('quiet', [
{ role: 'system', content: '你是一个翻译助手。' },
{ role: 'user', content: '翻译这段文字...' },
], signal, {
requestScope: 'extension_internal',
});第一个参数 'quiet' 表示这是一个后台请求,不会出现在聊天 UI 中。
预设覆盖
sendOpenAIRequest 接受覆盖参数来控制使用哪个模型、API 端点和生成设置:
const result = await sendOpenAIRequest('quiet', messages, signal, {
llmPresetName: 'my-low-temp', // 覆盖生成参数(温度、top_p 等)
apiSettingsOverride: profileOverride, // 覆盖连接设置(模型、API URL 等)
requestScope: 'extension_internal',
});| 参数 | 用途 |
|---|---|
llmPresetName | 加载 LLM 预设来覆盖生成参数(温度、top_p、frequency_penalty、max_tokens 等)。不影响连接字段。 |
apiPresetName | 加载 API 预设来覆盖连接字段(chat_completion_source、模型、API URL、reverse_proxy 等)。不影响生成参数。 |
apiSettingsOverride | 直接用对象覆盖连接设置(通常来自连接管理器的配置解析)。 |
requestScope | 设为 'extension_internal' 可跳过主聊天的 CHAT_COMPLETION 钩子。 |
工具调用
在请求中包含工具定义:
const result = await sendOpenAIRequest('quiet', messages, signal, {
tools: [
{
type: 'function',
function: {
name: 'search_web',
description: '搜索网页获取信息',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: '搜索关键词' },
},
required: ['query'],
},
},
},
],
toolChoice: 'auto',
functionCallMode: 'native', // 或 'prompt_xml' 使用纯文本模式
requestScope: 'extension_internal',
});这些 tools 仅用于本次请求,与全局工具注册表(见下方工具注册)是分开的。
配合 Prompt 组装
对于需要融入角色卡、世界书或 prompt 模板的请求,先使用 buildPresetAwarePromptMessages 组装消息:
const context = Luker.getContext();
// 第一步:解析世界书
const worldInfo = await context.resolveWorldInfoForMessages(rawMessages);
// 第二步:按 prompt 预设布局组装消息
const messages = context.buildPresetAwarePromptMessages({
messages: [
{ role: 'system', content: taskSystemPrompt },
{ role: 'user', content: taskUserPrompt },
],
envelopeOptions: {
includeCharacterCard: true,
api: 'openai',
},
runtimeWorldInfo: worldInfo,
});
// 第三步:发送组装好的消息
const result = await sendOpenAIRequest('quiet', messages, signal, {
llmPresetName,
apiSettingsOverride,
requestScope: 'extension_internal',
});buildPresetAwarePromptMessages 按照当前 prompt 预设的 prompt_order 排列消息,可选地注入角色卡和世界书条目。它控制发送什么;sendOpenAIRequest 的预设参数控制怎么发送(模型、温度、连接)。
工具注册
插件可以通过 getContext() 将工具注册到全局工具注册表。注册的工具会出现在主聊天的工具调用流程中——模型可以在正常对话中调用它们。
const context = Luker.getContext();
context.registerFunctionTool({
name: 'my_plugin_tool',
displayName: 'My Tool',
description: '执行某个有用的操作',
parameters: {
type: 'object',
properties: {
input: { type: 'string', description: '输入文本' },
},
required: ['input'],
},
action: async (args) => {
// 执行工具并返回结果字符串
return `结果:${args.input}`;
},
formatMessage: (args) => {
// 可选:格式化一条人类可读的消息显示在聊天中
return `使用了工具,输入:${args.input}`;
},
shouldRegister: async () => {
// 可选:返回 false 可条件性地跳过注册
return true;
},
stealth: false, // 可选:为 true 时工具结果不会显示在聊天中
});移除已注册的工具:
context.unregisterFunctionTool('my_plugin_tool');工具相关方法:
| 方法 | 说明 |
|---|---|
context.registerFunctionTool(tool) | 将工具注册到全局注册表 |
context.unregisterFunctionTool(name) | 从全局注册表移除工具 |
context.isToolCallingSupported() | 检查当前 API/模型是否支持工具调用 |
context.canPerformToolCalls(type) | 检查指定请求类型是否可以执行工具调用 |
全局工具 vs 单次请求工具
registerFunctionTool 将工具添加到全局注册表——它们在主聊天中可供模型调用。sendOpenAIRequest 的 tools 参数仅为该次请求提供工具,不影响全局注册表。
连接配置解析
当插件需要使用非当前预设的连接配置时,使用 presets.resolve():
const profile = context.presets.resolve(
{ collection: 'openai', name: 'My Preset' }
);
// profile 包含:
// - requestApi: 'openai'
// - requestModel: 'gpt-4o'
// - requestUrl: 'https://api.openai.com/v1'
// - secretId: '...'secret_id 请求覆盖:在 chat-completions 请求体中,可以通过 secret_id 字段指定使用哪个密钥,覆盖全局选择。这在多 Agent 场景中特别有用——不同的 Agent 可以使用不同的 API 密钥。
角色状态
角色状态是绑定到角色卡本身的持久化存储,在该角色的所有聊天之间共享。与聊天状态(仅在单个聊天内有效)不同,角色状态适合存储跨聊天的角色级别配置。
getCharacterState
getCharacterState(namespace: string): Promise<any | null>读取指定命名空间下的角色状态数据。如果该命名空间没有存储过数据,返回 null。
| 参数 | 说明 |
|---|---|
namespace | 存储命名空间,通常使用插件名称(如 'my-extension') |
setCharacterState
setCharacterState(namespace: string, data: any): Promise<void>写入指定命名空间下的角色状态数据。传入 null 作为 data 可以删除该命名空间的状态。
| 参数 | 说明 |
|---|---|
namespace | 存储命名空间 |
data | 要存储的数据(任意可序列化对象),传 null 删除 |
使用示例
const context = Luker.getContext();
// 读取角色状态
const state = await context.getCharacterState('my-extension');
console.log(state); // { someConfig: true } 或 null
// 写入角色状态
await context.setCharacterState('my-extension', {
someConfig: true,
lastUpdated: Date.now(),
});
// 删除角色状态
await context.setCharacterState('my-extension', null);角色状态 vs 聊天状态
| 角色状态 | 聊天状态 | |
|---|---|---|
| 作用范围 | 绑定到角色卡,所有聊天共享 | 绑定到单个聊天 |
| 典型用途 | 角色级别的插件配置、CardApp 应用状态 | 聊天内的临时数据、对话上下文 |
| API | getCharacterState / setCharacterState | getChatState / setChatStateBatch / updateChatState / deleteChatState |
| 存储位置 | 角色卡 JSON 文件 | 聊天元数据 |
扩展间通信
registerExtensionApi
context.registerExtensionApi('my-plugin', {
doSomething: () => { /* ... */ },
getData: () => myData,
});查找其他插件的 API
const api = context.getExtensionApi('other-plugin');
if (api) {
api.doSomething();
}事件系统
eventSource
// 监听
context.eventSource.on(eventName, handler, options?);
// 取消监听
context.eventSource.off(eventName, handler);
// 确保最先执行
context.eventSource.makeFirst(eventName, handler);
// 确保最后执行
context.eventSource.makeLast(eventName, handler);
// 查看监听器信息(调试用)
context.eventSource.getListenersMeta(eventName);
// 配置插件排序
context.eventSource.setOrderConfig(config);监听器选项
context.eventSource.on(eventName, handler, {
priority: 10, // 数字越大越先执行
});事件类型
所有事件类型通过 context.eventTypes 访问。完整的事件列表和回调参数请参阅插件开发基础。
世界书读写
插件可以通过 context API 读写世界书条目。
底层端点参考(高级 / 调试用)
WARNING
以下端点仅供高级调试和无法使用 Luker.getContext() 的集成场景参考。它们是同源 Web 应用路由,不是主要的插件 API 契约。正常插件开发应使用上述 Context API。
角色聊天
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/chats/save | 保存聊天(patch-first) |
| POST | /api/chats/get | 获取聊天列表 |
| POST | /api/chats/delete | 删除聊天 |
| POST | /api/chats/rename | 重命名聊天 |
| POST | /api/chats/export | 导出聊天 |
群组聊天
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/chats/group/save | 保存群组聊天 |
| POST | /api/chats/group/get | 获取群组聊天列表 |
| POST | /api/chats/group/delete | 删除群组聊天 |
聊天状态
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/chats/state/get | 批量读取状态 |
| POST | /api/chats/state/patch | 增量更新状态 |
| POST | /api/chats/state/delete | 删除状态 |
设置
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/settings/save | 保存设置(patch-first) |
| POST | /api/settings/get | 获取设置 |
世界书
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/worldinfo/save | 保存世界书(patch-first) |
| POST | /api/worldinfo/get | 获取世界书 |
搜索/访问
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/plugins/search/search | 执行搜索 |
| POST | /api/plugins/search/visit | 访问 URL 并提取内容 |
Patch 操作格式
消息 patch 使用 RFC 6902 JSON Patch 格式:
[
{ "op": "replace", "path": "/4/mes", "value": "新内容" },
{ "op": "add", "path": "/4/extra/note", "value": "备注" },
{ "op": "remove", "path": "/4/extra/old_field" }
]对象 patch(meta/patch、state/patch、settings/patch、worldinfo/patch)也使用相同的 RFC 6902 格式。
Patch 冲突与完整性语义
- 服务端会验证 patch 操作的路径是否存在
replace操作要求目标路径已存在add操作会创建不存在的路径- 冲突时返回错误,客户端应重试或回退到全量保存
Chat-Completions 请求体
{
"messages": [...],
"model": "gpt-4o",
"secret_id": "optional-override"
}secret_id 字段允许在请求级别覆盖使用的 API 密钥,适用于多 Agent 编排等需要不同密钥的场景。