見出し画像

AssistantServiceのStreamingの話


後半戦のurl

どっちが正しいのかわからない

さて。まだbetaが取れていないAssistantServiceですが、StreamingをするときにEventHandlerを使えという公式見解があります。
一方でAPI Reference - OpenAI APIここにはStream:Trueにするだけで簡単にSSEになることができるとも書いてあります。

最初のEventHandlerは挫折した

最初にhttps://platform.openai.com/docs/assistants/quickstartこのquickstartに従ってEventHandlerを実装しようとしましたが、まず、そもそもEventHandlerの起動はできるものの、例がprint()しかないので、呼び出し元にtextを戻してこれないというのではまりました。GPTに聞きながら解決しようとしましたが、まったく歯が立たず。おそらく普段からBackendの開発をされている方たちの常識が私になかったということだと思います。

発見?

ところがOpenAIのhttps://platform.openai.com/docs/api-reference/streamingここによると、CompletionもAssistantも同じようにStreamができるよ。とかいてあります。では試しにと

response = client.beta.threads.runs.create(
    thread_id=thread_id,
    assistant_id=assistant_id,
    stream=True
)
for message in response:
    if message.event == 'thread.message.created':
        print(f'event -> {message.event}')
    if message.event == 'thread.message.delta':
        text = message.data.delta.content[0].text.value
        if text is not None:
            yield text
    if message.event == 'thread.message.completed':
        print(f'event -> {message.event}')

こういうことをしてみました。これは動きました。そして当時は見つけられなかったのですが、Structured Outputs Parsing Helpersがgithubに公開されています。やっていたころは締め切りに追われて、焦っていたので見つけられなかったようです。
これはどこから見つけたかというと、上記のquickstartの最下段に

You can also see a list of SDK event listeners for these events in the Python & Node repository documentation.

OpenAIのquickstart

こんな記載があって、実際に飛ぶと上記のgithubのreadmeの最後のほうにある、Assistant Eventsに飛ぶのですが、その同じreadmeにのちょっと上にstreamingの仕方で、

with client.beta.threads.runs.stream(
  thread_id=thread.id,
  assistant_id=assistant.id
) as stream:
    for event in stream:
        # Print the text from text delta events
        if event.event == "thread.message.delta" and event.data.delta.content:
            print(event.data.delta.content[0].text)

こんな方法あるよ。というのが書いたあるではないですか。新しい技術に取り組む際は公式系ドキュメントはよく読みましょう。という自戒の念を強くしました。実際、これも動きました。

最初の結論

つまるところ

with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=assistant.id
) as stream:

OpenAIが公開しているgithubより

であろうと、

with client.beta.threads.runs.create(
    thread_id=thread_id,
    assistant_id=assistant_id,
    stream=True
) as stream:

自分のソース

動いたよという結論でした。

この後起きた問題

このソースで調子よくやっていき、やっとリリースしたのですが、ユーザから、時々会話の途中でとぎれる。ただし、どうも会話は裏で続いているようだ。みたいな報告をもらいました。確かにそういう挙動をします。
これは上記のソースではすでにカバーしているのですが、どういう仕様かわからないのですが、deltaテキストにNoneが入り込んでいることが判明しました。ですので、それはtext is not Noneの処理を入れたら直りました。

添付ファイル取り出したい

API経由でもGPTにファイルを作らせることができます。
if message.event == 'thread.message.completed':
上記のこのcompletedにその処理を入れてあげます。
Messageオブジェクトにattachmentsというのが入っていますので、
それを取り出してきて、fileのcontentをダウンロードするだけです。
詳細はAPI referenceのThe message objectこちらをご覧ください。
ファイルの取り扱いもこちらで。簡単ですので。

EventHandlerの役割はという疑問

じゃぁ、なんでイベントハンドラーがいるなんていう案内があったんだろう。。。と思うわけです。で、気が付いたんですが、このmessage.eventすべて出力させてみると、上記のgithubの記事からすると足りないんです。あ、そういうことねと。。。つまり、細かいことをこれからやるんだったら、EventHandlerを使えということだ。そういう理解をしました。
で、実際にEventHandlerは一度諦めたのですが、今度は締め切りもないので、本腰をいれてやってみることにしました。
長くなりましたので、それ件は次の記事で紹介します。

この記事が気に入ったらサポートをしてみませんか?