现已发布!阅读关于 11 月新增功能和修复的内容。

教程:使用 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.tspackage.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
  • 描述 "What can I teach you?",将显示在聊天输入字段中作为占位符文本。

最后,设置 isSticky: true 将在用户开始与参与者互动后,自动在聊天输入字段中预加参与者姓名。

步骤 3:设计提示

现在参与者已注册,您可以开始实现代码导师的逻辑。在 extension.ts 文件中,您将为请求定义一个提示。

设计一个好的提示是获得参与者最佳响应的关键。请参阅 这篇文章,了解有关提示工程的技巧。

您的代码导师应模仿真实导师,引导学生理解概念,而不是直接提供答案。此外,导师应专注于主题,避免回答非编程问题。

考虑以下两个提示。哪个更有可能产生指定的行为?

  1. 您是一位乐于助人的代码导师。您的工作是向用户提供简单的描述和概念示例代码。

  2. 您是一位乐于助人的代码导师。您的工作是向用户提供简单的描述和概念示例代码。通过一系列消息提供对概念的引导式概述。不要直接给出答案,而是引导用户自己找到答案。如果用户提出非编程问题,请礼貌地拒绝回答。

第二个提示更具体,并为参与者提供了清晰的响应方向。将此提示添加到 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.';

步骤 4:实现请求处理程序

现在已选择提示,您需要实现请求处理程序。这将处理用户的聊天请求。您将定义请求处理程序,执行处理请求的逻辑,并向用户返回响应。

首先,定义处理程序

// 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;
};

步骤 5:创建聊天参与者

处理程序实现后,最后一步是使用 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');

步骤 6:运行代码

您现在可以尝试您的聊天参与者了!按 F5 运行代码。将打开一个新的 VS Code 窗口,其中包含您的聊天参与者。

在 Copilot Chat 窗格中,您现在可以通过键入 @tutor 来调用您的参与者!

Participant in Chat pane

尝试键入您想了解的内容进行测试。您应该会看到一个响应,为您提供概念的概述!

如果您键入相关消息以继续对话,您会注意到参与者没有根据您的对话提供后续响应。这是因为我们当前的参与者只发送用户当前的消息,而不是参与者的消息历史记录。

在下面的屏幕截图中,导师正确地对堆栈的起始解释做出了回应。然而,在后续对话中,它不理解用户正在继续对话以查看 Python 中的堆栈实现,因此它提供了关于 Python 的通用响应。

Participant with no message history

步骤 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
  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 中堆栈的实现。

Participant with message history

步骤 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
  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,这将调出您的聊天参与者,您可以获得交互式练习来练习编码!

Participant with a slash command

后续步骤

恭喜!您已成功创建了一个聊天参与者,它可以为编程概念提供解释和示例练习。您可以通过微调提示、添加更多斜杠命令或利用其他 API(如 Language Model API)来进一步扩展您的参与者。准备好后,您还可以将扩展发布到 Visual Studio Code Marketplace

您可以在 vscode-extensions-sample 存储库中找到本教程的完整源代码。

© . This site is unofficial and not affiliated with Microsoft.