mcpField
O
openai-assistants

openai-assistants

github.com/jackspace開発1635 回インストール12 週間前に更新
リポジトリ

openai-assistants について

openai-assistantsは、OpenAI Assistants API v2を使うアプリの保守やResponses APIへの移行に取り組む開発者向けのスキルです。アシスタント、永続スレッド、実行、ストリーミング、組み込みツールを扱い、APIは2026年上半期に終了予定です。

主な機能

  1. 01アシスタントとスレッドのライフサイクル管理
  2. 02非同期実行とストリーミング
  3. 03Code Interpreter、File Search、関数
  4. 04ベクトルストアとファイルアップロード
  5. 05Responses APIへの移行ガイド

活用シーン

  1. 01既存のAssistants APIアプリの保守
  2. 02永続的なアシスタント会話の実装
  3. 03実行状況と応答のストリーミング
  4. 04AssistantsからResponses APIへの移行計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jackspace/claudeskillz --skill openai-assistants

インストール後、Claude Code で/openai-assistantsを入力して読み込まれたか確認します。

同梱ファイルREADME.mdSKILL.json

詳細

OpenAI Assistants API v2

ステータス:本番環境で利用可能(2026 年上半期に非推奨化) パッケージ:[email protected] 最終更新日:2025-10-25 v1 の非推奨化:2024 年 12 月 18 日 v2 の提供終了:2026 年上半期(Responses API への移行が必要)


⚠️ 重要:非推奨化のお知らせ

OpenAI は、Assistants API を Responses API に移行するため、非推奨化すると発表しました。

スケジュール:

  • ✅ 2024 年 12 月 18 日:Assistants API v1 の非推奨化
  • ⏳ 2026 年上半期:Assistants API v2 の提供終了予定
  • ✅ 現在:Responses API を利用可能(新規プロジェクトに推奨)

このスキルを今も使うべきですか?

  • ✅ はい、次の場合:既存の Assistants API コードがある(移行期間は 12~18 か月)
  • ✅ はい、次の場合:レガシーアプリケーションの保守が必要
  • ✅ はい、次の場合:Assistants から Responses への移行を計画している
  • ❌ いいえ、次の場合:新規プロジェクトを開始する(代わりに openai-responses スキルを使用)

移行方法: 移行ガイドの全容については、references/migration-to-responses.md を参照してください。


目次

  1. クイックスタート
  2. 主要な概念
  3. アシスタント
  4. スレッド
  5. メッセージ
  6. 実行
  7. ストリーミング実行
  8. ツール
  9. ベクトルストア
  10. ファイルのアップロード
  11. スレッドのライフサイクル管理
  12. エラー処理
  13. 本番環境でのベストプラクティス
  14. 他のスキルとの関係

クイックスタート

インストール
npm install [email protected]
環境の設定
export OPENAI_API_KEY="sk-..."
基本的なアシスタント(Node.js SDK)
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// 1. Create an assistant
const assistant = await openai.beta.assistants.create({
  name: "Math Tutor",
  instructions: "You are a personal math tutor. Write and run code to answer math questions.",
  tools: [{ type: "code_interpreter" }],
  model: "gpt-4o",
});

// 2. Create a thread
const thread = await openai.beta.threads.create();

// 3. Add a message to the thread
await openai.beta.threads.messages.create(thread.id, {
  role: "user",
  content: "I need to solve the equation `3x + 11 = 14`. Can you help me?",
});

// 4. Create a run
const run = await openai.beta.threads.runs.create(thread.id, {
  assistant_id: assistant.id,
});

// 5. Poll for completion
let runStatus = await openai.beta.threads.runs.retrieve(thread.id, run.id);

while (runStatus.status !== 'completed') {
  await new Promise(resolve => setTimeout(resolve, 1000));
  runStatus = await openai.beta.threads.runs.retrieve(thread.id, run.id);
}

