SpringAIで、テキストをエンベディングしてPineconeに登録してみたが。

## はじめに
SpringAIは、SpringBootを活用してAIアプリケーションを開発するためのフレームワークで、AIモデルプロバイダーや、ベクトルデータベースプロバイダーとの接続をサポートしている。この度は、OpenAIのモデルでテキストのエンベディングを行い、ベクトルデータベースPineconeに生成したベクトルの登録を確認した。
【参考】
https://docs.spring.io/spring-ai/reference/index.html

の情報では、プロパティファイルに接続情報を記載するのみで動作するはずだが、Embeddingがうまくできず、Javaのクラスに接続情報を記載した。

## 環境
* Windows11
* Java 17
* SpringAI 1.0.0-SNAPSHOT

## 事前に必要なもの
* PineconeのIndex
* エンドポイントのURL
* APIキー
* OpenAIのAPIキー

## 依存関係の追加
Mavenプロジェクトで実行しており、pom.xmlの依存関係には、次のものを追加した。
```xml
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-transformers</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

		<dependency>
    		<groupId>org.springframework.ai</groupId>
    		<artifactId>spring-ai-openai</artifactId>
			<version>1.0.0-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
			<version>1.0.0-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-pinecone-store</artifactId>
			<version>1.0.0-SNAPSHOT</version>
		</dependency>
```

## Pinecone接続情報の設定
次の4つの情報が必要。Severlessの場合、Environmentの情報がわかりにくかったが、エンドポイントのURLからAPIキー以外の情報が取得できる。
* APIキー
* Environment
* ProjectID
* 作成したIndex名

接続先のエンドポイントのURLは、以下のような形式。
```bash
https://<Index名>-<ProjectID>.svc.<Environment>.pinecone.io
```

Pinecone設定用のConfigクラスに、必要な情報を設定した。
【Pineconeの接続情報の設定】
```java

   @Bean
    public PineconeVectorStoreConfig pineconeConfig() {

        return PineconeVectorStoreConfig.builder()
            .withApiKey("<APIキー>")
            .withEnvironment("<Environment>")
            .withProjectId("<ProjectID>")
            .withIndexName("<Index名>")
            .withNamespace("") // the free tier doesn't support namespaces.
            .build();
    }
```

テキストのエンベディングのため、EmbeddingModelを指定。使用モデルは、「text-embedding-ada-002」を指定した。プロパティの設定のみでできるかと思うが、うまくいかず、結局OpenAiEmbeddingModel、OpenAiEmbeddingOptionsを使用した。
【EmbeddingModelの作成】
```java
    @Bean
    EmbeddingModel customEmbeddingModel(){
        return new OpenAiEmbeddingModel(
            new OpenAiApi(System.getenv("SPRING_AI_OPENAI_API_KEY")),
            MetadataMode.EMBED,
            OpenAiEmbeddingOptions.builder()
                    .withModel("text-embedding-ada-002")
                    .build(),
            RetryUtils.DEFAULT_RETRY_TEMPLATE);
    }
```

PineconeVectorStoreにcustomEmbeddingModelを登録することで、vectoreStoreで、エンベディングを行い、Pineconeにベクトルを登録できる。
【VectorStoreクラスの生成】
```java
private EmbeddingModel customEmbeddingModel;
    private VectorStore vectorStore ;

    public TextEmbeddingService(EmbeddingModel customEmbeddingModel,PineconeVectorStoreConfig pineconeConfig) {
        this.customEmbeddingModel = customEmbeddingModel;
        this.vectorStore = new PineconeVectorStore(pineconeConfig, customEmbeddingModel);
	}
```

Documentクラスに、エンベディングするテキストを設定し、vectorStore.add(documents)により、Pineconeに登録できる。
Metadataを登録する場合、DocumentクラスにMapクラスで登録する。
【データ登録】
```java
        List <Document> documents = List.of(
            new Document("TEST123 This is a test1", Map.of("meta1", "meta2")),
            new Document("TEST123 This is a test2"),
            new Document("TEST123 This is a test3.", Map.of("meta3", "meta4")));
        
            // Add the documents
        vectorStore.add(documents);
```
Documentクラスにエンベディングしたい文字列を設定すると、ベクトルに変換されてPineconeに登録されるが、設定した文字列も、METADATAとして、document_contentに登録される。

【Pineconeコンソール抜粋】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284235/f45cc280-43d5-e425-7d24-1f95dda2204f.png)

Pineconeを検索する場合、similaritySearch関数により指定したtextの類似項目を取得することができる。
```java
    public List<Document> getSimilarDocuments(String text, int topK) {
        return  vectorStore.similaritySearch(SearchRequest.query(text).withTopK(topK));
    }   

```
## おわりに
接続情報をソースコードに記載せずとも動作するはずだが、うまくいかなかった。