聊天扩展
Visual Studio Code 的 Copilot 聊天架构使扩展作者能够与 GitHub Copilot 聊天体验集成。聊天扩展是一种 VS Code 扩展,它通过贡献一个聊天参与者来使用聊天扩展 API。
聊天参与者是领域专家,可以回答特定领域内的用户查询。参与者可以使用不同的方法来处理用户查询
- 使用 AI 解释请求并生成响应,例如使用 语言模型 API
- 将用户请求转发到后端服务
- 使用程序逻辑和本地资源
参与者可以以多种方式使用语言模型。一些参与者仅利用语言模型来获取自定义提示的答案,例如 示例聊天参与者。其他参与者则更高级,像自主代理一样,在语言模型的帮助下调用多个工具。这种高级参与者的一个例子是内置的 @workspace
,它了解您的工作区并可以回答有关工作区的问题。在内部,@workspace
由多个工具提供支持:GitHub 的知识图谱,结合语义搜索、本地代码索引和 VS Code 的语言服务。
当用户在其聊天提示中显式提到 @participant
时,该提示将转发给贡献该特定聊天参与者的扩展。然后,参与者使用 ResponseStream
来响应请求。为了提供流畅的用户体验,聊天 API 是基于流的。聊天响应可以包含丰富的内容,例如 Markdown、文件树、命令按钮等。获取有关支持的响应输出类型的更多信息。
为了帮助用户进一步进行对话,参与者可以为每个响应提供后续操作。后续问题是在聊天用户界面中提供的建议,可能会给用户带来有关聊天扩展功能方面的启发。
参与者还可以贡献命令,这些命令是常用用户意图的简写符号,并以 /
符号表示。然后,扩展可以使用命令来相应地提示语言模型。例如,/explain
是 @workspace
参与者的命令,它对应于语言模型应该解释某些代码的意图。
通过 GitHub Apps 扩展 GitHub Copilot
或者,可以通过创建一个 GitHub App 来扩展 GitHub Copilot,该 GitHub App 在聊天视图中贡献一个聊天参与者。GitHub App 由服务支持,可在所有 GitHub Copilot 表面(例如 github.com、Visual Studio 或 VS Code)上运行。另一方面,GitHub App 没有完全访问 VS Code API 的权限。要了解有关通过 GitHub App 扩展 GitHub Copilot 的更多信息,请参阅 GitHub 文档。
链接
聊天用户体验的组成部分
以下屏幕截图显示了示例扩展在 Visual Studio Code 聊天体验中不同的聊天概念。
- 使用
@
语法调用@cat
聊天参与者 - 使用
/
语法调用/teach
命令 - 用户提供的查询,也称为用户提示
- 图标和参与者
fullName
,指示 Copilot 正在使用@cat
聊天参与者 - Markdown 响应,由
@cat
提供 - 包含在 Markdown 响应中的代码片段
- 包含在
@cat
响应中的按钮,该按钮调用 VS Code 命令 - 聊天参与者提供的建议的后续问题
- 聊天输入字段,其中包含聊天参与者的
description
属性提供的占位符文本
开发聊天扩展
聊天扩展是一个扩展,它向 聊天视图贡献一个聊天参与者。
实现聊天扩展所需的最少功能是
- 注册聊天参与者,以便用户可以使用 VS Code 聊天视图中的
@
符号调用它。 - 定义一个请求处理程序,该处理程序解释用户的问题,并在聊天视图中返回响应。
您可以使用以下可选功能进一步扩展聊天扩展的功能
- 注册聊天命令,为用户提供常见问题的简写符号
- 定义建议的后续问题,以帮助用户继续对话
作为开发聊天扩展的起点,您可以参考我们的 聊天扩展示例。此示例实现了一个简单的猫导师,可以使用猫的比喻来解释计算机科学主题。
注册聊天扩展
创建聊天扩展的第一步是通过提供唯一的 id
、name
和 description
在您的 package.json
中注册它。
"contributes": {
"chatParticipants": [
{
"id": "chat-sample.cat",
"name": "cat",
"fullName": "Cat",
"description": "Meow! What can I teach you?",
"isSticky": true
}
]
}
然后,用户可以使用 @
符号和您提供的 name
在聊天视图中引用聊天参与者。fullName
显示在来自您的参与者的响应的标题区域中。description
用作聊天输入字段中的占位符文本。
isSticky
属性控制聊天参与者是否持久,这意味着在用户开始与参与者交互后,参与者名称会自动预先添加到聊天输入字段中。
我们建议使用小写的 name
,并对 fullName
使用标题大小写,以与现有聊天参与者对齐。获取有关聊天参与者命名约定的更多信息。
[!NOTE] 某些参与者名称是保留的,如果您使用保留名称,VS Code 将显示您的参与者的完全限定名称(包括扩展 ID)。
需要预先在 package.json
中注册参与者和命令,以便 VS Code 可以在正确的时间激活您的扩展,而不是在需要之前激活。
注册后,您的扩展需要做的就是使用 vscode.chat.createChatParticipant
创建参与者。创建参与者时,您必须提供在 package.json
中定义的 ID,以及一个请求处理程序。
以下代码片段显示了如何创建 @cat
聊天参与者(在您在 package.json
中注册它之后)
export function activate(context: vscode.ExtensionContext) {
// Register the chat participant and its request handler
const cat = vscode.chat.createChatParticipant('chat-sample.cat', handler);
// Optionally, set some properties for @cat
cat.iconPath = vscode.Uri.joinPath(context.extensionUri, 'cat.jpeg');
// Add the chat request handler here
}
在注册和创建聊天参与者之后,您现在需要实现请求处理程序来处理用户的请求。
实现请求处理程序
请求处理程序负责处理 VS Code 聊天视图中的用户聊天请求。每次用户在聊天输入字段中输入提示时,都会调用聊天请求处理程序。以下是实现聊天请求处理程序的典型步骤
- 定义请求处理程序
- 确定用户请求的意图
- 执行逻辑以回答用户的问题
- 向用户返回响应
定义请求处理程序
您可以在扩展的 activate
函数中定义请求处理程序 (vscode.ChatRequestHandler
)。
以下代码片段显示了如何定义请求处理程序
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
): Promise<ICatChatResult> => {
// Chat request handler implementation goes here
};
确定请求意图
要确定用户请求的意图,您可以引用 vscode.ChatRequest
参数来访问用户的提示、命令和聊天位置。或者,您可以利用语言模型来确定用户的意图,而不是使用传统逻辑。作为 request
对象的一部分,您会获得用户在聊天模型下拉列表中选择的语言模型实例。了解如何在您的扩展中使用语言模型 API。
以下代码片段显示了首先使用命令,然后使用用户提示来确定用户意图的基本结构
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
): Promise<ICatChatResult> => {
// Test for the `teach` command
if (request.command == 'teach') {
// Add logic here to handle the teaching scenario
doTeaching(request.prompt, request.variables);
} else {
// Determine the user's intent
const intent = determineUserIntent(request.prompt, request.variables, request.model);
// Add logic here to handle other scenarios
}
};
处理请求
接下来,您需要实现用于处理用户请求的实际逻辑。通常,聊天扩展使用 request.model
语言模型实例来处理请求。在这种情况下,您可以调整语言模型提示以匹配用户的意图。或者,您可以通过调用后端服务、使用传统编程逻辑或使用所有这些选项的组合来实现扩展逻辑。例如,您可以调用 Web 搜索来收集其他信息,然后将其作为上下文提供给语言模型。
在处理当前请求时,您可能希望参考以前的聊天消息。例如,如果之前的响应返回了一个 C# 代码片段,则用户当前的请求可能是“用 Python 给出代码”。了解如何使用聊天消息历史记录。
如果您想根据聊天输入的位置以不同的方式处理请求,可以使用 vscode.ChatRequest
的 location
属性。例如,如果用户从终端内联聊天发送请求,您可能会查找 shell 命令。而如果用户使用“聊天”视图,您可以返回更详细的响应。
返回聊天响应
一旦处理完请求,您必须在“聊天”视图中向用户返回响应。聊天扩展可以使用流式传输来响应用户查询。响应可以包含不同的内容类型:markdown、图像、引用、进度、按钮和文件树。例如,要生成此响应
扩展可以使用以下方式中的响应流
stream.progress('Picking the right topic to teach...');
stream.markdown(`\`\`\`typescript
const myStack = new Stack();
myStack.push(1); // pushing a number on the stack (or let's say, adding a fish to the stack)
myStack.push(2); // adding another fish (number 2)
console.log(myStack.pop()); // eating the top fish, will output: 2
\`\`\`
So remember, Code Kitten, in a stack, the last fish in is the first fish out - which we tech cats call LIFO (Last In, First Out).`);
stream.button({
command: 'cat.meow',
title: vscode.l10n.t('Meow!'),
arguments: []
});
获取有关支持的聊天响应输出类型的更多信息。
实际上,扩展通常会向语言模型发送请求。一旦他们从语言模型获得响应,他们可能会进一步处理它,并决定是否应该将任何内容流式传输回给用户。VS Code 聊天 API 基于流式传输,并且与流式传输 语言模型 API 兼容。这允许扩展持续报告进度和结果,以实现流畅的用户体验。了解如何使用 语言模型 API。
使用聊天消息历史记录
参与者可以访问当前聊天会话的消息历史记录。参与者只能访问提到他们的消息。 history
项要么是 ChatRequestTurn
,要么是 ChatResponseTurn
。例如,使用以下代码片段检索用户在当前聊天会话中发送给您的参与者的所有先前请求
const previousMessages = context.history.filter(h => h instanceof vscode.ChatRequestTurn);
历史记录不会自动包含在提示中,是否在将消息传递给语言模型时添加历史记录作为附加上下文由参与者决定。
注册命令
聊天参与者可以贡献命令,这些命令是扩展提供的特定功能的快捷方式。用户可以使用 /
语法在聊天中引用命令,例如 /explain
。
回答问题时的任务之一是确定用户的意图。例如,VS Code 可以推断出 Create a new workspace with Node.js Express Pug TypeScript
表示您想要一个新项目,但 @workspace /new Node.js Express Pug TypeScript
更明确、简洁并节省输入时间。如果您在聊天输入字段中键入 /
,VS Code 会提供一个注册命令列表及其描述。
聊天参与者可以通过将其添加到 package.json
中来贡献带有描述的命令
"contributes": {
"chatParticipants": [
{
"id": "chat-sample.cat",
"name": "cat",
"fullName": "Cat",
"description": "Meow! What can I teach you?",
"isSticky": true,
"commands": [
{
"name": "teach",
"description": "Pick at random a computer science concept then explain it in purfect way of a cat"
},
{
"name": "play",
"description": "Do whatever you want, you are a cat after all"
}
]
}
]
}
获取有关斜杠命令的命名约定的更多信息。
注册后续请求
每次聊天请求后,VS Code 都会调用后续提供程序以获取建议的后续问题,向用户显示。然后,用户可以选择后续问题,并立即将其发送到聊天扩展。后续问题可以为用户提供进一步讨论的灵感,或发现聊天扩展的更多功能。
以下代码片段显示了如何在聊天扩展中注册后续请求
cat.followupProvider = {
provideFollowups(result: ICatChatResult, context: vscode.ChatContext, token: vscode.CancellationToken) {
if (result.metadata.command === 'teach') {
return [{
prompt: 'let us play',
title: vscode.l10n.t('Play with the cat')
} satisfies vscode.ChatFollowup];
}
}
};
[!TIP] 后续应该写成问题或指示,而不仅仅是简洁的命令。
实现参与者检测
为了更轻松地使用自然语言的聊天参与者,您可以实现参与者检测。参与者检测是一种将用户的问题自动路由到合适的参与者的方式,而无需在提示中显式提及参与者。例如,如果用户问“如何将登录页面添加到我的项目中?”,该问题将自动路由到 @workspace
参与者,因为它能回答有关用户项目的问题。
VS Code 使用聊天参与者描述和示例来确定将聊天提示路由到哪个参与者。您可以在扩展的 package.json
文件中的 disambiguation
属性中指定此信息。disambiguation
属性包含一个检测类别列表,每个类别都有描述和示例。
属性 | 描述 | 示例 |
---|---|---|
category |
检测类别。如果参与者服务于不同的目的,则可以为每个目的设置一个类别。 |
|
description |
对适合此参与者的问题类型进行详细描述。 |
|
examples |
具有代表性的示例问题列表。 |
|
您可以为整个聊天参与者、特定命令或两者的组合定义参与者检测。
以下代码片段显示了如何在参与者级别实现参与者检测。
"contributes": {
"chatParticipants": [
{
"id": "chat-sample.cat",
"fullName": "Cat",
"name": "cat",
"description": "Meow! What can I teach you?",
"disambiguation": [
{
"category": "cat",
"description": "The user wants to learn a specific computer science topic in an informal way.",
"examples": [
"Teach me C++ pointers using metaphors",
"Explain to me what is a linked list in a simple way",
"Can you explain to me what is a function in programming?"
]
}
]
}
]
}
同样,您还可以通过为 commands
属性中的一个或多个项添加 disambiguation
属性,在命令级别配置参与者检测。
应用以下准则可以提高扩展的参与者检测的准确性
- 具体:描述和示例应尽可能具体,以避免与其他参与者冲突。避免在参与者和命令信息中使用通用术语。
- 使用示例:示例应代表适合该参与者的问题类型。使用同义词和变体来涵盖广泛的用户查询。
- 使用自然语言:描述和示例应使用自然语言编写,就像您向用户解释参与者一样。
- 测试检测:使用各种示例问题测试参与者检测,并验证是否与内置聊天参与者冲突。
[!NOTE] 内置聊天参与者优先进行参与者检测。例如,在工作区文件上操作的聊天参与者可能会与内置的
@workspace
参与者冲突。
支持的聊天响应输出类型
要返回对聊天请求的响应,您可以使用 ChatResponseStream
参数(位于 ChatRequestHandler
上)。
以下列表提供了“聊天”视图中聊天响应的输出类型。聊天响应可以组合多种不同的输出类型。
-
Markdown
渲染 Markdown 文本片段、简单文本或图像。您可以使用属于 CommonMark 规范的任何 Markdown 语法。使用
ChatResponseStream.markdown
方法并提供 Markdown 文本。示例代码片段
// Render Markdown text stream.markdown('# This is a title \n'); stream.markdown('This is stylized text that uses _italics_ and **bold**. '); stream.markdown('This is a [link](https://vscode.js.cn).\n\n'); stream.markdown('![VS Code](https://vscode.js.cn/assets/favicon.ico)');
-
代码块
渲染支持 IntelliSense、代码格式和交互式控件的代码块,以将代码应用到活动编辑器。要显示代码块,请使用
ChatResponseStream.markdown
方法并应用代码块的 Markdown 语法(使用反引号)。示例代码片段
// Render a code block that enables users to interact with stream.markdown('```bash\n'); stream.markdown('```ls -l\n'); stream.markdown('```');
-
命令链接
在聊天响应中内联呈现用户可以选择以调用 VS Code 命令的链接。要显示命令链接,请使用
ChatResponseStream.markdown
方法并使用链接的 Markdown 语法[link text](command:commandId)
,其中您在 URL 中提供命令 ID。例如,以下链接将打开“命令面板”:[Command Palette](command:workbench.action.showCommands)
。为了防止在您从服务加载 Markdown 文本时发生命令注入,您必须使用
vscode.MarkdownString
对象,并将isTrusted
属性设置为可信 VS Code 命令 ID 的列表。此属性是启用命令链接工作所必需的。如果未设置isTrusted
属性或未列出某个命令,则命令链接将不起作用。示例代码片段
// Use command URIs to link to commands from Markdown let markdownCommandString: vscode.MarkdownString = new vscode.MarkdownString( `[Use cat names](command:${CAT_NAMES_COMMAND_ID})` ); markdownCommandString.isTrusted = { enabledCommands: [CAT_NAMES_COMMAND_ID] }; stream.markdown(markdownCommandString);
如果命令采用参数,则需要先对参数进行 JSON 编码,然后将 JSON 字符串编码为 URI 组件。然后,将编码后的参数作为查询字符串附加到命令链接。
// Encode the command arguments const encodedArgs = encodeURIComponent(JSON.stringify(args)); // Use command URIs with arguments to link to commands from Markdown let markdownCommandString: vscode.MarkdownString = new vscode.MarkdownString( `[Use cat names](command:${CAT_NAMES_COMMAND_ID}?${encodedArgs})` ); markdownCommandString.isTrusted = { enabledCommands: [CAT_NAMES_COMMAND_ID] }; stream.markdown(markdownCommandString);
-
命令按钮
渲染调用 VS Code 命令的按钮。该命令可以是内置命令,也可以是您在扩展中定义的命令。使用
ChatResponseStream.button
方法并提供按钮文本和命令 ID。示例代码片段
// Render a button to trigger a VS Code command stream.button({ command: 'my.command', title: vscode.l10n.t('Run my command') });
-
文件树
渲染一个文件树控件,允许用户预览各个文件。例如,在建议创建新工作区时显示工作区预览。使用
ChatResponseStream.filetree
方法并提供文件树元素数组和文件的基本位置(文件夹)。示例代码片段
// Create a file tree instance var tree: vscode.ChatResponseFileTree[] = [ { name: 'myworkspace', children: [{ name: 'README.md' }, { name: 'app.js' }, { name: 'package.json' }] } ]; // Render the file tree control at a base location stream.filetree(tree, baseLocation);
-
进度消息
在长时间运行的操作期间渲染进度消息,以便向用户提供中间反馈。例如,报告多步操作中每个步骤的完成情况。使用
ChatResponseStream.progress
方法并提供消息。示例代码片段
// Render a progress message stream.progress('Connecting to the database.');
-
参考
在列表引用中添加外部 URL 或编辑器位置的引用,以指示您用作上下文的信息。使用
ChatResponseStream.reference
方法并提供引用位置。示例代码片段
const fileUri: vscode.Uri = vscode.Uri.file('/path/to/workspace/app.js'); // On Windows, the path should be in the format of 'c:\\path\\to\\workspace\\app.js' const fileRange: vscode.Range = new vscode.Range(0, 0, 3, 0); const externalUri: vscode.Uri = vscode.Uri.parse('https://vscode.js.cn'); // Add a reference to an entire file stream.reference(fileUri); // Add a reference to a specific selection within a file stream.reference(new vscode.Location(fileUri, fileRange)); // Add a reference to an external URL stream.reference(externalUri);
-
内联参考
向 URI 或编辑器位置添加内联引用。使用
ChatResponseStream.anchor
方法并提供定位点位置和可选标题。要引用符号(例如,类或变量),您将在编辑器中使用位置。示例代码片段
const symbolLocation: vscode.Uri = vscode.Uri.parse('location-to-a-symbol'); // Render an inline anchor to a symbol in the workspace stream.anchor(symbolLocation, 'MySymbol');
重要:仅当图像和链接源自可信域列表中的域时,它们才可用。获取有关VS Code 中的链接保护的更多信息。
衡量成功
我们建议您通过为 Unhelpful
用户反馈事件以及您的参与者处理的请求总数添加遥测日志记录来衡量您的参与者的成功。然后,可以将初始参与者成功指标定义为:unhelpful_feedback_count / total_requests
。
const logger = vscode.env.createTelemetryLogger({
// telemetry logging implementation goes here
});
cat.onDidReceiveFeedback((feedback: vscode.ChatResultFeedback) => {
// Log chat result feedback to be able to compute the success metric of the participant
logger.logUsage('chatResultFeedback', {
kind: feedback.kind
});
});
与您的聊天响应进行的任何其他用户交互都应衡量为积极指标(例如,用户选择在聊天响应中生成的按钮)。在使用 AI 时,通过遥测技术衡量成功至关重要,因为它是一种非确定性技术。运行实验,衡量并迭代改进您的参与者,以确保良好的用户体验。
命名限制和约定
聊天参与者命名约定
属性 | 描述 | 命名指南 |
---|---|---|
id |
聊天参与者的全局唯一标识符 |
|
name |
聊天参与者的名称,用户通过 @ 符号引用 |
|
fullName |
(可选)参与者的全名,显示为来自该参与者的响应的标签 |
|
description |
(可选)简短描述聊天参与者的功能,显示在聊天输入字段或参与者列表中作为占位符文本 |
|
在任何面向用户的元素(例如属性、聊天回复或聊天用户界面)中引用你的聊天参与者时,建议不要使用术语参与者,因为它也是 API 的名称。例如,@cat
扩展可以称为“适用于 GitHub Copilot 的 Cat 扩展”。
斜杠命令命名约定
属性 | 描述 | 命名指南 |
---|---|---|
name |
斜杠命令的名称,用户通过 / 符号引用 |
|
description |
(可选)简短描述斜杠命令的功能,显示在聊天输入字段或参与者和命令列表中作为占位符文本 |
|
指南
聊天参与者不应仅仅是问答机器人。在构建聊天参与者时,要有创造力,并使用现有的 VS Code API 在 VS Code 中创建丰富的集成。用户也喜欢丰富且方便的交互,例如回复中的按钮、将用户带到聊天中的参与者的菜单项。思考一下 AI 可以在哪些实际场景中帮助你的用户。
并非每个扩展都贡献一个聊天参与者是有意义的。在聊天中拥有太多参与者可能会导致不良的用户体验。当你想要控制完整的提示,包括对语言模型的指令时,聊天参与者是最佳选择。你可以重用精心制作的 Copilot 系统消息,并且可以为其他参与者贡献上下文。
例如,语言扩展(例如 C++ 扩展)可以通过各种其他方式做出贡献
- 贡献工具,将语言服务智能引入用户查询。例如,C++ 扩展可以将
#cpp
工具解析为工作区的 C++ 状态。这为 Copilot 语言模型提供了正确的 C++ 上下文,以提高 Copilot 对 C++ 的回答质量。 - 贡献智能操作,这些操作使用语言模型,可以选择与传统的语言服务知识结合使用,以提供出色的用户体验。例如,C++ 可能已经提供了一个“提取到方法”智能操作,该操作使用语言模型为新方法生成合适的默认名称。
如果聊天扩展即将执行代价高昂的操作,或者即将编辑或删除无法撤消的内容,则应明确征求用户的同意。为了获得良好的用户体验,我们不鼓励扩展贡献多个聊天参与者。每个扩展最多一个聊天参与者的简单模型在 UI 中可以很好地扩展。
发布您的扩展
创建 AI 扩展后,你可以将扩展发布到 Visual Studio Marketplace
- 在发布到 VS Marketplace 之前,我们建议你阅读Microsoft AI 工具和实践指南。这些指南提供了负责任地开发和使用 AI 技术的最佳实践。
- 通过发布到 VS Marketplace,你的扩展遵守了GitHub Copilot 可扩展性可接受的开发和使用政策。
- 按照发布扩展中的描述上传到 Marketplace。
- 如果你的扩展已经贡献了聊天以外的功能,我们建议你不要在扩展清单中引入对 GitHub Copilot 的扩展依赖项。这确保了不使用 GitHub Copilot 的扩展用户可以使用非聊天功能,而无需安装 GitHub Copilot。