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 キーの取得
- OpenAI Platform にアクセス
- 「API keys」からキーを生成
- 環境変数に設定(コードに直接書かない)
# .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 を使った社内ツール開発のポイント:
- モデル選択: タスクに応じて使い分け
- エラーハンドリング: タイムアウトとリトライ
- レート制限: ユーザーごとの制限
- コスト管理: ログとアラート
- セキュリティ: キー保護とプロンプトインジェクション対策
- 最適化: キャッシュ、Batch API、max_tokens
業務で繰り返し行うタスク(要約、翻訳、分類、コードレビュー等)は、API を使って自動化すると生産性が劇的に向上します。まずは小さなツールから始めて、効果を実感してみてください。