0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

巨大コードベースのリファクタリングを効率化する2フェーズパイプライン 〜LSP統合とハイブリッド検索で実現するコンテキスト最小化戦略〜

Posted at

はじめに

大規模なコードベースのリファクタリングに取り組む際、こんな課題に直面したことはありませんか?

  • LLMのコンテキストがすぐに埋まる: 関連ファイルを読み込んだだけで上限に到達
  • 精度が劣化する: コンテキストが雑然として、的確な提案が得られない
  • 全体像が把握できない: どこから手をつければいいのか分からない

本記事では、これらの課題を解決するために設計した2フェーズパイプラインのアーキテクチャを詳しく解説します。

GitHub リポジトリ: https://github.com/nogataka/refactor-doc-pipeline


この記事で分かること

  • LLMのコンテキストを効率的に使う2フェーズ設計の考え方
  • Language Server Protocol (LSP) を活用した高度なコード解析手法
  • 疎検索と密検索を組み合わせたハイブリッド検索の実装
  • チャンク→ファイル→モジュール→システムの階層要約生成
  • 実装の詳細とスケーラビリティへの配慮

システムの全体像

このシステムは、データストア作成フェーズデータストア利用フェーズの2つに分かれています。

なぜ2フェーズなのか?

LLMのコンテキストウィンドウは限られています。数万行のコードベースを一度に投げても、LLMは適切に処理できません。

2フェーズ設計の利点:

  1. 事前処理で知識ベース化: ソースコードを検索可能な形式に変換
  2. 必要最小限の情報だけ抽出: クエリに関連する部分だけをLLMに渡す
  3. コンテキストの質を向上: 雑然とした全文ではなく、構造化された要約を活用

フェーズ1: データストア作成

ソースコードを事前処理し、検索可能な知識ベースを構築するフェーズです。

処理の流れ

1. チャンク化 (Chunker)

ソースコードを適切なサイズに分割します。

チャンク化の戦略:

実装の工夫:

  • TypeScript/JavaScript: ts-morphでAST解析し、関数・クラス単位で分割
  • その他の言語: 構造が取れない場合は6KBの固定長で分割
  • シグネチャ抽出: 型情報を含む関数シグネチャを保存
// 抽出されるシグネチャの例
"async functionName(param1: string, param2: number): Promise<void>"
"class ClassName extends Base implements Interface"

なぜ関数・クラス単位なのか?

  • 責務が明確で理解しやすい
  • 変更の影響範囲が特定しやすい
  • 検索時に関連コードをピンポイントで取得可能

2. 要約生成 (Summarizer)

各チャンクの責務を100-200字で要約します。これがシステムの核心部分です。

要約の構造:

interface ChunkSummary {
  responsibility: string;      // 責務の説明
  inputOutput: string;          // 入出力の説明
  sideEffects: string;          // 副作用の説明
  mainDependencies: string[];   // 主要な依存関係
  notes: string;                // 特記事項(例外、I/O、非同期など)
}

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

  • 簡潔さ: 100-200字に制限して冗長さを排除
  • 構造化: JSON形式で出力し、機械処理可能に
  • 安定性: temperature=0.3 で再現性を確保

要約例:

{
  "responsibility": "ユーザー認証を行い、JWTトークンを生成する",
  "inputOutput": "入力: email, password / 出力: JWT文字列",
  "sideEffects": "データベースのlast_login_atを更新",
  "mainDependencies": ["bcrypt", "jsonwebtoken", "UserModel"],
  "notes": "非同期処理、認証失敗時は例外をスロー"
}

なぜ全文ではなく要約なのか?

観点 全文埋め込み 要約埋め込み
コスト $$$$$ $
トークン数 数千〜数万 数百
ノイズ 多い 少ない
検索精度

要約により、コストを1/10以下に抑えながら、検索精度を向上させています。

3. メタデータ抽出 (MetadataExtractor)

ここがこのシステムの最大の特徴です。Language Server Protocol (LSP) を活用した高度な解析を行います。

LSPとは?

LSPは、IDEがコードを理解するために使う標準プロトコルです。VSCodeやIntelliJなどのIDEは、LSPを通じて言語サーバーと通信し、以下の機能を実現しています:

  • シンボル情報の取得: 関数、クラス、変数などの定義情報
  • 参照検索: どこでこの関数が使われているか
  • 定義ジャンプ: シンボルの定義箇所への移動
  • 型情報の取得: 型推論や型チェック

