教程:使用 Chat API 构建代码教程聊天参与者
在本教程中,您将学习如何创建一个集成到 GitHub Copilot Chat 体验中的 Visual Studio Code 扩展。您将使用 Chat 扩展 API 来贡献一个聊天参与者(chat participant)。您的参与者将是一名代码导师,能够针对编程概念提供解释和示例练习。
先决条件
要完成本教程,您需要准备好以下工具和帐户
第一步:设置您的项目
首先,使用 Yeoman 和 VS Code 扩展生成器来生成扩展项目。
npx --package yo --package generator-code -- yo code
选择以下选项以完成设置
# ? What type of extension do you want to create? New Extension (TypeScript)
# ? What's the name of your extension? Code Tutor
### Press <Enter> to choose default for all options below ###
# ? What's the identifier of your extension? code-tutor
# ? What's the description of your extension? LEAVE BLANK
# ? Initialize a git repository? Yes
# ? Bundle the source code with webpack? No
# ? Which package manager to use? npm
# ? Do you want to open the new folder with Visual Studio Code? Open with `code`
扩展项目生成后,您将使用两个主要文件:extension.ts 和 package.json,您可以在 Extension Anatomy 文档中了解更多相关信息。简要概述如下
extension.ts是扩展的主入口点,包含聊天参与者的逻辑。package.json包含扩展的元数据,例如参与者的名称和描述。
删除 extension.ts 中 activate() 方法内的自动生成代码。这里就是您将放置聊天参与者逻辑的地方。
第二步:注册聊天参与者
在 package.json 文件中,将自动生成的 contributes 部分替换为以下内容
"contributes":{
"chatParticipants": [
{
"id": "chat-tutorial.code-tutor",
"fullName": "Code Tutor",
"name": "tutor",
"description": "What can I teach you?",
"isSticky": true
}
]
}
此代码注册了一个具有以下属性的聊天参与者
- 唯一 ID
chat-tutorial.code-tutor,将在代码中被引用 - 全名
Code Tutor,将显示在参与者响应的标题区域 - 名称
tutor,用于在聊天视图中通过@tutor引用该聊天参与者 - 描述 "What can I teach you?",将作为占位符文本显示在聊天输入框中
最后,设置 isSticky: true 会在用户开始与参与者互动后,自动在聊天输入框中预置参与者名称。
第三步:编写提示词 (Prompt)
参与者注册完成后,您可以开始实现代码导师的逻辑。在 extension.ts 文件中,您将为请求定义一个提示词。
编写高质量的提示词是获得参与者最佳响应的关键。查看这篇文章以获取有关提示工程的技巧。
您的代码导师应该模拟现实生活中的导师,通过引导学生理解概念而不是直接提供答案来帮助他们。此外,导师应专注于主题,并避免回答非编程问题。
考虑以下两个提示词。哪一个更有可能产生预期的行为?
-
You are a helpful code tutor. Your job is to teach the user with simple descriptions and sample code of the concept.
-
You are a helpful code tutor. Your job is to teach the user with simple descriptions and sample code of the concept. Respond with a guided overview of the concept in a series of messages. Do not give the user the answer directly, but guide them to find the answer themselves. If the user asks a non-programming question, politely decline to respond.
第二个提示词更具体,为参与者提供了明确的响应指导。将此提示词添加到 extension.ts 文件中。
const BASE_PROMPT =
'You are a helpful code tutor. Your job is to teach the user with simple descriptions and sample code of the concept. Respond with a guided overview of the concept in a series of messages. Do not give the user the answer directly, but guide them to find the answer themselves. If the user asks a non-programming question, politely decline to respond.';
第四步:实现请求处理程序
选择了提示词后,您需要实现请求处理程序。这是处理用户聊天请求的部分。您将定义请求处理程序,执行处理逻辑,并将响应返回给用户。
首先,定义处理程序
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
return;
};
在此处理程序的主体内,初始化提示词以及一个包含该提示词的 messages 数组。然后,传入用户在聊天框中输入的内容。您可以通过 request.prompt 访问该内容。
使用 request.model.sendRequest 发送请求,这将使用当前选定的模型发送请求。最后,将响应流式传输给用户。
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
// initialize the prompt
let prompt = BASE_PROMPT;
// initialize the messages array with the prompt
const messages = [vscode.LanguageModelChatMessage.User(prompt)];
// add in the user's message
messages.push(vscode.LanguageModelChatMessage.User(request.prompt));
// send the request
const chatResponse = await request.model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
return;
};
第五步:创建聊天参与者
处理程序实现后,最后一步是使用 Chat 扩展 API 中的 createChatParticipant 方法来创建聊天参与者。请确保使用与 package.json 中相同的 ID。
您可以通过为参与者添加图标来进一步自定义它。这将在与参与者互动时显示在聊天视图中。
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
// initialize the prompt
let prompt = BASE_PROMPT;
// initialize the messages array with the prompt
const messages = [vscode.LanguageModelChatMessage.User(prompt)];
// add in the user's message
messages.push(vscode.LanguageModelChatMessage.User(request.prompt));
// send the request
const chatResponse = await request.model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
return;
};
// create participant
const tutor = vscode.chat.createChatParticipant('chat-tutorial.code-tutor', handler);
// add icon to participant
tutor.iconPath = vscode.Uri.joinPath(context.extensionUri, 'tutor.jpeg');
第六步:运行代码
现在您可以尝试您的聊天参与者了!按 F5 运行代码。一个新的 VS Code 窗口将打开,其中包含您的聊天参与者。
在 Copilot Chat 面板中,现在可以通过输入 @tutor 来调用您的参与者!

