Procházet zdrojové kódy

chore(deps): AgentScope 升级到 2.1.0-SNAPSHOT(本地 main 构建)

- agentscope.version 2.1.0-BETA1 → 2.1.0-SNAPSHOT,依赖本地 install 的 main 分支(94 个构件齐全);
  换机器 / CI 构建前需先执行一次该 install,否则依赖解析失败
- 新增 agentscope-extensions-jdbc:技能仓库改用统一 JDBC 实现。2.0.4 的该模块没有 skill 包,
  2.1.0-BETA1 的 JdbcAgentSkillRepository 没有 namespace,只有 main 两者都有
- 适配 embedding API:EmbeddingModel.embed() 返回 EmbeddingResponse(向量 + usage),不再返回裸 double[],
  EmbeddingProvider / ModelConnectivityTester 及其单测同步改
cc před 1 týdnem
rodič
revize
c34a68749e

+ 1 - 1
.qoder/repowiki/zh/content/Agent 与 RAG/Agent 与 RAG.md

@@ -44,7 +44,7 @@
 
 Agent 与 RAG 是清鉴平台中面向纪检监察与经侦场景的对话式研判能力。其目标是让业务人员用自然语言与案件数据交互:前端通过 SSE 流式对话,后端基于 AgentScope 的 `HarnessAgent` 驱动 LLM,并结合 pgvector 做表结构与知识库语义检索、意图识别、SQL 与 Python 执行器、附件解析、产物管理与 Insight 结构化研判输出。
 
