OllamaローカルモデルとSpring AI Alibabaを組み合わせて次世代のRAGアプリケーションを構築する

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/combining-the-ollama-local-model-with-spring-ai-alibaba-to-create-the-next-generation-of-rag-applications_602075?spm=a2c65.11461433.0.0.4ee35355MSxyUr)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

Musheng 作成

Spring AI Alibaba RAG サンプルプロジェクトのソースコードアドレス: [https://github.com/springaialibaba/spring-ai-alibaba-examples/tree/main/spring-ai-alibaba-rag-example](https://github.com/springaialibaba/spring-ai-alibaba-examples/tree/main/spring-ai-alibaba-rag-example)

## 1. RAG アプリケーションアーキテクチャの概要

### 1.1 コアコンポーネント
- **Spring AI**: Springエコシステム向けのJava AI開発フレームワークで、大規模モデルやベクトルデータベース、その他のAIインフラにアクセスするための一元化されたAPIを提供します。
- **Ollama**: ローカルモデル実行エンジン（Dockerに類似）で、オープンソースモデルの迅速なデプロイをサポートします。
- **Spring AI Alibaba**: Spring AIの拡張版で、DashScopeモデルプラットフォームを統合しています。
- **Elasticsearch**: テキストのベクトル化データを保存し、意味検索をサポートするベクトルデータベースです。

### 1.2 モデル選択

- **Embedding モデル**: `nomic-embed-text:latest`（テキストデータのベクトル化に使用）
- **Ollama Chat モデル**: `deepseek-r1:8b`（最終的な回答を生成）

---

## 2. 環境準備

### 2.1 Ollama サービスの起動
Docker Composeを使用してOllamaを起動します。（同時に、Ollamaモデルと対話するためのフロントエンドシステムも起動します。）yaml
services:
  ollama:
    container_name: ollama
    image: ollama/ollama:latest
    ports:
      - 11434:11434

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    ports:
      - 3005:8080
    environment:
      - OLLAMA_BASE_URL=http://host.docker.internal:11434
    extra_hosts:
      - host.docker.internal:host-gateway

### 2.2 モデルのダウンロード
以下のコマンドを実行します：bash
docker exec -it ollama ollama pull deepseek-r1:8b
docker exec -it ollama ollama pull nomic-embed-text:latest
open-webuiで`deepseek-r1:8b`モデルを呼び出します：
![1](https://yqintl.alicdn.com/f538f76cfb56321569f707fbb81fd3200c03ba54.png)

### 2.3 Elasticsearch のデプロイyaml
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.16.1
    container_name: elasticsearch
    privileged: true
    environment:
      - cluster.name=elasticsearch
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms512m -Xmx1096m
      - bootstrap.memory_lock=true
    volumes:
      - ./config/es.yaml:/usr/share/elasticsearch/config/elasticsearch.yml
    ports:
      - 9200:9200
      - 9300:9300
    deploy:
      resources:
        limits:
          cpus: 2
          memory: 1000M
        reservations:
          memory: 200M

esの起動用設定ファイルを準備します：yaml
cluster.name: docker-es
node.name: es-node-1
network.host: 0.0.0.0
network.publish_host: 0.0.0.0
http.port: 9200
http.cors.enabled: true
http.cors.allow-origin: "*"
bootstrap.memory_lock: true

# 認証を無効化（デフォルトではes 8.xで有効）
xpack.security.enabled: false

これで、シンプルなRAGアプリケーションを構築するための環境準備がすべて完了しました。次に、プロジェクトのセットアップを始めましょう。

---

## 3. プロジェクト設定

### 3.1 依存関係の導入xml
<!-- Spring Boot Web Starter -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>3.3.4</version>
</dependency>

<!-- Spring AI Ollama Starter -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
  <version>1.0.0-M5</version>
</dependency>

<!-- ベクトルストレージ -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-elasticsearch-store</artifactId>
  <version>1.0.0-M5</version>
</dependency>

<!-- PDF解析 -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-pdf-document-reader</artifactId>
  <version>1.0.0-M5</version>
</dependency>

### 3.2 コア設定yaml
spring:
  ai:
    # ollama設定
    ollama:
      base-url: http://127.0.0.1:11434
      chat:
        model: deepseek-r1:8b
      embedding:
        model: nomic-embed-text:latest
  
    # ベクトルデータベース設定
    vectorstore:
      elasticsearch:
        index-name: ollama-rag-embedding-index
        similarity: cosine
        dimensions: 768

  elasticsearch:
    uris: http://127.0.0.1:9200

ここで：
- `index-name`はesのベクトルインデックス名です。
- `dimensions`はベクトルモデルによって生成されるベクトルの次元数（ベクトルモデルによって生成される次元数と一致する必要があり、デフォルトは1576です）。
- `similarity`はベクトル間の類似度を測定するために使用されるアルゴリズムまたはメトリックを定義します。ここでは高次元疎ベクトルに適したコサイン類似度を使用しています。

カスタマイズされたesのインスタンス化設定を行うには、`spring-ai-elasticsearch-store`を導入します：xml
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-elasticsearch-store</artifactId>
  <version>1.0.0-M5</version>
</dependency>
プロジェクト内でカスタム設定ビーンを通じて実装します。

### 3.3 プロンプトテンプレート
あなたはMacOSの専門家です。次の文脈に基づいて回答してください：

---------------------
{question_answer_context}
---------------------

与えられた文脈と提供された履歴情報を組み合わせて中国語Markdown形式で回答してください。文脈に答えがない場合は、それを明確にしてください。

---

## 4. コア実装

### 4.1 テキストのベクトル化
Spring AIおよびSpring AI Alibabaでは、ほぼすべてのデータソースをナレッジベースソースとして使用できます。この例では、PDFをナレッジベースドキュメントとして使用しています。Spring AI Alibabaは、40以上のドキュメントリーダーおよびパーサープラグインを提供しており、RAGアプリケーションにデータをロードすることができます。java
public class KnowledgeInitializer implements ApplicationRunner {

    // VectorStoreインスタンスを注入し、ベクトル化データの増加およびクエリ操作を担当
    private final VectorStore vectorStore;

    // ベクトルデータベースクライアント、ここではesを使用
    private final ElasticsearchClient elasticsearchClient;

    // .....

    @Override
    public void run(ApplicationArguments args) {

        // 1. pdfリソースをロード
        List<Resource> pdfResources = loadPdfResources();

        // 2. pdfリソースをDocumentsに解析
        List<Document> documents = parsePdfResource(pdfResources);

        // 3. ESにインポート
        importToES(documents);
    }

    private List<Document> parsePdfResource(List<Resource> pdfResources) {

        // 指定されたポリシーに基づいてテキストを分割し、Documentリ
# RAGアプリケーションの構築と最適化

## 4.3 RAGサービスインターフェース層
ユーザーからのリクエストを処理し、大規模モデルのレスポンスを取得するためのユーザー向けリクエストインターフェースを作成します。java
@RestController
@RequestMapping(/rag/ai)
public class AIRagController {

    @Resource
    public AIRagService aiRagService;

    @GetMapping(/chat/{prompt})
    public Flux<String> chat(
        @PathVariable(prompt) String prompt,
        HttpServletResponse response
    ) {

        // ストリーム応答が文字化けしないようにレスポンスのエンコーディングを設定します。
        response.setCharacterEncoding(UTF-8);

        if (!StringUtils.hasText(prompt)) {
            return Flux.just(prompt is null.);
        }

        return aiRagService.retrieve(prompt);
    }
}

## 5. リクエストデモンストレーション
以下は例です：私は現在Mac初心者で、Macのトラックパッドをより使いやすく設定したいと考えています。何か提案はありますか？この例からわかるように、モデルからの直接的なレスポンスは比較的公式で、実用的ではありません。

### 5.1 open-webuiからの直接呼び出し
! [2](https://yqintl.alicdn.com/c38d3c5f18c3328eeb04bee915c798cea4fb49e4.png)

### 5.2 RAGアプリケーションインターフェースの呼び出し
! [3](https://yqintl.alicdn.com/9345feaee0074a6c4cab0b829d7d72cf2afde369.png)

RAGアプリケーションの出力はより正確であり、ユーザーのニーズに合致していることがわかります。

## 6. RAGの最適化

### 6.1 DashScopeプラットフォームモデルの使用
ローカルOllamaを使用してモデルサービスを展開する場合、モデルの実行速度はローカルリソース制約によって制限され、処理時間が長くなることがあります。したがって、クラウドプラットフォーム上にあるモデルを利用することでユーザーエクスペリエンスを向上させることができます。

`application.yaml`を次のように修正します：yaml
spring:
  application:
    name: ollama-rag

  ai:
   dashscope:
     api-key: ${AI_DASHSCOPE_API_KEY}
     chat:
       options:
         model: deepseek-r1
     embedding:
       enabled: false

    ollama:
      base-url: http://127.0.0.1:11434
      chat:
        model: deepseek-r1:8b
        enabled: false
      embedding:
        model: nomic-embed-text:latest

    vectorstore:
      elasticsearch:
        index-name: ollama-rag-embedding-index
        similarity: cosine
        dimensions: 768

  elasticsearch:
    uris: http://127.0.0.1:9200

これにより、Ollamaのチャット機能が無効になり、Spring AI Alibaba Starter依存関係を通じてDashScopeプラットフォーム上のDeepSeekR1モデルが使用されます。

依存関係を追加します：xml
<!-- Spring AI Alibaba DashScope -->
<dependency>
  <groupId>com.alibaba.cloud.ai</groupId>
  <artifactId>spring-ai-alibaba-starter</artifactId>
  <version>1.0.0-M6</version>
</dependency>

`AIRAGService.java`を編集します：java
public Flux<String> retrieve(String prompt) {

    // ベクトルストアプロンプトテンプレートを取得します。
    String promptTemplate = getPromptTemplate(systemResource);

    // ハイブリッド検索（埋め込みおよび全文検索）を有効にします。
    SearchRequest searchRequest = SearchRequest.builder()
            .topK(4)
            .similarityThresholdAll()
            .build();

     // 検索結果再ランキングアドバイザー付きのChatClientを構築します：
     ChatClient runtimeChatClient = ChatClient.builder(chatModel)
            .defaultAdvisors(new RetrievalRerankAdvisor(
                vectorStore, 
                rerankModel, 
                searchRequest, 
                promptTemplate, 
                0.1)
            ).build();

     // Spring AI RetrievalAugmentationAdvisor
     Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
            .queryTransformers(RewriteQueryTransformer.builder()
                    .chatClientBuilder(ChatClient.builder(ragChatModel).build().mutate())
                    .build())
            .documentRetriever(VectorStoreDocumentRetriever.builder()
                    .similarityThreshold(0.50)
                    .vectorStore(vectorStore)
                    .build())
            .build();

     // データの検索とLLM生成
     return ragClient.prompt()
            .advisors(retrievalAugmentationAdvisor)
            .user(prompt)
            .stream()
            .content();
}

### 6.2 検索の最適化
Spring AI RAG ドキュメント：<a href=https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html target=_blank>https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html</a>

Spring AI 構造化ドキュメント：<a href=https://java2ai.com/docs/1.0.0-M5.1/tutorials/rag/ target=_blank>https://java2ai.com/docs/1.0.0-M5.1/tutorials/rag/</a>

Spring AIを使用してRAGアプリケーションを構築する際、QuestionAnswerAdvisorを構築する際にパーソナライズされたパラメーターを設定することで、ベクトルデータの検索において最適なパフォーマンスを達成できます。

### 6.3 データ前処理の最適化
データ前処理フェーズでは、次の操作を行うことができます：

- 関連のない文書を削除する。
- ノイズデータや特殊文字などをテキストデータからクリーンアップする。
- インデックスデータの品質を向上させるためにメタデータを追加する。
- インデックス構造を最適化する。

## 7. 問題のトラブルシューティング
**Q:** ベクトル挿入に失敗しました  
**A:** ESインデックスの次元がモデル出力と一致しているか確認してください。

**Q:** 検索結果が関連性がない  
**A:** 埋め込みモデルがテキストタイプと一致しているか確認してください。

**Q:** 応答速度が遅い  
**A:** Ollamaの計算リソース設定を調整してください。

**Q:** spring-ai-alibaba-starter依存関係の取得に失敗しました  
**A:** mvnリポジトリの設定が必要です。xml
<repositories>
  <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/milestone</url>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
  <repository>
    <id>spring-snapshots</id>
    <name>Spring Snapshots</name>
    <url>https://repo.spring.io/snapshot</url>
    <releases>
      <enabled>false</enabled>
    </releases>
  </repository>
</repositories>

## 8. まとめ
RAGアプリケーションを構築する全体のプロセスは、以下の3つのステップに分けることができます：

1. **データの読み込みとクリーニング**: 外部ナレッジベースからデータを読み込み、ベクトル化後にElasticsearchに保存します。
2. **モデル呼び出しの最適化**: 検索強化技術（RAG）を使用して大規模モデルにコンテキスト情報を提供します。
3. **インタラクティブサービスの構築**: REST APIを構築し、アプリケーションとユーザー間での効率的な対話を実現します。

RAGによる検索強化により、モデルの応答がより文脈的に関連性を持つようになり、最終的にユーザーエクスペリエンスが向上します。