ChatGPT の登場により、大規模言語モデル (LLM) と生成 AI の可能性が、コンテンツ作成、画像生成、コードの最適化、情報検索など、多くの分野で実証されました。LLM は個人や企業にとって強力なツールとなり、スーパーアプリケーションへと進化し、新しいエコシステムを創造しています。本トピックでは、ApsaraDB RDS for PostgreSQL を使用して専用チャットボットを構築する方法について説明します。
背景
より多くの企業や個人が、LLM と生成 AI を使用して、自社の特定分野向けの AI 搭載製品を構築したいと考えています。LLM は一般的な問題に対しては優れたパフォーマンスを発揮しますが、専門知識やリアルタイム情報への対応には限界があります。これは、トレーニングデータと応答生成の方法に起因します。情報化時代において、企業はナレッジベースを頻繁に更新します。ドキュメント、画像、音声/動画ファイルなどを含むこれらの特定分野のナレッジベースは、プライベートまたは機密情報である可能性があります。LLM 上でドメイン固有の AI 製品を構築するには、企業は独自のナレッジベースでモデルを継続的にトレーニングする必要があります。
一般的に利用できる方法は2つあります:
ファインチューニング:新しいデータセットを提供して、既存モデルの重みをファインチューニングします。継続的に入力を更新して出力を調整し、望ましい結果を得ることができます。この方法は、小規模なデータセットや、特定のタスクタイプやスタイルでのトレーニングに適しています。ただし、トレーニングコストは高くなります。
プロンプトチューニング:モデルの重みを変更するのではなく、入力プロンプトを調整して出力を調整します。ファインチューニングと比較して、プロンプトチューニングは計算コストが低く、必要なリソースとトレーニング時間が少なく、より柔軟です。
ApsaraDB RDS for PostgreSQL 上にチャットボットを構築する利点は次のとおりです:
ApsaraDB RDS for PostgreSQL には pgvector プラグインがあり、これを使用して、リアルタイムのコンテンツやドメイン固有の知識をベクトル埋め込みに変換できます。これらの埋め込みを ApsaraDB RDS for PostgreSQL に保存することで、ベクトル検索が高速化されます。これにより、プライベートデータに関する Q&A の精度が向上します。
高度なオープンソースのトランザクション処理 (TP) エンジンとして、ApsaraDB RDS for PostgreSQL はオンラインでのユーザーインタラクションとデータストレージタスクの両方を同時に処理します。たとえば、会話履歴、インタラクション記録、チャットのタイムスタンプを管理できます。ApsaraDB RDS for PostgreSQL の多用途性により、プライベートなビジネスサービスの構築プロセスが簡素化され、アーキテクチャが軽量化されます。
pgvector プラグインは、開発者コミュニティやオープンソースの PostgreSQL データベースで広く使用されています。ChatGPT Retrieval Plugin などのツールも、PostgreSQL のサポートを迅速に追加しました。これは、ApsaraDB RDS for PostgreSQL がベクトル検索のための強力なエコシステムサポートと幅広いアプリケーション基盤を持ち、ユーザーに多くのツールとリソースを提供していることを示しています。
本トピックでは、ApsaraDB RDS for PostgreSQL のオープンソースベクトルインデックスプラグインであるpgvector と OpenAI の埋め込み機能を使用して、専用チャットボットを構築する方法を紹介します。
前提条件
次の条件を満たす RDS PostgreSQL インスタンスが作成されていること:
インスタンスは PostgreSQL 14 以降を実行している。
インスタンスのマイナーエンジンバージョンが 20230430 以降である。
説明詳細については、「メジャーエンジンバージョンのアップグレード」および「マイナーエンジンバージョンの更新」をご参照ください。
本トピックの専用チャットボットは、ApsaraDB RDS for PostgreSQL のオープンソース pgvector プラグインに基づいています。その使用方法と基本概念を完全に理解していることを確認してください。詳細については、「pgvector ユーザーガイド」をご参照ください。
本トピックの専用チャットボットは OpenAI の機能を使用します。
シークレット API キーがあり、ネットワーク環境が OpenAI にアクセスできることを確認してください。本トピックのコード例は、シンガポールリージョン の ECS インスタンスにデプロイされています。本トピックのサンプルコードは Python を使用しています。Python 開発環境があることを確認してください。この例では、Python
3.11.4と PyCharm2023.1.2を使用します。
関連概念
埋め込み
埋め込みとは、高次元データを低次元表現にマッピングするプロセスです。機械学習と自然言語処理 (NLP) では、埋め込みは離散的な記号やオブジェクトを連続的なベクトル空間内の点として表現するためによく使用されます。
NLP では、単語埋め込みは一般的な手法です。単語を実数ベクトルにマッピングすることで、コンピューターがテキストをよりよく理解し、処理できるようになります。単語埋め込みを通じて、単語間の意味的および文法的な関係をベクトル空間で表現できます。
OpenAI は Embeddings 機能を提供しています。
実装の原則
専用チャットボットの構築プロセスには、2つのフェーズがあります:
フェーズ1:データ準備
ナレッジベースからの情報の抽出とチャンク化:ドメインナレッジベースから関連テキストを抽出し、チャンク化します。これには、長いテキストを段落や文に分割したり、キーワードやエンティティを抽出したりすることが含まれます。このプロセスは、ナレッジベースのコンテンツを整理および管理するのに役立ちます。
LLM インターフェイスを呼び出して埋め込みを生成:OpenAI などの LLM が提供するインターフェイスを使用して、テキストチャンクを入力し、対応するベクトル埋め込みを生成します。これらの埋め込みは、テキストの意味情報とコンテキスト情報をキャプチャし、後の検索とマッチングの基礎となります。
埋め込み情報の保存:生成されたベクトル埋め込み、テキストチャンク、および関連するメタデータを ApsaraDB RDS for PostgreSQL データベースに保存します。
フェーズ2:Q&A
ユーザーが質問をします。
OpenAI が提供する埋め込みインターフェイスを使用して、質問の埋め込みを作成します。
pgvector を使用して、ApsaraDB RDS for PostgreSQL データベース内で類似度スコアが特定のしきい値を超えるドキュメントチャンクをフィルタリングし、結果を返します。
次のフローチャートにプロセスを示します:

