メインコンテンツへスキップ
Code & Craft
AI活用 約6分で読めます

ChatGPT API で社内ツールを作る実践ガイド|認証・レート制限・コスト管理

ChatGPT API(OpenAI API)を使った社内ツール開発の全手順。認証、エラーハンドリング、ストリーミング、コスト最適化まで実装例付きで完全解説。

OpenAI API(通称 ChatGPT API)を使えば、自社サービスに AI 機能を組み込んだり、業務効率化ツールを構築したりできます。この記事では、Node.js / TypeScript で OpenAI API を使った社内ツールの開発方法を、実務で必要な知識を網羅的に解説します。

OpenAI API の基本

利用料金(2026年4月時点)

モデル入力料金出力料金
GPT-4o$2.50 / 1M tokens$10.00 / 1M tokens
GPT-4o mini$0.15 / 1M tokens$0.60 / 1M tokens
GPT-4.5$5.00 / 1M tokens$20.00 / 1M tokens
o1$15.00 / 1M tokens$60.00 / 1M tokens

1,000 トークン = 約 750 英単語 = 約 500 日本語文字

API キーの取得

  1. OpenAI Platform にアクセス
  2. 「API keys」からキーを生成
  3. 環境変数に設定(コードに直接書かない)
# .env
OPENAI_API_KEY=sk-...

基本的な実装

セットアップ

npm install openai

シンプルなチャット

import OpenAI from 'openai';

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

async function chat(message: string) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      { role: 'system', content: 'あなたは親切なアシスタントです。' },
      { role: 'user', content: message },
    ],
  });

  return response.choices[0].message.content;
}

const answer = await chat('TypeScript の型安全性について教えて');
console.log(answer);

ストリーミング(リアルタイム応答)

async function streamChat(message: string) {
  const stream = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'user', content: message }],
    stream: true,
  });

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content || '';
    process.stdout.write(content);
  }
}

ユーザー体験が向上します。特に長い回答の場合は必須です。

Next.js での実装

API ルート

// app/api/chat/route.ts
import { NextRequest } from 'next/server';
import OpenAI from 'openai';

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

export async function POST(req: NextRequest) {
  const { message } = await req.json();

  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'user', content: message }],
    stream: true,
  });

  // ストリーミングレスポンス
  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const chunk of response) {
        const text = chunk.choices[0]?.delta?.content || '';
        controller.enqueue(encoder.encode(text));
      }
      controller.close();
    },
  });

  return new Response(readable, {
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  });
}

フロントエンド

'use client';

import { useState } from 'react';

export default function Chat() {
  const [input, setInput] = useState('');
  const [response, setResponse] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setResponse('');

    const res = await fetch('/api/chat', {
      method: 'POST',
      body: JSON.stringify({ message: input }),
    });

    const reader = res.body?.getReader();
    const decoder = new TextDecoder();

    while (reader) {
      const { done, value } = await reader.read();
      if (done) break;
      setResponse(prev => prev + decoder.decode(value));
    }

    setLoading(false);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button disabled={loading}>送信</button>
      <div>{response}</div>
    </form>
  );
}

実務で必要な機能

1. エラーハンドリング

import OpenAI from 'openai';

async function safeChat(message: string) {
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      messages: [{ role: 'user', content: message }],
    });
    return { success: true, data: response.choices[0].message.content };
  } catch (error) {
    if (error instanceof OpenAI.APIError) {
      if (error.status === 429) {
        return { success: false, error: 'レート制限に達しました' };
      }
      if (error.status === 401) {
        return { success: false, error: '認証エラー' };
      }
      return { success: false, error: error.message };
    }
    throw error;
  }
}

2. タイムアウト設定

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  timeout: 30 * 1000,  // 30秒
  maxRetries: 2,       // リトライ回数
});

3. レート制限の実装

社内ツールでは、ユーザーごとの使用制限が必要な場合があります。

import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '1 m'),  // 1分間に10回
});

export async function POST(req: NextRequest) {
  const userId = req.headers.get('x-user-id') || 'anonymous';
  const { success } = await ratelimit.limit(userId);

  if (!success) {
    return new Response('Too Many Requests', { status: 429 });
  }

  // 通常の処理
}

4. トークン数のカウント

import { encoding_for_model } from 'tiktoken';

function countTokens(text: string, model = 'gpt-4o') {
  const encoding = encoding_for_model(model);
  const tokens = encoding.encode(text);
  encoding.free();
  return tokens.length;
}

const tokens = countTokens('Hello, world!');
console.log(`${tokens} tokens`);

5. コスト見積もり

const PRICE = {
  'gpt-4o': { input: 2.50, output: 10.00 },
  'gpt-4o-mini': { input: 0.15, output: 0.60 },
};

function estimateCost(inputTokens: number, outputTokens: number, model = 'gpt-4o') {
  const p = PRICE[model];
  const cost = (inputTokens * p.input + outputTokens * p.output) / 1_000_000;
  return cost;
}

console.log(`Estimated cost: $${estimateCost(500, 200).toFixed(6)}`);

実践例: 社内ツール構築

例1: ミーティング議事録要約ツール

async function summarizeMeeting(transcript: string) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `
あなたはミーティング議事録の要約を専門とするアシスタントです。
以下の形式で要約してください:

## 参加者
(名前をリストアップ)

## アジェンダ
(議題をリストアップ)

## 決定事項
(箇条書きで)

## TODO
(担当者と期限付きで)

## 次回までの課題
(箇条書きで)
`
      },
      { role: 'user', content: transcript },
    ],
  });

  return response.choices[0].message.content;
}

例2: カスタマーサポート自動応答

