OpenAI Assistants API チートシート Part 1 [Assistant 編]

みなさんOpenAIのAPIは使用したことがありますか？？OpenAIのAPIって、いわば「一問一答」のAIですが，新しいAssistants APIは，まるで「常駐秘書」のように，あなたの仕事を長期的にサポートしてくれるスグレモノなんです！OpenAIのAssistants APIを使用すると、独自のアプリケーション内でAIアシスタントを構築することができます．
現在、Assistants APIでは、Code Interpreter、File Search、Function callingの3種類のツールがサポートされています．

OpenAI APIのドキュメント

https://platform.openai.com/docs/api-reference/assistants

# シリーズ OpenAI AssistantsAPI チートシート
**Part 2 Thread編**

https://qiita.com/JavaLangRuntimeException/items/7ddae318202d3afcb45d

**Part 3 Message編**

https://qiita.com/JavaLangRuntimeException/items/ec1807106b51bac06de4

**Part 4 Run編**

https://qiita.com/JavaLangRuntimeException/items/2bb6dea15fa5ddc75871

# 他のチートシート
git/gh コマンド(gitコマンド以外にもgitの概念も書いてあります)

https://qiita.com/JavaLangRuntimeException/items/6b46551f56e0def76eba

lazygit

https://qiita.com/JavaLangRuntimeException/items/42087d09728d5739d73d

Docker コマンド(dockerコマンド以外にもdockerの概念の記事へのリンクもあります)

https://qiita.com/JavaLangRuntimeException/items/21f7c7bf3d143f821697

ステータスコード

https://qiita.com/JavaLangRuntimeException/items/ab1bc7b976ed2dfad91c

TypeScript

https://qiita.com/JavaLangRuntimeException/items/5894391c08e0d8e28389

Go・Gorm

https://qiita.com/JavaLangRuntimeException/items/d388717fc1436bc3ec9d



C#/.NET/Unity

https://qiita.com/JavaLangRuntimeException/items/7849b32bc223d4aa0247

Ruby・Ruby on Rails

https://qiita.com/JavaLangRuntimeException/items/42d935cf92c212f1c7ec

SQL

https://qiita.com/JavaLangRuntimeException/items/f038fbaccdd92fb0308a

Vim

https://qiita.com/JavaLangRuntimeException/items/0c68ab96ea198e0a7294

プルリクエスト・マークダウン記法チートシート

https://qiita.com/JavaLangRuntimeException/items/329eb92a47a07ff4dde8

ファイル操作コマンドチートシート

https://qiita.com/JavaLangRuntimeException/items/16f244606a73f7d106e4

VSCode Github Copilot拡張機能

https://qiita.com/JavaLangRuntimeException/items/be13dc3a346cf6e5ee44

Github API

https://qiita.com/JavaLangRuntimeException/items/4f3551c31679233219ac

変数・関数(メソッド)・クラス命名規則

https://qiita.com/JavaLangRuntimeException/items/b93865c448f69bcfca4a
# 他のシリーズ記事
**TypeScriptで学ぶプログラミングの世界**
プログラミング言語を根本的に理解するシリーズです．

https://qiita.com/JavaLangRuntimeException/items/cadf49bb419076819963

**情報処理技術者試験合格への道[IP・SG・FE・AP]**
情報処理技術者試験に出題されるコンピュータサイエンス用語の紹介や単語集

https://qiita.com/JavaLangRuntimeException/items/991be402099542ccb936

**IAM AWS User クラウドサービスをフル活用しよう！**
AWSのサービスを例にしてバックエンドとインフラ開発の手法を説明するシリーズです．

https://qiita.com/JavaLangRuntimeException/items/371a334f5a6e07035db5

**AWS UserのGCP浮気日記**
GCPの様子をAWSと比較して考えてみるシリーズ

https://qiita.com/JavaLangRuntimeException/items/527d99e774165a763180

**Project Gopher: Unlocking Go’s Secrets**
Go言語や標準ライブラリの深掘り調査レポートです．

