paint-brush
単語の埋め込み: より良い回答を得るために ChatBot のコンテキストを提供する秘訣@tomfernblog
2,991 測定値
2,991 測定値

単語の埋め込み: より良い回答を得るために ChatBot のコンテキストを提供する秘訣

Tomas Fernandez18m2023/07/26
Read on Terminal Reader

長すぎる; 読むには

単語埋め込みと ChatGPT を使用してエキスパート ボットを構築する方法を学びます。ワード ベクトルの力を活用して、チャットボットの応答を強化します。
featured image - 単語の埋め込み: より良い回答を得るために ChatBot のコンテキストを提供する秘訣
Tomas Fernandez HackerNoon profile picture
0-item
1-item

OpenAI の ChatGPT が非常に知的であることは疑いの余地がありません。弁護士の司法試験に合格し、医師と同等の知識を持ち、一部のテストではIQ 155 を記録しました。しかし、無知を認める代わりに情報を捏造する傾向があります。この傾向は、その知識が 2021 年に終了するという事実と相まって、GPT API を使用して特殊な製品を構築する際に課題を引き起こしています。


どうすればこれらの障害を克服できるでしょうか? GPT-3 のようなモデルに新しい知識を与えるにはどうすればよいでしょうか?私の目標は、Python、OpenAI API、および単語埋め込みを使用して質問応答ボットを構築することで、これらの質問に対処することです。

私が構築するもの

プロンプトから継続的インテグレーションパイプラインを生成するボットを作成する予定です。ご存知のとおり、このパイプラインは Semaphore CI/CD のYAML でフォーマットされています


ボットが動作している例を次に示します。

実行中のプログラムのスクリーンショット。画面上で、コマンド python query.py 「Docker イメージを構築して Docker Hub にアップロードする CI パイプラインを作成する」が実行され、プログラムは、要求されたアクションを実行する CI パイプラインに対応する YAML を出力します。実行中のプログラムのスクリーンショット。画面上で、コマンドpython query.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub"実行され、プログラムは要求されたアクションを実行する CI パイプラインに対応する YAML を出力します。


DocsGPTMy AskAILibrariaなどのプロジェクトの精神に基づいて、セマフォとパイプライン構成ファイルの生成方法について GPT-3 モデルに「教える」予定です。既存のドキュメントを活用してこれを実現します。


ボット構築に関する事前知識は前提としていません。要件に適応できるようにクリーンなコードを維持します。

前提条件

このチュートリアルを実行するには、ボットのコーディングの経験やニューラル ネットワークの知識は必要ありません。ただし、次のものが必要になります。


でもChatGPTは学習できないんですよね?

ChatGPT、より正確には GPT-3 と GPT-4、それらを支える大規模言語モデル (LLM) は、2021 年 9 月頃のカットオフ日を設定して大規模なデータセットでトレーニングされています。


本質的に、GPT-3 はその日付以降の出来事についてほとんど知りません。これは簡単なプロンプトで確認できます。

ChatGPT のスクリーンショット ChatGPT は、2022 年のワールドカップで誰が優勝したかを知りません。


一部の OpenAI モデルは微調整できますが、関心のあるモデルなどのより高度なモデルは微調整できません。彼らのトレーニングデータを増強することはできません。


GPT-3 からトレーニング データ以外の答えを得るにはどうすればよいでしょうか? 1 つの方法には、テキスト理解能力を活用することが含まれます。関連するコンテキストを使用してプロンプトを強化することで、正しい答えが得られる可能性があります。


以下の例では、 FIFA の公式サイトからコンテキストを提供していますが、応答は大きく異なります。

質問に回答する 2 回目の試み提供されたコンテキストを使用して、ChatGPT は正確に応答できます。


関連するコンテキストが十分に与えられていれば、モデルはあらゆるプロンプトに応答できると推測できます。疑問は残ります。任意のプロンプトが与えられた場合、何が関連しているかをどうやって知ることができるのでしょうか?これに対処するには、単語の埋め込みとは何かを調査する必要があります。

Word の埋め込みとは何ですか?

言語モデルのコンテキストでは、埋め込みとは、単語、文、またはドキュメント全体をベクトルまたは数値のリストとして表現する方法です。


