AI/LLM実践活用ガイド:ChatGPTとLangChainによる開発入門

ChatGPTやLangChainを使ったAIアプリケーション開発の実践的なガイドです。プロンプトエンジニアリングからRAGの実装、セキュリティ考慮事項まで詳しく解説します。

技術ブログ

はじめに

大規模言語モデル(LLM)の発展により、AIを活用したアプリケーション開発が 身近なものとなっています。この記事では、ChatGPTやLangChainを使った 実践的な開発方法について、具体的なコード例を交えながら解説します。

この記事で学べること

  • 効果的なプロンプトの設計方法
  • LangChainを使ったアプリケーション開発
  • RAG(Retrieval Augmented Generation)の実装
  • AIアプリケーションのセキュリティ対策
  • コスト最適化の方法

LLMの基礎知識

LLM(Large Language Model)は、大量のテキストデータで学習された 言語モデルです。主なモデルとその特徴を見ていきましょう。

1. 主なLLMの比較

  • GPT-4: 高度な理解力と生成能力、マルチモーダル対応
  • Claude 2: 長文処理に強く、数学的な推論が得意
  • Llama 2: オープンソースで、ローカル実行可能
  • PaLM 2: 多言語対応と効率的な推論

2. APIの基本的な使い方

import OpenAI from 'openai'

// OpenAI クライアントの初期化
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY
})

// チャット完了APIの呼び出し
async function generateResponse(prompt: string) {
  const completion = await openai.chat.completions.create({
    model: "gpt-4",
    messages: [
      {
        role: "system",
        content: "あなたは優秀なAIアシスタントです。"
      },
      {
        role: "user",
        content: prompt
      }
    ],
    temperature: 0.7,
    max_tokens: 500
  })

  return completion.choices[0].message.content
}

プロンプトエンジニアリング

効果的なプロンプトの設計は、AIアプリケーションの性能を大きく左右します。 以下のベストプラクティスを紹介します。

プロンプト設計のポイント

  • 明確な指示と制約の提示
  • 具体的な例示(Few-shot learning)
  • ステップバイステップの分解
  • 出力フォーマットの指定
  • エッジケースの考慮

プロンプトテンプレートの例

const ANALYSIS_PROMPT = `
以下のテキストを分析し、次の形式で出力してください:

1. 要約(100文字以内)
2. 主要なポイント(箇条書き3-5項目)
3. 感情分析(ポジティブ/ネガティブ/中立)
4. 推奨アクション(具体的な提案を2-3項目)

入力テキスト:
{text}

出力は必ずJSON形式で返してください。
`

// プロンプトの使用例
const response = await analyzeText(ANALYSIS_PROMPT.replace('{text}', inputText))
const result = JSON.parse(response)

LangChainによる開発

LangChainは、LLMを使ったアプリケーション開発を効率化するフレームワークです。 主な機能と使用例を見ていきましょう。

1. チェーンの基本

import { OpenAI } from 'langchain/llms/openai'
import { PromptTemplate } from 'langchain/prompts'
import { LLMChain } from 'langchain/chains'

// LLMの初期化
const llm = new OpenAI({
  temperature: 0.7,
  modelName: 'gpt-4'
})

// プロンプトテンプレートの作成
const template = "以下の{topic}について、初心者向けに説明してください。"
const prompt = PromptTemplate.fromTemplate(template)

// チェーンの作成と実行
const chain = new LLMChain({ llm, prompt })
const result = await chain.call({ topic: "プロンプトエンジニアリング" })

2. 会話メモリの実装

import { ConversationChain } from 'langchain/chains'
import { BufferMemory } from 'langchain/memory'

// メモリの初期化
const memory = new BufferMemory()

// 会話チェーンの作成
const conversationChain = new ConversationChain({
  llm,
  memory,
  verbose: true
})

// 会話の実行
const response1 = await conversationChain.call({
  input: "こんにちは!"
})

const response2 = await conversationChain.call({
  input: "前回の会話を覚えていますか?"
})

RAGの実装

RAG(Retrieval Augmented Generation)は、外部知識を活用して LLMの回答の質を向上させる手法です。