https://qiita.com/JavaLangRuntimeException/items/dc45b412d3fbd2ccb9e8

**Gopher’s Journey: Exploring TCP Protocol**
Goを用いてTCPプロトコルを探求 & 作成するシリーズです．

https://qiita.com/JavaLangRuntimeException/items/38091220106d86651d2b

# 1問1答の形式(ChatCompletions API)では？
- **シンプルな一問一答型**
    - 1回のリクエストで1つの応答を返す
    - ステートレス（状態を保持しない）
- **コンテキスト制限**
    - 会話履歴は手動で管理が必要
    - トークン制限内で過去の会話を含める必要がある
- **機能**
    - テキスト生成が主な機能
    - function callingのサポート
    - システムプロンプトでの役割定義

使用例
```go
const url = "https://api.openai.com/v1/chat/completions"
// 環境変数からOPENAI_API_KEYを取ってくる
apiKey := os.Getenv("OPENAI_API_KEY")
	requestBody := map[string]interface{}{
		"model": "gpt-3.5-turbo",
		"messages": []map[string]string{
			{"role": "user", "content": prompt},
		},
		"max_tokens": 300,
	}

jsonData, err := json.Marshal(requestBody)
if err != nil {
    return "", fmt.Errorf("リクエストボディのマーシャルに失敗しました: %v", err)
}

req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
    return "", fmt.Errorf("リクエストの作成に失敗しました: %v", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
    return "", fmt.Errorf("リクエストの実行に失敗しました: %v", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
    return "", fmt.Errorf("リクエストが失敗しました: %s", resp.Status)
}

var responseBody struct {
    Choices []struct {
        Message struct {
            Content string `json:"content"`
        } `json:"message"`
    } `json:"choices"`
}

if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {
    return "", fmt.Errorf("レスポンスのデコードに失敗しました: %v", err)
}

if len(responseBody.Choices) == 0 {
    return "", fmt.Errorf("レスポンスにチョイスがありません")
}

return responseBody.Choices[0].Message.Content, nil
}
```

# Assistants APIでの実装の流れ
Assistant作成(Part 1で紹介) → Thread作成(Part 2で紹介) → メッセージ追加(Part 3で紹介) → Run実行(Part 4で紹介) → 応答取得

典型的な使用パターンは以下のようである．
```go
// 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)
```

本記事ではPart 1 としてアシスタント作成などのアシスタントに関する操作について説明する．

# プロジェクトのセットアップ
まずは、新しいGoプロジェクトを作成し、必要なパッケージをインストールする．
```bash
# プロジェクトディレクトリの作成
mkdir openai-assistants-threads
cd openai-assistants-threads

# Goモジュールの初期化
go mod init openai-assistants-threads

# 必要なパッケージのインストール
go get github.com/sashabaranov/go-openai
go get github.com/joho/godotenv
go mod tidy
```

環境変数にopanaiのAPIキーを記載する．
APIキーについては以下の記事を参考にしよう

```.env
OPENAI_API_KEY="あなたのAPIキー"
```

# Assistants APIの実装
OpenAIのAssistants APIを使って，カスタムアシスタントを作成・管理する方法を見ていく．以下の例では，Go言語とgo-openaiパッケージを活用する．

