究極の要求:私専用の知識ベースを作成すること。これは、究極の要求の第一歩に過ぎません。これは単なるデモであり、その後には山のように多くのことが待っています。
(まずは最終的な効果図を見てみましょう)
第一歩 - Telegram チャンネル情報のエクスポート#
始める前に、Python 3 がインストールされていることを確認してください。次のものも必要です:
- Telethon、PySocks ライブラリ:
pip install telethon PySocks
を使用してインストールできます。 - メッセージを取得したいチャンネルのメンバーであることを確認してください。
- 有効な Telegram アカウント、Telegram アプリケーションの API ID と API hash を取得する必要があります。これらは https://my.telegram.org で取得できます。(API キーを保護し、公共のリポジトリや公開の場で漏洩しないようにしてください。)
- プロキシサーバー(オプション、gfw 内にいる場合)
コード実装 - チャンネル情報のエクスポート#
以下の Python スクリプトを telegram_to_csv.py として保存します:
import csv
import socks
from telethon import TelegramClient
from telethon.tl.functions.messages import GetHistoryRequest
# TelegramClient を設定し、Telegram API に接続
client = TelegramClient(
'demo',
'api_id',
'api_hash',
proxy=(socks.SOCKS5, '127.0.0.1', 1080)
)
async def export_to_csv(filename, fieldnames, data):
"""
データを CSV ファイルにエクスポートします。
パラメータ:
filename -- エクスポートファイルの名前
fieldnames -- CSV ヘッダーのフィールド名リスト
data -- エクスポートする辞書のリスト
"""
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
async def fetch_messages(channel_username):
"""
指定されたチャンネルのすべてのメッセージを取得します。
パラメータ:
channel_username -- 対象チャンネルのユーザー名
"""
channel_entity = await client.get_input_entity(channel_username)
offset_id = 0 # 初期メッセージ ID オフセット
all_messages = [] # すべてのメッセージを格納するリスト
while True:
# メッセージ履歴をリクエスト
history = await client(GetHistoryRequest(
peer=channel_entity,
offset_id=offset_id,
offset_date=None,
add_offset=0,
limit=100, # 一度のリクエストで取得するメッセージ数
max_id=0,
min_id=0,
hash=0
))
if not history.messages: # もうメッセージがない場合はループを終了
break
for message in history.messages:
if message.message: # テキストコンテンツがあるメッセージのみ処理
# メッセージを辞書形式にシリアライズ
message_dict = {
'id': message.id,
'date': message.date.strftime('%Y-%m-%d %H:%M:%S'),
'text': message.message
}
all_messages.append(message_dict)
offset_id = history.messages[-1].id
print(f"取得したメッセージ数: {len(all_messages)}")
return all_messages
async def main():
"""
メインプログラム:指定されたチャンネルからメッセージを取得し、CSV ファイルに保存します。
"""
await client.start() # Telegram クライアントを起動
print("クライアント作成完了")
channel_username = 'niracler_channel' # 取得したい Telegram チャンネルのユーザー名
all_messages = await fetch_messages(channel_username) # メッセージを取得
# CSV ファイルのヘッダーを定義し、エクスポート
headers = ['id', 'date', 'text']
await export_to_csv('channel_messages.csv', headers, all_messages)
# このスクリプトがメインプログラムとして実行されるとき
if __name__ == '__main__':
client.loop.run_until_complete(main())
スクリプトを実行する - telegram_to_csv.py#
ターミナルでスクリプトを実行します:
python telegram_to_csv.py
スクリプトが実行され、指定された Telegram チャンネルからのすべてのメッセージが現在のディレクトリに channel_messages.csv という名前のファイルに保存されます。
上記の手順を完了すると、channel_messages.csv ファイル内にチャンネル内のテキストメッセージが見つかり、メッセージの ID、日付、および内容が含まれます。
(結果はここに貼りません~~)
第二歩 - openai の text-embedding-ada-002 モデルを使用してテキストを埋め込む#
- openai、pandas ライブラリをインストールします。
pip install openai pandas
を使用してインストールできます。 - 有効な OpenAI API キー
コード実装 - 埋め込み#
以下の Python スクリプトを embedding_generator.py として保存します:
import pandas as pd
from openai import OpenAI
# OpenAI クライアントを設定
client = OpenAI(api_key='YOUR_API_KEY')
def get_embedding(text, model="text-embedding-ada-002"):
"""
テキストの埋め込みベクトルを取得します。
"""
text = text.replace("\n", " ") # テキスト内の改行をクリーニング
response = client.embeddings.create(input=[text], model=model) # 埋め込みベクトルをリクエスト
return response.data[0].embedding # 埋め込みベクトルを抽出して返す
def embedding_gen():
"""
チュートリアルテキストの埋め込みベクトルデータを生成します。
"""
df = pd.read_csv('channel_messages.csv') # CSV ファイルを DataFrame に読み込む
df['text_with_date'] = df['date'] + " " + df['text'] # 日付とテキストを結合
df['ada_embedding'] = df[:100].text_with_date.apply(get_embedding) # テキスト埋め込み関数をバッチ適用
del df['text_with_date'] # 'text_with_date' 列を削除
df.to_csv('embedded_1k_reviews.csv', index=False) # 結果を新しい CSV ファイルに保存
# 確認のために DataFrame の最初の数行を印刷
print(df.head())
# スクリプトが直接実行されたとき
if __name__ == "__main__":
embedding_gen()
スクリプトを実行する#
python embedding_generator.py
第三歩 - 検索を行う#
- pandas、numpy、tabulate ライブラリをインストールします。
pip install pandas numpy tabulate
を使用してインストールできます。 - tabulate ライブラリは、DataFrame を表形式で印刷するために使用されます。
コード実装 - 検索#
以下の Python スクリプトを embedding_search.py として保存します:
import ast
import sys
import pandas as pd
import numpy as np
from tabulate import tabulate
from openai import OpenAI
# OpenAI クライアントを設定
client = OpenAI(api_key='YOUR_API_KEY')
def get_embedding(text, model="text-embedding-ada-002"):
"""
テキストの埋め込みベクトルを取得します。
"""
text = text.replace("\n", " ") # テキスト内の改行をクリーニング
response = client.embeddings.create(input=[text], model=model) # 埋め込みベクトルをリクエスト
return response.data[0].embedding # 埋め込みベクトルを抽出して返す
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def embedding_search(query, df, model="text-embedding-ada-002"):
"""
OpenAI API を使用して埋め込みベクトルを検索します。
"""
query_embedding = get_embedding(query, model=model) # クエリテキストの埋め込みベクトルを取得
df['similarity'] = df.ada_embedding.apply(lambda x: cosine_similarity(ast.literal_eval(x), query_embedding)) # 類似度を計算
df = df.sort_values(by='similarity', ascending=False) # 類似度で降順に並べ替え
df = df.drop(columns=['ada_embedding']) # 埋め込みベクトル列を削除
return df
if __name__ == "__main__":
df = pd.read_csv('embedded_1k_reviews.csv') # CSV ファイルを DataFrame に読み込む
query = sys.argv[1]
df = embedding_search(query, df) # 埋め込みベクトルを検索
print(tabulate(df.head(10), headers='keys', tablefmt='psql')) # 最初の 10 件の結果を印刷
スクリプトを実行する - 結果#
$ python embedding_search.py 動物森友会
+------+---------------------+--------------------------------------------------------------+----------------+
| | date | text | similarities |
|------+---------------------+--------------------------------------------------------------+----------------|
| 1041 | 2021-04-03 06:18:40 | ニール 森友会 | 0.843896 |
| 836 | 2021-10-16 02:37:16 | 動森直面会中文動画 | 0.826405 |
| | | https://www.youtube.com/watch?v=rI_jWfNd2dc | |
| 1208 | 2019-11-10 00:05:56 | 云养动物はとても面白そうだ | 0.822377 |
| 489 | 2023-06-16 09:33:15 | 小猫の生活を見ていると、西西佛神話の西西佛を思い出す | 0.802677 |
| 369 | 2023-08-16 02:15:54 | 家猫は退屈で寂しいのだろうか | 0.797062 |
| 13 | 2023-12-14 13:17:59 | 参加した🤗 | 0.796492 |
| 1177 | 2020-02-12 10:27:45 | 国人が野生動物を食べることが繰り返される理由は、頭の中に根深くある中医の観念に直接関係している | 0.796363 |
| | | それに加えて、養生、食療、進補、薬膳、形を補う、気血を補う…… | |
| | | 疑似科学が復活し、今後もこのようなことが起こるだろう。 | |
| | | 科学こそが唯一の道だ。 | |
| 801 | 2021-11-07 13:46:21 | まさか、今年の年度ゲームがまた動森と火紋になるとは。 | 0.796246 |
| | | 動森は以前に遊び足りなかったし、火紋はある最大の悪事のせいで再プレイすることになった。 | |
| 837 | 2021-10-16 02:37:16 | まさか、私の年度ゲームがまた動森になるのか? | 0.795871 |
| 423 | 2023-07-29 14:11:22 | 鬼畜で精神汚染と呼べるアイコンになった~~ | 0.794144 |
+------+---------------------+--------------------------------------------------------------+----------------+
道は長く、修行は遠い - まだやるべきことがたくさんあります#
- ベクトルデータベース: ボットはこのベクトルデータベースを使用して検索を行うことができます。毎回 CSV ファイルを使用するのは非効率的です。cloudflare の vectorize を使用することを考えています。ただし、まずは簡単な実験を行ってプロセスを理解したいと思っています。cloudflare の有料プランでしか vectorize を使用できないし、この機能が私のニーズを満たすかどうかもわかりません。
- データベースの継続的な更新: 私のチャンネルだけでなく、私の文章などもデータソースとして使用し、さらには私がフォローしているチャンネルも含めて、Telegram ボットを使用してデータベースを自動的に更新し続けます。
- プロンプトエンジニアリング: chatgpt に質問する際、このベクトルデータベースから関連する内容を見つけて、それをプロンプトに組み込んで chatgpt に尋ねることができます。
- 基礎知識: 基礎知識が十分になるまで待ってからこれらのことを行うわけにはいきません。私は学びながら進めるべきです。すでに理解したいくつかのステップを実行しましたが、今後はそれに対応する知識を補充する必要があります。
- 質の向上: 低品質のコンテンツは入れるべきではなく、画像関連のコンテンツはできるだけ減らす必要があります。なぜなら、画像は埋め込みができないからです。
- CLI の作成: 実際、この機能は奈亞子 CLI に書かれていますが、コードはまだ整理されておらず、例外処理も全くありませんので、まずはデモとして公開します。ここに長いコードを貼るのもあまり良い結果ではありません。
参考資料#
- Embedding paragraphs from my blog with E5-large-v2 - 私がこのことを始めたきっかけはこの文章です。ただし、基本的には考え方を見ているだけで、私の埋め込みは直接 OpenAI の API を使用しているため、ローカルモデルではありません。
- telethon のドキュメント - これは個人アカウントの Telegram API の Python ラッパーで、個人アカウントは mtproto プロトコルを使用しているため、単純に API を呼び出すことはできません。このライブラリを使用する必要性は非常に高いです。
- openai embeddings use cases - 私はこの例に従って埋め込みを行いました。
後記#
技術的な内容はあまりない記事ですが、私がまたいくつかのことを学んだ記録として残しておきます。