1. 基本的なRAGの実装

import { OpenAIEmbeddings } from 'langchain/embeddings/openai'
import { MemoryVectorStore } from 'langchain/vectorstores/memory'
import { Document } from 'langchain/document'

// ドキュメントの準備
const docs = [
  new Document({ pageContent: "RAGは外部知識を活用する手法です" }),
  new Document({ pageContent: "ベクトルDBを使用して関連情報を検索します" })
]

// ベクトルストアの作成
const vectorStore = await MemoryVectorStore.fromDocuments(
  docs,
  new OpenAIEmbeddings()
)

// 類似度検索の実行
const results = await vectorStore.similaritySearch(
  "RAGとは何ですか?",
  2
)

2. RAGを使用したQAチェーン

import { RetrievalQAChain } from 'langchain/chains'

// QAチェーンの作成
const qaChain = RetrievalQAChain.fromLLM(
  llm,
  vectorStore.asRetriever()
)

// 質問応答の実行
const response = await qaChain.call({
  query: "RAGの利点を教えてください"
})

ベストプラクティス

1. エラーハンドリング

async function safeGenerateResponse(prompt: string) {
  try {
    const response = await openai.chat.completions.create({
      model: "gpt-4",
      messages: [{ role: "user", content: prompt }]
    })
    return response.choices[0].message.content
  } catch (error) {
    if (error.code === 'context_length_exceeded') {
      // プロンプトを短くして再試行
      return await safeGenerateResponse(truncatePrompt(prompt))
    }
    // レート制限の場合は待機して再試行
    if (error.code === 'rate_limit_exceeded') {
      await sleep(1000)
      return await safeGenerateResponse(prompt)
    }
    throw error
  }
}

2. 応答の検証

interface ValidatedResponse {
  content: string
  format: 'json' | 'text'
  sentiment: 'positive' | 'negative' | 'neutral'
  confidence: number
}

async function validateResponse(
  response: string
): Promise<ValidatedResponse> {
  // スキーマ検証
  const validation = await validateSchema(response)
  if (!validation.success) {
    throw new Error('Invalid response format')
  }

  // 内容の妥当性チェック
  const contentCheck = await moderateContent(response)
  if (!contentCheck.safe) {
    throw new Error('Unsafe content detected')
  }

  return {
    content: response,
    format: detectFormat(response),
    sentiment: analyzeSentiment(response),
    confidence: calculateConfidence(response)
  }
}

セキュリティ考慮事項

主なセキュリティリスク

  • プロンプトインジェクション攻撃
  • 機密情報の漏洩
  • 不適切なコンテンツの生成
  • APIキーの露出
  • データプライバシーの侵害

セキュリティ対策の実装例

// プロンプトのサニタイズ
function sanitizePrompt(prompt: string): string {
  // 特殊文字のエスケープ
  prompt = escapeSpecialChars(prompt)
  
  // 機密情報のマスク
  prompt = maskSensitiveData(prompt)
  
  // 長さの制限
  prompt = truncateIfNeeded(prompt, MAX_PROMPT_LENGTH)
  
  return prompt
}

// レスポンスのフィルタリング
function filterResponse(response: string): string {
  // 不適切なコンテンツの検出
  if (containsInappropriateContent(response)) {
    throw new Error('Inappropriate content detected')
  }
  
  // PII(個人識別情報)の検出と削除
  response = removePII(response)
  
  return response
}

コスト最適化

コスト削減のポイント

  • 適切なモデルの選択
  • トークン数の最適化
  • キャッシュの活用
  • バッチ処理の実装
  • レスポンスの再利用

キャッシュの実装例

import { Redis } from 'ioredis'

const redis = new Redis()

async function getCachedResponse(
  prompt: string,
  ttl: number = 3600
): Promise<string | null> {
  const key = `llm:response:${hashPrompt(prompt)}`
  
  // キャッシュの確認
  const cached = await redis.get(key)
  if (cached) {
    return cached
  }
  
  // 新しいレスポンスの生成
  const response = await generateResponse(prompt)
  
  // キャッシュの保存
  await redis.setex(key, ttl, response)
  
  return response
}