## アシスタントの作成
まずは，新しいアシスタントを作成するコードを紹介する．
```create_assistant.go
package main

import (
	"context"
	"fmt"
	"github.com/joho/godotenv"
	"log"
	"os"

	openai "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()

	name := "Math Tutor" // アシスタントの表示名(回答生成に影響なし)
	description := "A personal math tutor" // アシスタントの簡単な説明(回答生成に影響なし)
	instructions := "You are a personal math tutor. When asked a question, write and run Python code to answer the question."

	req := openai.AssistantRequest{
		Model:        openai.GPT3Dot5Turbo, // モデル名
		Name:         &name,
		Description:  &description,
		Instructions: &instructions,
		Tools: []openai.AssistantTool{
			{Type: openai.AssistantToolTypeCodeInterpreter}, // ツールタイプを定義，Code Interpreter、File Search、Function callingの3種類から選択
		},
	}

	assistant, err := client.CreateAssistant(ctx, req)
	if err != nil {
		log.Fatalf("アシスタントの作成に失敗: %v", err)
	}

	fmt.Printf("アシスタントが作成されました: %+v\n", assistant)
}

```
:::note info
**ツールタイプの定義**
ドキュメント内で以下のように定義してあるので使ってみよう
```go
const (
	AssistantToolTypeCodeInterpreter AssistantToolType = "code_interpreter"
	AssistantToolTypeRetrieval       AssistantToolType = "retrieval"
	AssistantToolTypeFunction        AssistantToolType = "function"
	AssistantToolTypeFileSearch      AssistantToolType = "file_search"
)
```
モデル名も以下のように定義してあるます
```go
const (
	O1Mini                = "o1-mini"
	O1Mini20240912        = "o1-mini-2024-09-12"
	O1Preview             = "o1-preview"
	O1Preview20240912     = "o1-preview-2024-09-12"
	GPT432K0613           = "gpt-4-32k-0613"
	GPT432K0314           = "gpt-4-32k-0314"
	GPT432K               = "gpt-4-32k"
	GPT40613              = "gpt-4-0613"
	GPT40314              = "gpt-4-0314"
	GPT4o                 = "gpt-4o"
	GPT4o20240513         = "gpt-4o-2024-05-13"
	GPT4o20240806         = "gpt-4o-2024-08-06"
	GPT4o20241120         = "gpt-4o-2024-11-20"
	GPT4oLatest           = "chatgpt-4o-latest"
	GPT4oMini             = "gpt-4o-mini"
	GPT4oMini20240718     = "gpt-4o-mini-2024-07-18"
	GPT4Turbo             = "gpt-4-turbo"
	GPT4Turbo20240409     = "gpt-4-turbo-2024-04-09"
	GPT4Turbo0125         = "gpt-4-0125-preview"
	GPT4Turbo1106         = "gpt-4-1106-preview"
	GPT4TurboPreview      = "gpt-4-turbo-preview"
	GPT4VisionPreview     = "gpt-4-vision-preview"
	GPT4                  = "gpt-4"
	GPT3Dot5Turbo0125     = "gpt-3.5-turbo-0125"
	GPT3Dot5Turbo1106     = "gpt-3.5-turbo-1106"
	GPT3Dot5Turbo0613     = "gpt-3.5-turbo-0613"
	GPT3Dot5Turbo0301     = "gpt-3.5-turbo-0301"
	GPT3Dot5Turbo16K      = "gpt-3.5-turbo-16k"
	GPT3Dot5Turbo16K0613  = "gpt-3.5-turbo-16k-0613"
	GPT3Dot5Turbo         = "gpt-3.5-turbo"
	GPT3Dot5TurboInstruct = "gpt-3.5-turbo-instruct"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3TextDavinci003 = "text-davinci-003"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3TextDavinci002 = "text-davinci-002"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3TextCurie001 = "text-curie-001"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3TextBabbage001 = "text-babbage-001"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3TextAda001 = "text-ada-001"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3TextDavinci001 = "text-davinci-001"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3DavinciInstructBeta = "davinci-instruct-beta"
	// Deprecated: Model is shutdown. Use davinci-002 instead.
	GPT3Davinci    = "davinci"
	GPT3Davinci002 = "davinci-002"
	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
	GPT3CurieInstructBeta = "curie-instruct-beta"
	GPT3Curie             = "curie"
	GPT3Curie002          = "curie-002"
	// Deprecated: Model is shutdown. Use babbage-002 instead.
	GPT3Ada    = "ada"
	GPT3Ada002 = "ada-002"
	// Deprecated: Model is shutdown. Use babbage-002 instead.
	GPT3Babbage    = "babbage"
	GPT3Babbage002 = "babbage-002"
)

```
本記事では`openai.AssistantToolTypeCodeInterpreter`を使用します
:::
実行
```bash
go run create_assistant.go
```
実際に実行して，成功すると以下のようなレスポンスが表示される．
```bash
{
    "ID": "asst_abc123xyz456",    # アシスタントの一意の識別子
    "Object": "assistant",    # オブジェクトタイプを示す固定値
    "CreatedAt": 1733651787,  # アシスタントが作成された Unix タイムスタンプ
    "Name": "Math Tutor",    # アシスタントの表示名
    "Description": "A personal math tutor",    # アシスタントの簡単な説明
    "Model": "gpt-3.5-turbo",    # 使用する言語モデル
    "Instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.",    # アシスタントの振る舞いを定義する指示
    "Tools": [    # アシスタントが使用できるツールのリスト
        {
            "Type": "code_interpreter",            # ツールの種類（code_interpreter, retrieval, function のいずれか）
            "Function": null            # 関数呼び出し用の設定（code_interpreterの場合は null）
        }
    ],
    "ToolResources": null,    # ツールに関連するリソース情報
    "FileIDs": [],    # アシスタントに関連付けられたファイルIDのリスト
    "Metadata": {},    # カスタムメタデータを格納するキーバリューペア
    "Temperature": 0,    # 応答のランダム性を制御（0-2の範囲、低いほど決定的）
    "TopP": 0,    # 確率分布の上位を制限するパラメータ（0-1の範囲）
    "ResponseFormat": "auto",    # レスポンスのフォーマット指定
    "httpHeader": {    # API レスポンスの HTTP ヘッダー情報
        "Alt-Svc": ["h3=\":443\"; ma=86400"],        # HTTP/3 サポートの告知
        "Cf-Cache-Status": ["DYNAMIC"],        # Cloudflare のキャッシュステータス
        "Cf-Ray": ["abc123xyz456-NRT"],        # Cloudflare のリクエスト識別子
        "Content-Type": ["application/json"],        # レスポンスの内容タイプ
        "Date": ["Sun, 08 Dec 2024 09:56:27 GMT"],        # レスポンス生成時刻
        "Openai-Organization": ["org-abc123xyz456"],        # OpenAI 組織ID
        "Openai-Processing-Ms": ["441"],        # API リクエスト処理時間（ミリ秒）
        "Openai-Version": ["2020-10-01"],       # OpenAI API バージョン
        "Server": ["cloudflare"],        # 使用されているWebサーバー
        "Set-Cookie": [        # セッション管理用クッキー
            "__cf_bm=sample_cookie_value; path=/; expires=Sun, 08-Dec-24 10:26:27 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None",
            "_cfuvid=sample_cfuvid_value; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None"
        ],
        "Strict-Transport-Security": ["max-age=31536000; includeSubDomains; preload"],        # HTTPS 強制設定
        "X-Content-Type-Options": ["nosniff"],        # MIME タイプスニッフィング防止
        "X-Request-Id": ["req_abc123xyz456"]        # リクエストの一意の識別子
    }
}
```