手順
フェーズ1:データ準備
本トピックでは、「RDS for PostgreSQL インスタンスの作成」ドキュメントのテキストコンテンツを例として使用します。コンテンツは分割され、ApsaraDB RDS for PostgreSQL データベースに保存されます。独自のドメイン固有のナレッジベースを準備する必要があります。
データ準備フェーズの鍵は、ドメイン固有の知識をベクトル埋め込みに変換し、この情報を効果的に保存および照合することです。LLM の強力な意味理解能力を利用することで、特定のドメインに関連する高品質な回答と提案を得ることができます。現在、LangChain や OpenAI のオープンソースChatGPT Retrieval Pluginなどのいくつかのオープンソースフレームワークを使用すると、URL、Markdown、PDF、Word などの形式のナレッジベースファイルを簡単にアップロードして解析できます。LangChain と ChatGPT Retrieval Plugin はどちらも、バックエンドのベクトルデータベースとして pgvector 拡張機能を持つ PostgreSQL をすでにサポートしています。これにより、ApsaraDB RDS for PostgreSQL インスタンスとの統合が容易になります。この統合により、フェーズ 1 でドメインナレッジベースのデータ準備を完了できます。また、pgvector のベクトルインデックス作成機能と類似検索機能を最大限に活用して、効率的なテキストマッチングとクエリを実行できます。
テストデータベースを作成します。この例では
rds_pgvector_testを使用します。CREATE DATABASE rds_pgvector_test;テストデータベースに接続し、pgvector プラグインを作成します。
CREATE EXTENSION IF NOT EXISTS vector;ナレッジベースのコンテンツを保存するためのテストテーブルを作成します。この例では
rds_pg_help_docsを使用します。CREATE TABLE rds_pg_help_docs ( id bigserial PRIMARY KEY, title text, -- ドキュメントのタイトル description text, -- 説明 doc_chunk text, -- ドキュメントチャンク token_size int, -- ドキュメントチャンクのトークン数 embedding vector(1536)); -- ベクトル埋め込み情報埋め込み列にインデックスを作成して、クエリを最適化および高速化します。
CREATE INDEX ON rds_pg_help_docs USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);説明ベクトル列へのインデックス作成の詳細については、「pgvector ユーザーガイド」をご参照ください。
PyCharm でプロジェクトを作成し、ターミナルを開いて、次のコマンドを実行して必要な依存関係をインストールします。
pip install openai psycopg2 tiktoken requests beautifulsoup4 numpyナレッジベースドキュメントを分割してデータベースに保存するための
.pyファイルを作成します。この例ではknowledge_chunk_storage.pyを使用します。サンプルコードは次のとおりです:説明以下のサンプルコードでは、カスタム分割メソッドは固定文字数でナレッジベースドキュメントを分割するだけです。分割には、LangChain や OpenAI のオープンソースChatGPT Retrieval Pluginなどのオープンソースフレームワークが提供するメソッドを使用できます。ナレッジベース内のドキュメントの品質とチャンク化の結果は、最終的な出力に大きな影響を与えます。
import openai import psycopg2 import tiktoken import requests from bs4 import BeautifulSoup EMBEDDING_MODEL = "text-embedding-ada-002" tokenizer = tiktoken.get_encoding("cl100k_base") # RDS for PostgreSQL データベースへの接続 conn = psycopg2.connect(database="<database_name>", host="<instance_endpoint>", user="<username>", password="<password>", port="<port>") conn.autocommit = True # OpenAI API キー openai.api_key = '<Secret API Key>' # カスタム分割メソッド (一例) def get_text_chunks(text, max_chunk_size): chunks_ = [] soup_ = BeautifulSoup(text, 'html.parser') content = ''.join(soup_.strings).strip() length = len(content) start = 0 while start < length: end = start + max_chunk_size if end >= length: end = length chunk_ = content[start:end] chunks_.append(chunk_) start = end return chunks_ # 分割する Web ページを指定 url = 'https://www.alibabacloud.com/help/document_detail/148038.html' response = requests.get(url) if response.status_code == 200: # Web ページのコンテンツを取得 web_html_data = response.text soup = BeautifulSoup(web_html_data, 'html.parser') # タイトル (H1 タグ) を取得 title = soup.find('h1').text.strip() # 説明 (クラス 'shortdesc' の p タグのコンテンツ) を取得 description = soup.find('p', class_='shortdesc').text.strip() # 分割と保存 chunks = get_text_chunks(web_html_data, 500) for chunk in chunks: doc_item = { 'title': title, 'description': description, 'doc_chunk': chunk, 'token_size': len(tokenizer.encode(chunk)) } query_embedding_response = openai.Embedding.create( model=EMBEDDING_MODEL, input=chunk, ) doc_item['embedding'] = query_embedding_response['data'][0]['embedding'] cur = conn.cursor() insert_query = ''' INSERT INTO rds_pg_help_docs (title, description, doc_chunk, token_size, embedding) VALUES (%s, %s, %s, %s, %s); ''' cur.execute(insert_query, ( doc_item['title'], doc_item['description'], doc_item['doc_chunk'], doc_item['token_size'], doc_item['embedding'])) conn.commit() else: print('Failed to fetch web page')Python プログラムを実行します。
データベースにログインし、次のコマンドを実行して、ナレッジベースのコンテンツがベクトルデータとして分割および保存されているかを確認します。
SELECT * FROM rds_pg_help_docs;
フェーズ2:Q&A
Python プロジェクトで、質問を作成し、データベース内のナレッジベースコンテンツとの類似度を比較し、結果を返すための
.pyファイルを作成します。この例ではchatbot.pyを使用します。import openai import psycopg2 from psycopg2.extras import DictCursor GPT_MODEL = "gpt-3.5-turbo" EMBEDDING_MODEL = "text-embedding-ada-002" GPT_COMPLETIONS_MODEL = "text-davinci-003" MAX_TOKENS = 1024 # OpenAI API キー openai.api_key = '<Secret API Key>' prompt = 'How to create an RDS for PostgreSQL instance' prompt_response = openai.Embedding.create( model=EMBEDDING_MODEL, input=prompt, ) prompt_embedding = prompt_response['data'][0]['embedding'] # RDS for PostgreSQL データベースへの接続 conn = psycopg2.connect(database="<database_name>", host="<instance_endpoint>", user="<username>", password="<password>", port="<port>") conn.autocommit = True def answer(prompt_doc, prompt): improved_prompt = f""" 提供されたドキュメントと手順に基づいて、次の質問に答えてください: (1) まず、ドキュメントの内容を分析し、質問と関連があるかどうかを確認します。 (2) 次に、返信にはドキュメントの内容のみを使用します。できるだけ詳細に、Markdown 形式で出力してください。 (3) 最後に、質問が RDS for PostgreSQL に関連しない場合は、「私は RDS for PostgreSQL 以外のトピックについては詳しくありません。」と返信してください。 ドキュメント: \"\"\" {prompt_doc} \"\"\" 質問: {prompt} """ response = openai.Completion.create( model=GPT_COMPLETIONS_MODEL, prompt=improved_prompt, temperature=0.2, max_tokens=MAX_TOKENS ) print(f"{response['choices'][0]['text']}\n") similarity_threshold = 0.78 max_matched_doc_counts = 8 # pgvector を使用して、特定のしきい値より大きい類似度を持つドキュメントチャンクをフィルタリング similarity_search_sql = f''' SELECT doc_chunk, token_size, 1 - (embedding <=> '{prompt_embedding}') AS similarity FROM rds_pg_help_docs WHERE 1 - (embedding <=> '{prompt_embedding}') > {similarity_threshold} ORDER BY id LIMIT {max_matched_doc_counts}; ''' cur = conn.cursor(cursor_factory=DictCursor) cur.execute(similarity_search_sql) matched_docs = cur.fetchall() total_tokens = 0 prompt_doc = '' print('Answer: \n') for matched_doc in matched_docs: if total_tokens + matched_doc['token_size'] <= 1000: prompt_doc += f"\n---\n{matched_doc['doc_chunk']}" total_tokens += matched_doc['token_size'] continue answer(prompt_doc,prompt) total_tokens = 0 prompt_doc = '' answer(prompt_doc,prompt)Python プログラムを実行すると、実行ウィンドウに次のような回答が表示されます:
説明分割方法と質問プロンプトを最適化することで、より正確で完全な回答を得ることができます。本トピックでは一例のみを提供します。

まとめ
専用のナレッジベースが接続されていない場合、「RDS for PostgreSQL インスタンスを作成するにはどうすればよいですか?」という質問に対する OpenAI の回答は、Alibaba Cloud とは無関係な内容になることがよくあります。例:

ApsaraDB RDS for PostgreSQL データベースに保存されている専用のナレッジベースに接続すると、「RDS for PostgreSQL インスタンスを作成するにはどうすればよいですか?」という質問に対する回答は、Alibaba Cloud の ApsaraDB RDS for PostgreSQL に特化したものになります。
前述の例から、ApsaraDB RDS for PostgreSQL が、特定分野向けの LLM ベースのナレッジベースを構築する能力を完全に備えていることは明らかです。