-从仓库定位看,Agent 子系统位于 `ai-server` 的 `module/agent` 域,使用 Spring Boot 3 + MyBatis-Plus + AgentScope 2.0.4;主库为 PostgreSQL + pgvector,分析库为 DuckDB(按工作空间切换),Redis 用于会话缓存与记忆隔离,Elasticsearch 用于知识文档索引。
+从仓库定位看,Agent 子系统位于 `ai-server` 的 `module/agent` 域,使用 Spring Boot 3 + MyBatis-Plus + AgentScope 2.1.0-SNAPSHOT;主库为 PostgreSQL + pgvector,分析库为 DuckDB(按工作空间切换),Redis 用于会话缓存与记忆隔离,Elasticsearch 用于知识文档索引。
 
 **章节来源**
 - [AI_AGENT.md:1-120](file://AI_AGENT.md#L1-L120)

+ 1 - 1
.qoder/repowiki/zh/content/Agent 与 RAG/LLM Provider 管理与模型工厂.md

@@ -45,7 +45,7 @@
 - 连通性测试与健康检查机制;
 - 新增一个 LLM Provider、切换默认模型、通过控制器查询模型列表和状态的完整流程。
 
-本项目的 Agent 子系统基于 **AgentScope 2.0.4**,后端是 Spring Boot 3 + MyBatis-Plus,模型配置不写 YAML,而是持久化到平台库的 `agent_model` / `agent_model_provider` 表,由前端「模型管理」界面维护。
+本项目的 Agent 子系统基于 **AgentScope 2.1.0-SNAPSHOT**,后端是 Spring Boot 3 + MyBatis-Plus,模型配置不写 YAML,而是持久化到平台库的 `agent_model` / `agent_model_provider` 表,由前端「模型管理」界面维护。
 
 **章节来源**
 - [AI_AGENT.md:1-60](file://AI_AGENT.md#L1-L60)

+ 1 - 1
.qoder/repowiki/zh/content/后端服务架构/后端服务架构.md

@@ -72,7 +72,7 @@ graph TB
 - **Spring Boot 基础栈**:web、validation、redis、aop、configuration-processor、Lombok、Slf4j。
 - **数据访问**:MyBatis-Plus 3.5.17、Dynamic Datasource 4.5.0、JSqlParser、PostgreSQL 驱动、pgvector Java SDK、DuckDB JDBC。
 - **认证授权**:Sa-Token 1.46.0,仅做登录态校验,不引入完整权限体系。
-- **AI 生态**:AgentScope 2.0.4、Spring AI BOM、DashScope/Ollama/OpenAI 模型扩展、mem0、RAG simple、Redis 扩展、Tika 文档读取。
+- **AI 生态**:AgentScope 2.1.0-SNAPSHOT、Spring AI BOM、DashScope/Ollama/OpenAI 模型扩展、mem0、RAG simple、Redis 扩展、Tika 文档读取。
 - **推理与向量**:DJL HuggingFace tokenizers、ONNX Runtime CPU、本地 bge-m3 嵌入模型(可选)。
 - **外部系统**:Neo4j、Elasticsearch、Redis。
 

+ 3 - 3
.qoder/repowiki/zh/content/技术栈与依赖.md

@@ -151,7 +151,7 @@ graph LR
 | ONNX Runtime 1.30.0 | 本地模型推理 | 大模型会显著增加内存和安装包体积 |
 | DuckDB JDBC 1.5.5.1 | 嵌入式数据分析 | 不适合高并发写入库场景 |
 | Spring AI Tika | 文档解析 | 依赖 Tika 生态,大型文档解析耗时较长 |
-| AgentScope 2.0.4 | Agent 框架与模型扩展 | 已排除 MCP,外部 MCP 客户端不会随包引入 |
+| AgentScope 2.1.0-SNAPSHOT | Agent 框架与模型扩展 | 已排除 MCP,外部 MCP 客户端不会随包引入 |
 | Elasticsearch Starter | 全文检索 | 注释明确服务端须与 Spring Boot 托管客户端 8.18.x 匹配 |
 
 **章节来源**
@@ -302,7 +302,7 @@ flowchart TD
 - **DJL HuggingFace Tokenizers 0.38.0 与 ONNX Runtime 1.30.0**:两者都涉及 native 库,Windows、macOS、Linux 平台的二进制包和行为需要分别验证。
 - **DuckDB JDBC 1.5.5.1**:适合嵌入式分析,但不应视为通用高并发数据库。
 - **Spring AI Tika**:文档解析依赖 Tika 生态,升级 Spring AI BOM 时需同步验证解析器兼容性。
-- **AgentScope 2.0.4**:已排除 MCP SDK,如果未来引入外部 MCP 客户端,需要显式添加依赖。
+- **AgentScope 2.1.0-SNAPSHOT**:已排除 MCP SDK,如果未来引入外部 MCP 客户端,需要显式添加依赖。
 - **Elasticsearch Starter**:注释明确指出服务端版本应与 Spring Boot 托管的客户端 8.18.x 匹配,不能随意升级服务端 Elasticsearch。
 
 ### 前端兼容性
@@ -340,7 +340,7 @@ flowchart TD
    网关与服务端必须同步升级,且 Redis 配置、token key、序列化实现要保持一致。
 5. **pgvector、DJL、ONNX Runtime**  
    这些依赖涉及 native 库和平台二进制,升级时必须逐平台验证安装、加载、内存占用和推理结果。
-6. **AgentScope 2.0.4**  
+6. **AgentScope 2.1.0-SNAPSHOT**  
    如果未来引入 MCP 客户端,需要显式引入依赖,因为当前已排除 MCP SDK。
 7. **Elasticsearch Starter**  
    升级服务端 Elasticsearch 时,必须与 Spring Boot 托管的客户端 8.18.x 匹配,不能只看版本号。

+ 6 - 1
ai-server/pom.xml

@@ -24,7 +24,7 @@
         <commonmark.version>0.17.0</commonmark.version>
         <commons-compress.version>1.28.0</commons-compress.version>
         <testcontainers.version>1.21.4</testcontainers.version>
-        <agentscope.version>2.0.4</agentscope.version>
+        <agentscope.version>2.1.0-SNAPSHOT</agentscope.version>
         <pgvector.version>0.1.6</pgvector.version>
         <djl.version>0.38.0</djl.version>
         <onnxruntime.version>1.30.0</onnxruntime.version>
@@ -90,6 +90,11 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>io.agentscope</groupId>
+            <artifactId>agentscope-extensions-jdbc</artifactId>
+            <version>${agentscope.version}</version>
+        </dependency>
         <dependency>
             <groupId>io.agentscope</groupId>
             <artifactId>agentscope-extensions-model-dashscope</artifactId>

+ 4 - 1
ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingProvider.java

@@ -2,6 +2,7 @@ package com.zsjz.ai.module.agent.rag;
 
 import com.zsjz.ai.module.agent.rag.EmbeddingModelFactory.EmbeddingSpec;
 import com.zsjz.ai.module.embedding.service.EmbeddingService;
+import io.agentscope.core.embedding.EmbeddingResponse;
 import io.agentscope.core.message.TextBlock;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -137,9 +138,11 @@ public class EmbeddingProvider {
         if (spec == null) {
             throw new IllegalStateException(unavailableMessage());
         }
-        double[] vector = spec.model()
+        // 2.1.0-SNAPSHOT 起 embed 返回 EmbeddingResponse(向量 + usage),不再是裸 double[]
+        EmbeddingResponse response = spec.model()
                 .embed(TextBlock.builder().text(text).build())
                 .block(EMBED_TIMEOUT);
+        double[] vector = response == null ? null : response.getEmbedding();
         if (vector == null || vector.length == 0) {
             throw new IllegalStateException("在线嵌入模型返回空向量: " + spec.modelName());
         }

+ 3 - 1
ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java

@@ -5,6 +5,7 @@ import com.zsjz.ai.common.exception.ServerException;
 import com.zsjz.ai.module.agent.entity.AgentModel;
 import com.zsjz.ai.module.agent.rag.EmbeddingModelFactory;
 import com.zsjz.ai.module.agent.vo.ModelTestResultVO;
+import io.agentscope.core.embedding.EmbeddingResponse;
 import io.agentscope.core.message.Msg;
 import io.agentscope.core.message.MsgRole;
 import io.agentscope.core.message.TextBlock;
@@ -102,8 +103,9 @@ public class ModelConnectivityTester {
      */
     private void testEmbedding(AgentModel config) {
         EmbeddingModelFactory.EmbeddingSpec spec = embeddingModelFactory.create(config);
-        double[] vec = spec.model().embed(TextBlock.builder().text(PING_TEXT).build())
+        EmbeddingResponse response = spec.model().embed(TextBlock.builder().text(PING_TEXT).build())
                 .block(TEST_TIMEOUT);
+        double[] vec = response == null ? null : response.getEmbedding();
         if (vec == null || vec.length == 0) {
             throw new ServerException(500, "嵌入服务返回空向量");
         }

+ 3 - 2
ai-server/src/test/java/com/zsjz/ai/module/agent/rag/EmbeddingProviderTest.java

@@ -2,6 +2,7 @@ package com.zsjz.ai.module.agent.rag;
 
 import com.zsjz.ai.module.embedding.service.EmbeddingService;
 import io.agentscope.core.embedding.EmbeddingModel;
+import io.agentscope.core.embedding.EmbeddingResponse;
 import io.agentscope.core.message.TextBlock;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -62,7 +63,7 @@ class EmbeddingProviderTest {
     void localDisabledUsesOnlineModel() {
         when(localEmbeddingService.isEnabled()).thenReturn(false);
         EmbeddingModel onlineModel = mock(EmbeddingModel.class);
-        when(onlineModel.embed(any(TextBlock.class))).thenReturn(Mono.just(new double[]{0.3, 0.4}));
+        when(onlineModel.embed(any(TextBlock.class))).thenReturn(Mono.just(EmbeddingResponse.builder().embedding(new double[]{0.3, 0.4}).build()));
         when(embeddingModelFactory.resolveDefault())
                 .thenReturn(new EmbeddingModelFactory.EmbeddingSpec(1L, "qwen-embed", onlineModel, 2));
 
@@ -106,7 +107,7 @@ class EmbeddingProviderTest {
     void onlineDimensionMismatchFails() {
         when(localEmbeddingService.isEnabled()).thenReturn(false);
         EmbeddingModel onlineModel = mock(EmbeddingModel.class);
-        when(onlineModel.embed(any(TextBlock.class))).thenReturn(Mono.just(new double[]{0.3, 0.4}));
+        when(onlineModel.embed(any(TextBlock.class))).thenReturn(Mono.just(EmbeddingResponse.builder().embedding(new double[]{0.3, 0.4}).build()));
         when(embeddingModelFactory.resolveDefault())
                 .thenReturn(new EmbeddingModelFactory.EmbeddingSpec(1L, "qwen-embed", onlineModel, 4));