このシステムでは、同じLSPを使ってプログラマティックにコード解析を行います。

LSP統合のアーキテクチャ

3段階のフォールバック戦略:

  1. LSP解析: 言語サーバーからシンボル情報を取得(最も正確)
  2. ts-morph解析: TypeScript/JavaScript向けのAST解析
  3. 正規表現解析: テキストベースのパターンマッチング(最終手段)

この多段階フォールバックにより、言語サーバーが利用できない環境でも動作し、あらゆる言語に対応できます。

対応言語:

言語 言語サーバー インストール方法
TypeScript/JavaScript typescript-language-server npm i -g typescript-language-server
Python pyright-langserver pip install pyright
Go gopls go install golang.org/x/tools/gopls@latest
Rust rust-analyzer rustup component add rust-analyzer
Java eclipse.jdt.ls 公式サイト
C# csharp-ls dotnet tool install --global csharp-ls
C/C++ clangd brew install llvm

抽出されるメタデータ:

interface ChunkMetadata {
  chunkId: string;
  imports: string[];          // import文の一覧
  exports: string[];          // export情報
  callers: string[];          // このコードを呼び出している箇所
  callees: string[];          // このコードが呼び出している関数
  isAsync: boolean;           // 非同期処理を含むか
  throwsException: boolean;   // 例外をスローする可能性
  securityBoundary: boolean;  // セキュリティ境界に関わるか
}

LSP解析の利点:

正確性: IDEと同じセマンティック理解
言語非依存: 統一インターフェースで多言語対応
参照追跡: 呼び出し関係の完全な把握
型情報: 静的型付け言語の型情報を活用

LSP統合の実装詳細:

システムは以下の3つのコンポーネントで構成されています:

  1. LspServerRegistry: ファイル拡張子から適切な言語サーバーを特定
  2. LspProcessManager: 言語サーバープロセスのライフサイクル管理
  3. LspClientPool: 開かれたドキュメントのプール管理

マージ戦略の詳細:

LSP解析とフォールバック解析の結果をインテリジェントにマージします:

マージルール:

  • imports/exports/callees: 両方の結果を和集合(補完し合う)
  • isAsync/throwsException: LSP優先、なければフォールバック
  • callers: buildCallGraphフェーズで全体から構築

この戦略により、LSPだけでは取れない情報も補完しながら、LSPの高精度な情報を最大限活用できます。

4. 埋め込み生成 (Embedder)

要約+シグネチャからベクトル埋め込みを生成します。

埋め込み対象テキスト:

const embeddingText = `${signature}\n\n${summary.responsibility}\n${summary.inputOutput}`;

実装の工夫:

  • バッチ処理: 100件ずつバッチ化してAPI呼び出し回数を削減
  • 進捗表示: 長時間処理の状況を可視化
  • エラーハンドリング: 失敗したチャンクをスキップして続行

使用モデル: OpenAI text-embedding-3-small (1536次元)

コサイン類似度の計算:

static cosineSimilarity(a: number[], b: number[]): number {
  const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const normA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
  const normB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
  return dotProduct / (normA * normB);
}

5. 保存 (Storage)

インデックス化されたドキュメントをJSONL形式で保存します。

JSONL形式を選んだ理由:

特徴 利点
1行1ドキュメント ストリーム処理が可能
追記が容易 ファイル末尾に追加するだけ
差分更新 変更ファイルだけ再処理して更新可能
並列処理 行単位で分割して処理可能

保存形式:

{"chunk":{...},"summary":{...},"metadata":{...},"embedding":{...},"indexedAt":"2024-01-01T00:00:00.000Z"}
{"chunk":{...},"summary":{...},"metadata":{...},"embedding":{...},"indexedAt":"2024-01-01T00:00:00.000Z"}

フェーズ2: データストア利用

構築した知識ベースから必要な情報だけを検索するフェーズです。

ハイブリッド検索の仕組み

疎検索(Sparse Search)と密検索(Dense Search)を組み合わせます

疎検索 (Sparse Search)

BM25アルゴリズムベースのキーワード検索で候補を200件に絞り込みます。

BM25とは?