输入您想要学习的内容进行测试。您应该会看到一个提供概念概述的回复!
如果您输入相关消息以继续对话,您会注意到参与者并没有根据您的对话内容给出后续回复。这是因为我们当前的参与者仅发送了用户的当前消息,而没有发送参与者的消息历史记录。
在下面的截图中,导师正确地以对堆栈的初步解释作为回应。但是,在后续对话中,它并没有理解用户是在继续对话以查看 Python 中堆栈的实现,因此它反而给出了关于 Python 的通用回答。

第七步:添加消息历史以获取更多上下文
Copilot Chat 的最大价值之一是能够迭代多条消息以获得最佳响应。要实现这一点,您需要将参与者的消息历史发送到聊天请求中。您可以通过 context.history 访问此历史记录。
您需要检索该历史记录并将其添加到 messages 数组中。您需要在添加 request.prompt 之前完成此操作。
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
// initialize the prompt
let prompt = BASE_PROMPT;
// initialize the messages array with the prompt
const messages = [vscode.LanguageModelChatMessage.User(prompt)];
// get all the previous participant messages
const previousMessages = context.history.filter(
h => h instanceof vscode.ChatResponseTurn
);
// add the previous messages to the messages array
previousMessages.forEach(m => {
let fullMessage = '';
m.response.forEach(r => {
const mdPart = r as vscode.ChatResponseMarkdownPart;
fullMessage += mdPart.value.value;
});
messages.push(vscode.LanguageModelChatMessage.Assistant(fullMessage));
});
// add in the user's message
messages.push(vscode.LanguageModelChatMessage.User(request.prompt));
// send the request
const chatResponse = await request.model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
return;
};
现在运行代码时,您可以使用之前所有消息的上下文与您的参与者进行对话!在下面的截图中,参与者正确地理解了用户正在请求查看 Python 中堆栈的实现。

第八步:添加命令
实现基本参与者后,您可以通过添加命令来扩展它。命令是常见用户意图的简写表示,由 / 符号指示。扩展随后可以使用该命令相应地提示语言模型。
添加一个命令来提示您的导师给出概念练习会很棒。您需要在 package.json 文件中注册该命令,并在 extension.ts 中实现逻辑。您可以将命令命名为 exercise,这样可以通过输入 /exercise 来调用它。
在 package.json 中,将 commands 属性添加到 chatParticipants 属性中。在这里,您将指定命令的名称和简短说明
"contributes": {
"chatParticipants": [
{
"id": "chat-tutorial.code-tutor",
"fullName": "Code Tutor",
"name": "tutor",
"description": "What can I teach you?",
"isSticky": true,
"commands": [
{
"name": "exercise",
"description": "Provide exercises to practice a concept."
}
]
}
]
},
要实现从导师处获取示例练习的逻辑,最简单的方法是更改发送到请求的提示词。创建一个名为 EXERCISES_PROMPT 的新提示词,要求参与者返回示例练习。以下是一个示例
const EXERCISES_PROMPT =
'You are a helpful tutor. Your job is to teach the user with fun, simple exercises that they can complete in the editor. Your exercises should start simple and get more complex as the user progresses. Move one concept at a time, and do not move on to the next concept until the user provides the correct answer. Give hints in your exercises to help the user learn. If the user is stuck, you can provide the answer and explain why it is the answer. If the user asks a non-programming question, politely decline to respond.';
在请求处理程序中,您需要添加逻辑来检测用户是否引用了该命令。您可以通过 request.command 属性实现这一点。
如果引用了该命令,请将提示词更新为新创建的 EXERCISES_PROMPT
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
// initialize the prompt
let prompt = BASE_PROMPT;
if (request.command === 'exercise') {
prompt = EXERCISES_PROMPT;
}
// initialize the messages array with the prompt
const messages = [vscode.LanguageModelChatMessage.User(prompt)];
// get all the previous participant messages
const previousMessages = context.history.filter(
h => h instanceof vscode.ChatResponseTurn
);
// add the previous messages to the messages array
previousMessages.forEach(m => {
let fullMessage = '';
m.response.forEach(r => {
const mdPart = r as vscode.ChatResponseMarkdownPart;
fullMessage += mdPart.value.value;
});
messages.push(vscode.LanguageModelChatMessage.Assistant(fullMessage));
});
// add in the user's message
messages.push(vscode.LanguageModelChatMessage.User(request.prompt));
// send the request
const chatResponse = await request.model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
return;
};
这就是所有需要添加的内容!获取消息历史记录、发送请求以及流式传输请求的其余逻辑保持不变。
现在您可以输入 /exercise,这将调出您的聊天参与者,您可以获取交互式练习来练习编程!

后续步骤
恭喜!您已成功创建了一个可以为编程概念提供解释和示例练习的聊天参与者。您可以通过微调提示词、添加更多斜杠命令或利用其他 API(如 Language Model API)来进一步扩展您的参与者。准备好后,您还可以将扩展发布到 Visual Studio Code Marketplace。
您可以在 vscode-extensions-sample 仓库中找到本教程的完整源代码。