DeepSeek R1を活用したプライベート知識ベース構築手法

DeepSeek R1はその優れた推論チェーン処理能力で注目を集めていますが、Spring AIなどの主要フレームワークでは流式出力や思考過程の保持に制限があり、十分なサポートが提供されていません。deepseek4j 1.4では、ベクトルモデル対応など重要な機能拡張が実装されました。

設計思想

このライブラリは、DeepSeek APIの利用を簡素化し、Function CallingやJSONパース、Reasonerといった高度な機能を容易にアプリケーションへ統合できるように設計されています。ただし、DeepSeek公式にはベクトルエンコーダーが存在しないため、当初はベクトル検索機能は含まれていませんでした。

技術的選択

現在、R1モデル上でのプライベートナレッジベース構築ニーズが高まっており、私たちはOpenAI互換プロトコルを介してベクトルモデルを統合するアプローチを採用しました。これにより以下の利点が得られます:

  • 軽量性:追加依存ライブラリ不要
  • 互換性:既存コードとの衝突なし
  • 標準化:業界標準プロトコルによる学習コスト低減

環境構築

Ollama モデル設定

# 推論用モデル
ollama run deepseek-r1:14b

# 埋め込み用モデル
ollama run bge-m3:latest

Milvus データベース起動

# スクリプト取得
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh

# 起動
bash standalone_embed.sh start

Maven 依存関係

<dependency>
    <groupId>io.github.pig-mesh.ai</groupId>
    <artifactId>deepseek-spring-boot-starter</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>io.milvus</groupId>
    <artifactId>milvus-sdk-java</artifactId>
    <version>2.5.3</version>
</dependency>

application.yml 設定例

deepseek:
  base-url: http://localhost:11434/v1
  model: deepseek-r1:14b
  api-key: ollama-local

embedding:
  api-key: ${deepseek.api-key}
  base-url: ${deepseek.base-url}
  model: bge-m3:latest

ナレッジベース初期化

データベース接続

ConnectConfig config = ConnectConfig.builder()
        .uri("YOUR_CLUSTER_ENDPOINT")
        .token("YOUR_TOKEN")
        .build();

MilvusClientV2 client = new MilvusClientV2(config);

テキストのベクトル化と登録

@Autowired
private EmbeddingClient embedder;

public void indexDocuments() {
    String content = Files.readString(Paths.get("/path/to/doc.txt"));
    List<String> chunks = splitText(content, 400);

    List<JsonObject> records = chunks.stream().map(chunk -> {
        List<Float> vector = embedder.embed(chunk);
        
        JsonObject obj = new JsonObject();
        JsonArray vecArray = new JsonArray();
        vector.forEach(vecArray::add);
        
        obj.add("vector", vecArray);
        obj.addProperty("content", chunk);
        return obj;
    }).collect(Collectors.toList());

    InsertReq req = InsertReq.builder()
            .collectionName("knowledge_base_v1")
            .data(records)
            .build();

    client.insert(req);
}

RAGエンドポイント実装

@GetMapping(value = "/ask", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ChatCompletionResponse> handleQuery(@RequestParam String question) {
    List<Float> queryVec = embeddingClient.embed(question);

    SearchReq searchReq = SearchReq.builder()
            .collectionName("knowledge_base_v1")
            .data(Collections.singletonList(new FloatVec(queryVec)))
            .outputFields(List.of("content"))
            .topK(3)
            .build();

    SearchResp results = milvusClient.search(searchReq);
    
    String context = results.getSearchResults().stream()
        .flatMap(List::stream)
        .map(r -> r.getEntity().get("content").toString())
        .collect(Collectors.joining("\n---\n"));

    ChatCompletionRequest chatReq = ChatCompletionRequest.builder()
            .model("deepseek-r1:14b")
            .addUserMessage(
                "以下の参考情報を基に、ユーザーの質問に答えてください。\n\n" +
                "【質問】\n" + question + "\n\n" +
                "【参考情報】\n" + context
            )
            .build();

    return deepSeekClient.chatFluxCompletion(chatReq);
}

改善ポイント

  • ハイブリッド検索:キーワード+ベクトルの併用
  • 結果再ランキング:関連度スコアによる並び替え
  • Prompt最適化:指示文の精密化とフォーマット統一
  • 定期更新:ナレッジベースの陳腐化防止
  • キャッシュ戦略:高頻度クエリのレスポンス高速化

タグ: DeepSeek RAG Milvus Ollama SpringBoot

7月17日 16:30 投稿