编程导航踩坑记录话题讨论

踩坑记录

8 参与
分享

快来分享你的内容吧~

点击登录,快来和大家讨论吧~
表情
图片
话题
打卡
综合
交流
文章
问答

AI超级智能体项目_踩坑记录_PGVector维度冲突

# 踩坑记录:Spring AI + PGVector 维度不一致导致 DataIntegrityViolationException > **日期**:2026-07-23 > **作者**:puipui > **影响范围**:`pui-ai-agent` RAG 模块 / PGVector 向量存储 --- ## 一、问题现象 在本地 / 测试环境启动 `PgVectorVectorStoreConfigTest` 或主程序时,出现以下异常: ```text org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO public.vector_store ...]; ERROR: expected 1536 dimensions, not 1024 ``` ### 表现特征 - 有时第一次启动成功,重启后立刻失败 - 报错维度在 `1536 / 1024 / 1532` 之间反复变化 - 日志中出现 `Is empty: false`,说明表已存在但维度不匹配 --- ## 二、环境信息 | 组件 | 版本 | |---|---| | Spring Boot | 3.5.7 | | Spring AI | 1.1.2 | | PGVector (Java) | 0.1.6 | | PostgreSQL (阿里云 RDS) | 15.x | | Embedding 模型 | DashScope(通义千问) | | JDK | 21 | --- ## 三、根因分析 ### 3.1 一句话版 > **DashScope Embedding 模型未显式指定 → 输出维度不稳定 → PGVector 表维度不可变 → 维度冲突。** --- ### 3.2 PGVector 的硬性约束 ```sql embedding vector(N) ``` - `N` 在建表时确定,**后续无法 ALTER** - Spring AI 不会自动迁移或重建已有表 - 一旦维度对不上,**100% 报错** --- ### 3.3 DashScope Embedding 模型维度不固定 | 模型 | 输出维度 | 是否稳定 | |---|---|---| | `text-embedding-v1` | 1536 | ✅ | | `text-embedding-v2` | 1536 | ✅ | | `text-embedding-v3` | 1024 / 1536 / 2048 | ❌ | | **未显式指定** | SDK 自动选择 | ❌ 不稳定 | Spring AI 未指定模型时: - 首次启动可能输出 `1024` - 后续启动可能输出 `1536` - → "刚成功又失败"的元凶 --- ### 3.4 Spring AI 的建表策略 ```java if (table exists) { // 直接复用已有表结构,完全无视 dimensions 配置 reuse existing schema; } else { // 只有表不存在时才用 dimensions 建表 create table using dimensions; } ``` **关键结论**:只要表存在,`application.yml` 里的 `dimensions: 1024` 配置**完全失效**。 --- ### 3.5 阿里云 RDS 的 schema / search_path 干扰 - 默认 `search_path` 可能包含多个 schema - Spring AI 实际使用的表,可能与 DMS 中看到的不是同一张 - 典型表现:"我明明删了表,为什么还在报错" --- ## 四、排查过程中的典型误区 | 误区 | 真实情况 | |---|---| | 以为是 Mockito / JDK 21 问题 | 完全无关,可忽略 | | 只在 `application.yml` 配置 dimensions | profile 环境下被覆盖,未生效 | | 在 `postgres` 默认库删表 | 实际连接的是业务库 `pui_loveAi_agent` | | 只看表是否存在 | 未检查实际维度是否一致 | | 多次重启不清理缓存 | IDEA / Spring Test 上下文复用旧配置 | | 删表后仍然报错 | 另一个 schema 里还有同名表 | --- ## 五、最终解决方案(✅ 已验证) ### Step 1:显式固定 Embedding 模型(最关键) ```yaml spring: ai: dashscope: embedding: options: model: text-embedding-v1 # ✅ 固定 1536 维 ``` --- ### Step 2:维度与模型强绑定 ```yaml spring: ai: vectorstore: pgvector: dimensions: 1536 schema: public initialize-schema: true index-type: HNSW distance-type: COSINE_DISTANCE max-document-batch-size: 10000 ``` --- ### Step 3:彻底清理旧表 在阿里云 DMS / Navicat 中执行: ```sql DROP TABLE IF EXISTS public.vector_store CASCADE; ``` 确认删除成功(应报错): ```sql SELECT a.atttypmod - 4 AS dims FROM pg_attribute a JOIN pg_class c ON a.attrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = 'public' AND c.relname = 'vector_store' AND a.attname = 'embedding'; -- 期望结果:ERROR: relation "vector_store" does not exist ``` --- ### Step 4:防止 SQL 初始化脚本干扰 ```yaml spring: sql: init: mode: never ``` > 防止 `schema.sql` / `schema-postgresql.sql` 在项目启动前抢先建表。 --- ### Step 5:JDBC URL 锁定 schema ```yaml spring: datasource: url: jdbc:postgresql://xxx.aliyuncs.com:5432/pui_loveAi_agent?currentSchema=public ``` --- ### Step 6:IDE / 构建环境清理 ```text 1. 停止所有 Run / Debug 进程 2. IDEA → File → Invalidate Caches / Restart 3. mvn clean 4. 重新运行测试 ``` --- ## 六、成功标志(必须满足) 启动日志中必须看到: ```text Using the vector table name: vector_store. Is empty: true Creating table: vector_store embedding VECTOR(1536) ``` 只要看到 `VECTOR(1536)`,问题永久解决。 --- ## 七、经验总结 ### 7.1 最佳实践清单 - [ ] Embedding 模型必须显式指定(禁止依赖默认值) - [ ] PGVector 维度一旦确定,不可更改 - [ ] 测试环境每次重建表,避免历史污染 - [ ] 所有向量相关配置集中在 `application-local.yml` - [ ] 启动后第一时间检查向量维度 - [ ] 使用「启动自检 Bean」自动校验(见附件) ### 7.2 推荐稳定组合 ```text Embedding 模型:text-embedding-v1 向量维度: 1536 索引类型: HNSW 距离算法: Cosine ``` --- ## 八、常见 Q&A ### Q:我可以用 1024 维吗? ✅ 可以,但必须: - Embedding 模型稳定输出 1024 - 表维度也是 1024 - 否则必须删表重建 ### Q:为什么我删了表还是报错? 99% 是以下原因之一: - 删错了库 / schema - `schema.sql` 脚本重建了旧表 - IDEA 缓存未清理 ### Q:这个坑以后还会遇到吗? 按本文方案配置 + 启动自检 Bean,**不会再出现**。 --- ## 九、参考报错日志 ```text Caused by: org.postgresql.util.PSQLException: ERROR: expected 1536 dimensions, not 1024 ``` --- ## 附件:启动自检 Bean 详见项目源码: `com.puicode.puiaiagent.config.VectorStoreHealthCheck` ```java package com.puicode.puiaiagent.config; import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.EmbeddingRequest; import org.springframework.ai.vectorstore.pgvector.PgVectorStore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; /** * 向量存储启动自检器 * * <p>功能:在应用启动时自动校验以下三项一致性,防止维度不匹配问题:</p> * <ol> * <li>Embedding 模型实际输出维度</li> * <li>PGVector 表的实际维度</li> * <li>配置中声明的维度</li> * </ol> * * <p>任意一项不匹配,立即 Fail Fast,抛出明确异常,阻止应用继续启动。</p> * * @author Shihui * @date 2026-07-23 */ @Slf4j @Configuration @ConditionalOnBean({EmbeddingModel.class, PgVectorStore.class}) @ConditionalOnProperty( name = "spring.ai.vectorstore.pgvector.initialize-schema", havingValue = "true", matchIfMissing = false ) public class VectorStoreHealthCheck { private final EmbeddingModel embeddingModel; private final JdbcTemplate jdbcTemplate; private final org.springframework.core.env.Environment environment; public VectorStoreHealthCheck( EmbeddingModel embeddingModel, JdbcTemplate jdbcTemplate, org.springframework.core.env.Environment environment) { this.embeddingModel = embeddingModel; this.jdbcTemplate = jdbcTemplate; this.environment = environment; } @PostConstruct public void validateVectorStoreConsistency() { log.info("========================================"); log.info("🔍 [向量存储自检] 开始启动校验..."); log.info("========================================"); // 1️⃣ 检查 Embedding 模型输出维度 int modelDimension = checkEmbeddingModelDimension(); // 2️⃣ 检查 PGVector 表维度 int tableDimension = checkTableDimension(); // 3️⃣ 检查配置声明的维度 Integer configDimension = checkConfigDimension(); // 4️⃣ 一致性校验 validateConsistency(modelDimension, tableDimension, configDimension); log.info("========================================"); log.info("✅ [向量存储自检] 全部通过,维度一致 = {}D", modelDimension); log.info("========================================"); } /** * 检查 Embedding 模型实际输出维度 */ private int checkEmbeddingModelDimension() { try { var response = embeddingModel.call( new EmbeddingRequest(List.of("test")) ); int dim = response.getResult().getOutput().length; log.info("📐 Embedding 模型输出维度: {}D", dim); return dim; } catch (Exception e) { throw new IllegalStateException( "❌ [向量存储自检] Embedding 模型调用失败,请检查 DashScope API Key 和网络连接", e); } } /** * 检查 PGVector 表的实际维度 */ private int checkTableDimension() { String sql = """ SELECT a.atttypmod - 4 AS dims FROM pg_attribute a JOIN pg_class c ON a.attrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = 'public' AND c.relname = 'vector_store' AND a.attname = 'embedding' """; try { Integer dim = jdbcTemplate.queryForObject(sql, Integer.class); if (dim == null) { throw new IllegalStateException( "❌ [向量存储自检] vector_store 表不存在或 embedding 列不存在," + "请确认 initialize-schema: true 且表已正确创建" ); } log.info("📐 PGVector 表实际维度: {}D", dim); return dim; } catch (Exception e) { if (e instanceof IllegalStateException) { throw e; } throw new IllegalStateException( "❌ [向量存储自检] 查询 vector_store 维度失败: " + e.getMessage(), e); } } /** * 检查配置中声明的维度 */ private Integer checkConfigDimension() { String dimStr = environment.getProperty( "spring.ai.vectorstore.pgvector.dimensions"); if (dimStr == null || dimStr.isBlank()) { log.warn("⚠️ [向量存储自检] 配置中未声明 spring.ai.vectorstore.pgvector.dimensions," + "强烈建议显式指定以避免维度漂移"); return null; } int dim = Integer.parseInt(dimStr); log.info("📐 配置声明维度: {}D", dim); return dim; } /** * 校验三者一致性 */ private void validateConsistency(int modelDim, int tableDim, Integer configDim) { // 模型维度 vs 表维度(必须一致) if (modelDim != tableDim) { throw new IllegalStateException(String.format( "❌ [向量存储自检] 维度不匹配!\n" + " Embedding 模型输出: %dD\n" + " PGVector 表维度: %dD\n" + " 解决方案:\n" + " 1. 确认 spring.ai.dashscope.embedding.options.model 已固定\n" + " 2. DROP TABLE public.vector_store CASCADE;\n" + " 3. 重启应用让 Spring AI 用正确维度重建表\n" + " 详细排查步骤见 docs/踩坑记录-PGVector维度冲突.md", modelDim, tableDim )); } // 配置维度 vs 实际维度(如果配置了的话) if (configDim != null && !configDim.equals(tableDim)) { log.warn("⚠️ [向量存储自检] 配置维度({}D) 与实际表维度({}D) 不一致," + "但模型输出与表维度匹配,应用可正常运行。" + "建议将配置改为 {}D 以保持一致。", configDim, tableDim, tableDim); } } } ``` **功能**: - 启动时自动校验 Embedding 模型输出维度 - 自动校验 PGVector 表维度 - 不一致时 **Fail Fast**,直接抛出明确异常 - 防止后人再次踩坑