埋め込みを計算するには、 word2vectext-embedding-ada-002などのニューラル ネットワークが必要です。これらのネットワークは大量のテキストでトレーニングされており、トレーニング データに特定のパターンが現れる頻度を分析することで単語間の関係を見つけることができます。


次のような単語があるとします。

  • ボール


これらの埋め込みネットワークの 1 つを使用して各単語のベクトルを計算すると想像してください。例えば:

言葉

ベクター

コンテクスト

[0.1、0.2、0.3、0.4、0.5]

動物、物、小物

[0.6、0.7、0.8、0.9、1.0]

動物、物、大きなもの

ボール

[0.2、0.4、0.6、0.8、1.0]

オブジェ、おもちゃ、小物

[0.3、0.6、0.9、1.2、1.5]

建物、家、大きなもの

各単語のベクトルを取得したら、それを使用してテキストの意味を表すことができます。たとえば、「猫はボールを追いかけた」という文は、ベクトル [0.1, 0.2, 0.3, 0.4, 0.5] + [0.2, 0.4, 0.6, 0.8, 1.0] = [0.3, 0.6, 0.9, 1.2, 1.5] として表すことができます。このベクトルは、物体を追いかける動物についての文を表します。


単語の埋め込みは、同様の意味を持つ単語や文が近接している多次元空間として視覚化できます。ベクトル間の「距離」を計算して、入力テキストに対して同様の意味を見つけることができます。

ベクトルの 3 つの 3 次元表現。最初のものは「男性-女性」というラベルが付けられ、男性-女性およびキング-女王というデータ ポイントを持ちます。2 つ目は「動詞-時制」というラベルが付けられ、歩く、歩く、泳ぐ、泳ぐなどの動詞を持ちます。最後のものは「国-首都」というラベルが付けられており、その国に関連する首都がいくつかあります。ベクトル空間としてのエンベディングの 3D 表現。実際には、これらの空間は数百または数千の次元を持つ可能性があります。出典: AI のマルチツールの紹介: ベクトル埋め込み


このすべての背後にある実際の数学については、この記事の範囲を超えています。ただし、重要な点は、ベクトル演算により、数学を使用して意味を操作したり決定したりできるということです。 「女王」という単語を表すベクトルを取得し、そこから「女性」ベクトルを減算し、「男性」ベクトルを加算します。結果は「king」の近くのベクトルになるはずです。 「息子」を加えると「王子」に近づくはずです。

トークンを使用したニューラル ネットワークの埋め込み

これまで、単語を入力、数値を出力として受け取るニューラル ネットワークの埋め込みについて説明してきました。ただし、現代のネットワークの多くは、単語の処理からトークンの処理に移行しています。


トークンは、モデルによって処理できるテキストの最小単位です。トークンは、単語、文字、句読点、記号、または単語の一部です。


OpenAI オンライン トークナイザーを試してみることで、単語がどのようにトークンに変換されるかを確認できます。このトークナイザーは、バイト ペア エンコーディング(BPE) を使用してテキストをトークンに変換し、それぞれを数字で表します。

OpenAI トークナイザーのスクリーンショット。いくつかのテキストが入力されており、各トークンは異なる色で表され、単語がトークンにどのようにマッピングされているかを確認できます。テキストは次のようになります。埋め込みモデルの背後には、入力テキストをベクトルに変換するニューラル ネットワークがあります。埋め込みモデルの各タイプには、異なる機能と速度があります。たとえば、Word2vec は単語を受け取り、100 ~ 300 次元の範囲のベクトルを生成します。多くの場合、トークンと単語の間には 1 対 1 の関係があります。ほとんどのトークンには、単語と先頭のスペースが含まれます。ただし、「embed」と「ding」の 2 つのトークンで構成される「embedding」や、4 つのトークンで構成される「capabilities」などの特殊なケースもあります。 [トークン ID] をクリックすると、各トークンのモデルの数値表現が表示されます。

埋め込みを使用したよりスマートなボットの設計

埋め込みとは何かを理解したところで、次の疑問は、埋め込みがよりスマートなボットの構築にどのように役立つのかということです。


まず、GPT-3 API を直接使用すると何が起こるかを考えてみましょう。ユーザーがプロンプトを発行すると、モデルは可能な限りの応答を返します。

ユーザーと GPT-3 間の対話を示す図。ユーザーがプロンプトを送信すると、モデルが応答します。


