banner
niracler

niracler

长门大明神会梦到外星羊么?
github
email
steam_profiles
douban
nintendo switch
tg_channel
twitter_id

導出 Telegram 頻道資訊並使用 text-embedding-ada-002 對頻道文本進行嵌入 - v1

終極需求:做一個專屬於我的知識庫。這個嘛,只是這個終極需求的第一步。這個僅僅是作為 demo,後面還有如山一樣多的事情要做。

(先來一個最終的效果圖看看)
image

第一步 - 導出 Telegram 頻道資訊#

在開始之前,你需要確保已經安裝 Python 3。你還需要以下內容:

  • Telethon、PySocks 庫:可以使用 pip install telethon PySocks 安裝。
  • 確保你是你想要獲取消息的頻道的成員。
  • 一個有效的 Telegram 帳號,獲取 Telegram 應用程序的 API ID 和 API hash,你可以在 https://my.telegram.org 獲取。(保護好你的 API 密鑰,不要在公共倉庫或公開場合洩露。)
  • 代理伺服器(可選,如果你處於 gfw 之內的話)

代碼實現 - 導出頻道資訊#

  1. 將下面的 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"Fetched messages: {len(all_messages)}")
    return all_messages

async def main():
    """
    主程序:從指定頻道獲取消息並保存到 CSV 文件中。
    """
    await client.start()  # 啟動 Telegram 客戶端
    print("Client Created")

    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 模型對文本進行 embedding#

  • 安裝 openai、pandas 庫,可以使用 pip install openai pandas 安裝。
  • 一個有效的 OpenAI API 密鑰

代碼實現 - embedding#

將下面的 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 的 paid plan 才能用 vectorize,而且我也不知道這個功能是否能夠滿足我的需求。
  • 後續持續更新數據庫: 不僅僅是我的頻道,還有我的文章什麼的也是相應的數據源,甚至是一些我關注的頻道,而且用 telegram bot 機器人持續自動更新數據庫。
  • Prompt Engineering: 向 chatgpt 提問時,可以從這個向量數據庫中找出相關內容,然後一起拼到 prompt 去問 chatgpt。
  • 基礎知識: 我不能等到基礎知識夠了再去幹這些事情吧,我應該是要邊幹邊學的。現在已經將我理解的幾步給做了,後面要補充一下對應的知識儲備。
  • 提高質量: 一些低質量的內容不應該放進去,而且要儘量減少一些圖片相關的內容才行,因為圖片是不能被 embedding 的。
  • 做成 CLI: 其實這個功能是寫在 奈亞子 CLI 裡面,但是代碼還沒整理好,也完全沒有異常處理,所以先作為 demo 放出來。在這裡貼這麼長串的代碼效果也是不太好。

參考資料#

  • Embedding paragraphs from my blog with E5-large-v2 - 我做這個事情的初心就是這篇文章,不過基本也就看個思路,畢竟我 embedding 是直接用的 openai 的 api,而不是本地模型
  • telethon 的文檔 - 這是一個個人帳號的 telegram api 的 python 封裝,因為個人帳號用的是 mtproto 協議,所以不能簡單地調用接口,使用這個庫的必要程度還是很高的
  • openai embeddings use cases - 我就是照著這個例子做 embedding 的

後記#

也是一個沒有什麼技術含量的文章,算是記錄了我又學到了一些東西吧。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。