## アシスタントの一覧取得
作成したアシスタントの一覧を取得するものの実装例を以下に示す．
```list_assistants.go
package main

import (
	"context"
	"fmt"
	"github.com/joho/godotenv"
	"log"
	"os"
	"time"

	openai "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()

	limit := 20
	order := "desc"

	assistants, err := client.ListAssistants(ctx, &limit, &order, nil, nil)
	if err != nil {
		log.Fatalf("アシスタント一覧の取得に失敗: %v", err)
	}

	fmt.Println("アシスタント一覧:")
	for _, assistant := range assistants.Assistants {
		name := "<unnamed>"
		if assistant.Name != nil {
			name = *assistant.Name
		}

		description := "<no description>"
		if assistant.Description != nil {
			description = *assistant.Description
		}

		createTime := time.Unix(assistant.CreatedAt, 0).Format("2006-01-02 15:04:05")

		fmt.Printf("ID: %s\n", assistant.ID)
		fmt.Printf("名前: %s\n", name)
		fmt.Printf("説明: %s\n", description)
		fmt.Printf("モデル: %s\n", assistant.Model)
		fmt.Printf("作成日時: %s\n", createTime)
		fmt.Println("使用可能なツール:")
		for _, tool := range assistant.Tools {
			fmt.Printf("- %s\n", tool.Type)
		}
		fmt.Println("-------------------")
	}
}


```
実行
```bash
go run list_assistants.go
```
先ほど作成したアシスタントの一覧が出てくる．
```bash
アシスタント一覧:
ID: asst_sample123
名前: Math Tutor
説明: 数学の問題を解決するアシスタント
モデル: gpt-3.5-turbo
作成日時: 2024-12-08 10:28:30
使用可能なツール:
- code_interpreter
-------------------
ID: asst_sample456
名前: Writing Helper
説明: 文章作成をサポートするアシスタント
モデル: gpt-3.5-turbo
作成日時: 2024-12-08 10:27:27
使用可能なツール:
- code_interpreter
-------------------
```
:::note info
**ここで出てくるidとは？**
`asst_abc123`は OpenAI の Assistants API が生成するアシスタントの一意の識別子（ID）
- asst_ で始まる
- その後にランダムな英数字の文字列が続く
- システムによって自動的に生成される
- 一度生成されたIDは変更できない