async function customerSupport(question: string, context: string) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o-mini',  // コスト重視
    messages: [
      {
        role: 'system',
        content: `
あなたは Code & Craft のカスタマーサポート担当者です。
以下の情報のみを使って回答してください。情報にない内容は
「お問い合わせフォームからご連絡ください」と答えてください。

# 会社情報
${context}
`
      },
      { role: 'user', content: question },
    ],
    temperature: 0.3,  // 一貫性重視
  });

  return response.choices[0].message.content;
}

例3: コードレビュー支援

async function reviewCode(code: string, language: string) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `
あなたはシニアソフトウェアエンジニアです。コードをレビューし、
JSON 形式で以下を返してください:

{
  "issues": [
    { "severity": "critical|major|minor", "line": 10, "message": "..." }
  ],
  "suggestions": ["..."],
  "overall": "..."
}
`
      },
      {
        role: 'user',
        content: `Language: ${language}\n\n\`\`\`${language}\n${code}\n\`\`\``
      },
    ],
    response_format: { type: 'json_object' },
  });

  return JSON.parse(response.choices[0].message.content || '{}');
}

構造化出力

response_format で JSON スキーマを指定できます(GPT-4o 以降)。

const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'ユーザー情報を抽出: 田中太郎さん、30歳、エンジニア' }],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'user_info',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          age: { type: 'number' },
          occupation: { type: 'string' },
        },
        required: ['name', 'age', 'occupation'],
        additionalProperties: false,
      },
    },
  },
});

const data = JSON.parse(response.choices[0].message.content!);

Function Calling

外部ツールと連携する機能です。

const tools = [
  {
    type: 'function',
    function: {
      name: 'get_weather',
      description: '特定の都市の天気を取得',
      parameters: {
        type: 'object',
        properties: {
          location: {
            type: 'string',
            description: '都市名(例: 東京)',
          },
        },
        required: ['location'],
      },
    },
  },
];

const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: '東京の天気は?' }],
  tools,
});

// AI が関数を呼ぶべきかを判断
const toolCall = response.choices[0].message.tool_calls?.[0];
if (toolCall?.function.name === 'get_weather') {
  const args = JSON.parse(toolCall.function.arguments);
  const weather = await getWeather(args.location);
  // 結果を AI に戻して最終応答を得る
}

コスト最適化

1. モデルの使い分け

async function pickModel(task: 'simple' | 'complex') {
  return task === 'simple' ? 'gpt-4o-mini' : 'gpt-4o';
}

簡単なタスクには gpt-4o-mini を使うと 17倍安価です。

2. プロンプトキャッシュ

同じシステムプロンプトを繰り返し使う場合、自動的にキャッシュされます。

// 同じ system メッセージでキャッシュが効く
const baseMessages = [
  { role: 'system', content: LONG_SYSTEM_PROMPT },
];

// 異なる user メッセージ
for (const question of questions) {
  const res = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [...baseMessages, { role: 'user', content: question }],
  });
}

キャッシュヒット時は入力料金が 50% 割引になります。

3. max_tokens で出力制限

const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [...],
  max_tokens: 500,  // 最大 500 トークンに制限
});

4. Batch API(50% 割引)

急ぎでない処理は、Batch API でまとめて送信するとコスト半減できます。

const batch = await openai.batches.create({
  input_file_id: 'file-abc123',
  endpoint: '/v1/chat/completions',
  completion_window: '24h',
});

詳しくは Claude Batch APIで大量リクエストのコストを50%削減する実践ガイド の考え方も参考になります。

セキュリティ対策

1. API キーの保護

// ❌ 絶対にやってはいけない
const openai = new OpenAI({ apiKey: 'sk-...' });

// ✅ 環境変数で管理
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

2. クライアントからの直接呼び出しを避ける

// ❌ クライアントから直接 OpenAI を呼ぶ(キーが漏れる)
const openai = new OpenAI({ apiKey: '...', dangerouslyAllowBrowser: true });

// ✅ 自社 API 経由でプロキシ
fetch('/api/chat', { method: 'POST', body: JSON.stringify(...) });

3. プロンプトインジェクション対策

ユーザー入力を system プロンプトに直接含めないこと。

// ❌ 危険
const systemPrompt = `あなたは ${userInput} です`;

// ✅ 安全
const systemPrompt = 'あなたはアシスタントです';
const messages = [
  { role: 'system', content: systemPrompt },
  { role: 'user', content: userInput },
];

ログとモニタリング

async function loggedChat(message: string, userId: string) {
  const start = Date.now();

  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      messages: [{ role: 'user', content: message }],
    });

    const duration = Date.now() - start;
    const tokens = response.usage?.total_tokens || 0;
    const cost = estimateCost(
      response.usage?.prompt_tokens || 0,
      response.usage?.completion_tokens || 0
    );

    await logToDB({
      userId,
      model: 'gpt-4o',
      tokens,
      cost,
      duration,
      status: 'success',
    });

    return response.choices[0].message.content;
  } catch (error) {
    await logToDB({
      userId,
      status: 'error',
      error: String(error),
    });
    throw error;
  }
}

まとめ

ChatGPT API を使った社内ツール開発のポイント:

  1. モデル選択: タスクに応じて使い分け
  2. エラーハンドリング: タイムアウトとリトライ
  3. レート制限: ユーザーごとの制限
  4. コスト管理: ログとアラート
  5. セキュリティ: キー保護とプロンプトインジェクション対策
  6. 最適化: キャッシュ、Batch API、max_tokens

業務で繰り返し行うタスク(要約、翻訳、分類、コードレビュー等)は、API を使って自動化すると生産性が劇的に向上します。まずは小さなツールから始めて、効果を実感してみてください。

参考リンク

#OpenAI #ChatGPT #API #TypeScript #Node.js
シェア: