函数调用

函数调用允许你为扩展添加动态功能,方法是让 LLM 使用结构化数据,随后你再用这些数据来触发扩展的某项具体功能。

示例用例

  1. 查询外部 API 以获取额外信息(新闻、天气、网页搜索等)。
  2. 根据用户输入执行计算或单位换算。
  3. 存储并回忆重要的记忆或事实,包括 RAG 和数据库查询。
  4. 为对话引入真正的随机性(掷骰子、抛硬币等)。

官方支持函数调用的扩展

  1. 图像生成(内置)- 根据用户的提示词生成图像。
  2. 网页搜索 - 针对查询触发一次网页搜索。
  3. RSS - 从 RSS 源抓取最新新闻。
  4. Weather - 从天气 API 获取天气信息。
  5. D&D Dice - 为 D&D 游戏掷骰子。

前置条件与限制

  1. 此功能仅适用于聊天补全 API,支持以下来源:Custom(OpenAI 兼容)、AI/ML API、AI21、Azure OpenAI、Chutes、Claude、Cloudflare Workers AI、Cohere、DeepSeek、Electron Hub、Fireworks、Google AI Studio、Google Vertex AI、Groq、MiniMax、MistralAI、Moonshot (Kimi)、NanoGPT、OpenAI、OpenRouter、Pollinations、SiliconFlow、xAI (Grok) 和 Z.AI (GLM)。
  2. 文本补全 API 不支持函数调用,但某些本地托管的 LLM 后端(如 Ollama 和 TabbyAPI)可以在聊天补全下以 Custom OpenAI 兼容模式运行。
  3. 必须先由用户显式允许,才能使用函数调用。方法是启用 AI 响应配置面板中的“启用函数调用(Enable function calling)”选项。
  4. 不保证 LLM 一定会执行任何函数调用。大多数 LLM 需要通过提示词进行显式“激活”(例如用户请求“掷一个骰子”“查一下天气”等)。
  5. 并非所有提示词都能触发工具调用。续写、模拟他人、后台(静默)提示词不允许触发工具调用,但它们仍可在回复中使用以往成功的工具调用。
  6. 即便 API 来源支持函数调用,某些模型也可能不支持。关于哪些模型支持函数调用,请参阅你的 API 提供商的文档。

工具调用递归上限

为防止工具调用出现无限循环,设有递归上限(默认:5 轮)。如果 LLM 持续重复调用工具,达到上限后将停止执行。你可以在 AI 响应配置设置中、“启用函数调用”选项旁边调整此上限。

交替思考

当对推理模型使用工具调用时,你可以利用随工具调用请求一同返回的推理内容来维持交替思考的上下文。对某些模型而言,这对于确保一致性、在多次工具调用之间保留重要细节是必要的。

在 AI 响应配置中选择以下设置之一:

  • 禁用(Disabled):工具调用请求中不包含任何推理上下文。兼容性最高,为默认设置。
  • 自上一条用户消息起(Since Last User Message):包含在最近一条用户消息之后发生的所有工具轮次的推理。
  • 活动工具链(Active Tool Chain):仅在仍处于当前未完成的工具循环中时包含推理;一旦产生了正常的助手回复,旧的工具链推理就不再重新发送。

如何制作一个函数工具

检查该功能是否受支持

使用 SillyTavern.getContext() 中的 isToolCallingSupported() 来检查当前 API 是否支持函数工具调用,以及是否已在设置中启用:

const { isToolCallingSupported } = SillyTavern.getContext();

if (isToolCallingSupported()) {
    console.log('Function tool calling is supported');
}

你还可以检查某个特定生成类型是否可以执行工具调用。续写、模拟他人和后台(静默)提示词不允许触发工具调用:

const { canPerformToolCalls } = SillyTavern.getContext();

if (canPerformToolCalls('normal')) {
    console.log('Can perform tool calls for this generation');
}

注册函数工具

使用 SillyTavern.getContext() 中的 registerFunctionTool() 注册一个工具。该工具定义的参数遵循 JSON Schema 格式:

const { registerFunctionTool } = SillyTavern.getContext();

registerFunctionTool({
    name: 'get_weather',
    displayName: 'Get Weather',
    description: 'Get the current weather for a given location',
    parameters: {
        $schema: 'http://json-schema.org/draft-04/schema#',
        type: 'object',
        properties: {
            location: {
                type: 'string',
                description: 'The city name, e.g. "London"',
            },
            unit: {
                type: 'string',
                enum: ['celsius', 'fahrenheit'],
                description: 'Temperature unit',
            },
        },
        required: ['location'],
    },
    action: async ({ location, unit }) => {
        // Perform your logic here (API calls, computations, etc.)
        const data = await fetchWeatherData(location, unit);
        return JSON.stringify(data);
    },
    formatMessage: ({ location }) => `Checking weather for ${location}...`,
    shouldRegister: () => isWeatherFeatureEnabled(),
    stealth: false,
});

注册字段

字段 是否必填 说明
name 工具的唯一标识符
displayName 在 UI 中显示的、对用户友好的名称
description 发送给 LLM 的描述,用于说明工具的作用及何时使用
parameters 定义工具输入参数的 JSON Schema
action 当 LLM 调用该工具时执行的函数。以对象形式接收解析后的参数。可以是异步函数。必须返回字符串结果(非字符串值会被 JSON 序列化)。
formatMessage 返回一个字符串,在工具执行期间作为 toast 显示。返回空字符串可隐藏该 toast。
shouldRegister 返回布尔值的函数;若为 false,则该工具被排除在当前请求之外。若未提供,则该工具会针对每次提示词都注册。
stealth 若为 true,工具调用结果不会记录到可见的聊天历史中,也不会触发后续生成

注销函数工具

要停用一个函数工具,请用该工具的名称调用 unregisterFunctionTool()

const { unregisterFunctionTool } = SillyTavern.getContext();

unregisterFunctionTool('get_weather');

技巧与提示

  1. 成功的工具调用会作为可见聊天历史的一部分保存,并显示在聊天 UI 中,因此你可以检查实际的参数和结果。如果不希望如此,请在注册工具时设置 stealth: true
  2. 要用自定义 CSS 美化或隐藏工具调用消息,请针对 .mes 元素上的 toolCall 类进行设置,例如 .mes.toolCall { display: none; }.mes.toolCall { opacity: 0.5; }
  3. 为你的工具编写清晰、具体的描述——LLM 会据此决定何时以及如何调用它们。请包含关于工具使用时机的指导。
  4. 保持参数 schema 简洁且有充分说明。每个属性的 description 都有助于 LLM 正确填入值。
  5. 工具调用设有递归上限(默认:5 轮)。如果 LLM 持续重复调用工具,达到上限后将停止执行。