VS Codeのエージェントモードを拡張するには、を試してください!

チュートリアル: 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 の2つです。拡張機能の構造に関するドキュメントで詳細を確認できます。簡単な概要として

  • extension.ts は拡張機能の主要なエントリポイントであり、チャット参加者のロジックを含んでいます。
  • package.json は、参加者の名前や説明などの拡張機能のメタデータを含んでいます。

extension.tsactivate() メソッドにある自動生成されたコードを削除します。ここにチャット参加者のロジックを配置します。

ステップ 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。これは Chat ビューでチャット参加者を @tutor として参照するために使用されます
  • 説明 "What can I teach you?"。これはチャット入力フィールドにプレースホルダーテキストとして表示されます

最後に、isSticky: true を設定すると、ユーザーが参加者との対話を開始した後、チャット入力フィールドに自動的に参加者名が前置されます。

ステップ 3: プロンプトを作成する

参加者が登録されたので、コードチューターのロジックの実装を開始できます。extension.ts ファイルで、リクエストのプロンプトを定義します。

良いプロンプトを作成することが、参加者から最高の応答を得る鍵です。プロンプトエンジニアリングのヒントについては、この記事を参照してください。

あなたのコードチューターは、直接的な答えを提供するのではなく、学生が概念を理解するように導くことで、現実のチューターを模倣するべきです。さらに、チューターはトピックに集中し、プログラミング以外の質問には答えないようにすべきです。

以下の2つのプロンプトを検討してください。指定された動作を与える可能性が高いのはどちらですか?

  1. あなたは役立つコードチューターです。あなたの仕事は、概念の簡単な説明とサンプルコードを用いてユーザーを教えることです。

  2. あなたは役立つコードチューターです。あなたの仕事は、概念の簡単な説明とサンプルコードを用いてユーザーを教えることです。一連のメッセージで、概念のガイド付き概要を返答してください。ユーザーに直接答えを与えるのではなく、自分で答えを見つけるように導いてください。ユーザーがプログラミング以外の質問をした場合は、丁重に返答を辞退してください。

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 の最大の価値の1つは、最適な応答を得るために複数のメッセージを反復処理できることです。これを行うには、参加者のメッセージ履歴をチャットリクエストに送信する必要があります。これは 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 のような他の API を活用したりすることで、参加者をさらに拡張できます。準備ができたら、拡張機能を Visual Studio Code Marketplace に公開することもできます。

このチュートリアルの完全なソースコードは、vscode-extensions-sample リポジトリで見つけることができます。