GitHub Copilot in VS Code カスタムインストラクションの設定と効果検証【実践編】

こんにちは。

ファインディ で Tech Lead をやらせてもらってる戸田です。

弊社では開発生産性の向上のための投資、検証を継続して行っており、生成AIの活用にも取り組んでいます。

前回の記事で、導入編と題しましてGitHub Copilot in VS Codeのカスタムインストラクションの設定、利用方法を紹介しました。

tech.findy.co.jp

今回は実践編と題しまして、カスタムインストラクションに設定する内容の調整方法について紹介したいと思います。

それでは見ていきましょう!

まず叩き台を用意する

兎にも角にも、まず何かしらの叩き台を用意するのが良いです。一番手っ取り早いのは、開発組織におけるコーディングガイドラインを参考にすることです。

自分の場合は弊社のコーディングガイドラインのマークダウンテキストを丸っとコピペしてきました。カスタムインストラクションに渡す内容は日本語のテキストで問題なく動作するため、そのままコピペしてきても問題ありません。

.github/copilot-instructions.md を作成し、丸っとコピペしましょう。これを叩き台にして、Copilotが理解しやすい形に書き換えていきます。

Copilotが理解しやすい形に書き換えていく

コーディングガイドライン等から引用して、カスタムインストラクションに渡す内容の叩き台は用意できました。

次はその叩き台をCopilotが理解しやすい形に書き換えていきます。コーディングガイドラインは人間が理解しやすい形になっており、これをCopilot用に書き換えていきます。

※これから紹介する調整方法は、あくまで筆者の経験、調整内容に基づくもので、GitHubが公式に明言している内容ではありません。

不要なものは削除する

カスタムインストラクションに渡す内容は、正しい内容のみを簡潔に渡す方がCopilotから出力される内容がイメージに近いものになることが多かったです。

例えば、次のような内容があるとします。

## イベントハンドラの命名規則

- Component が受け取る Props は `on` + `アクション名` + `対象名`
- 関数そのものの名称は `handle` + `アクション名` + `対象名`

bad case
```tsx
export const HogeComponent = () => {
  const { onChangeHoge } = useHogeHooks();
  return <FugaComponent handleChangeHoge={onChangeHoge} />
}
```

good case
```tsx
export const HogeComponent = () => {
  const { handleChangeHoge } = useHogeHooks();

  return <FugaComponent onChangeHoge={handleChangeHoge} />
}
```

良い例と悪い例の両方をサンプルコードで説明しています。しかし、自分が色々調整した結果、Badケースの内容も取り込まれた生成されるコードになってしまうケースがありました。

これは仮説ですが、Copilot的には書いてある内容が正解であり、不要なケースは逆にノイズになるのではないかと思います。

そのため、次のようにBadケースやそこに至るまでの議論の内容などは全て削除し、結論のみを完結に記述するようにしました。

## イベントハンドラの命名規則

- Component が受け取る Props は `on` + `アクション名` + `対象名`
- 関数そのものの名称は `handle` + `アクション名` + `対象名`

```tsx
export const HogeComponent = () => {
  const { handleChangeHoge } = useHogeHooks();

  return <FugaComponent onChangeHoge={handleChangeHoge} />
}
```

これによりBadケースの内容が生成結果に反映されることはなく、生成されるコードがイメージに近いものになることが増えました。

長い文章よりも、簡潔な階層構造

カスタムインストラクションには、長文を渡すよりもマークダウンの階層構造で渡すほうが、Copilotが理解しやすいようでした。

例えば、次のような文章があるとします。

boolean型のPropertyを定義する場合、引数名には `is` をprefixで付け、それが共通コンポーネントの場合はoptionalにする。

このような文章の場合、次のようにマークダウンの階層構造で渡したほうがCopilotが理解しやすいようでした。

- boolean 型のProperty
  - 引数名に `is` を prefix で付ける
  - 共通コンポーネントの場合
    - optional にする

叩き台に長文が書かれていた場合、文章を分解し、マークダウン形式の段落構造などに書き換えるのをおすすめします。