BM25(Best Matching 25)は、情報検索における古典的なランキング関数です。以下の要素を考慮してスコアを計算します:

  • TF (Term Frequency): クエリのキーワードが文書内に何回出現するか
  • IDF (Inverse Document Frequency): キーワードの希少性(全文書で何回出現するか)
  • 文書の長さ: 長い文書はペナルティ

使用ライブラリ: MiniSearch(軽量なBM25実装)

検索対象フィールドとブースト設定:

{
  fields: ['name', 'signature', 'responsibility', 'inputOutput', 'notes'],
  searchOptions: {
    boost: {
      name: 3,              // 関数名/クラス名を最重視
      signature: 2,         // シグネチャを次に重視
      responsibility: 1.5,
      inputOutput: 1,
      notes: 1
    },
    fuzzy: 0.2,            // タイポ許容(編集距離)
    prefix: true           // 前方一致検索
  }
}

疎検索の特徴:

特徴 説明
✅ 高速 インデックスベースの検索で数ミリ秒
✅ 完全一致に強い 関数名やキーワードの完全マッチ
✅ 軽量 メモリ使用量が少ない
❌ 意味は捉えられない "ログイン"と"認証"は別物として扱われる
❌ 多言語対応が弱い 日本語と英語の対応が困難

密検索 (Dense Search)

ベクトル類似度による意味的検索で、疎検索の結果を20件に再ランキングします。

検索フロー:

密検索の特徴:

特徴 説明
✅ 意味的類似性 "ログイン"と"認証"を同じ文脈として認識
✅ 多言語対応 日本語クエリで英語コードも検索可能
✅ 同義語対応 "削除"と"delete"を同じ意味として扱う
❌ 計算コスト 全ベクトルとの比較が必要(O(n))
❌ メモリ使用量 ベクトルデータの保持が必要

なぜハイブリッドなのか?

疎検索と密検索は相補的です。

検索方法 速度 精度 適用範囲 コスト
疎検索のみ ⚡⚡⚡ キーワードマッチ 無料
密検索のみ ⚡⚡ 意味的マッチ $
ハイブリッド ⚡⚡ ⚡⚡⚡ 両方の良いとこ取り $

実装上の工夫:

  1. 疎検索で候補を200件に絞り込む(高速、無料)

    • 全10,000チャンクから200件に削減
    • BM25スコアでランキング
  2. その200件だけ密検索で再ランキング(精度向上、低コスト)

    • 200件のベクトル比較のみ(10,000件の1/50)
    • コサイン類似度で意味的に近いものを抽出
  3. 最終的に上位20件をLLMに渡す(コンテキスト最小化)

    • 必要十分な情報だけを提供
    • コンテキストウィンドウを効率的に使用

クエリ拡張機能:

さらに精度を上げるため、LLMでクエリを拡張する機能も実装しています:

// クエリ拡張の例
expandQuery("認証")
// → ["認証", "ログイン", "セッション", "トークン", "JWT"]

// これらのキーワードで検索してマージ

階層要約生成

チャンク→ファイル→モジュール→システムの階層で要約を生成します。

階層構造

各レベルの責務:

レベル 粒度 内容 文字数
チャンク 関数/クラス 責務・入出力・副作用 100-200字
ファイル 1ファイル 役割・公開API・依存 200字
モジュール ディレクトリ 境界・責務分割・他モジュール依存 200字
システム プロジェクト全体 レイヤ構成・ドメイン境界 300字

生成プロセス

出力ドキュメント構造

docs/refactor/
├── overview.md              # システム全体の統計と概要
├── decisions.md             # ADRスタイルの設計判断ログ
├── summaries/
│   ├── files/               # ファイルごとの要約
│   ├── modules/             # モジュールごとの要約
│   ├── system.md            # システム全体の要約
│   └── hierarchy.json       # 構造化データ
├── modules/
│   └── {module}.md          # モジュールの詳細と公開API
├── files/
│   └── {path}.md            # ファイル役割・依存・チャンク一覧
└── plans/
    ├── milestones.md        # フェーズ別リファクタリング計画
    └── {query}.md           # 改善提案(クエリ指定時)

階層要約の利点

粒度に応じた情報取得:

  • システムレベル: 全体のアーキテクチャを把握したい
  • モジュールレベル: 特定の機能領域を理解したい
  • ファイルレベル: 実装の詳細を確認したい
  • チャンクレベル: 特定の関数を変更したい