ただし、方程式にコンテキストを追加すると、状況は変わります。たとえば、背景を提供した後でワールドカップの勝者について ChatGPT に質問したところ、大きな違いが生じました。


したがって、よりスマートなボットを構築する計画は次のとおりです。

  1. ユーザーのプロンプトをインターセプトします。
  2. そのプロンプトの埋め込みを計算し、ベクトルを生成します。
  3. 最初のプロンプトと意味的に関連している必要があるため、ベクトルに近いドキュメントをデータベースで検索します。
  4. 元のプロンプトを、関連するコンテキストとともに GPT-3 に送信します。
  5. GPT-3 の応答をユーザーに転送します。

ボットのより複雑な実装。ユーザーはプロンプトをチャットボット アプリに送信します。チャットボット アプリはコンテキスト データベースを検索し、それを使用してプロンプトを強化します。プロンプトは GPT-3 に送信され、その応答がユーザーに転送されます。


ほとんどのプロジェクトと同様に、データベースを設計することから始めましょう。

埋め込みを使用したナレッジ データベースの作成

私たちのコンテキスト データベースには、元のドキュメントとそれぞれのベクトルが含まれている必要があります。原則として、このタスクには任意のタイプのデータベースを使用できますが、ベクトル データベースがこの作業に最適なツールです。


ベクトル データベースは、高次元ベクトル データを保存および取得するように設計された特殊なデータベースです。検索に SQL などのクエリ言語を使用する代わりに、ベクトルを提供し、N 個の最近傍を要求します。


ベクトルを生成するには、OpenAI のtext-embedding-ada-002を使用します。これは、OpenAI が提供する最も高速でコスト効率の高いモデルだからです。このモデルは入力テキストをトークンに変換し、 Transformerとして知られるアテンション メカニズムを使用してそれらの関係を学習します。このニューラル ネットワークの出力は、テキストの意味を表すベクトルです。

トークン化プロセスを示す図。ドキュメントはトークン化され、埋め込みニューラル ネットワークに送信されます。ネットワークの出力はベクトルです。


コンテキスト データベースを作成するには、次の手順を実行します。

  1. すべてのソースドキュメントを収集します。
  2. 無関係なドキュメントをフィルタリングして除外します。
  3. 各ドキュメントの埋め込みを計算します。
  4. ベクトル、元のテキスト、およびその他の関連メタデータをデータベースに保存します。

コンテキスト データベースにデータを保存するプロセスを示す図。ソース文書は埋め込みニューラル ネットワークに送信されます。データベースには、元のテキストとともにベクトルが保存されます。

ドキュメントをベクターに変換する

まず、OpenAI API キーを使用して環境ファイルを初期化する必要があります。 API キーは非公開であり、アカウントに関連付けられているため、このファイルはバージョン管理にコミットしないでください。

 export OPENAI_API_KEY=YOUR_API_KEY

次に、Python アプリケーション用の virtualenv を作成します。

 $ virtualenv venv $ source venv/bin/activate $ source .env

