尝试以扩展 VS Code 中的代理模式!

语言模型工具 API

语言模型工具使你能够在聊天中通过特定领域的功能扩展大型语言模型 (LLM) 的功能。为了处理用户的聊天提示,VS Code 中的代理模式可以自动调用这些工具,作为对话的一部分执行专门任务。通过在 VS Code 扩展中贡献语言模型工具,你可以扩展代理式编码工作流,同时提供与编辑器的深度集成。

在本扩展指南中,你将学习如何使用语言模型工具 API 创建语言模型工具,以及如何在聊天扩展中实现工具调用。

你还可以通过贡献MCP 服务器来扩展聊天体验,提供专门工具。有关不同选项以及如何决定使用哪种方法的详细信息,请参阅AI 可扩展性概述

LLM 中的工具调用是什么?

语言模型工具是一个可以作为语言模型请求的一部分调用的函数。例如,你可能有一个从数据库检索信息、执行某些计算或调用在线 API 的函数。当你向 VS Code 扩展贡献一个工具时,代理模式就可以根据对话上下文调用该工具。

LLM 实际上从不执行工具本身,而是 LLM 生成用于调用你的工具的参数。清楚地描述工具的用途、功能和输入参数非常重要,以便工具可以在正确的上下文下被调用。

下图展示了 VS Code 中代理模式下的工具调用流程。有关所涉及具体步骤的详细信息,请参阅工具调用流程

Diagram that shows the Copilot tool-calling flow

在 OpenAI 文档中阅读有关函数调用的更多信息。

为什么要在扩展中实现语言模型工具?

在你的扩展中实现语言模型工具具有以下几个优点

  • 扩展代理模式:通过专门的、特定领域的工具,这些工具在响应用户提示时会自动调用。例如,启用数据库脚手架和查询功能,动态地向 LLM 提供相关上下文。
  • 与 VS Code 深度集成:通过使用广泛的扩展 API。例如,使用调试 API 获取当前调试上下文,并将其用作工具功能的一部分。
  • 分发和部署:通过 Visual Studio Marketplace 分发和部署工具,为用户提供可靠、无缝的体验。用户无需为你的工具进行单独的安装和更新过程。

在以下场景中,你可能需要考虑使用MCP 服务器来实现语言模型工具

  • 你已经有一个 MCP 服务器实现,并且也想在 VS Code 中使用它。
  • 你希望在不同的开发环境和平台中重用相同的工具。
  • 你的工具作为服务远程托管。
  • 你不需要访问 VS Code API。

创建语言模型工具

实现语言模型工具主要包括两部分

  1. 在你的扩展的 package.json 文件中定义工具的配置。
  2. 使用语言模型 API 参考在你的扩展代码中实现工具。

你可以从一个基本示例项目开始。

1. package.json 中的静态配置

在你的扩展中定义语言模型工具的第一步是在扩展的 package.json 文件中定义它。此配置包括工具名称、描述、输入架构和其他元数据

  1. 在你的扩展的 package.json 文件的 contributes.languageModelTools 部分添加你的工具条目。

  2. 给工具一个唯一的名称

    属性 描述
    name 工具的唯一名称,用于在扩展实现代码中引用该工具。将名称格式化为 {verb}_{noun} 格式。请参阅命名指南
    displayName 工具的用户友好名称,用于在 UI 中显示。
  3. 如果工具可以在代理模式中使用或通过 # 在聊天提示中引用,请添加以下属性

    用户可以在聊天视图中启用或禁用该工具,这与模型上下文协议 (MCP) 工具的操作方式类似。

    属性 描述
    canBeReferencedInPrompt 如果工具可以在代理模式中使用或在聊天中引用,则设置为 true
    toolReferenceName 用户通过 # 在聊天提示中引用工具的名称。
    icon 要在 UI 中显示工具的图标。
    userDescription 工具的用户友好描述,用于在 UI 中显示。
  4. modelDescription 中添加详细描述。LLM 使用此信息来确定你的工具应该在什么上下文中使用。

    • 该工具具体做什么?
    • 它返回哪种信息?
    • 何时应该使用,何时不应该使用?
    • 描述工具的重要限制或约束。
  5. 如果工具接受输入参数,请添加一个 inputSchema 属性来描述工具的输入参数。

    此 JSON 架构描述了一个对象,其中包含工具作为输入接受的属性,以及它们是否是必需的。文件路径应该是绝对路径。

    描述每个参数的作用以及它与工具功能的关系。

  6. 添加一个 when 子句来控制工具何时可用。

    languageModelTools 贡献点允许你通过使用when 子句来限制工具何时可用于代理模式或在提示中引用。例如,获取调试调用堆栈信息的工具只应在用户调试时可用。

    "contributes": {
        "languageModelTools": [
            {
                "name": "chat-tools-sample_tabCount",
                ...
                "when": "debugState == 'running'"
            }
        ]
    }
    
工具定义示例

以下示例展示了如何定义一个工具,该工具计算选项卡组中活动选项卡的数量。

"contributes": {
    "languageModelTools": [
        {
            "name": "chat-tools-sample_tabCount",
            "tags": [
                "editors",
                "chat-tools-sample"
            ],
            "toolReferenceName": "tabCount",
            "displayName": "Tab Count",
            "modelDescription": "The number of active tabs in a tab group in VS Code.",
            "userDescription": "Count the number of active tabs in a tab group.",
            "canBeReferencedInPrompt": true,
            "icon": "$(files)",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "tabGroup": {
                        "type": "number",
                        "description": "The index of the tab group to check. This is optional- if not specified, the active tab group will be checked.",
                        "default": 0
                    }
                }
            }
        }
    ]
}