コンテキスト最小化の実現:

必要な粒度の情報だけをLLMに渡すことで、コンテキストウィンドウを効率的に使えます。

例えば、「認証モジュールのリファクタリング計画を立てたい」という場合:

  1. システム要約で全体像を把握(300字)
  2. 認証モジュール要約で詳細を確認(200字)
  3. 関連ファイル要約で実装を理解(200字×3ファイル)

合計900字程度で必要な情報を取得(全文なら数万字)


使い方

インストール

git clone https://github.com/nogataka/refactor-doc-pipeline
cd refactor-doc-pipeline
npm install
npm run build

環境変数の設定

cp env.example .env

.envファイルを編集:

# OpenAI設定
OPENAI_PROVIDER=openai
OPENAI_API_KEY=sk-your-api-key

# モデル設定
OPENAI_SUMMARIZER_MODEL=gpt-4o-mini
OPENAI_DOC_MODEL=gpt-4o
OPENAI_EMBEDDING_MODEL=text-embedding-3-small

# LSP設定(対応言語をカンマ区切りで指定)
LSP_ENABLE_LANGS=ts,js,py,go,rust,java,cs,cpp

Azure OpenAIを使う場合:

OPENAI_PROVIDER=azure
AZURE_OPENAI_API_KEY=your-azure-key
AZURE_OPENAI_ENDPOINT=https://<resource>.openai.azure.com
OPENAI_API_VERSION=2024-05-01-preview

# デプロイ名をモデルとして指定
OPENAI_SUMMARIZER_MODEL=<chat-deployment-name>
OPENAI_EMBEDDING_MODEL=<embedding-deployment-name>

基本的な使い方

1. インデックス化

npm run index -- \
  --source ./your-project/src \
  --output ./output

処理内容:

  • ソースコードをチャンク化
  • 要約とメタデータを生成
  • ベクトル埋め込みを作成
  • JSONL形式で保存

オプション:

  • --no-lsp: LSP解析を無効化
  • --lsp-only: フォールバックを禁止(LSP解析のみ)
  • --chunk-size 6144: 固定長チャンクのサイズ指定

実行例:

$ npm run index -- --source ./src --output ./output

=== Indexing Pipeline Started ===
Source directory: /Users/you/project/src

[1/5] Finding files...
Found 150 files

[2/5] Chunking files...
  src/auth/login.ts: 5 chunks
  src/auth/register.ts: 3 chunks
  ...
Created 1,234 chunks

[3/5] Generating summaries...
  Progress: 100/1234
  Progress: 200/1234
  ...
Generated 1,234 summaries

[4/5] Extracting metadata...
Extracted metadata for 1,234 chunks

[5/5] Generating embeddings...
Generated 1,234 embeddings

=== Indexing Complete ===
Total documents: 1,234
Total files: 150
Average chunk size: 487 bytes
Index file size: 12 MB

2. 検索

npm run query -- \
  --query "認証処理" \
  --output ./output

処理内容:

  • 疎検索で200件に絞り込み
  • 密検索で上位20件を抽出
  • 結果をコンソールに表示

実行例:

$ npm run query -- --query "ユーザー認証" --output ./output

Starting query...
  Query: "ユーザー認証"
  Index: /Users/you/project/output

[1/2] Sparse search...
Indexed 1,234 documents for sparse search
Sparse search: query="ユーザー認証", found 187 results

[2/2] Dense search...
Dense search: query="ユーザー認証", re-ranked 187 → 20 results

=== Query Results ===

1. authenticate
   File: src/auth/login.ts:45
   Score: 0.9234
   Responsibility: ユーザー認証を行い、JWTトークンを生成する

2. validateCredentials
   File: src/auth/validator.ts:12
   Score: 0.8976
   Responsibility: 入力されたメールアドレスとパスワードの妥当性をチェック

...

✅ Found 20 results

3. 階層要約生成

npm run summarize -- \
  --output ./output

処理内容:

  • チャンク→ファイル→モジュール→システムの要約を生成
  • docs/refactor/summaries/ に保存

4. ドキュメント生成

npm run gendocs -- \
  --output ./output \
  --query "認証処理の改善"

処理内容:

  • 階層要約を活用してドキュメント一式を生成
  • クエリ指定時は改善提案も生成
  • docs/refactor/ に保存

5. 統計情報

npm run stats -- \
  --output ./output

