前一篇手動擷取 ES 內的電影資料提供給 LLM,今天要來透過 python 腳本自動化。
首先要做的就是把要問 LLM 的問題轉為 vector 並在 ES 搜尋相關文本,搜尋詞轉 vecotr 之前寫過了,可以直接使用:
def embedding(model_id, texts):
# hf_token = os.getenv("HF_TOKEN")
api_url = f"https://api-inference.huggingface.co/pipeline/feature-extraction/{model_id}"
headers = {"Authorization": f"Bearer {hf_token}"}
body = {"inputs": texts, "options":{"wait_for_model":True}}
response = requests.post(api_url, headers=headers, json=body)
if response.status_code != 200:
print(f"Error: {response.status_code}, {response.text}")
raise Exception("Error")
return response.json()
vector search 的部分參考前幾篇的 query 加上 ES 的 python client,回傳至多 20 筆資料,要注意的是,最後要傳給 LLM 的是原始的電影介紹文字內容,而不是 vector 。
def get_movie_from_es(index_name, question):
vector_search ={
"query": {
"knn": {
"field": "overview_vector",
"query_vector": embedding(model_id, question),
"k": 20,
"num_candidates": 100
}
}
}
r = es_cli.search(index=index_name, body=vector_search)
output_movie_list = []
for hit in r['hits']['hits']:
movie_title = hit['_source']['title']
movie_overview = hit['_source']['overview']
output_movie_list.append(f"{movie_title}: {movie_overview}")
return "\n\n".join(output_movie_list)
接著結合前一篇的 prompt 就完成了,整個 script 如下:
from openai import OpenAI
import elasticsearch
import requests
import argparse
def embedding(model_id, texts):
# hf_token = os.getenv("HF_TOKEN")
api_url = f"https://api-inference.huggingface.co/pipeline/feature-extraction/{model_id}"
headers = {"Authorization": f"Bearer {hf_token}"}
body = {"inputs": texts, "options":{"wait_for_model":True}}
response = requests.post(api_url, headers=headers, json=body)
if response.status_code != 200:
print(f"Error: {response.status_code}, {response.text}")
raise Exception("Error")
return response.json()
def get_prompt(instruction, question, context):
prompt = f"""
Instructions:
{instruction}
Context:
{context}
Question:
{question}
"""
return prompt
def get_movie_from_es(index_name, question):
vector_search ={
"query": {
"knn": {
"field": "overview_vector",
"query_vector": embedding(model_id, question),
"k": 20,
"num_candidates": 100
}
}
}
r = es_cli.search(index=index_name, body=vector_search)
output_movie_list = []
for hit in r['hits']['hits']:
movie_title = hit['_source']['title']
movie_overview = hit['_source']['overview']
output_movie_list.append(f"{movie_title}: {movie_overview}")
return "\n\n".join(output_movie_list)
def get_rag(prompt):
client = OpenAI(api_key=OPEN_AI_KEY)
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{
"role": "user",
"content": prompt
}
]
)
return completion.choices[0].message.content
if __name__ == "__main__":
argparse = argparse.ArgumentParser()
argparse.add_argument("--question", type=str, help="The question you want to ask")
question = argparse.parse_args().question
# question = "請推薦我黑道主題的電影"
instruction = """
你是我一個電影熱愛者,熟知各類型熱門電影資訊,經常有人來徵詢你不同的建議。
但如果沒有推薦的電影,可以說沒有想法或是沒有推薦的,請只從提供的資訊中尋找推薦的電影。
當推薦時你會提供電影名稱和關於這部電影的簡單描述。"""
model_id = "intfloat/multilingual-e5-small"
es_cli = elasticsearch.Elasticsearch("http://localhost:9200")
index_name = 'tmdb_top_rated_movies_e5_small'
context = get_movie_from_es(index_name, question)
prompt = get_prompt(instruction, question, context)
suggestion = get_rag(prompt)
print(suggestion)
將 script 存成 movie_suggest.py
,就得到一個超級陽春電影推薦小幫手了。
python movie_suggest.py --question 請推薦我黑道主題的電影
得到的回答如下:
以下是幾部黑道主題的電影推薦:
1. **M就是兇手 (M)**:
這部電影是影史上首部以變態殺手為題材的作品,講述了在德國柏林,一名連續殺手所引發的恐慌,警察和黑幫都參與了抓捕的鬥爭。這部影片探討了社會的黑暗面和暴力的影響,堪稱經典之作。
2. **神鬼無間 (The Departed)**:
這部由馬丁·史柯西斯執導的影片,是對香港電影《無間道》的完美翻拍。故事圍繞波士頓的黑幫和警方的鬥爭,描繪了臥底警察和黑幫之間的緊張局勢,展現了身份和忠誠的複雜性。
3. **黑獄亡魂 (The Third Man)**:
這部經典黑色電影以二次大戰後的維也納為背景,透過一名小說家的調查,引出了一系列與黑市有關的神秘事件。影片在展示陰暗角落的同時,展現了人性中的自私和善惡的對立。
這些作品都在不同的層面上探討了黑道文化與社會的關係,十分值得一看。
OpenAi 等 LLM 崛起、各大工具搶著結合並不是沒有其道理的,截至目前為止的操作都沒有任何艱深難懂的理論、算法、數學計算等,卻完成了在 AI 出現前可能需要大量工程的應用。
但 AI 相關的內容就到這,下一篇會看看 ES 跟 data pipeline(ETL / ELT) 有關的功能。