ちょっと話題の記事

LlamaIndexを使ってAWS CDKの記事200本以上でインデックスを作りOpenAIに質問してみた

OpenAIのAPIに対してLlamaIndexを使い特定の分野の記事を取り込むと正しい答えが返ってくるのか確認してみました。本記事ではAWS CDKを対象に調査しています。
2023.03.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

CX事業本部アーキテクトチームの佐藤智樹です。

今回はタイトルの通り、LlamaIndexを使いOpenAIにAWS CDKに関する記事(DevelopersIOのもの)を200本以上でインデックスを作り、どのような返答が返ってくるのか試してみました。LlamaIndexとは、簡単な実装で個別のインデックスを作成しOpenAIのモデルに対して問い合わせできるインターフェースです。詳細は以下をご確認ください。

インデックスの対象となる記事

AWS CDKのタグが付いている記事で、2021年12月21日から2023年3月8日までの合計240本の記事を対象にします。こちらで1つのAWS CDKというジャンルに対してどれほど正確に回答できるようになっていくのか確認します。

やってみた

ここからはOpenAIのキー設定やインデックス作成用のコード、回答に対してどんな結果を返すのか確認します。

OpenAIのAPIキー取得

OpenAIに登録済みの場合、以下のURLからシークレットAPIキーの取得ページに移れます。

「+Create new secret key」から新規のシークレットAPIキーを取得できます。(※永続的な認証情報となるため、試す場合は扱いに十分ご注意ください) 取得したAPIキーを環境変数などに格納します。

$ export OPENAI_API_KEY={取得したAPIキー}

実装

コード自体はLlamaIndexを使うことで非常にシンプルになります。今回はインデックス作成用のllmaindex_tuning.pyと推論用のllmaindex_inference.pyに分けて作ります。

llmaindex_tuning.py

import csv

from llama_index import GPTSimpleVectorIndex, SimpleWebPageReader
from llama_index.langchain_helpers.chatgpt import ChatGPTLLMPredictor

article_urls = []
with open('tmp/article-urls.txt') as f:
    reader = csv.reader(f)
    for row in reader:
        article_urls.append(row[0])
documents = SimpleWebPageReader().load_data(article_urls)
index = GPTSimpleVectorIndex(documents, llm_predictor=ChatGPTLLMPredictor())

# save to disk
index.save_to_disk('index.json')

上記のコードでページをスクレイピングしインデックスを作成します。スクレイピングとインデックス作成を同時に行っているのでかなり時間がかかります。ここはスクレイピングとインデックス作成は分断した方が良さそうです。次に以下のコードで推論の準備をします。

llmaindex_inference.py

import sys
from llama_index import GPTSimpleVectorIndex, LLMPredictor
from langchain import OpenAI

llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, max_tokens=350))
index = GPTSimpleVectorIndex.load_from_disk("index.json")

args = sys.argv
question = args[1]
print("Q: "+ question)
output = index.query(question+"回答には出典のリンクを含めるようにしてください。", llm_predictor=llm_predictor)
print("A: ", end="")
print(output)

上記で準備完了です。

使ってみる

最後に実際に使ってみます。以下のようにpyhtonコマンドの第二引数に質問を追加して使います。まずはインデックスにあるデータについて質問してみます。

$ python llmaindex_inference.py "AWS CDKのテストコードのスナップショットでアセットの差分を消す方法は何?"
Q: AWS CDKのテストコードのスナップショットでアセットの差分を消す方法は何?
A: 
AWS CDK v2でアセットの差分を消す方法は、test/snapshot-plugin.tsを次のように修正することで可能です(Snapshot Serializerを利用している場合)。

module.exports = {
  test: (val: unknown) => typeof val === 'string',
  serialize: (val: string) => {
    return `"${val.replace(/([A-Fa-f0-9]{64}.zip)/, 'HASH-REPLACED.zip')}"`;
  },
};

参考:https://dev.classmethod.jp/articles/aws-cdk-v2-unit-test-ignore-assets/

参考に記載されたものは、インデックスにあるものでした。インデックスの中にある情報だとかなり正確に答えてくれるパターンもあることがわかりました。他にも何件か聞いてみます。(読みやすいように改行など調整を入れています。文章の内容は変更していません)

Q: AWS CDKでLambdaを書く方法を教えてください
A: 
AWS CDKでLambdaを書く方法は、AWS公式のワークショップ「AWS CDK Intro Workshop」を参考にすることができます。
ワークショップでは、API Gateway - Lambda - DynamoDB のサーバレスな構成をCDKでデプロイする方法を学ぶことができます。

参考:
AWS CDK Intro Workshop :: AWS Cloud Development Kit (AWS CDK) Workshop
https://cdkworkshop.com/
Q: AWS CDKでECSのデプロイはどうやるべき?
A: 
AWS CDKを使用してECSのデプロイを行う場合、CI/CDパイプライン上で自動デプロイすることをお勧めします。
また、mainブランチ1本で、DEV→STG→PRDの順番で環境に反映されるフローを構築することをお勧めします。
IaC管理と手動管理のリソースが混在している場合は、ポリシーの策定やタグ付けを行うことで管理方法の判別をしやすくすることができます。

参考:
AWS CDKで管理しているリソースに一括で共通タグを付与する | DevelopersIO
「https://dev.classmethod.jp/articles/aws_cdk_add_common_tag/」

Terraformなら簡単に全AWSリソースに共通タグを設定できます | DevelopersIO
「https://dev.classmethod.jp/articles/terraform」
Q: AWS CDKでStackとConstructはどう使い分けると開発しやすい?
A: 
AWS CDKでは、StackとConstructを使い分けることで、インフラストラクチャをより効率的に開発することができます。
Stackは、AWSリソースを定義するためのコンテナであり、Constructは、Stack内で定義されたリソースを構成するためのコンポーネントです。
Stackは、Constructを組み合わせて、AWSリソースを定義します。
Constructは、AWSリソースを構成するための詳細な情報を提供します。StackとConstructを使い分けることで、インフラストラクチャをより効率的に開発することができます。

参考:
https://dev.classmethod.jp/articles/aws-cdk-stack-construct/

インデックスにない内容だと若干正しくない情報(Stackがコンテナ?)も混じっていました。やはりインデックス自体にないことを聞くのは難しいので何か継続的に正しい情報を取り込める必要がありそうです。

余談

トークンサイズ(max_tokens)を調整しないと最後まで結果が返ってこなかったので、微調整しています。トークンに関しては以下の記事が参考になりました。

参考