スケーラビリティとパフォーマンス

コンテキスト最小化戦略

全文を埋め込まない:

  • 要約+シグネチャ+コード抜粋のみをベクトル化
  • 生成コードや型定義全量は対象外

階層要約:

  • チャンク→ファイル→モジュールと段階的に集約
  • 必要な粒度の情報だけを取り出せる

疎→密検索:

  • 大量の候補から効率的に絞り込み
  • 最終的に20件程度をLLMに渡す

バッチ処理によるスループット向上

// 埋め込み生成は100件ずつバッチ化
async embedBatch(chunks: CodeChunk[], summaries: ChunkSummary[]): Promise<Embedding[]> {
  const batchSize = 100;
  const embeddings: Embedding[] = [];

  for (let i = 0; i < chunks.length; i += batchSize) {
    const batch = chunks.slice(i, i + batchSize);
    const texts = batch.map((chunk, idx) => {
      const summary = summaries[i + idx];
      return `${chunk.signature}\n\n${summary.responsibility}\n${summary.inputOutput}`;
    });

    // バッチでAPI呼び出し
    const response = await this.openai.embeddings.create({
      model: this.embeddingModel,
      input: texts
    });

    // 結果を格納
    response.data.forEach((item, idx) => {
      embeddings.push({
        chunkId: batch[idx].id,
        text: texts[idx],
        vector: item.embedding
      });
    });

    console.log(`  Embedded ${i + batch.length}/${chunks.length}`);
  }

  return embeddings;
}

バッチ処理の効果:

  • API呼び出し回数: 10,000回 → 100回(1/100)
  • 処理時間: 約50分 → 約5分(1/10)
  • レート制限回避: 安定した処理

差分更新による継続的メンテナンス

// Git差分から変更ファイルのみ再インデックス
async updateDiff(sourceDir: string, changedFiles: string[]): Promise<void> {
  console.log(`\n=== Updating ${changedFiles.length} changed files ===`);

  // 変更ファイルのみ処理
  const chunks = await this.chunkFiles(changedFiles);
  const summaries = await this.generateSummaries(chunks);
  const metadataList = await this.extractMetadata(chunks);
  const embeddings = await this.embedder.embedBatch(chunks, summaries);

  const newDocuments = this.buildDocuments(
    chunks,
    summaries,
    metadataList,
    embeddings
  );

  // 既存インデックスから該当ファイルを削除して新データを追加
  await this.storage.updateDiff(changedFiles, newDocuments);

  console.log('=== Update Complete ===');
}

差分更新の利点:

項目 全体再インデックス 差分更新
処理時間 10分 10秒(変更1%の場合)
APIコスト $2 $0.02
更新頻度 週1回 毎日可能

コスト見積もり

中規模プロジェクト(500ファイル、10,000チャンク)

初回インデックス化:

  • 要約生成: 10,000回 × $0.15/1M tokens ≈ $1.5
  • 埋め込み: 10,000回 × $0.02/1M tokens ≈ $0.2
  • 合計: 約$2

検索:

  • クエリ埋め込み: 1回 × $0.02/1M tokens ≈ $0.0001
  • クエリごとに約 $0.001(クエリ拡張含む)

ドキュメント生成:

  • 階層要約: 約 $0.5
  • 詳細ドキュメント: 約 $1-2

差分更新(変更率1%の場合):

  • 要約生成: 100回 × $0.15/1M tokens ≈ $0.015
  • 埋め込み: 100回 × $0.02/1M tokens ≈ $0.002
  • 合計: 約$0.02

月間運用コスト例:

項目 頻度 コスト
初回インデックス 1回 $2
差分更新(平均変更率3%) 20回/月 $1.2
検索 100回/月 $0.1
ドキュメント生成 4回/月 $6
月間合計 - 約$9

拡張性

ベクトルDB対応

現在はインメモリですが、ベクトルDBへの移行が容易です:

// Qdrant統合例
class QdrantDenseSearch extends DenseSearch {
  private qdrantClient: QdrantClient;

  constructor(client: OpenAI, qdrantUrl: string) {
    super(client, models);
    this.qdrantClient = new QdrantClient({ url: qdrantUrl });
  }

