みなさんOpenAIのAPIは使用したことがありますか??このシリーズのチートシートではOpenAIのAssistantsAPIの紹介をします.
従来のCompletion APIは1問1答で答える方式でchatgptのように会話履歴を保持してくれません.そのため毎回どんな回答が欲しいか言わないといけません.AssistantsAPIを使えば会話を続けたい場合に活用できます!本記事ではOpenAI Assistants API チートシート Part 2としてThreadの作成方法を紹介します.
これはPart 1で紹介したAsssistantの作成が前提なのでまだPart 1を読んでいない方は以下の記事を参考にしてください.
参考
OpenAI APIのドキュメント
シリーズ OpenAI AssistantsAPI チートシート
Part 1 Assistant編
Part 3 Message編
Part 4 Run編
他のチートシート
git/gh コマンド(gitコマンド以外にもgitの概念も書いてあります)
lazygit
Docker コマンド(dockerコマンド以外にもdockerの概念の記事へのリンクもあります)
ステータスコード
TypeScript
Go/Gorm
SQL
Vim
プルリクエスト・マークダウン記法チートシート
ファイル操作コマンドチートシート
VSCode Github Copilot拡張機能
他のシリーズ記事
TypeScriptで学ぶプログラミングの世界
プログラミング言語を根本的に理解するシリーズです.
情報処理技術者試験合格への道[IP・SG・FE・AP]
情報処理技術者試験の単語集です.
IAM AWS User クラウドサービスをフル活用しよう!
AWSのサービスを例にしてバックエンドとインフラ開発の手法を説明するシリーズです.
Assistants APIでの実装の流れ
Assistant作成(Part 1で紹介) → Thread作成(Part 2で紹介) → メッセージ追加(Part 3で紹介) → Run実行(Part 4で紹介) → 応答取得
典型的な使用パターンは以下のようである.
// 1. アシスタント作成
assistant, _ := client.CreateAssistant(ctx, openai.AssistantRequest{
Name: "Math Tutor",
Model: "gpt-4",
})
// 2. スレッド作成
thread, _ := client.CreateThread(ctx, openai.ThreadRequest{})
// 3. メッセージ追加
message, _ := client.CreateMessage(ctx, thread.ID, openai.MessageRequest{
Content: "Help with math",
})
// 4. スレッド実行
run, _ := client.CreateRun(ctx, thread.ID, openai.RunRequest{
AssistantID: assistant.ID,
})
// 5. 結果取得
messages, _ := client.ListMessages(ctx, thread.ID, nil)
Threadとは?
スレッド(Thread)は,OpenAIのAssistants APIにおける会話管理の機能.ユーザーとAIアシスタント間の対話を永続的に保存し,管理するためのコンテナのような役割を果たす.
スレッドの主な目的
会話の文脈や履歴を維持すること.従来のCompletion APIでは,各リクエストが独立しており,文脈を維持するためには過去の会話履歴を毎回送信する必要があった.一方,スレッドを使用すると,会話の流れや文脈が自動的に保持され、長期的な対話が可能になる.
Threadの流れ
1. スレッドは会話を開始する際に作成される
Assistantを作成したらuserとassistantで会話するためのコンテナであるthreadを作成する.
2. スレッド内でメッセージのやり取りが行われる
ユーザーからの入力,ファイルの添付,アシスタントからの応答など,すべてのやり取りがスレッド内に時系列で記録される.これにより,複雑な対話や段階的な問題解決,継続的なプロジェクトサポートなどが可能になる.
3. スレッド内での実行管理(Run)
アシスタントによる処理を非同期で実行し,その状態を追跡する機能.ユーザーはRunの状態を確認しながら,処理の進行状況をモニタリングできる.
このRunに関してはPart 4で説明します.
threadの使用例や実装の特徴
長期的なプロジェクトや継続的な学習支援
まスレッドは複数のセッションにまたがって利用することができ,必要に応じて過去の会話を参照したり,中断した会話を再開したりすることも可能.
継続的なコミュニケーションが必要なアプリケーション
スレッドの利用は、チュータリング、カスタマーサポート、プロジェクト管理などに適している.ファイル処理や分析タスクなども,スレッド内で一貫して管理できる.
実装の特徴
スレッドの作成からメッセージの追加,実行の管理まで,一連の操作をAPIを通じて行うことができる.ただし,不要なスレッドの管理やエラーハンドリング,セキュリティ考慮など,適切な運用管理も重要である.
このように,ThreadはAssistants APIにおいて、構造化された対話管理を実現する重要な要素として機能していて,より高度で継続的なAIアシスタントとの対話が可能になっている.
スレッドの作成
スレッドの作成を行う.スレッドの作成時はAssistantの情報は不要で,Part 4で説明するRunで必要になる.
package main
import (
"context"
"fmt"
"github.com/joho/godotenv"
"log"
"os"
openai "github.com/sashabaranov/go-openai"
)
type ThreadRequest struct {
Messages []Message `json:"messages,omitempty"`
}
type Message struct {
Role string `json:"role"`
Content interface{} `json:"content"`
}
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("環境変数 OPENAI_API_KEY が設定されていません")
}
client := openai.NewClient(apiKey)
ctx := context.Background()
// スレッドを作成
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
Role: "user", // userとassistantのどちらかを指定
Content: "Hello, I'm looking for a tutor to help me with my math homework.", // ユーザーのメッセージ
},
},
})
if err != nil {
log.Fatalf("スレッドの作成に失敗: %v", err)
}
fmt.Printf("スレッドが作成されました: %v\n", thread)
}
実行
go run create_thread.go
実行に成功すると以下のようなレスポンスが返ってくる.
スレッドが作成されました:
{
"id": "thread_abc123xyz789", # スレッドID
"object": "thread", # オブジェクトタイプ
"created_at": 1702089600, # 作成日時(2024-12-09 00:00:00 UTC)
"metadata": {}, # メタデータ
"headers": {
"Alt-Svc": "h3=\":443\"; ma=86400", # HTTP/3設定
"Cf-Cache-Status": "DYNAMIC", # Cloudflareキャッシュ状態
"Content-Type": "application/json", # コンテンツタイプ
"Date": "Mon, 09 Dec 2024 00:00:00 GMT", # レスポンス日時
"Openai-Organization": "org-abc123xyz789", # 組織ID
"Openai-Processing-Ms": "300", # 処理時間(ms)
"Openai-Version": "2020-10-01", # APIバージョン
"Server": "cloudflare", # サーバー
"Strict-Transport-Security": "max-age=31536000", # HSTSポリシー
"X-Content-Type-Options": "nosniff", # セキュリティヘッダー
"X-Request-Id": "req_abc123xyz789" # リクエストID
}
}
roleについて
user
かassistant
を指定できる.
// 使用例
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
// ユーザーからのメッセージ
Role: "user",
Content: "質問です:なぜ空は青いのですか?",
},
{
// アシスタントからの応答
Role: "assistant",
Content: "空が青く見える主な理由は光の散乱現象によるものです。",
},
},
})
userはユーザーからのメッセージを表し,アシスタントに向けてのメッセージである.
assistantを使う用途は
- 会話の文脈を設定する場合
// 既に進行中の会話の途中からスレッドを作成する場合
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
Role: "user",
Content: "Python入門について教えて",
},
{
Role: "assistant",
Content: "Pythonは初心者に優しい言語です。まず基本を説明しましょう",
},
{
Role: "user",
Content: "変数の使い方を教えて",
},
},
})
- 過去の会話を復元する場合
// データベースなどから過去の会話を復元してスレッドを作成する場合
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
Role: "user",
Content: "昨日の続きの説明をお願いします",
},
{
Role: "assistant",
Content: "昨日は配列の基本まで説明しました",
},
{
Role: "user",
Content: "そうでした。では次に進みましょう",
},
},
})
- 特定の文脈から会話を開始する場合
// 特定の状況や前提条件がある会話を始める場合
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
Role: "assistant",
Content: "このレッスンでは中学2年生の数学、特に二次方程式について学習します",
},
{
Role: "user",
Content: "はい、お願いします",
},
},
})
である.ただし、多くの場合、新しい会話を開始する際はuserのメッセージだけで十分である.assistantのメッセージは,通常はCreateRun(Part 4で説明)を実行した後に自動的に生成される.
このようにThread,Message(Part 3で説明),Run(Part 4で説明)を実装すべきである.
// 適切な会話の流れ
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
Role: "user",
Content: "質問1、質問2、質問3があります:\n1. ...\n2. ...\n3. ...",
},
},
})
// または、一つずつ質問を送る場合
// 1. 最初の質問
thread, err := client.CreateThread(ctx, openai.ThreadRequest{
Messages: []openai.ThreadMessage{
{
Role: "user",
Content: "質問1",
},
},
})
// CreateRunを実行して応答を待つ
// 2. 次の質問(AddMessage使用)
message, err := client.CreateMessage(ctx, thread.ID, openai.Message
Request{
Role: "user",
Content: "質問2",
})
// CreateRunを実行して応答を待つ
// 3. さらに次の質問
message, err := client.CreateMessage(ctx, thread.ID, openai.MessageRequest{
Role: "user",
Content: "質問3",
})
// CreateRunを実行して応答を待つ
スレッドは以下のような情報を持っている.メッセージなどの情報はこれから説明するスレッドの詳細情報取得や,スレッドの情報更新では扱えないので,Part 3のmessage編で説明します.
// Thread構造体の詳細説明
type Thread struct {
// 基本情報
ID string `json:"id"` // スレッドの一意のID
Object string `json:"object"` // オブジェクトタイプ("thread")
CreatedAt int64 `json:"created_at"` // 作成時刻(Unixタイム)
// メタデータ(interface{}型なので様々な型のデータを格納可能)
Metadata map[string]any `json:"metadata"`
// ツールリソース情報
ToolResources ToolResources `json:"tool_resources,omitempty"`
// HTTP関連情報
httpHeader
}
// ツールリソース構造体
type ToolResources struct {
// コードインタープリタツール用のリソース
CodeInterpreter *CodeInterpreterToolResources `json:"code_interpreter,omitempty"`
// ファイル検索ツール用のリソース
FileSearch *FileSearchToolResources `json:"file_search,omitempty"`
}
スレッドの取得
スレッドの詳細情報を取得するコードは以下の通りである.メッセージの取得はここではできないので別で行う必要がある.
package main
import (
"context"
"log"
"os"
"github.com/sashabaranov/go-openai"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("環境変数 OPENAI_API_KEY が設定されていません")
}
client := openai.NewClient(apiKey)
ctx := context.Background()
// スレッドIDを指定して取得
threadID := "thread_abc123"
thread, err := client.RetrieveThread(ctx, threadID)
if err != nil {
log.Fatal(err)
}
// 取得したスレッド情報を使用
log.Printf("Thread ID: %s", thread.ID)
log.Printf("Object Type: %s", thread.Object)
log.Printf("Created At: %d", thread.CreatedAt)
log.Printf("Metadata: %v", thread.Metadata)
log.Printf("ToolResourcs: %v", thread.ToolResources)
}
実行
go run retrieve_thread.go
成功すると以下のようなレスポンスが返ってくる.
2024/12/09 13:22:55 Thread ID: thread_abc123xyz789
2024/12/09 13:22:55 Object Type: thread
2024/12/09 13:22:55 Created At: 1733711242
2024/12/09 13:22:55 Metadata: map[]
2024/12/09 13:22:55 ToolResourcs: {0xc000098120 <nil>}
スレッドの詳細情報を修正する
以下のようなコードでスレッドの詳細情報を更新できる.
package main
import (
"context"
"log"
"os"
"github.com/sashabaranov/go-openai"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("環境変数 OPENAI_API_KEY が設定されていません")
}
client := openai.NewClient(apiKey)
ctx := context.Background()
threadID := "thread_abc123"
// 例1: ユーザー情報を追加
thread1, err := client.ModifyThread(ctx, threadID, openai.ThreadRequest{
Metadata: map[string]string{
"user_id": "user_123",
"status": "active",
},
})
if err != nil {
log.Fatal(err)
}
// 例2: カテゴリとタグを設定
thread2, err := client.ModifyThread(ctx, threadID, openai.ThreadRequest{
Metadata: map[string]string{
"category": "support",
"tags": "python,beginner",
"priority": "high",
},
})
if err != nil {
log.Fatal(err)
}
// 例3: 状態管理
thread3, err := client.ModifyThread(ctx, threadID, openai.ThreadRequest{
Metadata: map[string]string{
"status": "completed",
"completed_at": "2024-12-09",
"resolved_by": "agent_789",
},
})
if err != nil {
log.Fatal(err)
}
// 例4: 既存のメタデータを全て削除(空のマップを設定)
thread4, err := client.ModifyThread(ctx, threadID, openai.ThreadRequest{
Metadata: map[string]string{},
})
if err != nil {
log.Fatal(err)
}
}
実行
go run modify_thread.go
スレッドの削除
スレッドを削除するコードは以下のようなコードで実装できる.
package main
import (
"context"
"log"
"os"
"github.com/sashabaranov/go-openai"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("環境変数 OPENAI_API_KEY が設定されていません")
}
client := openai.NewClient(apiKey)
ctx := context.Background()
threadID := "thread_abc123"
// スレッドの削除
deletedThread, err := client.DeleteThread(ctx, threadID)
if err != nil {
log.Fatal(err)
}
// 削除結果の確認
log.Printf("Thread ID: %s", deletedThread.ID)
log.Printf("Object: %s", deletedThread.Object)
log.Printf("Deleted: %v", deletedThread.Deleted)
}
実行
go run remove_thread.go
実行結果は以下の通りである
Thread ID: thread_abc123
Object: thread.deleted
Deleted: true
これでスレッドを作成したり,作成したスレッドの情報を取得することができる.次のpartでメッセージを作成してスレッドに追加し,会話をするための直前の準備をしましょう.
Part 3 OpenAI Assistants APIチートシート [Message編]
それではQiitaアドカレ.24企画の今日のクリスマスツリーです.
詳しくはこちらの記事から