Claude 3 (Opus)で公開しているライブラリの説明文を作らせてみた(パッケージ編)

この記事では、Claudeを使って複数ディレクトリ・ファイルで構成されたアプリケーションやライブラリについて説明できるAIチャットを作る仕組みを検討した記録が紹介されています。目標はGitHubリポジトリに関する質問に回答できる仕組みを作ることで、RAGでプロンプトに挿入する最適なテキストを検討しました。AWS Lambda関数でAPIGatewayのレスポンスを構築するためのライブラリ群について詳細な説明があります。

広告ここから
広告ここまで

目次

    この記事では、Claudeを使って複数ディレクトリ・ファイルで構成されたアプリケーションやライブラリについて説明できるAIチャットを作る仕組みを検討した記録を紹介します。これは「Anthropic Claude で出来るもん! Advent Calendar 2024」4日目の記事として公開しました。

    目指したいもの

    最終的なゴールとしては、特定のGitHubリポジトリについて、使い方やコードに関する質問に回答できる仕組みを作ることを目指しています。そのための前段階として、どのような情報をモデルに渡して質問するのがよいかや、構成すべきベクトルDBの内容について検討することを目指しました。ここではRAGでプロンプトに挿入する最適なテキストを検討するためのプロンプトをClaudeのGUIで検証しています。

    パッケージ(ディレクトリ単位)の説明文を間に挟んでみる

    今回試した方法では、ソースコードそのものの代わりにコードの説明文を生成させました。例えば次のようなプロンプトを用意します。<説明文>には、以前試した「コードを読ませるプロンプト」で生成したテキストを挿入します。

    以下にnpmで"aws-utils"として公開されているライブラリに含まれるコードのうち、`responsebuilder`ディレクトリに含まれているコードの説明文を入力します。
    
    ## libs/ResponseBuilder.ts
    <説明文>
    
    ## libs/index.ts
    <説明文>
    
    ==
    このディレクトリにあるライブラリ群の説明文を詳細に作成して。
    

    このプロンプトをClaudeで実行すると、次のような結果が得られました。説明文を元にしているため、コードの説明よりは実装の詳細について言及するテキストが目立つ結果となりました。

    このディレクトリには、AWS Lambda関数でAPIGatewayのレスポンスを構築するためのライブラリ群が含まれています。これらのライブラリは、レスポンスの構築を簡潔で読みやすいコードで行うことを目的としており、型安全性も確保されています。
    
    1. `ResponseBuilder.ts`:
       - このファイルは、APIGatewayのレスポンスを構築するためのインターフェースとビルダークラスを定義しています。
       - `APIGatewayResponseHeaders`インターフェースは、APIGatewayのレスポンスヘッダーを表現するために使用されます。
       - `ResponseBuilder`インターフェースは、レスポンスを構築するためのビルダーインターフェースを定義しています。このインターフェースには、ステータスコード、ヘッダー、ボディを設定するためのメソッドが含まれています。
       - `ResponseBuilder`インターフェースは、ジェネリック型パラメータ`Body`と`Headers`を使用して、レスポンスのボディとヘッダーの型を柔軟に指定できます。
       - このファイルは、レスポンスを構築するための基本的な構造を提供し、実際の実装は`ResponseFactory.ts`で行われます。
    
    2. `ResponseFactory.ts`:
       - このファイルは、`ResponseBuilder`インターフェースを実装する`ResponseFactory`クラスを提供しています。
       - `ResponseFactory`クラスは、`init<Body, Headers>()`静的メソッドを提供し、`ResponseBuilder`オブジェクトを初期化します。
       - `init<Body, Headers>()`メソッドは、デフォルトのステータスコード、ヘッダー、ボディを設定し、`ResponseBuilder`オブジェクトを返します。
       - `ResponseBuilder`オブジェクトは、フルエントインターフェースパターンに従っており、メソッドチェーンを使ってレスポンスのプロパティを設定することができます。
       - `setStatusCode()`、`updateHeader()`、`putHeaders()`、`setBody()`、`unsetBody()`メソッドを使用して、レスポンスのステータスコード、ヘッダー、ボディを設定できます。
       - `getResponse()`メソッドを呼び出すことで、構築されたレスポンスを`APIGatewayProxyResult`型で取得できます。
    
    3. `index.ts`:
       - このファイルは、ライブラリのエントリーポイントとして機能します。
       - `ResponseFactory`クラスを外部に公開し、他のモジュールからアクセスできるようにしています。
    
    これらのライブラリを使用することで、AWS Lambda関数でAPIGatewayのレスポンスを簡単かつ型安全に構築することができます。以下は、ライブラリの使用例です:
    
    ```typescript
    import { ResponseFactory } from 'aws-utils/responsebuilder';
    
    const response = ResponseFactory.init<{ message: string }>()
      .setStatusCode(200)
      .updateHeader('Content-Type', 'application/json')
      .setBody({ message: 'Success' })
      .getResponse();
    ```
    
    上記の例では、`ResponseFactory.init()`メソッドを呼び出してレスポンスビルダーを初期化し、ステータスコード、ヘッダー、ボディを設定しています。最後に`getResponse()`メソッドを呼び出して、構築されたレスポンスを取得しています。
    
    このライブラリ群は、AWS Lambda関数でのレスポンス構築を簡素化し、コードの可読性と保守性を向上させるために役立ちます。

    この作業を、ライブラリのファイル数だけ繰り返しましょう。そしてその後、結合させるプロンプトを新しく実行します。

    以下にnpmで"aws-utils"として公開されているライブラリのコードを説明する文章を入力します。内容を読んで、README.mdを生成してください。
    <生成結果をまとめて貼り付ける>

    これを実行した結果が以下のとおりです。想像以上にそれらしいReadmeファイルが生成されていることがわかります。コードからReadmeを生成させると、内部的な実装に関する言及が増えがちな印象を持っているのですが、この方法であれば使う側に向けたコンテンツが多くなっているように感じます。

    # AWS Utils
    
    このライブラリは、AWSを使用する際に役立つユーティリティ関数を提供します。主に、AWS Lambda関数でのAPIGatewayレスポンスの構築と、タイプに基づいたResolveTargetの解決機能を提供します。
    
    ## インストール
    
    ```bash
    npm install aws-utils
    ```
    
    ## 機能
    
    ### Response Builder
    
    `responsebuilder`ディレクトリには、AWS Lambda関数でAPIGatewayのレスポンスを構築するためのライブラリが含まれています。これらのライブラリは、レスポンスの構築を簡潔で読みやすいコードで行うことを目的としており、型安全性も確保されています。
    
    #### 使用例
    
    ```typescript
    import { ResponseFactory } from 'aws-utils/responsebuilder';
    
    const response = ResponseFactory.init<{ message: string }>()
      .setStatusCode(200)
      .updateHeader('Content-Type', 'application/json')
      .setBody({ message: 'Success' })
      .getResponse();
    ```
    
    ### Resolver
    
    `resolver`ディレクトリには、特定のタイプに対応するResolveTargetオブジェクトを管理し、実行時にタイプに基づいて適切なResolveTargetを解決するためのResolverクラスが含まれています。
    
    #### 使用例
    
    ```typescript
    import { Resolver } from 'aws-utils/resolver';
    
    const resolver = new Resolver<string>();
    resolver.setUpdaters(
      {
        supports: (type: string) => type === 'typeA',
        handle: () => 'This is type A',
      },
      {
        supports: (type: string) => type === 'typeB',
        handle: () => 'This is type B',
      }
    );
    
    const result = resolver.resolve('typeA');
    console.log(result); // "This is type A"
    ```
    
    ## ドキュメント
    
    各ディレクトリには、詳細な説明とコード例を含むREADME.mdファイルが用意されています。
    
    - [Response Builder](./responsebuilder/README.md)
    - [Resolver](./resolver/README.md)
    
    ## ライセンス
    
    このプロジェクトはMITライセンスの下で公開されています。

    ライブラリについて質問してみる

    テキスト生成だけでなく、GitHubのIssueへの回答に使えないかも試してみましょう。

    プロンプトにないが、推測できそうな質問

    ということで、早速質問をしてみます。最終成果物を考慮し、RAGを想定したプロンプトにしてみました。

    以下にユーザーからこのライブラリへの質問を紹介します。
    事前に渡している内容から、解決策を考えてください。もし事前の情報で回答が見つからない場合は、どのディレクトリの実装を確認すべきかなどのヒントを紹介するにとどめてください。
    
    質問:
    ResponseFactoryを使ったのですが、APIレスポンスがInternal Server Errorになりました。どうしてですか?
    

    これに対する回答がこちらです。N=1ではありますが、そこまで見当違いな回答をしているようには見えませんでした。

    ResponseFactoryを使用してAPIレスポンスを構築する際に、Internal Server Errorが発生する可能性があるいくつかの原因が考えられます。
    
    1. レスポンスのボディやヘッダーに不適切な値が設定されている可能性があります。例えば、ボディにシリアライズできないオブジェクトを設定したり、無効なヘッダー名や値を指定したりすると、エラーが発生する可能性があります。
    
    2. レスポンスの構築プロセスでエラーが発生している可能性があります。ResponseFactoryの実装を確認し、エラーが発生する可能性のある箇所を特定する必要があります。
    
    3. AWS Lambda関数の実行環境に問題がある可能性もあります。Lambda関数のログを確認し、エラーメッセージやスタックトレースを確認することで、問題の原因を特定できるかもしれません。
    
    ResponseFactoryの実装を確認するには、`responsebuilder`ディレクトリ内の`ResponseFactory.ts`ファイルを参照してください。このファイルには、レスポンスを構築するためのメソッドが定義されています。エラーが発生する可能性のある箇所を特定するために、このファイルを詳しく確認することをお勧めします。
    
    また、ResponseFactoryを使用する際には、以下の点に注意してください:
    
    1. レスポンスのボディには、シリアライズ可能なオブジェクトを設定してください。
    2. ヘッダー名と値は、有効な文字列である必要があります。
    3. ステータスコードは、有効なHTTPステータスコードである必要があります。
    
    これらの点に注意し、ResponseFactoryの実装とAWS Lambda関数のログを確認することで、Internal Server Errorの原因を特定し、問題を解決できると思います。

    プロンプト内に答えがある質問

    続いてプロンプトに渡す文章に答えがある種類の質問をしてみました。

    以下にユーザーからこのライブラリへの質問を紹介します。
    事前に渡している内容から、解決策を考えてください。もし事前の情報で回答が見つからない場合は、どのディレクトリの実装を確認すべきかなどのヒントを紹介するにとどめてください。
    
    質問:
    以下のコードが動きません。なぜですか?
    ```
    const resolver = new Resolver<string>();
    resolver.setUpdaters(
      {
        supports: (type: string) => type === 'typeC',
        handle: () => 'This is type A',
      },
      {
        supports: (type: string) => type === 'typeB',
        handle: () => 'This is type B',
      }
    );
    
    const result = resolver.resolve('typeA');
    ```

    この場合は、より詳細な説明が生成されます。

    提供されたコードが動作しない理由は、`resolver.resolve('typeA')`を呼び出しているためです。`Resolver`クラスは、`supports`メソッドを使用して、指定されたタイプをサポートするResolveTargetをフィルタリングします。しかし、コード内で定義されているResolveTargetは、`'typeC'`と`'typeB'`のみをサポートしており、`'typeA'`をサポートするResolveTargetは定義されていません。
    
    したがって、`resolver.resolve('typeA')`を呼び出すと、対応するResolveTargetが見つからず、エラーがスローされます。
    
    この問題を解決するには、以下のいずれかの方法を試してください:
    
    1. `'typeA'`をサポートするResolveTargetを追加する:
    
    ```typescript
    resolver.setUpdaters(
      {
        supports: (type: string) => type === 'typeA',
        handle: () => 'This is type A',
      },
      {
        supports: (type: string) => type === 'typeB',
        handle: () => 'This is type B',
      }
    );
    ```
    
    2. `resolver.resolve()`の引数を、定義されているResolveTargetがサポートするタイプに変更する:
    
    ```typescript
    const result = resolver.resolve('typeB');
    ```
    
    また、`resolver`ディレクトリ内の`Resolver.ts`ファイルを確認することで、`Resolver`クラスの実装の詳細を確認できます。このファイルには、`resolve`メソッドの実装が含まれており、指定されたタイプに対応するResolveTargetを解決するロジックが定義されています。

    プロンプトの中で詳しく説明されていない関数について聞いてみる

    最後に触れられてはいるものの、説明が省略されているものについても試してみましょう。

    以下にユーザーからこのライブラリへの質問を紹介します。
    事前に渡している内容から、解決策を考えてください。もし事前の情報で回答が見つからない場合は、どのディレクトリの実装を確認すべきかなどのヒントを紹介するにとどめてください。
    
    質問:
    レスポンスヘッダーを一括で設定する方法は?

    これについても、ある程度不自然ではない回答が生成されていました。

    レスポンスヘッダーを一括で設定するには、`ResponseFactory`の`putHeaders()`メソッドを使用します。このメソッドは、`APIGatewayResponseHeaders`型のオブジェクトを受け取り、レスポンスのヘッダーを一括で設定します。
    
    使用例:
    
    ```typescript
    import { ResponseFactory } from 'aws-utils/responsebuilder';
    
    const headers: APIGatewayResponseHeaders = {
      'Content-Type': 'application/json',
      'Cache-Control': 'no-cache',
      'X-Custom-Header': 'custom-value',
    };
    
    const response = ResponseFactory.init()
      .setStatusCode(200)
      .putHeaders(headers)
      .setBody({ message: 'Success' })
      .getResponse();
    ```
    
    上記の例では、`headers`オブジェクトを定義し、`'Content-Type'`、`'Cache-Control'`、`'X-Custom-Header'`の各ヘッダーを設定しています。そして、`ResponseFactory`の`putHeaders()`メソッドを呼び出して、これらのヘッダーをレスポンスに一括で設定しています。
    
    `putHeaders()`メソッドの詳細な実装は、`responsebuilder`ディレクトリ内の`ResponseFactory.ts`ファイルで確認できます。このメソッドは、`APIGatewayResponseHeaders`型のオブジェクトを`headers`プロパティに設定し、`ResponseBuilder`オブジェクトを返します。これにより、他のメソッドをチェーンして呼び出すことができます。

    プロンプトを試してみて

    コードをそのままEmbeddingするだけでなく、このような「ファイル・コード・クラス単位の説明書」を作成してからEmbeddingすることで、使い方やトラブルシューティングに使えるRAGが作れそうです。悩ましいのは、どの粒度で説明書を生成し、インデックスしていくかでしょうか。この記事で紹介した手法を試したころよりもトークン数などが増えてはいます。とはいえ数百・数千行のコードが記述されたファイルが出てくる可能性なども踏まえると、チャンク生成ロジックを慎重に考える必要があるかと思います。あとはprotectedprivateなメソッドを除外して、それを使った解決提案などを行わないようにするとかでしょうか。

    とはいえREADME.mdやプロダクトサイト向けの雛形生成などにはかなり使えそうです。Difyなども出てきたことですので、試したものを仕組み化する部分まで、年内にやりきりたいところです。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark