AWS BedrockでGitHubリポジトリをAI分析する機能を実装した話

開発者向けポートフォリオサービス「Brightfolio」の核心機能として、AWS BedrockのClaude APIを活用したGitHubリポジトリの自動分析機能を実装しました。

この機能により、開発者のコードを自動で解析し、技術スタック、プロジェクトの特徴、改善提案まで含む詳細なレポートを生成しています。実際に私のポートフォリオ(daisuke-matsuura.brightfolio.link)でも、AI生成された分析結果を確認できます。

今回は、この AI 分析機能の技術的な実装方法と、開発過程で得た知見をお話しします。

目次

なぜAWS Bedrockを選んだのか

OpenAI APIとの比較検討

当初はOpenAI GPT-4での実装も検討しましたが、最終的にAWS Bedrockを選択した理由は以下の通りです:

AWS Bedrockの優位点:

  • 多様なモデル選択肢: Claude、Llama、Titanなど複数のLLMを統一APIで利用可能
  • コスト効率: 大量処理時のトークン単価がOpenAI比で約30%削減
  • フォールバック戦略: 複数モデルの組み合わせでサービス安定性向上
  • プロンプトキャッシュ: 繰り返し処理でのコスト最適化

Claudeモデルの特徴

特にAnthropic社のClaudeモデルは、コード解析タスクにおいて優秀な性能を発揮:

  • 長文コンテキスト: 最大200K トークンで大規模リポジトリにも対応
  • 構造化出力: JSON形式での一貫した回答生成
  • 技術的理解: プログラミング言語やフレームワークの深い理解

技術実装の詳細

Bedrockクライアントの初期化

