From a3d3ba426c2a5a6015888d5e32e0f9704b4d125c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 24 九月 2026 14:50:34 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_天津_建诚恒商贸' into dev_天津_建诚恒商贸

---
 src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java |   76 ++++++++++++++++++++++++++++++++++++--
 1 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java b/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
index eaa487c..a31c540 100644
--- a/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
+++ b/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
@@ -7,15 +7,26 @@
 import dev.langchain4j.store.embedding.pinecone.PineconeServerlessIndexConfig;
 import io.pinecone.clients.Index;
 import io.pinecone.clients.Pinecone;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import java.io.IOException;
+import java.util.function.Supplier;
+
 /**
  * 鍚戦噺瀛樺偍閰嶇疆
  */
+@Slf4j
 @Configuration
 public class EmbeddingStoreConfig {
+
+    /** Pinecone 杩炴帴閲嶈瘯娆℃暟锛氭瀯閫犳湡浼氬悓姝ュ彂璧� HTTP/2 鎻℃墜锛屽伓鍙� "Required SETTINGS preface not received" */
+    private static final int CONNECT_ATTEMPTS = 3;
+
+    /** 閲嶈瘯闂撮殧鍩烘暟锛堟绉掞級锛屾寜娆℃暟绾挎�ч��閬� */
+    private static final long RETRY_BACKOFF_MILLIS = 1500L;
 
     @Value("${pinecone.api-key:pcsk_4SJLnh_tNB3wSLJU8tc4E5P28PcXX8eCLdURqZpVhg1FMV8CRYxjneWdzqRdB5Ftqooi9}")
     private String pineconeApiKey;
@@ -33,12 +44,12 @@
 
     @Bean
     public Index pineconeIndex(Pinecone pinecone) {
-        return pinecone.getIndexConnection(indexName);
+        return retry("鑾峰彇 Pinecone 绱㈠紩杩炴帴", () -> pinecone.getIndexConnection(indexName));
     }
 
     @Bean
     public EmbeddingStore<TextSegment> embeddingStore(EmbeddingModel embeddingModel) {
-        return PineconeEmbeddingStore.builder()
+        return retry("鍒濆鍖� Pinecone 鍚戦噺瀛樺偍", () -> PineconeEmbeddingStore.builder()
                 .apiKey(pineconeApiKey)
                 .index(indexName)
                 .nameSpace(namespace)
@@ -47,6 +58,63 @@
                         .region("us-east-1")
                         .dimension(embeddingModel.dimension())
                         .build())
-                .build();
+                .build());
     }
-}
\ No newline at end of file
+
+    /**
+     * 瀵� Pinecone 鍚姩鏈熻繛鎺ュ仛鏈夐檺娆¢噸璇曘��
+     * <p>
+     * Pinecone 瀹㈡埛绔浐瀹氳蛋 HTTP/2锛屾瀯閫� EmbeddingStore / 鑾峰彇绱㈠紩杩炴帴鏃朵細鍚屾璋冪敤鎺у埗闈㈡帴鍙o紝
+     * 鍋跺彂鎻℃墜澶辫触锛坖ava.io.IOException: Required SETTINGS preface not received锛変細浠ヨ繍琛屾椂寮傚父鎶涘嚭锛�
+     * 鐩存帴瀵艰嚧鏁翠釜 ApplicationContext 鍔犺浇澶辫触銆傝繖閲屼粎瀵硅繖绫荤灛鏃剁綉缁滃紓甯搁噸璇曪紝鍏跺畠寮傚父绔嬪嵆鎶涘嚭銆�
+     */
+    private <T> T retry(String action, Supplier<T> supplier) {
+        RuntimeException last = null;
+        for (int attempt = 1; attempt <= CONNECT_ATTEMPTS; attempt++) {
+            try {
+                return supplier.get();
+            } catch (RuntimeException e) {
+                if (!isTransientConnectFailure(e) || attempt == CONNECT_ATTEMPTS) {
+                    throw e;
+                }
+                last = e;
+                log.warn("{}澶辫触锛堢 {}/{} 娆★級锛寋}ms 鍚庨噸璇曪細{}",
+                        action, attempt, CONNECT_ATTEMPTS, RETRY_BACKOFF_MILLIS * attempt, rootMessage(e));
+                sleep(RETRY_BACKOFF_MILLIS * attempt);
+            }
+        }
+        throw last;
+    }
+
+    /** 娌� cause 閾惧垽鏂槸鍚︿负 HTTP/2 鎻℃墜绫荤殑鐬椂杩炴帴澶辫触 */
+    private boolean isTransientConnectFailure(Throwable e) {
+        for (Throwable t = e; t != null; t = t.getCause()) {
+            if (t instanceof IOException) {
+                return true;
+            }
+            String message = t.getMessage();
+            if (message != null && (message.contains("SETTINGS preface")
+                    || message.contains("Connection reset")
+                    || message.contains("timed out"))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String rootMessage(Throwable e) {
+        Throwable root = e;
+        while (root.getCause() != null && root.getCause() != root) {
+            root = root.getCause();
+        }
+        return root.getMessage();
+    }
+
+    private void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+}

--
Gitblit v1.9.3