用户中心系统正向代理无法响应后端

在学习第23个视频代理知识的过程中,前端添加代理完善/api,后端添加/api后,登录一直响应不了8080,显示8000。在和通义灵码的一系列对话中,终于发现问题,在 config/config.ts 的第 59-64 行,代理配置被硬编码了,同时引用 proxy.ts 的配置被注释掉了。这导致:前端运行在 localhost:8000,代理配置也指向 localhost:8000(自己代理自己),实际的后端服务器 localhost:8080 没有被使用。需要修改 config/config.ts,启用正确的代理配置或者 启用proxy.ts 的配置方式,不过大概率是因为这个项目是几年前的了,Ant Design Pro本身也在一直完善,才导致的信息差,扶额~~~ ![屏幕截图 2026-03-11 061447.png](https://pic.code-nav.cn/post_picture/1984507671742521346/NhUD5x3jtUpYo1IC.webp) ![屏幕截图 2026-03-11 061459.png](https://pic.code-nav.cn/post_picture/1984507671742521346/DATK9SFxlKtwwuzc.png)

Docker-Java操作Windows Docker踩坑记录

最近正在学习OJ判题系统,需要用到Docker来对用户提交的代码进行环境隔离,鱼皮哥推荐使用的是虚拟机的方式,我这里由于已经使用WSL2 按照了Docker,所有就记录一下,踩到的坑 # 坑点1:JDK版本问题导致日志无法输出 我jdk用的17,导致引入运行Docker-Java快速入门案例时,没有日志打印的问题 <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/quhy0PEKKFyjbmAM.webp" alt="image.png" width="100%" /> 就是出现了SLF4J的提示,这个解决办法很简单,引入一个依赖就好 ```xml <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.11</version> </dependency> ``` 引入后,启动日志就显示正常了 > 引入依赖后,可以通过这段代码对输入日志进行控制 > ```java > import ch.qos.logback.classic.Level; > import ch.qos.logback.classic.Logger; > import org.slf4j.LoggerFactory; > > Logger dockerLogger = (Logger) LoggerFactory.getLogger("com.github.dockerjava"); > Logger clientLogger = (Logger) LoggerFactory.getLogger("org.apache.hc.client5.http"); > > dockerLogger.setLevel(Level.INFO); > clientLogger.setLevel(Level.INFO); > ``` # 坑点2:使用默认的快速入门代码,无法连接Win Docker 默认的快速入门方式 ```java DockerClient client = DockerClientBuilder.getInstance().build(); client.pingCmd().exec(); client.close(); ``` 运行提示 <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/yxgjumXBveZ5xfNT.webp" alt="image.png" width="100%" /> `Exception in thread "main" java.lang.IllegalArgumentException: Unsupported protocol scheme: npipe:////./pipe/dockerDesktopLinuxEngine` 这个错误表示默认连接的是Linux的Docker,可是我要连接Windows的上的,哪该怎么办?<img src="https://pic.code-nav.cn/post_picture/1738491648276455426/LhbEJ8fICdlKlMTn.webp" alt="image.png" width="100px" /> AI 大法,直接问Deespeek或者通义千问,通义给出,我们需要修改windows的一个配置,然后再修改对应的DockerClient 配置 <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/6wr6aLpKTeHtIUc8.webp" alt="image.png" width="100%" />ok 按照他的配置修改了,我们再修改DockerClient的连接配置 ```java public static void main(String[] args) { // 配置 Docker 客户端 DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withDockerHost("tcp://localhost:2375") // 指定 Docker 主机地址 .withDockerTlsVerify(false) // 禁用 TLS 验证(测试环境) .build(); // 创建 Docker 客户端实例 DockerClient dockerClient = DockerClientBuilder.getInstance(config).build(); dockerClient.pingCmd().exec(); dockerClient.close(); } ``` 运行可以发现,控制台已经正确打印响应信息了 <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/zI4JNfSEDszCMGUq.webp" alt="image.png" width="100%" /> 但是我觉得还不行,开启TSL 存在风险,不能使用默认的npipe吗? 继续问AI, <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/iAmTiYjEKutdWo2K.webp" alt="image.png" width="100%" /> AI 提示我们可以使用 `npipe:////./pipe/docker_engine` 来进行连接,于是修改代码 ```java public static void main(String[] args) throws IOException { // 配置 Docker 客户端 DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withDockerHost("npipe:////./pipe/docker_engine") // 指定 Docker 主机地址 .withDockerTlsVerify(false) // 禁用 TLS 验证(测试环境) .build(); DockerClient client = DockerClientBuilder.getInstance(config).build(); client.pingCmd().exec(); client.close(); } ``` 我非常高兴的运行,以为这次成功了,没想到继续包了一个跟上面一样的错误 `Exception in thread "main" java.lang.IllegalArgumentException: Unsupported protocol scheme: npipe:////./pipe/docker_engine` 翻译一下,发现是客户端不认真npipe这个命令<img src="https://pic.code-nav.cn/post_picture/1738491648276455426/LhbEJ8fICdlKlMTn.webp" alt="image.png" width="100px" /> 随后查看官网的 [Available transports](https://github.com/docker-java/docker-java/blob/main/docs/transports.md) 文档 ,发现默认的客户端(Jersey)不支持npipe命令 <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/NMo1sg000e7u65Lf.png" alt="image.png" width="577px" /> 我原以为引入`docker-java-transport-httpclient5` 这个依赖后,会自动使用,但有想想,又不是spring管理的,它凭什么自动使用。因此还需要我自己配置HttpClient,修改后的代码 ```java public static void main(String[] args) throws IOException { ApacheDockerHttpClient httpClient = new ApacheDockerHttpClient.Builder() .dockerHost(URI.create("npipe:////./pipe/docker_engine")).build(); DockerClient client = DockerClientBuilder.getInstance() .withDockerHttpClient(httpClient).build(); client.pingCmd().exec(); client.close(); } ``` > 由于鱼皮哥给的pom.xml 依赖中已经引入了 `docker-java-transport-httpclient5` 这里就不写了 运行 <img src="https://pic.code-nav.cn/post_picture/1738491648276455426/cIp0uSBiRj3mOhNZ.webp" alt="image.png" width="100%" /> 哈哈哈哈哈 成功 # 总结 虽然由于环境给鱼皮哥的不同,踩了点坑,解决坑点时间,但是在解决坑点的过程中,提高了自身解决问题的能力。

用户管理项目关于两次请求sessionid不一致的解决

网上找了很多方法都试过,今天早上起来在项目里面搜索withCredentials的配置,找到 <img src="https://pic.code-nav.cn/post_picture/1834461461087260673/Dz2IHRs3bB1trjdy.webp" alt="屏幕截图 2024-11-09 130400.png" width="100%" /> 发现在request里设置credentials=='include'就行 app.tsx: <img src="https://pic.code-nav.cn/post_picture/1834461461087260673/bIPAt2oSZPU1xZwj.png" alt="image.png" width="100%" /> 另外我们还要在后端配置跨域的config类 `package com.zjy.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { //添加映射路径 registry.addMapping("/**") //是否发送Cookie .allowCredentials(true) .allowedOriginPatterns("*") //放行哪些请求方式 .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"}) //放行哪些原始请求头部信息 .allowedHeaders("*") .maxAge(3600) //暴露哪些原始请求头部信息 .exposedHeaders("*"); } } `然后就解决了登录不上的问题~

axios.create()的baseURL协议应为http而不是https,否则将报错ERR_SSL_PROTOCOL_ERROR,因为SSL证书与服务器地址不一致

总结今天踩到的坑,前端配置跨域出错导致后端收不到数据请求,全程以为是数据格式问题,捣鼓半天,最终通过百度解决 https://blog.csdn.net/qq_41967899/article/details/90442946 文章地址如上

亲测!解决Error: ORA-12170: TNS:Connect timeout occurred连接超时

### 案例 > [Nest] 7696 - 2024/07/26 11:30:31 [TypeOrmModule] Unable to connect to the database. Retrying (3)... +8ms Error: ORA-12170: TNS:Connect timeout occurred 今天遇到一个Oracle的报错`Error: ORA-12170: TNS:Connect timeout occurred`,连接超时,项目是TS作为后端开发语言,使用的持久层框架是[TypeORM](https://typeorm.bootcss.com/)。 分析原因: - 网络问题 - 防火墙(端口) 我出现这个问题是因为开了vpn,关掉后再次运行项目就好了 ### 小结 针对以上可能查阅资料,简单总结一下解决方式: **1.检查网络** a. ping ip地址,如果ping不通的话,就是网络问题 b. tnsping ip地址(或者服务器的实例名SID),如果报TNS-12535:操作超时,可能是服务器端防火墙原因 c. netstat -na,查看对应端口是否关闭 **2.防火墙问题** a. 关闭防火墙:`chkconfig iptables off;`(重启失效),`/etc/init.d/iptables stop;`(立即失效) b. 执行`vim /etc/sysconfig/iptables`命令编辑防火墙配置,在默认的22端口下一行添加`-A INPUT -m state --state NEW -m tcp -p tcp --dport 1521 -j ACCEPT`,保存配置文件,执行`/etc/init.d/iptables restart`重启防火墙 不过现在有很多可视化界面工具,操作起来也很方便,也不用一条条命令的排错。

#踩坑记录# 问题描述: 自定义SDK在测试项目已经跑通,但是引入项目后说找不到相关的bean(图1) 问题分析: 包扫描配置问题: 确保项目中包扫描的范围包含了 SDK 中的包路径。如果使用了注解扫描(例如 @ComponentScan),则需要确保扫描路径包含 SDK 中的包路径 解决问题:(图2) @ComponentScan(basePackages = {"com.mq.*"})

下载 APP