import { BedrockRuntimeClient, InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime";

// AWS Bedrockクライアントの初期化
const bedrockClient = new BedrockRuntimeClient({
  region: process.env.AWS_REGION || "us-east-1",
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
});

モデル選択とフォールバック戦略

複数のClaudeモデルを順番に試行することで、サービスの可用性を確保:

// 利用可能なモデルを優先順位で定義
const modelIds = [
  "anthropic.claude-3-7-sonnet-20250219-v1:0", // 最新の Claude 3.7 Sonnet
  "anthropic.claude-3-5-sonnet-20241022-v2:0", // Claude 3.5 Sonnet
  "anthropic.claude-3-5-sonnet-20240620-v1:0", // 旧バージョン
  "anthropic.claude-3-haiku-20240307-v1:0",    // 軽量版(フォールバック)
];

// モデルを順番に試行
for (let modelIndex = 0; modelIndex < modelIds.length; modelIndex++) {
  const modelId = modelIds[modelIndex];
  
  try {
    const command = new InvokeModelCommand({
      modelId,
      body: JSON.stringify(modelInput),
      contentType: "application/json",
      accept: "application/json",
    });

    const response = await bedrockClient.send(command);
    // 成功した場合の処理
    return parseResponse(response);
    
  } catch (error) {
    console.error(`Model ${modelId} failed:`, error);
    // 次のモデルを試行
    continue;
  }
}

リトライ機能と指数バックオフ

API呼び出しの信頼性向上のため、指数バックオフによるリトライ機能を実装:

let retryCount = 0;
const maxRetries = 3;

while (retryCount < maxRetries) {
  try {
    const response = await bedrockClient.send(command);
    return response;
  } catch (error) {
    retryCount++;
    
    if (retryCount >= maxRetries) {
      throw error;
    }
    
    // 指数バックオフで再試行
    await new Promise(resolve => 
      setTimeout(resolve, Math.pow(2, retryCount) * 1000)
    );
  }
}

プラン別プロンプト戦略

Brightfolioでは、ユーザープランに応じて異なる分析深度を提供しています。

Freeプラン: シンプル概要生成

const freePrompt = `
あなたはソフトウェアプロジェクトの厳格な評価者です。
以下の情報を基に、プロジェクトの概要を日本語で50字程度で記述してください。

- プロジェクト名: ${repoFullName}
- 主要技術: ${technologies}

客観的で、改善の余地がある点も含めて評価してください。
`;

特徴:

  • 文字数制限: 50字程度
  • 基本情報のみ使用
  • シンプルな評価

Starterプラン: 機能分析付き

const starterPrompt = `
あなたはソフトウェアプロジェクトの客観的な評価者です。
分析結果を基に、プロジェクトの概要を日本語で150字程度で生成してください。

## リポジトリ情報
- プロジェクト名: ${repoFullName}
- プロジェクトタイプ: ${projectType}
- 主要技術: ${technologies}
- フレームワーク: ${frameworks}

## 分析結果のハイライト
${highlights.map(h => `- ${h}`).join('\\n')}

技術的な強みと改善の余地がある点の両方を評価してください。
`;

特徴:

  • 文字数制限: 150字程度
  • プロジェクトタイプ分析
  • ハイライト機能

Proプラン: 技術負債分析

const proPrompt = `
あなたはソフトウェアプロジェクトの技術負債分析とコード品質評価の専門家です。

## 分析の重点項目
1. プロジェクト概要と技術的価値
2. 技術負債の詳細分析
3. 具体的な改善アクションプラン
4. ポートフォリオとしての価値

## 技術負債詳細分析
- 複雑度リスク: ${complexityRisk}
- テスト戦略: ${testStrategy}
- ドキュメント状況: ${documentationStatus}
- 依存関係管理: ${dependencyManagement}

構造化された技術負債分析を含む包括的な概要を500字程度で生成してください。
`;

特徴:

  • 文字数制限: 500字程度
  • 技術負債分析
  • 改善提案
  • 構造化された出力

プロンプトキャッシュによるコスト最適化

AWS Bedrockのプロンプトキャッシュ機能を活用して、API呼び出しコストを大幅削減:

const cachedPrompt = `
<prompt_cache>
あなたはソフトウェアプロジェクトの技術評価専門家です。
リポジトリの詳細な分析結果を基に、日本語でアプリケーション概要を生成してください。

分析観点:
- アプリケーションの目的と主要機能
- 使用技術スタックの特徴と適切性
- プロジェクトの技術的な価値や特色
- 開発者のスキルレベルと成長の証拠
- 改善の余地がある点と具体的な提案
</prompt_cache>

// 以下、プロジェクト固有の情報
プロジェクト名: ${repoFullName}
主要技術: ${technologies}
...
`;

効果:

  • 繰り返し部分のトークン消費が約90%削減
  • レスポンス時間の短縮
  • 月間API費用が約40%削減

エラーハンドリングと運用監視

包括的なエラー分類

try {
  const response = await bedrockClient.send(command);
  return parseResponse(response);
} catch (error) {
  // エラーの種類に応じた適切な処理
  if (error.message.includes("rate limit")) {
    return { error: "レート制限に達しました", retryAfter: 3600 };
  }
  
  if (error.message.includes("model not available")) {
    // 次のモデルにフォールバック
    continue;
  }
  
  if (error.message.includes("content policy")) {
    return { error: "コンテンツポリシー違反が検出されました" };
  }
  
  // 予期しないエラー
  throw new Error("AI分析中に予期しないエラーが発生しました");
}

トークン使用量の監視

const responseBody = JSON.parse(new TextDecoder().decode(response.body));

// トークン使用量を記録
const tokenUsage = {
  prompt: responseBody.usage?.input_tokens || 0,
  completion: responseBody.usage?.output_tokens || 0,
  model: modelId
};

console.log(`Tokens used - Prompt: ${tokenUsage.prompt}, Completion: ${tokenUsage.completion}`);

// データベースに使用量を保存(コスト分析用)
await saveTokenUsage(userId, tokenUsage);

実装で苦労したポイント

1. モデル可用性の変動

問題: Bedrockの新しいモデルが地域によって利用できない

最新のClaude 3.7 Sonnetをus-east-1で利用しようとしたところ、時折「ModelNotAvailableException」が発生しました。

解決策: フォールバック戦略の実装

// 地域とモデルの組み合わせを事前に検証
const availableModels = await validateModelsAvailability();
const modelIds = availableModels.filter(model => 
  model.region === process.env.AWS_REGION
);

2. プロンプトトークン制限の管理

問題: 大規模リポジトリの解析時にトークン上限に到達

数千行のコードを含むリポジトリを分析する際、Claudeの200Kトークン制限に引っかかることがありました。

解決策: 段階的な情報圧縮

// コード量に応じて情報を圧縮
const compressAnalysisData = (analysisResult, maxTokens) => {
  if (estimateTokens(analysisResult) > maxTokens) {
    // ハイライトを最重要項目に絞る
    analysisResult.highlights = analysisResult.highlights.slice(0, 3);
    // 詳細なコード例を除去
    delete analysisResult.codeExamples;
  }
  return analysisResult;
};

3. レスポンス形式の一貫性確保

問題: モデルによって出力形式が微妙に異なる

同じプロンプトでも、Claude 3.5と3.7で出力形式に差が生じることがありました。

解決策: 厳密なスキーマ検証

const validateResponse = (responseText) => {
  // JSON形式の強制
  const cleanedResponse = responseText
    .replace(/```json\n?/, '')
    .replace(/```\n?$/, '')
    .trim();
    
  try {
    const parsed = JSON.parse(cleanedResponse);
    return validateSchema(parsed);
  } catch (error) {
    // パースエラーの場合は再生成
    throw new Error("Invalid response format");
  }
};

パフォーマンス最適化の取り組み

非同期処理による高速化

// リポジトリ解析とAI分析を並行実行
const [analysisResult, repositoryMetadata] = await Promise.all([
  analyzer.analyze(repoFullName),
  fetchRepositoryMetadata(repoFullName)
]);

// AI概要生成は別プロセスで実行(ノンブロッキング)
generateApplicationSummary(analysisResult, repoFullName, planId)
  .then(summary => saveSummaryToDatabase(summary))
  .catch(error => logError("Summary generation failed", error));

キャッシュ戦略の実装

// 7日間のキャッシュでAPI呼び出しを削減
const checkCache = async (repoFullName, userId) => {
  const { data: cachedAnalysis } = await supabase
    .from("repository_analysis")
    .select("*")
    .eq("profile_id", userId)
    .eq("repository_full_name", repoFullName)
    .single();

  if (cachedAnalysis && 
      new Date(cachedAnalysis.expires_at) > new Date()) {
    return cachedAnalysis.analysis_result;
  }
  
  return null;
};

運用開始後の課題と改善

コスト管理の最適化

初期の課題: 想定以上のAPI利用料金

月間のBedrock利用料金が当初予算の3倍に達し、急遽コスト削減策を実装しました。

改善策:

  1. プラン別制限: 無料ユーザーは月3回まで
  2. キャッシュ期間延長: 7日 → 30日に延長
  3. プロンプト最適化: 不要な説明文を削除して30%短縮

分析精度の向上

フィードバック分析: ユーザーからの「分析が表面的」との指摘

初期のプロンプトは汎用的すぎて、プロジェクト固有の特徴を捉えきれていませんでした。

改善策:

  • 技術別プロンプト: React、Node.js、Pythonなど技術別に特化
  • ドメイン認識: Webアプリ、CLI、ライブラリなど用途別分析
  • 段階的詳細化: 概要 → 詳細 → 改善提案の3段階プロンプト

スケーラビリティ対応

// 同時実行数制限でAPI負荷を制御
const analysisQueue = new Queue('ai-analysis', {
  concurrency: 3, // 同時実行3件まで
  defaultJobOptions: {
    removeOnComplete: 10,
    removeOnFail: 5,
    attempts: 3,
    backoff: 'exponential'
  }
});

analysisQueue.process(async (job) => {
  const { repoFullName, userId, planId } = job.data;
  return await performAIAnalysis(repoFullName, userId, planId);
});

今後の展開

多言語対応

現在は日本語のみですが、英語、中国語への対応を計画:

const generateMultiLanguageSummary = async (analysisResult, language = 'ja') => {
  const prompts = {
    'ja': japanesePrompt,
    'en': englishPrompt,
    'zh': chinesePrompt
  };
  
  return await generateSummary(analysisResult, prompts[language]);
};

リアルタイム分析

WebSocketを使用したリアルタイム進捗表示:

// 分析進捗をリアルタイム配信
const updateProgress = async (userId, progress) => {
  await supabase
    .channel(`analysis-${userId}`)
    .send({
      type: 'broadcast',
      event: 'progress',
      payload: progress
    });
};

カスタム分析ルール

企業向けに独自の分析基準を設定可能な機能:

// 企業固有の評価基準
const customAnalysisRules = {
  security: ['dependency-check', 'secret-scan'],
  performance: ['bundle-size', 'lighthouse-score'],
  maintainability: ['test-coverage', 'documentation']
};

まとめ

AWS BedrockとClaudeを活用したGitHubリポジトリ分析機能の実装により、以下の成果を得ることができました:

技術的成果:

  • 複数モデルのフォールバック戦略で99.5%の可用性を実現
  • プロンプトキャッシュにより40%のコスト削減
  • プラン別分析機能で収益化モデルを確立

ビジネス成果:

  • ユーザーの分析利用率が80%向上
  • 有料プランへのコンバージョン率が15%に到達
  • 月間API費用を予算内に収めることに成功

学んだこト:

  • AI機能の実装は技術とビジネスの両面での戦略が重要
  • フォールバック機能による可用性確保の重要性
  • ユーザーフィードバックに基づく継続的な改善の必要性

AWS Bedrockは、個人開発でも本格的なAI機能を実装できる強力なプラットフォームです。特に複数モデルの活用による安定性と、プロンプトキャッシュによるコスト効率は、サービス運営において大きなメリットとなりました。

今後も技術の進歩に合わせて、より高精度で有用な分析機能を提供していきたいと思います。

関連リンク


この記事が役に立ったら、BrightfolioでAI分析機能を体験してみてください!GitHubリポジトリの詳細な分析レポートを自動生成できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Web Developer / Educator

コメント

コメントする