曖昧な表現はNG

曖昧な言葉を使うと、Copilotはそれらを無視することが多かったです。

例えば次のような文章をカスタムインストラクションに渡すとします。

- 親コンポーネントからまとめてテストコードを書く
  - 子コンポーネントへの直接的なテストコードは不要

この場合、不要 という言葉が曖昧に受け取られ、この命令文を無視されることが多かったです。

こういったケースでは、次のように書き換えることでCopilotが理解しやすくなりました。

- 親コンポーネントからまとめてテストコードを書く
  - 子コンポーネントへの直接的なテストコードは書かない

不要書かない とシンプルに言い切ることによって、Copilotが理解しやすくなりました。

このようにCopilotが中々理解してくれないケースでは、言葉の表現を少し変えるだけで一気に理解度が上がることが多かったです。

確認用プロンプトでカスタムインストラクションに渡す内容を調整していく

ここまでで、ある程度の内容をカスタムインストラクションに渡すことが出来ているはずです。

次は、そのカスタムインストラクションの内容がCopilotにどのように理解されるかを確認していきます。

例えば、次のような内容をプロンプトとして用意します。

以下の条件を全て満たすコードを生成してください

- hooksである
- 引数は次の通り
  - カウント数の初期値
    - 任意項目
- 次の内容を変数で管理する
  - カウント数
    - 引数で受け取ったカウント数の初期値で初期化する
- 次の関数を用意する
  - カウント数をインクリメントする
  - カウント数をデクリメントする
- hooksから次の内容を返す
  - カウント数
  - カウント数をインクリメントする関数
  - カウント数をデクリメントする関数

このプロンプトをCopilotに渡して、生成されたコードを確認します。

import { useState } from 'react';

function useCounter(initialCount = 0) {
  const [count, setCount] = useState(initialCount);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);

  return { count, increment, decrement };
}

export default useCounter;

プロンプトの内容を反映したコードが出力されました。ですが、これだけでは弊社のコードガイドラインと一致していない箇所がありました。そこでカスタムインストラクションの調整を行います。

次のような内容を .github/copilot-instructions.md に追記します。

## Hooks
- hooksから返す値は `as const` を付けてreadonlyにする

## イベントハンドラの命名規則

- Component が受け取る Props は `on` + `アクション名` + `対象名`
- 関数そのものの名称は `handle` + `アクション名` + `対象名`

```tsx
export const HogeComponent = () => {
  const { handleChangeHoge } = useHogeHooks();

  return <FugaComponent onChangeHoge={handleChangeHoge} />
}
```

この状態で先程のプロンプトを再実行すると、次のようなコードが生成されました。

import { useState } from 'react';

function useCounter(initialCount = 0) {
  const [count, setCount] = useState(initialCount);

  const handleIncrementCount = () => setCount(count + 1);
  const handleDecrementCount = () => setCount(count - 1);

  return { count, handleIncrementCount, handleDecrementCount } as const;
}

export default useCounter;

カスタムインストラクションに渡した内容通りに、関数名の変更と as const の追加が反映されました。更にここから何かしら調整を加えたい場合は、またカスタムインストラクションの内容を変更し、同じプロンプトを実行して新たに生成されたコードの内容をチェックする手順を繰り返します。

この作業を繰り返すことで、カスタムインストラクションの内容を調整していくことが出来ます。

まとめ

いかがでしたでしょうか?

今回は実戦編と題しまして、GitHub Copilot in VS Codeのカスタムインストラクションに設定する内容の調整方法について紹介しました。

カスタムインストラクションの説明や導入方法については次の記事を参考にしてみてください。

tech.findy.co.jp

弊社としましてもまだまだ検証段階ではありますが、カスタムインストラクションの内容を調整することで、Copilotのサジェスト内容や自動生成コードの質が向上したことを確認しています。

今回の記事が皆さんの開発生産性向上に少しでもお役に立てれば幸いです。

現在、ファインディでは一緒に働くメンバーを募集中です。

興味がある方はこちらから ↓ herp.careers