教程:使用 Chat API 构建代码教程聊天参与者
在本教程中,你将学习如何创建一个与 GitHub Copilot Chat 体验集成的 Visual Studio Code 扩展。你将使用 Chat 扩展 API 贡献一个聊天参与者。你的参与者将是一个代码导师,可以提供编程概念的解释和示例练习。
先决条件
完成本教程需要以下工具和帐户
步骤 1:设置你的项目
首先,使用 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.ts
是扩展的主要入口点,包含聊天参与者的逻辑。package.json
包含扩展的元数据,例如参与者的名称和描述。
删除 extension.ts
activate()
方法中自动生成的代码。你将在其中放置聊天参与者的逻辑。
步骤 2:注册聊天参与者
在 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
- 描述 “我能教你什么?”,将在聊天输入字段中显示为占位符文本
最后,设置 isSticky: true
将在用户开始与参与者交互后自动在聊天输入字段中预先添加参与者名称。
步骤 3:编写提示并选择模型
现在参与者已经注册,你可以开始实现代码导师的逻辑。在 extension.ts
文件中,你将定义提示并选择模型以进行请求。
编写一个好的提示是获得参与者最佳回复的关键。查看 这篇文章 以获取有关提示工程的技巧。
你的代码导师应该通过引导学生理解概念而不是直接提供答案来模拟现实世界的导师。此外,导师应该专注于主题,避免回答非编程问题。
考虑以下两个提示。哪个更有可能给出指定的行为?
-
你是一位乐于助人的代码导师。你的工作是用简单的描述和示例代码教导用户概念。
-
你是一位乐于助人的代码导师。你的工作是用简单的描述和示例代码教导用户概念。以一系列消息的形式回复概念的引导概述。不要直接给出答案,而是引导用户自己找到答案。如果用户提出非编程问题,请礼貌地拒绝回答。
第二个提示更具体,并为参与者提供了关于如何回复的明确指导。将此提示添加到 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.';
你还需要选择请求的模型。建议使用 gpt-4o,因为它速度快且质量高。
const MODEL_SELECTOR: vscode.LanguageModelChatSelector = {
vendor: 'copilot',
family: 'gpt-4o'
};
步骤 4:实现请求处理程序
现在提示和模型已经选择,你需要实现请求处理程序。这将处理用户的聊天请求。你将定义请求处理程序,执行处理请求的逻辑,并向用户返回响应。
首先,定义处理程序
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
return;
};
在这个处理程序的主体中,初始化提示和模型。检查模型是否成功返回。
// define a chat handler
const handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken
) => {
// initialize the prompt and model
let prompt = BASE_PROMPT;
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
// make sure the model is available
if (model) {
}
return;
};
使用我们在上一步中编写的提示初始化 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 and model
let prompt = BASE_PROMPT;
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
// make sure the model is available
if (model) {
// 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 model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
}
return;
};
步骤 5:创建聊天参与者
实现处理程序后,最后一步是使用 Chat 扩展 API 中的 createChatParticipant
方法创建聊天参与者。确保使用与你在 package.json
中使用的 ID 相同的 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 and model
let prompt = BASE_PROMPT;
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
// make sure the model is available
if (model) {
// 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 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');
步骤 6:运行代码
现在你可以试用你的聊天参与者了!按 F5 运行代码。一个新的 VS Code 窗口将打开,包含你的聊天参与者。
在 Copilot Chat 面板中,你现在可以通过输入 @tutor
来调用你的参与者!
尝试输入你想学习的内容。你应该会看到一个回复,其中包含该概念的概述!
如果你键入相关消息以继续对话,你会发现参与者不会根据你的对话提供后续回复。这是因为我们当前的参与者只发送了用户的当前消息,而不是参与者消息历史记录。
在下面的截图中,导师正确地回复了堆栈的开始解释。但是,在后续回复中,它没有理解用户正在继续对话以查看堆栈在 Python 中的实现,因此它反而给出了关于 Python 的通用回复。
步骤 7:添加消息历史记录以获得更多上下文
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 and model
let prompt = BASE_PROMPT;
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
// make sure the model is available
if (model) {
// 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 model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
}
return;
};
现在运行代码时,你就可以与你的参与者进行对话,包括所有先前消息的上下文!在下面的截图中,参与者正确地理解了用户正在请求查看堆栈在 Python 中的实现。
步骤 8:添加命令
现在基本参与者已经实现,你可以通过添加命令来扩展它。命令是常用用户意图的简写符号,用 /
符号表示。然后,扩展可以使用命令相应地提示语言模型。
最好添加一个命令来提示导师提供某个概念的练习。你需要在 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 and model
let prompt = BASE_PROMPT;
if (request.command === 'exercise') {
prompt = EXERCISES_PROMPT;
}
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
// make sure the model is available
if (model) {
// 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 model.sendRequest(messages, {}, token);
// stream the response
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
}
return;
};
这就是需要添加的所有内容!获取消息历史记录、发送请求和流式传输请求的其余逻辑保持不变。
现在,你可以键入 ` /exercise` ,这将调出你的聊天参与者,你可以获得交互式练习来练习编码!
下一步
恭喜!你已成功创建了一个可以提供编程概念的解释和示例练习的聊天参与者。你可以通过微调提示、添加更多斜杠命令或利用其他 API(如 语言模型 API)来进一步扩展你的参与者。准备就绪后,你也可以将你的扩展发布到 Visual Studio Code 市场。
你可以在 vscode-extensions-sample 存储库 中找到本教程的完整源代码。