# by nitta@tsuda.ac.jp
import os
is_colab = 'google.colab' in str(get_ipython())   # for Google Colab
if is_colab:
    from google.colab import drive
    drive.mount('/content/drive')
    SAVE_PATH='/content/drive/MyDrive/DeepLearning'
else:
    SAVE_PATH='.'
# # variable definition in 'account.py' to access HuggingFace Hub
# MyAccount = {
#     'HuggingFace': {
#         'user': 'YourUserName',
#         'token': 'YourTokenWithWritePermission'
#     },
#     'OpenAI': {
#         'api-key': 'YourOpenAI_API_Key'
#     },
# }
ACCOUNT_FILE = os.path.join(SAVE_PATH, 'account.py')
%run {ACCOUNT_FILE}             # set 'MyAccount' variable
os.environ["OPENAI_API_KEY"] = MyAccount['OpenAI']['api-key']   # by nitta
"File Search" とは、いろいろな形式のテキストを使って、アシスタントに外部からの知識を拡張できる機能である。
公式ドキュメント:
OpenAI Assistant API: File Search
「投資信託説明書(交付目論見書)」 (使用開始日 2024.06.21) 「<購入・換金手数料なし>ニッセイNASDAQ100インデックスファンド」https://search.sbisec.co.jp/v2/popwin/info/connect/fund/2931323300000005.pdf
「投資信託説明書(交付目論見書)」 (使用開始日 2024.1.25) 「eMAXIS Slim 米国株式(S&P500)」https://search.sbisec.co.jp/v2/popwin/info/connect/fund/0331118700000029.pdf
# pdf file へのパス
pdf_path1 = f'{SAVE_PATH}/openai_assistant/data/nissei_nasdaq100_prospectus.pdf'
pdf_path2 = f'{SAVE_PATH}/openai_assistant/data/emaxis_slim_sp500_prospectus.pdf'
# パッケージのインストール
!pip install openai
from openai import OpenAI
# クライアントの準備
client = OpenAI()
import json
# JSON出力ヘルパーの準備
def show_json(obj):
    display(json.loads(obj.model_dump_json()))
tools
パラメータとして
file_search
を渡して、アシスタントを作成する。
assistant = client.beta.assistants.create(
    name = "Finanicial Analyst Assistant",
    instructions = "あなたは株式投資の専門家です。知識ベースを利用して、投資信託の目論見書に関する質問に回答してください。",
    model="gpt-4o",
    tools=[{'type': 'file_search'}],
)
追加したファイルにアクセスするために、file_search ツールは VectorStore を使う。
vector_stores.file.batches.upload_and_poll() は、
ファイルをアップロードした後、
すべてのコンテンツの処理が終了するのを待ってから値を返す。
! ls -l {pdf_path1}
# pdf file を upload する。
vector_store = client.beta.vector_stores.create(name="Investment Trust Prospectus")
file_paths = [pdf_path1, pdf_path2]
file_streams = [ open(path, "rb") for path in file_paths ]
file_batch = client.beta.vector_stores.file_batches.upload_and_poll(
    vector_store_id=vector_store.id,
    files=file_streams
)
show_json(vector_store)
show_json(file_batch)
print(file_batch.status)
print(file_batch.file_counts)
アシスタントが追加したファイルにアクセスできるように、
アシスタントの tool_resources を
新しい VectorStore の ID で更新する。
assistant = client.beta.assistants.update(
    assistant_id = assistant.id,
    tool_resources={ 'file_search': { 'vector_store_ids': [vector_store.id]}}
)
スレッドに Message Attatchment としてファイルを添付することもできる。 これを行うと、新しい VectorStore が作成されて、スレッドに添付される。 既にスレッドに VectorStore が添付されている場合は、新しいファイルを既存の スレッド VectorStore に添付する。
このスレッドで Run を作成すると、 file search ツールはアシスタントのVectorStoreとスレッドのVectorStore の両方を探索する。
message_file = client.files.create(
    file=open(pdf_path1, "rb"),
    purpose='assistants',
)
thread = client.beta.threads.create(
    messages = [
        {
            'role': 'user',
            'content': 'nasdaq100 の特色を教えてください。',
            'attachments': [
                {
                    'file_id': message_file.id,
                    'tools': [ { 'type': 'file_search' }]
                }
            ],
        }
    ]
)
print(thread.tool_resources.file_search)
"With Streaming" と "Without Streaming" の2通りがある。
ここでは、簡単のため "Without Streaming" の場合を述べる。
# Run を作成する。
run = client.beta.threads.runs.create_and_poll(
    thread_id = thread.id,
    assistant_id = assistant.id,
)
print(run)
show_json(run)
messages = list(client.beta.threads.messages.list(thread_id=thread.id, run_id=run.id))
print(messages)
print(len(messages))
print(messages[0])
show_json(messages[0])
message_content = messages[0].content[0].text
annotations = message_content.annotations
citations = []
for index, annotation in enumerate(annotations):
    message_content.value = message_content.value.replace(annotation.text, f"[{index}]")
    if file_citation := getattr(annotation, "file_citation", None):
        cited_file = client.files.retrieve(file_citation.file_id)
        citations.append(f'[{index}] {cited_file.filename}')
print(message_content.value)
print('\n'.join(citations))