:::

## アシスタントの詳細取得
特定のアシスタントの詳細情報を取得するコードは以下の通りである．
```retrieve_assistant.go
package main

import (
	"context"
	"fmt"
	"github.com/joho/godotenv"
	"log"
	"os"
	"time"

	openai "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()

	assistantID := "asst_sample123f" // 取得したいアシスタントのID

	assistant, err := client.RetrieveAssistant(ctx, assistantID)
	if err != nil {
		log.Fatalf("アシスタントの取得に失敗: %v", err)
	}

	fmt.Println("アシスタントの詳細:")
	name := "<unnamed>"
	if assistant.Name != nil {
		name = *assistant.Name
	}

	description := "<no description>"
	if assistant.Description != nil {
		description = *assistant.Description
	}

	createTime := time.Unix(assistant.CreatedAt, 0).Format("2006-01-02 15:04:05")

	fmt.Printf("ID: %s\n", assistant.ID)
	fmt.Printf("名前: %s\n", name)
	fmt.Printf("説明: %s\n", description)
	fmt.Printf("モデル: %s\n", assistant.Model)
	fmt.Printf("作成日時: %s\n", createTime)
	fmt.Println("使用可能なツール:")
	for _, tool := range assistant.Tools {
		fmt.Printf("- %s\n", tool.Type)
	}
	fmt.Println("-------------------")

}
```
実行
```
go run retrieve_assistant.go
```
成功すると以下のようなレスポンスが返ってくる．
```bash
アシスタントの詳細:
ID: asst_sample123
名前: Math Tutor
説明: 数学の問題を解決するアシスタント
モデル: gpt-3.5-turbo
作成日時: 2024-12-08 10:28:30
使用可能なツール:
- code_interpreter
-------------------
```
これでアシスタントを作成したり，作成したアシスタントの情報を取得することができる．次のpartではスレッドを作成して会話を始める準備をする説明をします．

**Part 2**　OpenAI Assistants APIチートシート [Threads編]

https://qiita.com/JavaLangRuntimeException/items/7ddae318202d3afcb45d

それではQiitaアドカレ.24企画の今日のクリスマスツリーです．

> 詳しくは[こちら](https://qiita.com/JavaLangRuntimeException/items/1f4a6febf957f522ba45)の記事から

![スクリーンショット 2024-12-08 19.44.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3757442/dd2ede56-646f-d6a8-3f22-1fad4a4a2486.png)