2. 工具实现

使用语言模型 API 实现语言模型工具。这包括以下步骤

  1. 扩展激活时,使用vscode.lm.registerTool 注册工具。

    提供你在 package.jsonname 属性中指定的工具名称。

    如果你希望工具对你的扩展是私有的,请跳过工具注册步骤。

    export function registerChatTools(context: vscode.ExtensionContext) {
      context.subscriptions.push(
        vscode.lm.registerTool('chat-tools-sample_tabCount', new TabCountTool())
      );
    }
    
  2. 创建一个实现vscode.LanguageModelTool<> 接口的类。

  3. prepareInvocation 方法中添加工具确认消息。

    对于来自扩展的工具,将始终显示一个通用确认对话框,但工具可以自定义确认消息。向用户提供足够的上下文,以便他们了解工具正在做什么。消息可以是包含代码块的 MarkdownString

    以下示例展示了如何为选项卡计数工具提供确认消息。

    async prepareInvocation(
        options: vscode.LanguageModelToolInvocationPrepareOptions<ITabCountParameters>,
        _token: vscode.CancellationToken
    ) {
        const confirmationMessages = {
            title: 'Count the number of open tabs',
            message: new vscode.MarkdownString(
                `Count the number of open tabs?` +
                (options.input.tabGroup !== undefined
                    ? ` in tab group ${options.input.tabGroup}`
                    : '')
            ),
        };
    
        return {
            invocationMessage: 'Counting the number of tabs',
            confirmationMessages,
        };
    }
    

    如果 prepareInvocation 返回 undefined,则会显示通用确认消息。请注意,用户还可以选择“始终允许”某个工具。

  4. 定义一个描述工具输入参数的接口。

    该接口在 vscode.LanguageModelTool 类的 invoke 方法中使用。输入参数根据你在 package.jsoninputSchema 中定义的 JSON 架构进行验证。

    以下示例展示了选项卡计数工具的接口。

    export interface ITabCountParameters {
      tabGroup?: number;
    }
    
  5. 实现 invoke 方法。当在处理聊天提示时调用语言模型工具时,将调用此方法。

    invoke 方法在 options 参数中接收工具输入参数。参数根据 package.jsoninputSchema 中定义的 JSON 架构进行验证。

    发生错误时,抛出一个对 LLM 有意义的消息的错误。或者,提供关于 LLM 接下来应该做什么的指令,例如使用不同的参数重试,或执行不同的操作。

    以下示例展示了选项卡计数工具的实现。工具的结果是 vscode.LanguageModelToolResult 类型的一个实例。

    async invoke(
        options: vscode.LanguageModelToolInvocationOptions<ITabCountParameters>,
        _token: vscode.CancellationToken
    ) {
        const params = options.input;
        if (typeof params.tabGroup === 'number') {
            const group = vscode.window.tabGroups.all[Math.max(params.tabGroup - 1, 0)];
            const nth =
                params.tabGroup === 1
                    ? '1st'
                    : params.tabGroup === 2
                        ? '2nd'
                        : params.tabGroup === 3
                            ? '3rd'
                            : `${params.tabGroup}th`;
            return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`There are ${group.tabs.length} tabs open in the ${nth} tab group.`)]);
        } else {
            const group = vscode.window.tabGroups.activeTabGroup;
            return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`There are ${group.tabs.length} tabs open.`)]);
        }
    }
    

在 VS Code 扩展示例仓库中查看实现语言模型工具的完整源代码。

工具调用流程

当用户发送聊天提示时,会发生以下步骤

  1. Copilot 根据用户配置确定可用工具列表。

    工具列表包括内置工具、扩展注册的工具以及来自MCP 服务器的工具。你可以通过扩展或 MCP 服务器(在图中显示为绿色)向代理模式贡献内容。

  2. Copilot 将请求发送到 LLM,并向其提供提示、聊天上下文以及要考虑的工具定义列表。

    LLM 生成响应,其中可能包含一个或多个调用工具的请求。

  3. 如果需要,Copilot 使用 LLM 提供的参数值调用建议的工具。

    工具响应可能会导致更多的工具调用请求。

  4. 如果存在错误或后续工具请求,Copilot 会迭代工具调用流程,直到所有工具请求都得到解决。

  5. Copilot 将最终响应返回给用户,其中可能包含来自多个工具的响应。

指南和约定

  • 命名:为工具和参数编写清晰、描述性的名称。

    • 工具名称:应是唯一的,并清晰描述其意图。将工具名称格式化为 {verb}_{noun} 格式。例如,get_weatherget_azure_deploymentget_terminal_output

    • 参数名称:应描述参数的用途。将参数名称格式化为 {noun} 格式。例如,destination_locationtickerfile_name

  • 描述:为工具和参数编写详细描述。

    • 描述工具的作用以及何时应该使用和不应该使用它。例如,“此工具检索给定位置的天气。”
    • 描述每个参数的作用以及它与工具功能的关系。例如,“destination_location 参数指定要检索天气的位置。它应该是一个有效的位置名称或坐标。”
    • 描述工具的重要限制或约束。例如,“此工具仅检索美国境内位置的天气数据。它可能不适用于其他地区。”
  • 用户确认:为工具调用提供确认消息。对于来自扩展的工具,将始终显示一个通用确认对话框,但工具可以自定义确认消息。向用户提供足够的上下文,以便他们了解工具正在做什么。

  • 错误处理:发生错误时,抛出一个对 LLM 有意义的消息的错误。或者,提供关于 LLM 接下来应该做什么的指令,例如使用不同的参数重试,或执行不同的操作。

OpenAI 文档Anthropic 文档中获取更多创建工具的最佳实践。