そして、OpenAI パッケージをインストールします。

 ```bash $ pip install openai numpy

文字列「Docker Container」の埋め込みを計算してみましょう。これは、Python REPL 上で、または Python スクリプトとして実行できます。

 $ python >>> import openai >>> embeddings = openai.Embedding.create(input="Docker Containers", engine="text-embedding-ada-002") >>> embeddings JSON: { "data": [ { "embedding": [ -0.00530336843803525, 0.0013223182177171111, ... 1533 more items ..., -0.015645816922187805 ], "index": 0, "object": "embedding" } ], "model": "text-embedding-ada-002-v2", "object": "list", "usage": { "prompt_tokens": 2, "total_tokens": 2 } }

ご覧のとおり、OpenAI のモデルは 1536 個の項目 (text-embedding-ada-002 のベクトル サイズ) を含むembeddingリストで応答します。

埋め込みを Pinecone に保存する

オープンソースのChromaなど、複数のベクター データベース エンジンから選択できますが、 Pineconeを選択したのは、無料利用枠のあるマネージド データベースであり、作業が簡単であるためです。彼らのスターター プランは、私が必要とするすべてのデータを十分に処理できます。


Pinecone アカウントを作成し、API キーと環境を取得した後、両方の値を.envファイルに追加します。

Pinecone API キー生成のスクリーンショット

これで.envに Pinecone と OpenAI のシークレットが含まれるはずです。

 export OPENAI_API_KEY=YOUR_API_KEY # Pinecone secrets export PINECONE_API_KEY=YOUR_API_KEY export PINECONE_ENVIRONMENT=YOUR_PINECONE_DATACENTER

次に、Python 用の Pinecone クライアントをインストールします。

 $ pip install pinecone-client

データベースを初期化する必要があります。これらはdb_create.pyスクリプトの内容です。

 # db_create.py import pinecone import openai import os index_name = "semaphore" embed_model = "text-embedding-ada-002" api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, environment=env) embedding = openai.Embedding.create( input=[ "Sample document text goes here", "there will be several phrases in each batch" ], engine=embed_model ) if index_name not in pinecone.list_indexes(): print("Creating pinecone index: " + index_name) pinecone.create_index( index_name, dimension=len(embedding['data'][0]['embedding']), metric='cosine', metadata_config={'indexed': ['source', 'id']} )

スクリプトによるデータベースの作成には数分かかる場合があります。

 $ python db_create.py

次に、 tiktokenパッケージをインストールします。これを使用して、ソース ドキュメントに含まれるトークンの数を計算します。埋め込みモデルは最大 8191 個のトークンしか処理できないため、これは重要です。

 $ pip install tiktoken

パッケージをインストールするときに、見栄えの良い進行状況バーを作成するためにtqdmもインストールしましょう。

 $ pip install tqdm

次に、ドキュメントをデータベースにアップロードする必要があります。このスクリプトは、 index_docs.pyと呼ばれます。まず、必要なモジュールをインポートし、いくつかの定数を定義します。

 # index_docs.py # Pinecone db name and upload batch size index_name = 'semaphore' upsert_batch_size = 20 # OpenAI embedding and tokenizer models embed_model = "text-embedding-ada-002" encoding_model = "cl100k_base" max_tokens_model = 8191

次に、トークンをカウントする関数が必要になります。 OpenAI ページにトークンカウンターの例があります。

 import tiktoken def num_tokens_from_string(string: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_model) num_tokens = len(encoding.encode(string)) return num_tokens

最後に、元のドキュメントを使用可能なサンプルに変換するために、いくつかのフィルタリング関数が必要になります。ドキュメント内のほとんどの例はコード フェンスの間にあるため、すべてのファイルからすべての YAML コードを抽出します。

 import re def extract_yaml(text: str) -> str: """Returns list with all the YAML code blocks found in text.""" matches = [m.group(1) for m in re.finditer("```yaml([\w\W]*?)```", text)] return matches

これで機能は完了です。次に、ファイルをメモリにロードし、サンプルを抽出します。

 from tqdm import tqdm import sys import os import pathlib repo_path = sys.argv[1] repo_path = os.path.abspath(repo_path) repo = pathlib.Path(repo_path) markdown_files = list(repo.glob("**/*.md")) + list( repo.glob("**/*.mdx") ) print(f"Extracting YAML from Markdown files in {repo_path}") new_data = [] for i in tqdm(range(0, len(markdown_files))): markdown_file = markdown_files[i] with open(markdown_file, "r") as f: relative_path = markdown_file.relative_to(repo_path) text = str(f.read()) if text == '': continue yamls = extract_yaml(text) j = 0 for y in yamls: j = j+1 new_data.append({ "source": str(relative_path), "text": y, "id": f"github.com/semaphore/docs/{relative_path}[{j}]" })

この時点で、すべての YAML がnew_dataリストに保存されている必要があります。最後のステップは、埋め込みを Pinecone にアップロードすることです。

 import pinecone import openai api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, enviroment=env) index = pinecone.Index(index_name) print(f"Creating embeddings and uploading vectors to database") for i in tqdm(range(0, len(new_data), upsert_batch_size)): i_end = min(len(new_data), i+upsert_batch_size) meta_batch = new_data[i:i_end] ids_batch = [x['id'] for x in meta_batch] texts = [x['text'] for x in meta_batch] embedding = openai.Embedding.create(input=texts, engine=embed_model) embeds = [record['embedding'] for record in embedding['data']] # clean metadata before upserting meta_batch = [{ 'id': x['id'], 'text': x['text'], 'source': x['source'] } for x in meta_batch] to_upsert = list(zip(ids_batch, embeds, meta_batch)) index.upsert(vectors=to_upsert)

参考として、完全なindex_docs.pyファイルはデモリポジトリにあります。

インデックス スクリプトを実行して、データベースのセットアップを完了しましょう。

 $ git clone https://github.com/semaphoreci/docs.git /tmp/docs $ source .env $ python index_docs.py /tmp/docs

データベースのテスト

Pinecone ダッシュボードには、データベース内のベクトルが表示されるはずです。

合計 79 個のベクトルを含むデータベースを表示する Pinecone ダッシュボードのスクリーンショット

次のコードを使用してデータベースにクエリを実行できます。このコードは、スクリプトとして実行することも、Python REPL で直接実行することもできます。

 $ python >>> import os >>> import pinecone >>> import openai # Compute embeddings for string "Docker Container" >>> embeddings = openai.Embedding.create(input="Docker Containers", engine="text-embedding-ada-002") # Connect to database >>> index_name = "semaphore" >>> api_key = os.getenv("PINECONE_API_KEY") >>> env = os.getenv("PINECONE_ENVIRONMENT") >>> pinecone.init(api_key=api_key, environment=env) >>> index = pinecone.Index(index_name) # Query database >>> matches = index.query(embeddings['data'][0]['embedding'], top_k=1, include_metadata=True) >>> matches['matches'][0] {'id': 'github.com/semaphore/docs/docs/ci-cd-environment/docker-authentication.md[3]', 'metadata': {'id': 'github.com/semaphore/docs/docs/ci-cd-environment/docker-authentication.md[3]', 'source': 'docs/ci-cd-environment/docker-authentication.md', 'text': '\n' '# .semaphore/semaphore.yml\n' 'version: v1.0\n' 'name: Using a Docker image\n' 'agent:\n' ' machine:\n' ' type: e1-standard-2\n' ' os_image: ubuntu1804\n' '\n' 'blocks:\n' ' - name: Run container from Docker Hub\n' ' task:\n' ' jobs:\n' ' - name: Authenticate docker pull\n' ' commands:\n' ' - checkout\n' ' - echo $DOCKERHUB_PASSWORD | docker login ' '--username "$DOCKERHUB_USERNAME" --password-stdin\n' ' - docker pull /\n' ' - docker images\n' ' - docker run /\n' ' secrets:\n' ' - name: docker-hub\n'}, 'score': 0.796259582, 'values': []}

ご覧のとおり、最初に一致するのは、Docker イメージをプルして実行するセマフォ パイプラインの YAML です。 「Docker Containers」の検索文字列に関連しているため、これは良いスタートです。

ボットの構築

私たちはデータを持っており、それをクエリする方法を知っています。ボットで動作させてみましょう。

プロンプトを処理する手順は次のとおりです。

  1. ユーザーのプロンプトに従います。
  2. そのベクトルを計算します。
  3. 関連するコンテキストをデータベースから取得します。
  4. ユーザーのプロンプトをコンテキストとともに GPT-3 に送信します。
  5. モデルの応答をユーザーに転送します。

ボットのデータ フローの図。左側にユーザー プロンプトが入力されます。プロンプトは埋め込みニューラル ネットワークによって処理され、コンテキスト データベースに送信されます。検索により関連するテキストが得られ、GPT-3 モデルに送信されます。モデルの出力は最終的な回答としてユーザーに送信されます。いつものように、ボットのメイン スクリプトであるcomplete.pyでいくつかの定数を定義することから始めます。

 # complete.py # Pinecone database name, number of matched to retrieve # cutoff similarity score, and how much tokens as context index_name = 'semaphore' context_cap_per_query = 30 match_min_score = 0.75 context_tokens_per_query = 3000 # OpenAI LLM model parameters chat_engine_model = "gpt-3.5-turbo" max_tokens_model = 4096 temperature = 0.2 embed_model = "text-embedding-ada-002" encoding_model_messages = "gpt-3.5-turbo-0301" encoding_model_strings = "cl100k_base" import pinecone import os # Connect with Pinecone db and index api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, environment=env) index = pinecone.Index(index_name)

次に、 OpenAI の例に示すように、トークンをカウントする関数を追加します。最初の関数は文字列内のトークンをカウントし、2 番目の関数はメッセージ内のトークンをカウントします。メッセージについては後ほど詳しく見ていきます。とりあえず、会話の状態を記憶に残しておく構造、とだけ言っておきましょう。

 import tiktoken def num_tokens_from_string(string: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_model_strings) num_tokens = len(encoding.encode(string)) return num_tokens def num_tokens_from_messages(messages): """Returns the number of tokens used by a list of messages. Compatible with model """ try: encoding = tiktoken.encoding_for_model(encoding_model_messages) except KeyError: encoding = tiktoken.get_encoding(encoding_model_strings) num_tokens = 0 for message in messages: num_tokens += 4 # every message follows {role/name}\n{content}\n for key, value in message.items(): num_tokens += len(encoding.encode(value)) if key == "name": # if there's a name, the role is omitted num_tokens += -1 # role is always required and always 1 token num_tokens += 2 # every reply is primed with assistant return num_tokens

次の関数は、元のプロンプトとコンテキスト文字列を取得して、GPT-3 用に強化されたプロンプトを返します。

 def get_prompt(query: str, context: str) -> str: """Return the prompt with query and context.""" return ( f"Create the continuous integration pipeline YAML code to fullfil the requested task.\n" + f"Below you will find some context that may help. Ignore it if it seems irrelevant.\n\n" + f"Context:\n{context}" + f"\n\nTask: {query}\n\nYAML Code:" )

get_message関数は、API と互換性のある形式でプロンプトをフォーマットします。

 def get_message(role: str, content: str) -> dict: """Generate a message for OpenAI API completion.""" return {"role": role, "content": content}

モデルの反応に影響を与える役割には 3 つのタイプがあります。

  • ユーザー: ユーザーの元のプロンプト用。
  • システム: アシスタントの動作を設定するのに役立ちます。その有効性については 議論がありますが、メッセージ リストの最後に送信するとより効果的であるようです。
  • Assistant : モデルの過去の応答を表します。 OpenAI API には「メモリ」がありません。代わりに、会話を維持するために、各対話中にモデルの以前の応答を送り返す必要があります。


さて、魅力的な部分です。 get_context関数は、プロンプトを受け取り、データベースにクエリを実行し、次の条件のいずれかが満たされるまでコンテキスト文字列を生成します。

  • 完全なテキストは、コンテキスト用に予約したスペースであるcontext_tokens_per_queryを超えています。
  • 検索機能は、要求されたすべての一致を取得します。
  • match_min_scoreを下回る類似性スコアを持つ一致は無視されます。
 import openai def get_context(query: str, max_tokens: int) -> list: """Generate message for OpenAI model. Add context until hitting `context_token_limit` limit. Returns prompt string.""" embeddings = openai.Embedding.create( input=[query], engine=embed_model ) # search the database vectors = embeddings['data'][0]['embedding'] embeddings = index.query(vectors, top_k=context_cap_per_query, include_metadata=True) matches = embeddings['matches'] # filter and aggregate context usable_context = "" context_count = 0 for i in range(0, len(matches)): source = matches[i]['metadata']['source'] if matches[i]['score'] < match_min_score: # skip context with low similarity score continue context = matches[i]['metadata']['text'] token_count = num_tokens_from_string(usable_context + '\n---\n' + context) if token_count < context_tokens_per_query: usable_context = usable_context + '\n---\n' + context context_count = context_count + 1 print(f"Found {context_count} contexts for your query") return usable_context

次の最後の関数complete 、OpenAI に API リクエストを発行し、モデルの応答を返します。

 def complete(messages): """Query the OpenAI model. Returns the first answer. """ res = openai.ChatCompletion.create( model=chat_engine_model, messages=messages, temperature=temperature ) return res.choices[0].message.content.strip()

それで全部です;これで、コマンド ライン引数を処理し、正しい順序で関数を呼び出すだけで済みます。

 import sys query = sys.argv[1] context = get_context(query, context_tokens_per_query) prompt = get_prompt(query, context) # initialize messages list to send to OpenAI API messages = [] messages.append(get_message('user', prompt)) messages.append(get_message('system', 'You are a helpful assistant that writes YAML code for Semaphore continuous integration pipelines and explains them. Return YAML code inside code fences.')) if num_tokens_from_messages(messages) >= max_tokens_model: raise Exception('Model token size limit reached') print("Working on your query... ") answer = complete(messages) print("Answer:\n") print(answer) messages.append(get_message('assistant', answer))

スクリプトを実行して、どのように機能するかを確認します。

 $ python complete.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub"

結果は次のとおりです。

 version: v1.0 name: Docker Build and Push agent: machine: type: e1-standard-2 os_image: ubuntu1804 blocks: - name: "Build and Push Docker Image" task: jobs: - name: "Docker Build and Push" commands: - checkout - docker build -t /: . - echo "$DOCKERHUB_PASSWORD" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin - docker push /: promotions: - name: Deploy to production pipeline_file: deploy-production.yml auto_promote: when: "result = 'passed' and branch = 'master'"

初めての良い結果です。モデルは、提供されたコンテキストの例から構文を推測しました。

ボットの機能拡張についての考え

私が YAML パイプラインを作成するアシスタントを作成するというささやかな目標から始めたことを思い出してください。ベクター データベースのコンテンツが豊富になると、Semaphore (または任意の製品 - ドキュメントを/tmpにクローンしたことを覚えていますか?) に関するあらゆる質問に答えるボットを一般化できます。


良い答えを得る鍵となるのは、当然のことながら、質の高いコンテキストです。すべてのドキュメントを単にベクター データベースにアップロードするだけでは、良い結果が得られる可能性は低いです。コンテキスト データベースは厳選され、説明的なメタデータでタグ付けされ、簡潔である必要があります。そうしないと、無関係なコンテキストでプロンプト内のトークン クォータがいっぱいになる危険があります。


したがって、ある意味、ニーズを満たすためにボットを微調整するのには技術が必要であり、多くの試行錯誤が必要です。類似性スコアを調整することで、コンテキストの制限を試したり、低品質のコンテンツを削除したり、要約したり、無関係なコンテキストを除外したりできます。

適切なチャットボットの導入

私のボットでは ChatGPT のような実際の会話ができないことに気づいたかもしれません。 1 つの質問をすると、1 つの答えが得られます。


ボットを本格的なチャットボットに変換することは、原則としてそれほど難しいことではありません。各 API リクエストで以前の応答をモデルに再送信することで、会話を維持できます。以前の GPT-3 回答は、「アシスタント」ロールの下で返送されます。例えば:

 messages = [] while True: query = input('Type your prompt:\n') context = get_context(query, context_tokens_per_query) prompt = get_prompt(query, context) messages.append(get_message('user', prompt)) messages.append(get_message('system', 'You are a helpful assistant that writes YAML code for Semaphore continuous integration pipelines and explains them. Return YAML code inside code fences.')) if num_tokens_from_messages(messages) >= max_tokens_model: raise Exception('Model token size limit reached') print("Working on your query... ") answer = complete(messages) print("Answer:\n") print(answer) # remove system message and append model's answer messages.pop() messages.append(get_message('assistant', answer))

残念ながら、この実装はかなり初歩的なものです。インタラクションごとにトークン数が増加するため、長時間の会話はサポートされません。まもなく、GPT-3 のトークン制限 4096 に達し、それ以上の対話ができなくなるでしょう。


したがって、リクエストをトークン制限内に保つ何らかの方法を見つける必要があります。いくつかの戦略が続きます。

  • 古いメッセージを削除します。これは最も単純な解決策ですが、会話の「メモリ」が最新のメッセージのみに制限されます。
  • 以前のメッセージを要約します。 「モデルに質問」を利用して、以前のメッセージを要約し、元の質問と回答の代わりに使用できます。このアプローチではコストとクエリ間の遅延が増加しますが、過去のメッセージを単に削除するよりも優れた結果が得られる可能性があります。
  • インタラクションの数に厳密な制限を設定します。
  • GPT-4 API の一般公開を待ちます。GPT-4 API はよりスマートなだけでなく、トークン容量が 2 倍になります。
  • 最大 16,000 個のトークンを処理できる「gpt-3.5-turbo-16k」などの新しいモデルを使用してください。

結論

ボットの応答を強化するには、単語の埋め込みと適切なコンテキスト データベースを使用します。これを達成するには、高品質のドキュメントが必要です。主題を理解しているように見えるボットの開発には、かなりの量の試行錯誤が伴います。


単語の埋め込みと大規模な言語モデルについてのこの徹底的な調査が、要件に合わせてカスタマイズされた、より強力なボットの構築に役立つことを願っています。


ハッピービルディング!


ここでも公開されています。