// 6. Retrieve messages
const messages = await openai.beta.threads.messages.list(thread.id);
console.log(messages.data[0].content[0].text.value);
基本的なアシスタント(Fetch - Cloudflare Workers)
// 1. Create assistant
const assistant = await fetch('https://api.openai.com/v1/assistants', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.OPENAI_API_KEY}`,
    'Content-Type': 'application/json',
    'OpenAI-Beta': 'assistants=v2',
  },
  body: JSON.stringify({
    name: "Math Tutor",
    instructions: "You are a helpful math tutor.",
    model: "gpt-4o",
  }),
});

const assistantData = await assistant.json();

// 2. Create thread
const thread = await fetch('https://api.openai.com/v1/threads', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.OPENAI_API_KEY}`,
    'Content-Type': 'application/json',
    'OpenAI-Beta': 'assistants=v2',
  },
});

const threadData = await thread.json();

// 3. Add message and create run
const run = await fetch(`https://api.openai.com/v1/threads/${threadData.id}/runs`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.OPENAI_API_KEY}`,
    'Content-Type': 'application/json',
    'OpenAI-Beta': 'assistants=v2',
  },
  body: JSON.stringify({
    assistant_id: assistantData.id,
    additional_messages: [{
      role: "user",
      content: "What is 3x + 11 = 14?",
    }],
  }),
});

// Poll for completion...

主要な概念

Assistants API は、主に次の 4 つのオブジェクトを使用します。

1. アシスタント

以下を設定した AI エンティティです。

  • 指示(システムプロンプト、最大 256k 文字)
  • モデル(gpt-4o、gpt-5 など)
  • ツール(コードインタープリター、ファイル検索、関数)
  • ファイルの添付
  • メタデータ
2. スレッド

以下の機能を持つ会話コンテナです。

  • メッセージ履歴を保存する
  • 複数の実行にわたって保持される
  • メタデータを設定できる
  • 最大 100,000 件のメッセージをサポートする
3. メッセージ

スレッド内の個々のメッセージです。

  • ユーザーメッセージ(入力)
  • アシスタントメッセージ(出力)
  • ファイルを添付できる
  • テキストと画像コンテンツをサポートする
4. 実行

スレッド上でアシスタントを実行することです。

  • 非同期処理
  • 複数の状態(queued、in_progress、completed、failed など)
  • 結果をストリーミングできる
  • ツール呼び出しを自動的に処理する

アシスタント

アシスタントを作成する
const assistant = await openai.beta.assistants.create({
  name: "Data Analyst",
  instructions: "You are a data analyst. Use code interpreter to analyze data and create visualizations.",
  model: "gpt-4o",
  tools: [
    { type: "code_interpreter" },
    { type: "file_search" },
  ],
  tool_resources: {
    file_search: {
      vector_store_ids: ["vs_abc123"],
    },
  },
  metadata: {
    department: "analytics",
    version: "1.0",
  },
});

パラメーター:

  • model(必須):モデル ID(gpt-4o、gpt-5、gpt-4-turbo)
  • instructions:システムプロンプト(v2 では最大 256k 文字。v1 では 32k 文字)
  • name:アシスタント名(最大 256 文字)
  • description:説明(最大 512 文字)
  • tools:ツールの配列(最大 128 個)
  • tool_resources:ツール用のリソース(ベクトルストア、ファイル)
  • temperature:0~2(デフォルトは 1)
  • top_p:0~1(デフォルトは 1)
  • response_format:"auto"、"json_object"、または JSON スキーマ
  • metadata:キーと値のペア(最大 16 組)
アシスタントを取得する
const assistant = await openai.beta.assistants.retrieve("asst_abc123");
アシスタントを更新する
const updatedAssistant = await openai.beta.assistants.update("asst_abc123", {
  instructions: "Updated instructions",
  tools: [{ type: "code_interpreter" }, { type: "file_search" }],
});
アシスタントを削除する
await openai.beta.assistants.del("asst_abc123");
アシスタントの一覧を取得する
const assistants = await openai.beta.assistants.list({
  limit: 20,
  order: "desc",
});

スレッド

スレッドは会話履歴を保存し、複数の実行にわたって保持されます。

スレッドを作成する
// Empty thread
const thread = await openai.beta.threads.create();

// Thread with initial messages
const thread = await openai.beta.threads.create({
  messages: [
    {
      role: "user",
      content: "Hello! I need help with Python.",
      metadata: { source: "web" },
    },
  ],
  metadata: {
    user_id: "user_123",
    session_id: "session_456",
  },
});
スレッドを取得する
const thread = await openai.beta.threads.retrieve("thread_abc123");
スレッドのメタデータを更新する
const thread = await openai.beta.threads.update("thread_abc123", {
  metadata: {
    user_id: "user_123",
    last_active: new Date().toISOString(),
  },
});
スレッドを削除する
await openai.beta.threads.del("thread_abc123");

⚠️ 警告:スレッドを削除すると、すべてのメッセージと実行も削除されます。この操作は取り消せません。


メッセージ

スレッドにメッセージを追加する
const message = await openai.beta.threads.messages.create("thread_abc123", {
  role: "user",
  content: "Can you analyze this data?",
  attachments: [
    {
      file_id: "file_abc123",
      tools: [{ type: "code_interpreter" }],
    },
  ],
  metadata: {
    timestamp: new Date().toISOString(),
  },
});

パラメーター:

  • role:"user" のみ(アシスタントメッセージは実行によって作成されます)
  • content:テキストまたはコンテンツブロックの配列
  • attachments:関連するツールを含むファイル
  • metadata:キーと値のペア
メッセージを取得する
const message = await openai.beta.threads.messages.retrieve(
  "thread_abc123",
  "msg_abc123"
);
メッセージの一覧を取得する
const messages = await openai.beta.threads.messages.list("thread_abc123", {
  limit: 20,
  order: "desc", // "asc" or "desc"
});

// Iterate through messages
for (const message of messages.data) {
  console.log(`${message.role}: ${message.content[0].text.value}`);
}
メッセージのメタデータを更新する
const message = await openai.beta.threads.messages.update(
  "thread_abc123",
  "msg_abc123",
  {
    metadata: {
      edited: "true",
      edit_timestamp: new Date().toISOString(),
    },
  }
);
メッセージを削除する
await openai.beta.threads.messages.del("thread_abc123", "msg_abc123");

実行

実行では、スレッド上でアシスタントを実行します。

実行を作成する
const run = await openai.beta.threads.runs.create("thread_abc123", {
  assistant_id: "asst_abc123",
  instructions: "Please address the user as Jane Doe.",
  additional_messages: [
    {
      role: "user",
      content: "Can you help me with this?",
    },
  ],
});

パラメーター:

  • assistant_id(必須):使用するアシスタント
  • instructions:アシスタントへの指示を上書きします
  • additional_messages:実行前にメッセージを追加します
  • tools:アシスタントのツールを上書きします
  • metadata:キーと値のペア
  • temperature:temperature を上書きします
  • top_p:top_p を上書きします
  • max_prompt_tokens:入力トークン数を制限します
  • max_completion_tokens:出力トークン数を制限します
実行を取得する
const run = await openai.beta.threads.runs.retrieve(
  "thread_abc123",
  "run_abc123"
);

console.log(run.status); // queued, in_progress, requires_action, completed, failed, etc.
実行の状態
状態説明
queued実行の開始待ちです
in_progress実行中です
requires_action関数呼び出しに入力が必要です
cancellingキャンセル処理中です
cancelled実行がキャンセルされました
failed実行に失敗しました(last_error を確認してください)
completed実行が正常に終了しました
expired実行の有効期限が切れました(最大 10 分)
ポーリングのパターン
async function pollRunCompletion(threadId: string, runId: string) {
  let run = await openai.beta.threads.runs.retrieve(threadId, runId);

  while (['queued', 'in_progress', 'cancelling'].includes(run.status)) {
    await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
    run = await openai.beta.threads.runs.retrieve(threadId, runId);
  }

  if (run.status === 'failed') {
    throw new Error(`Run failed: ${run.last_error?.message}`);
  }

  if (run.status === 'requires_action') {
    // Handle function calling (see Function Calling section)
    return run;
  }

  return run; // completed
}

const run = await openai.beta.threads.runs.create(threadId, { assistant_id: assistantId });
const completedRun = await pollRunCompletion(threadId, run.id);
実行をキャンセルする
const run = await openai.beta.threads.runs.cancel("thread_abc123", "run_abc123");

⚠️ 重要:キャンセルは非同期です。status が cancelled になることを確認してください。

実行の一覧表示
const runs = await openai.beta.threads.runs.list("thread_abc123", {
  limit: 10,
  order: "desc",
});

ストリーミング実行

Server-Sent Events(SSE)を使用して、実行イベントをリアルタイムでストリーミングします。

基本的なストリーミング
const stream = await openai.beta.threads.runs.stream("thread_abc123", {
  assistant_id: "asst_abc123",
});

for await (const event of stream) {
  if (event.event === 'thread.message.delta') {
    const delta = event.data.delta.content?.[0]?.text?.value;
    if (delta) {
      process.stdout.write(delta);
    }
  }
}
ストリームイベントの種類
イベント説明
thread.run.created実行が作成されました
thread.run.in_progress実行が開始されました
thread.run.step.createdステップが作成されました(ツール呼び出し、メッセージ作成)
thread.run.step.deltaステップの進行状況が更新されました
thread.message.createdメッセージが作成されました
thread.message.deltaメッセージの内容をストリーミングしています
thread.message.completedメッセージが完了しました
thread.run.completed実行が完了しました
thread.run.failed実行に失敗しました
thread.run.requires_action関数呼び出しが必要です
ストリーミングの完全な例
async function streamAssistantResponse(threadId: string, assistantId: string) {
  const stream = await openai.beta.threads.runs.stream(threadId, {
    assistant_id: assistantId,
  });

  for await (const event of stream) {
    switch (event.event) {
      case 'thread.run.created':
        console.log('\\nRun started...');
        break;

      case 'thread.message.delta':
        const delta = event.data.delta.content?.[0];
        if (delta?.type === 'text' && delta.text?.value) {
          process.stdout.write(delta.text.value);
        }
        break;

      case 'thread.run.step.delta':
        const toolCall = event.data.delta.step_details;
        if (toolCall?.type === 'tool_calls') {
          const codeInterpreter = toolCall.tool_calls?.[0]?.code_interpreter;
          if (codeInterpreter?.input) {
            console.log('\\nExecuting code:', codeInterpreter.input);
          }
        }
        break;

      case 'thread.run.completed':
        console.log('\\n\\nRun completed!');
        break;

      case 'thread.run.failed':
        console.error('\\nRun failed:', event.data.last_error);
        break;

      case 'thread.run.requires_action':
        // Handle function calling
        console.log('\\nFunction calling required');
        break;
    }
  }
}

ツール

Assistants API は、次の 3 種類のツールをサポートしています。

Code Interpreter

サンドボックス環境で Python コードを実行します。

機能:

  • Python コードの実行
  • チャートやグラフの生成
  • ファイルの処理(CSV、JSON、テキスト、画像など)
  • ファイル出力(画像、データファイル)の返却
  • パッケージのインストール(利用可能なものは限定されています)

例:

const assistant = await openai.beta.assistants.create({
  name: "Data Analyst",
  instructions: "You are a data analyst. Use Python to analyze data and create visualizations.",
  model: "gpt-4o",
  tools: [{ type: "code_interpreter" }],
});

// Upload a file
const file = await openai.files.create({
  file: fs.createReadStream("sales_data.csv"),
  purpose: "assistants",
});

// Create thread with file
const thread = await openai.beta.threads.create({
  messages: [{
    role: "user",
    content: "Analyze this sales data and create a visualization.",
    attachments: [{
      file_id: file.id,
      tools: [{ type: "code_interpreter" }],
    }],
  }],
});

// Run
const run = await openai.beta.threads.runs.create(thread.id, {
  assistant_id: assistant.id,
});

// Poll for completion and retrieve outputs

出力ファイル:

Code Interpreter ではファイル(画像、CSV など)を生成できます。次の方法でアクセスしてください。

const messages = await openai.beta.threads.messages.list(thread.id);
const message = messages.data[0];

for (const content of message.content) {
  if (content.type === 'image_file') {
    const fileId = content.image_file.file_id;
    const fileContent = await openai.files.content(fileId);
    // Save or process file
  }
}
File Search

ベクトルストアを使用して、アップロードされたドキュメントをセマンティック検索します。

主な機能:

  • アシスタントあたり最大 10,000 ファイル(v1 の 500 倍)
  • 自動チャンク分割と埋め込み生成
  • ベクトル検索とキーワード検索
  • マルチスレッドによる並列クエリ
  • 高度な再ランキング

料金:

  • ベクトルストレージは $0.10/GB/日
  • 最初の 1GB は無料

例:

// 1. Create vector store
const vectorStore = await openai.beta.vectorStores.create({
  name: "Product Documentation",
  metadata: { category: "docs" },
});

// 2. Upload files to vector store
const file = await openai.files.create({
  file: fs.createReadStream("product_guide.pdf"),
  purpose: "assistants",
});

await openai.beta.vectorStores.files.create(vectorStore.id, {
  file_id: file.id,
});

// 3. Create assistant with file search
const assistant = await openai.beta.assistants.create({
  name: "Product Support",
  instructions: "Use file search to answer questions about our products.",
  model: "gpt-4o",
  tools: [{ type: "file_search" }],
  tool_resources: {
    file_search: {
      vector_store_ids: [vectorStore.id],
    },
  },
});

// 4. Create thread and run
const thread = await openai.beta.threads.create({
  messages: [{
    role: "user",
    content: "How do I install the product?",
  }],
});

const run = await openai.beta.threads.runs.create(thread.id, {
  assistant_id: assistant.id,
});

ベストプラクティス:

  • 使用前にベクトルストアの状態が completed になるまで待ちます
  • フィルタリングにはメタデータを使用します(近日対応予定)
  • 大きなドキュメントは適切にチャンク分割します
  • ストレージコストを監視します
関数呼び出し

アシスタントが呼び出すカスタム関数を定義します。

例:

const assistant = await openai.beta.assistants.create({
  name: "Weather Assistant",
  instructions: "You help users get weather information.",
  model: "gpt-4o",
  tools: [{
    type: "function",
    function: {
      name: "get_weather",
      description: "Get the current weather for a location",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "City name, e.g., 'San Francisco'",
          },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "Temperature unit",
          },
        },
        required: ["location"],
      },
    },
  }],
});

// Create thread and run
const thread = await openai.beta.threads.create({
  messages: [{
    role: "user",
    content: "What's the weather in San Francisco?",
  }],
});

let run = await openai.beta.threads.runs.create(thread.id, {
  assistant_id: assistant.id,
});

// Poll until requires_action
while (run.status === 'in_progress' || run.status === 'queued') {
  await new Promise(resolve => setTimeout(resolve, 1000));
  run = await openai.beta.threads.runs.retrieve(thread.id, run.id);
}

if (run.status === 'requires_action') {
  const toolCalls = run.required_action.submit_tool_outputs.tool_calls;

  const toolOutputs = [];
  for (const toolCall of toolCalls) {
    if (toolCall.function.name === 'get_weather') {
      const args = JSON.parse(toolCall.function.arguments);
      // Call your actual weather API
      const weather = await getWeatherAPI(args.location, args.unit);

      toolOutputs.push({
        tool_call_id: toolCall.id,
        output: JSON.stringify(weather),
      });
    }
  }

  // Submit tool outputs
  run = await openai.beta.threads.runs.submitToolOutputs(thread.id, run.id, {
    tool_outputs: toolOutputs,
  });

  // Continue polling...
}

ベクトルストア

ベクトルストアを使用すると、大規模なドキュメントコレクションを効率的にセマンティック検索できます。

ベクトルストアを作成する
const vectorStore = await openai.beta.vectorStores.create({
  name: "Legal Documents",
  metadata: {
    department: "legal",
    category: "contracts",
  },
  expires_after: {
    anchor: "last_active_at",
    days: 7, // Auto-delete 7 days after last use
  },
});
ベクトルストアにファイルを追加する

単一ファイル:

const file = await openai.files.create({
  file: fs.createReadStream("contract.pdf"),
  purpose: "assistants",
});

await openai.beta.vectorStores.files.create(vectorStore.id, {
  file_id: file.id,
});

バッチアップロード:

const fileBatch = await openai.beta.vectorStores.fileBatches.create(vectorStore.id, {
  file_ids: ["file_abc123", "file_def456", "file_ghi789"],
});

// Poll for batch completion
let batch = await openai.beta.vectorStores.fileBatches.retrieve(vectorStore.id, fileBatch.id);
while (batch.status === 'in_progress') {
  await new Promise(resolve => setTimeout(resolve, 1000));
  batch = await openai.beta.vectorStores.fileBatches.retrieve(vectorStore.id, fileBatch.id);
}
ベクトルストアの状態を確認する
const vectorStore = await openai.beta.vectorStores.retrieve("vs_abc123");

console.log(vectorStore.status); // "in_progress", "completed", "failed"
console.log(vectorStore.file_counts); // { in_progress: 0, completed: 50, failed: 0 }

⚠️ 重要:ファイル検索で使用する前に、status: "completed" になるまで待ってください。

ベクトルストアの一覧表示
const stores = await openai.beta.vectorStores.list({
  limit: 20,
  order: "desc",
});
ベクトルストアを更新する
const vectorStore = await openai.beta.vectorStores.update("vs_abc123", {
  name: "Updated Name",
  metadata: { updated: "true" },
});
ベクトルストアを削除する
await openai.beta.vectorStores.del("vs_abc123");

ファイルのアップロード

Code Interpreter または File Search で使用するファイルをアップロードします。

ファイルをアップロードする
import fs from 'fs';

const file = await openai.files.create({
  file: fs.createReadStream("document.pdf"),
  purpose: "assistants",
});

console.log(file.id); // file_abc123

対応フォーマット:

  • Code Interpreter:.c、.cpp、.csv、.docx、.html、.java、.json、.md、.pdf、.php、.pptx、.py、.rb、.tex、.txt、.css、.jpeg、.jpg、.js、.gif、.png、.tar、.ts、.xlsx、.xml、.zip
  • File Search:.c、.cpp、.docx、.html、.java、.json、.md、.pdf、.php、.pptx、.py、.rb、.tex、.txt、.css、.js、.ts、.go

サイズ制限:

  • Code Interpreter:ファイルあたり512 MB
  • File Search:ファイルあたり512 MB
  • Vector Store:最大10,000ファイル
ファイル情報を取得する
const file = await openai.files.retrieve("file_abc123");
ファイルの内容をダウンロードする
const content = await openai.files.content("file_abc123");
// Returns binary content
ファイルを削除する
await openai.files.del("file_abc123");
ファイルを一覧表示する
const files = await openai.files.list({
  purpose: "assistants",
});

スレッドのライフサイクル管理

スレッドのライフサイクルを適切に管理することで、よくあるエラーを防げます。

パターン1:ユーザーごとに1つのスレッド
async function getOrCreateUserThread(userId: string): Promise<string> {
  // Check if thread exists in your database
  let threadId = await db.getThreadIdForUser(userId);

  if (!threadId) {
    // Create new thread
    const thread = await openai.beta.threads.create({
      metadata: { user_id: userId },
    });
    threadId = thread.id;
    await db.saveThreadIdForUser(userId, threadId);
  }

  return threadId;
}
パターン2:実行中の Run を確認する
async function ensureNoActiveRun(threadId: string) {
  const runs = await openai.beta.threads.runs.list(threadId, {
    limit: 1,
    order: "desc",
  });

  const latestRun = runs.data[0];
  if (latestRun && ['queued', 'in_progress', 'cancelling'].includes(latestRun.status)) {
    throw new Error('Thread already has an active run. Wait or cancel first.');
  }
}

// Before creating new run
await ensureNoActiveRun(threadId);
const run = await openai.beta.threads.runs.create(threadId, { assistant_id });
パターン3:スレッドをクリーンアップする
async function cleanupOldThreads(maxAgeHours = 24) {
  const threads = await openai.beta.threads.list({ limit: 100 });

  for (const thread of threads.data) {
    const createdAt = new Date(thread.created_at * 1000);
    const ageHours = (Date.now() - createdAt.getTime()) / (1000 * 60 * 60);

    if (ageHours > maxAgeHours) {
      await openai.beta.threads.del(thread.id);
    }
  }
}

エラー処理

よくあるエラーと解決策

1. スレッドにすでに実行中の Run がある

Error: 400 Can't add messages to thread_xxx while a run run_xxx is active.

解決策:

// Wait for run to complete or cancel it
const run = await openai.beta.threads.runs.retrieve(threadId, runId);
if (['queued', 'in_progress'].includes(run.status)) {
  await openai.beta.threads.runs.cancel(threadId, runId);
  // Wait for cancellation
  while (run.status !== 'cancelled') {
    await new Promise(resolve => setTimeout(resolve, 500));
    run = await openai.beta.threads.runs.retrieve(threadId, runId);
  }
}

2. Run のポーリングがタイムアウトする

長時間実行されるタスクは、妥当なポーリング時間を超える場合があります。

解決策:

async function pollWithTimeout(threadId: string, runId: string, maxSeconds = 300) {
  const startTime = Date.now();

  while (true) {
    const run = await openai.beta.threads.runs.retrieve(threadId, runId);

    if (!['queued', 'in_progress'].includes(run.status)) {
      return run;
    }

    const elapsed = (Date.now() - startTime) / 1000;
    if (elapsed > maxSeconds) {
      await openai.beta.threads.runs.cancel(threadId, runId);
      throw new Error('Run exceeded timeout');
    }

    await new Promise(resolve => setTimeout(resolve, 1000));
  }
}

3. Vector Store の準備ができていない

インデックス作成が完了する前に Vector Store を使用しています。

解決策:

async function waitForVectorStore(vectorStoreId: string) {
  let store = await openai.beta.vectorStores.retrieve(vectorStoreId);

  while (store.status === 'in_progress') {
    await new Promise(resolve => setTimeout(resolve, 2000));
    store = await openai.beta.vectorStores.retrieve(vectorStoreId);
  }

  if (store.status === 'failed') {
    throw new Error('Vector store indexing failed');
  }

  return store;
}

4. ファイルのアップロード形式の問題

対応していないファイル形式を使用するとエラーが発生します。

解決策:

const SUPPORTED_FORMATS = {
  code_interpreter: ['.csv', '.json', '.pdf', '.txt', '.py', '.js', '.xlsx'],
  file_search: ['.pdf', '.docx', '.txt', '.md', '.html'],
};

function validateFile(filename: string, tool: string) {
  const ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
  if (!SUPPORTED_FORMATS[tool].includes(ext)) {
    throw new Error(`Unsupported file format for ${tool}: ${ext}`);
  }
}

エラーの全一覧については、references/top-errors.md を参照してください。


本番環境でのベストプラクティス

1. Assistant ID を使用する(再作成しない)

❌ 悪い例:

// Creates new assistant on every request!
const assistant = await openai.beta.assistants.create({ ... });

✅ 良い例:

// Create once, store ID, reuse
const ASSISTANT_ID = process.env.ASSISTANT_ID || await createAssistant();

async function createAssistant() {
  const assistant = await openai.beta.assistants.create({ ... });
  console.log('Save this ID:', assistant.id);
  return assistant.id;
}
2. 適切なエラー処理を実装する
async function createRunWithRetry(threadId: string, assistantId: string, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await openai.beta.threads.runs.create(threadId, {
        assistant_id: assistantId,
      });
    } catch (error) {
      if (error.status === 429) {
        // Rate limit - wait and retry
        await new Promise(resolve => setTimeout(resolve, 2000 * (i + 1)));
        continue;
      }

      if (error.message?.includes('active run')) {
        // Wait for active run to complete
        await new Promise(resolve => setTimeout(resolve, 5000));
        continue;
      }

      throw error; // Other errors
    }
  }

  throw new Error('Max retries exceeded');
}
3. コストを監視する
// Track usage
const run = await openai.beta.threads.runs.retrieve(threadId, runId);
console.log('Tokens used:', run.usage);
// { prompt_tokens: 150, completion_tokens: 200, total_tokens: 350 }

// Set limits
const run = await openai.beta.threads.runs.create(threadId, {
  assistant_id: assistantId,
  max_prompt_tokens: 1000,
  max_completion_tokens: 500,
});
4. リソースをクリーンアップする
// Delete old threads
async function cleanupUserThread(userId: string) {
  const threadId = await db.getThreadIdForUser(userId);
  if (threadId) {
    await openai.beta.threads.del(threadId);
    await db.deleteThreadIdForUser(userId);
  }
}

// Delete unused vector stores
async function cleanupVectorStores(keepDays = 30) {
  const stores = await openai.beta.vectorStores.list({ limit: 100 });

  for (const store of stores.data) {
    const ageSeconds = Date.now() / 1000 - store.created_at;
    const ageDays = ageSeconds / (60 * 60 * 24);

    if (ageDays > keepDays) {
      await openai.beta.vectorStores.del(store.id);
    }
  }
}
5. UX を向上させるためにストリーミングを使用する
// Show progress in real-time
async function streamToUser(threadId: string, assistantId: string) {
  const stream = await openai.beta.threads.runs.stream(threadId, {
    assistant_id: assistantId,
  });

  for await (const event of stream) {
    if (event.event === 'thread.message.delta') {
      const delta = event.data.delta.content?.[0]?.text?.value;
      if (delta) {
        // Send to user immediately
        sendToClient(delta);
      }
    }
  }
}

他のスキルとの関係

openai-api スキルとの比較

openai-api(Chat Completions):

  • ステートレスなリクエスト
  • 履歴を手動で管理
  • 直接的な応答
  • 用途:シンプルなテキスト生成、関数呼び出し

openai-assistants:

  • ステートフルな会話(スレッド)
  • 履歴を自動管理
  • 組み込みツール(Code Interpreter、File Search)
  • 用途:チャットボット、データ分析、RAG
openai-responses スキルとの比較

openai-responses(Responses API):

  • ✅ 新規プロジェクトに推奨
  • 推論の保持がより優れている
  • 最新の MCP 統合
  • 開発が継続中

openai-assistants:

  • ⚠️ 2026年上半期に非推奨
  • レガシーアプリで使用
  • 移行手段あり

移行: references/migration-to-responses.md を参照してください。


v1 から v2 への移行

v1 は非推奨:2024年12月18日

主な変更点:

  1. Retrieval → File Search:retrieval ツールが file_search に置き換えられました
  2. Vector Store:ファイルが Vector Store に整理されるようになりました(上限は10,000ファイル)
  3. Instructions の上限:32k 文字から256k 文字に増加しました
  4. ファイル添付:Assistant レベルではなく、メッセージレベルで行うようになりました

完全なガイドについては、references/migration-from-v1.md を参照してください。


次のステップ

テンプレート:

  • templates/basic-assistant.ts - シンプルな数学チューター
  • templates/code-interpreter-assistant.ts - データ分析
  • templates/file-search-assistant.ts - Vector Store を使った RAG
  • templates/function-calling-assistant.ts - カスタムツール
  • templates/streaming-assistant.ts - リアルタイムストリーミング

リファレンス:

  • references/top-errors.md - 12件のよくあるエラーと解決策
  • references/thread-lifecycle.md - スレッド管理のパターン
  • references/vector-stores.md - Vector Store の詳細解説

関連スキル:

  • openai-responses - 最新の代替手段(推奨)
  • openai-api - Chat Completions(ステートレス)

最終更新日:2025-10-25 パッケージバージョン:[email protected] ステータス:本番環境対応(2026年上半期に非推奨)

コメント