  async search(query: string, topK: number): Promise<SearchResult[]> {
    // クエリをベクトル化
    const queryVector = await this.embedQuery(query);

    // Qdrantで検索
    const results = await this.qdrantClient.search('code_chunks', {
      vector: queryVector,
      limit: topK,
      with_payload: true
    });

    // SearchResult形式に変換
    return results.map(result => ({
      document: result.payload as IndexedDocument,
      score: result.score,
      method: 'dense'
    }));
  }
}

対応可能なベクトルDB:

  • Qdrant(推奨)
  • Weaviate
  • Chroma
  • Pinecone
  • Milvus

新言語の追加

言語サーバーを追加するだけで対応言語を拡張できます:

// config/lsp.ts
export const LANGUAGE_REGISTRATIONS: LanguageRegistration[] = [
  // 既存の言語...
  {
    language: 'ruby',
    extensions: ['rb'],
    defaultLanguageId: 'ruby',
    defaultCommand: 'solargraph',
    defaultArgs: ['stdio']
  },
  {
    language: 'php',
    extensions: ['php'],
    defaultLanguageId: 'php',
    defaultCommand: 'intelephense',
    defaultArgs: ['--stdio']
  }
];

カスタム検索戦略

// カスタム検索戦略の実装
interface SearchStrategy {
  search(query: string, documents: IndexedDocument[], topK: number): Promise<SearchResult[]>;
}

class SemanticCodeSearch implements SearchStrategy {
  async search(query: string, documents: IndexedDocument[], topK: number): Promise<SearchResult[]> {
    // 1. クエリを構造化
    const structuredQuery = await this.structureQuery(query);

    // 2. 複数の観点で検索
    const byName = await this.searchByName(structuredQuery.name, documents);
    const byPurpose = await this.searchByPurpose(structuredQuery.purpose, documents);
    const bySignature = await this.searchBySignature(structuredQuery.signature, documents);

    // 3. スコアを統合
    const merged = this.mergeResults([byName, byPurpose, bySignature]);

    return merged.slice(0, topK);
  }
}

まとめ

このシステムの主な強みは以下の通りです:

1. LSP統合による高度な解析 🔍

IDEと同等のセマンティック理解により、正確なメタデータ抽出を実現。

  • シンボル情報の正確な取得
  • 参照関係の完全な追跡
  • 型情報の活用
  • 8言語以上に対応

2. 3段階フォールバック戦略 🛡️

LSP → ts-morph → 正規表現で、あらゆる言語・コードに対応。

  • 言語サーバーが利用できない環境でも動作
  • 段階的に精度を保ちながらフォールバック
  • インテリジェントなマージで情報を補完

3. 2フェーズ設計 📊

事前処理と検索を分離し、LLMコンテキストを効率化。

  • データストア作成は一度だけ
  • 検索は高速かつ低コスト
  • 差分更新で継続的メンテナンス

4. ハイブリッド検索 🔎

疎検索と密検索を組み合わせて、精度と速度を両立。

  • BM25で高速に候補を絞り込み
  • ベクトル類似度で意味的に再ランキング
  • クエリ拡張で検索精度を向上

5. 階層要約 📚

チャンク→ファイル→モジュール→システムと段階的に集約。

  • 粒度に応じた情報取得
  • コンテキスト最小化
  • 必要十分な情報のみをLLMに提供

6. JSONL永続化 💾

差分更新が容易で、大規模データに対応。

  • ストリーム処理可能
  • 追記が容易
  • 並列処理対応

7. 高い拡張性 🚀

ベクトルDB、カスタム検索、新言語の追加が容易。

  • プラグイン可能なアーキテクチャ
  • 標準プロトコル(LSP)の活用
  • モジュール化された設計

おわりに

大規模コードベースのリファクタリングは、適切なアーキテクチャがあれば効率的に行えます。

このシステムでは、LSP統合ハイブリッド検索階層要約という3つの柱により、LLMのコンテキストを効率的に使いながら、高精度な解析とドキュメント生成を実現しました。

実装のポイント:

  • 全文を埋め込まず、要約+シグネチャのみをベクトル化
  • LSPでIDEと同等のセマンティック理解
  • 疎→密のハイブリッド検索で精度と速度を両立
  • 階層要約で必要な粒度の情報を提供
  • JSONL形式で差分更新を容易に

ぜひ、あなたのプロジェクトでも試してみてください!

GitHub リポジトリ: https://github.com/nogataka/refactor-doc-pipeline

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?