找回密码
 立即注册
首页 业界区 业界 3. LangChain4j-RAG,实现简单的text-sql功能

3. LangChain4j-RAG,实现简单的text-sql功能

颜清华 2025-6-11 21:55:11
1. 简介

前两章我们讲了如何使用LangChain4J进行AI交互, 其中包括

  • 使用ChatLanguageModel、ChatMessage、ChatMemory等底层组件进行灵活/自由的与AI交互 传送门
  • 使用AI Services高级对象, 只关注业务逻辑, 使用简单的Api即可进行AI交互 传送门
RAG(Retrieval-Augmented Generation,检索增强生成): 是一种利用向量数据库召回专业知识并融入Prompt的大模型应用方案.
RAG其实分为两个阶段: 构建索引 和 检索
1.jpeg

1.1 构建索引

构建索引的过程其实是构建知识库的过程 如上图上半部分, 执行步骤如下:

  • 数据准备 (数据形式不限 如 文件, 字符串, 数据表内容 甚至是多媒体类型都可以)
  • 读取数据内容并分割成文本块 (数据规模很可能非常庞大,整体存储具有难度,并且在查询的时候可能仅仅和其中的一个或几个段落有关系,所以需要分块处理 将解析后的文档内容切分为适当的片段)
  • 将分割的文本块向量化 (转换成机器能识别的内容)
  • 将向量化的内容存储进向量数据库
1.2 检索

检索过程其实就是当我们进行提问的时候, 先使用之前准备好的知识库把提问内容相关的知识检索出来,然后一并将问题和检索结果交给大模型,辅助大模型生成回答 如上图下半部分 执行步骤如下:

  • 用户提问
  • 将用户提问的内容向量化
  • 使用问题向量化的结果到向量数据库进行检索,并返回相关的知识
  • 将问题和相关的知识组织成提示词交给大模型
  • 大模型根据提示词思考并生成回答
  • 响应用户
1.3 解决了什么问题

RAG核心是为了弥补传统通用大模型的两大短板:

  • 知识时效性不足:传统大模型训练数据固定,难以及时获取最新信息(如 近期的新闻、政策),而 RAG 通过实时检索外部知识库,让 AI 能调用最新数据生成回答。
  • 专业领域知识受限:通用模型对垂直领域(如医疗、法律)的深度知识掌握有限,RAG 可连接行业专属数据库,提升回答的专业性和准确性。
    简言之,RAG 让 AI “边查边答”,解决 “知识旧” 和 “不够专” 的问题,让回答更实时、更精准。
2. 实践案例

本章我们使用LangChain4J来实现一个简单的RAG需求: 整理一份学生成绩系统的数据库表ddl信息, 通过RAG实现让大模型帮我们生成sql语句, 比如向大模型提问: 张铁牛上学期语文考试成绩是多少?  希望AI给我们响应查询sql语句.
2.1 环境信息

使用SDK版本信息如下:
  1. Java: 21
  2. SpringBoot: 3.4.5
  3. LangChain4j: 1.0.1
  4. LLM: (使用在线的百炼(阿里)平台)
  5.         embedding模型: text-embedding-v3  
  6.         chat模型: qwen-plus
  7. PGVector(postgresql版本的向量数据库): 0.8.0-pg17
复制代码
2.1.1 部署PGVector

推荐使用docker-compose部署, yml文件如下:
  1. version: '3'
  2. services:
  3.   pgvector:  
  4.     container_name: pgvector
  5.     restart: always
  6.     image: pgvector/pgvector:0.8.0-pg17
  7.     privileged: true
  8.     ports:
  9.       - 5431:5432
  10.     environment:
  11.       POSTGRES_USER: root
  12.       POSTGRES_PASSWORD: 123456
  13.       PGDATA: /var/lib/postgresql/data/
  14.     volumes:
  15.       - ./data:/var/lib/postgresql/data/
复制代码
2.2 Maven
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.     <modelVersion>4.0.0</modelVersion>
  5.     <parent>
  6.         <groupId>org.springframework.boot</groupId>
  7.         spring-boot-starter-parent</artifactId>
  8.         <version>3.4.5</version>
  9.         <relativePath/>
  10.     </parent>
  11.     <groupId>com.genuai</groupId>
  12.     langchain-test</artifactId>
  13.     <version>0.0.1-SNAPSHOT</version>
  14.     <name>langchain-test</name>
  15.     <description>langchain-test</description>
  16.     <properties>
  17.         <java.version>21</java.version>
  18.         <guava.version>33.0.0-jre</guava.version>
  19.     </properties>
  20.     <dependencyManagement>
  21.         <dependencies>
  22.             <dependency>
  23.                 <groupId>dev.langchain4j</groupId>
  24.                 langchain4j-bom</artifactId>
  25.                 <version>1.0.1</version>
  26.                 <type>pom</type>
  27.                 <scope>import</scope>
  28.             </dependency>
  29.         </dependencies>
  30.     </dependencyManagement>
  31.     <dependencies>
  32.         <dependency>
  33.             <groupId>org.springframework.boot</groupId>
  34.             spring-boot-starter-web</artifactId>
  35.         </dependency>
  36.         <dependency>
  37.             <groupId>dev.langchain4j</groupId>
  38.             langchain4j-open-ai-spring-boot-starter</artifactId>
  39.         </dependency>
  40.         <dependency>
  41.             <groupId>dev.langchain4j</groupId>
  42.             langchain4j-spring-boot-starter</artifactId>
  43.         </dependency>
  44.         <dependency>
  45.             <groupId>dev.langchain4j</groupId>
  46.             langchain4j-open-ai</artifactId>
  47.         </dependency>
  48.         <dependency>
  49.             <groupId>com.google.guava</groupId>
  50.             guava</artifactId>
  51.             <version>${guava.version}</version>
  52.         </dependency>
  53.         <dependency>
  54.             <groupId>dev.langchain4j</groupId>
  55.             langchain4j-reactor</artifactId>
  56.         </dependency>
  57.         <dependency>
  58.             <groupId>dev.langchain4j</groupId>
  59.             langchain4j-pgvector</artifactId>
  60.         </dependency>
  61.         <dependency>
  62.             <groupId>org.projectlombok</groupId>
  63.             lombok</artifactId>
  64.             <optional>true</optional>
  65.         </dependency>
  66.       
  67.         <dependency>
  68.             <groupId>org.springframework.boot</groupId>
  69.             spring-boot-starter-test</artifactId>
  70.             <scope>test</scope>
  71.         </dependency>
  72.     </dependencies>
  73.     <build>
  74.         <plugins>
  75.             <plugin>
  76.                 <groupId>org.apache.maven.plugins</groupId>
  77.                 maven-compiler-plugin</artifactId>
  78.             </plugin>
  79.             <plugin>
  80.                 <groupId>org.springframework.boot</groupId>
  81.                 spring-boot-maven-plugin</artifactId>
  82.                 <configuration>
  83.                     <excludes>
  84.                         <exclude>
  85.                             <groupId>org.projectlombok</groupId>
  86.                             lombok</artifactId>
  87.                         </exclude>
  88.                     </excludes>
  89.                 </configuration>
  90.             </plugin>
  91.         </plugins>
  92.     </build>
  93. </project>
复制代码
2.3 构建模型和存储对象
  1. private static ChatModel chatModel;
  2. private static EmbeddingModel embeddingModel;
  3. private static EmbeddingStore<TextSegment> embeddingStore;
  4. private static JdkHttpClientBuilder jdkHttpClientBuilder;
  5. @BeforeAll
  6. public static void init_embed_model() {
  7.     jdkHttpClientBuilder = JdkHttpClient
  8.             .builder()
  9.             .httpClientBuilder(HttpClient.newBuilder());
  10.     chatModel = OpenAiChatModel
  11.             .builder()
  12.             .httpClientBuilder(jdkHttpClientBuilder)
  13.             .apiKey(System.getenv("LLM_API_KEY"))
  14.             .modelName("qwen-plus")
  15.             .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
  16.             // 打印请求日志
  17.             //.logRequests(true)
  18.             // 打印响应日志
  19.             //.logResponses(true)
  20.             .build();
  21.     embeddingModel = OpenAiEmbeddingModel
  22.             .builder()
  23.             .httpClientBuilder(jdkHttpClientBuilder)
  24.             .apiKey(System.getenv("LLM_API_KEY"))
  25.             .modelName("text-embedding-v3")
  26.             .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
  27.             .build();
  28.     embeddingStore = PgVectorEmbeddingStore
  29.             .builder()
  30.             .host("localhost")                           // 必需:PostgresSQL 实例的主机
  31.             .port(5431)                                  // 必需:PostgresSQL 实例的端口
  32.             .database("postgres")                        // 必需:数据库名称
  33.             .user("root")                                // 必需:数据库用户
  34.             .password("123456")                          // 必需:数据库密码
  35.             .table("my_sql_embeddings")                  // 必需:存储嵌入的表名
  36.             .dimension(embeddingModel.dimension())       // 必需:嵌入的维度
  37.             .metadataStorageConfig(DefaultMetadataStorageConfig.defaultConfig()) // 将元数据存储配置
  38.             .build();
  39. }
复制代码
2.4 测试向量化

测试使用向量模型 将sql信息转为向量数据
  1. @Test
  2. public void should_print_embedding_content_when_embed_sql_resource() {
  3.     // 资源信息在文章末尾
  4.     final Document document = ClassPathDocumentLoader.loadDocument("student_ddl.sql");
  5.     // 创建 SQL 内容的分割器
  6.     DocumentSplitter splitter = new DocumentByRegexSplitter(
  7.             ";",     // 分割方式(正则)
  8.             ";",     // 连接方式(分割完之后每段内容末尾的分隔符)
  9.             2000,    // 每段内容最大片段长度
  10.             100      // 相邻片段之间的最大重叠字符数 (为了保证内容的完整性 分割时片段之间可能会重叠)
  11.     );
  12.     final List<TextSegment> textSegments = splitter.split(document);
  13.     final Response<List<Embedding>> embedResult = embeddingModel.embedAll(textSegments);
  14.     final List<Embedding> content = embedResult.content();
  15.     log.info("embedding content: {}", content);
  16. }
复制代码
测试结果如下:
  1. [main] INFO com.genuai.langchaintest.rag.AiRagTest -- embedding content: [Embedding { vector = [-0.05850703, -0.0043222117, -0.054852795, 0.0036689683, -0.07005912, -0.015255443, -0.0048452974, 0.060707428,
  2. ...
  3. -0.0153250545, -0.06653458, -0.0023748502, -0.011913025, 0.052188545, 0.02347709, -0.0043522767, -0.017399414, 0.028711455, 0.0027141145, 0.008481608, 0.002006506, -0.00725541, 0.011253882] }]
复制代码
2.5 测试构建索引
  1. @Test
  2. public void should_store_vector_when_embed_sql_resource() {
  3.   final Document document = ClassPathDocumentLoader.loadDocument("student_ddl.sql");
  4.   DocumentSplitter splitter = new DocumentByRegexSplitter(";",";", 2000, 100);
  5.   final List<TextSegment> textSegments = splitter.split(document);
  6.   final Response<List<Embedding>> embedResult = embeddingModel.embedAll(textSegments);
  7.   final List<Embedding> content = embedResult.content();
  8.   // 将向量化结果和元数据一并存储到向量库
  9.   embeddingStore.addAll(content, textSegments);
  10. }
复制代码
测试结果如下:
2.png

2.6 测试检索
  1. @Test
  2. public void should_return_sql_when_chat_embed_sql_resource() {
  3.     ContentRetriever contentRetriever = EmbeddingStoreContentRetriever
  4.             .builder()
  5.             .embeddingStore(embeddingStore)
  6.             .embeddingModel(embeddingModel)
  7.             // 最大返回多少条相关的结果
  8.             .maxResults(10)
  9.             // 最小的得分(检索后每条数据都会有跟当前问题匹配度的得分, 得分越高越相近)
  10.             .minScore(0.65)
  11.             .build();
  12.         // 使用ai services 进行ai call
  13.     AiAssistantServiceWithMemoryTest assistant = AiServices
  14.             .builder(AiAssistantServiceWithMemoryTest.class)
  15.             .chatModel(chatModel)
  16.             // 注入内容检索器
  17.             .contentRetriever(contentRetriever)
  18.             .chatMemoryProvider(memoryId -> MessageWindowChatMemory
  19.                     .builder()
  20.                     .id(memoryId)
  21.                     .maxMessages(10)
  22.                     .build())
  23.             .build();
  24.     String id = "zhangtieniu-01";
  25.     final String q = "张铁牛上学期语文考试成绩是多少";
  26.     final String a = assistant.chatSql(id, q);
  27.     log.info("call ai q:{}\na: {}", q, a);
  28. }
复制代码
AI Services
  1. import dev.langchain4j.service.MemoryId;
  2. import dev.langchain4j.service.SystemMessage;
  3. import dev.langchain4j.service.UserMessage;
  4. import dev.langchain4j.service.spring.AiService;
  5. /**
  6. * ai svc
  7. *
  8. * @author ludangxin
  9. * @date 2025/6/5
  10. */
  11. @AiService
  12. public interface AiAssistantServiceWithMemoryTest {
  13.     String chat(@MemoryId String memoryId, @UserMessage String message);
  14.     @SystemMessage("你是一名sql分析专家 我会将sql相关的ddl给你, 需要你根据ddl生成合理且可执行的sql语句并返回")
  15.     String chatSql(@MemoryId String memoryId, @UserMessage String message);
  16. }
复制代码
测试结果如下(开启了请求相应日志) :
  1. [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
  2. - method: POST
  3. - url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
  4. - headers: [Authorization: Beare...ff], [User-Agent: langchain4j-openai], [Content-Type: application/json]
  5. - body: {
  6.   "model" : "qwen-plus",
  7.   "messages" : [ {
  8.     "role" : "system",
  9.     "content" : "你是一名sql分析专家 我会将sql相关的ddl给你, 需要你根据ddl生成合理且可执行的sql语句并返回"
  10.   }, {
  11.     "role" : "user",
  12.     "content" : "张铁牛上学期语文考试成绩是多少\n\nAnswer using the following information:\nALTER TABLE courses ADD CONSTRAINT fk_courses_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);\nALTER TABLE courses ADD CONSTRAINT fk_courses_teacher_id FOREIGN KEY (teacher_id) REFERENCES teachers(teacher_id);\nALTER TABLE teachers ADD CONSTRAINT fk_teachers_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);\nALTER TABLE exam_arrangements ADD CONSTRAINT fk_exam_arrangements_course_id FOREIGN KEY (course_id) REFERENCES courses(course_id);\nALTER TABLE scores ADD CONSTRAINT fk_scores_student_id FOREIGN KEY (student_id) REFERENCES students(student_id);\nALTER TABLE scores ADD CONSTRAINT fk_scores_exam_id FOREIGN KEY (exam_id) REFERENCES exam_arrangements(exam_id)\n\n-- 创建成绩表\nCREATE TABLE scores (\n                        score_id INT PRIMARY KEY AUTO_INCREMENT,\n                        student_id INT NOT NULL COMMENT '学生ID',\n                        exam_id INT NOT NULL COMMENT '考试ID',\n                        score DECIMAL(5,2) COMMENT '成绩',\n                        score_type ENUM('原始分', '平时分', '卷面分', '最终分') NOT NULL DEFAULT '最终分' COMMENT '成绩类型',\n                        remark VARCHAR(200) COMMENT '备注',\n                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n                        UNIQUE KEY unique_student_exam (student_id, exam_id)\n);\n\n-- 创建索引\nCREATE INDEX idx_students_student_no ON students(student_no);\nCREATE INDEX idx_students_major_id ON students(major_id);\nCREATE INDEX idx_students_class_id ON students(class_id);\nCREATE INDEX idx_majors_department_id ON majors(department_id);\nCREATE INDEX idx_classes_major_id ON classes(major_id);\nCREATE INDEX idx_courses_department_id ON courses(department_id);\nCREATE INDEX idx_courses_teacher_id ON courses(teacher_id);\nCREATE INDEX idx_teachers_department_id ON teachers(department_id);\nCREATE INDEX idx_exam_arrangements_course_id ON exam_arrangements(course_id);\nCREATE INDEX idx_scores_student_id ON scores(student_id);\nCREATE INDEX idx_scores_exam_id ON scores(exam_id);\n\n-- 添加外键约束\nALTER TABLE students ADD CONSTRAINT fk_students_major_id FOREIGN KEY (major_id) REFERENCES majors(major_id);\nALTER TABLE students ADD CONSTRAINT fk_students_class_id FOREIGN KEY (class_id) REFERENCES classes(class_id);\nALTER TABLE majors ADD CONSTRAINT fk_majors_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);\nALTER TABLE classes ADD CONSTRAINT fk_classes_major_id FOREIGN KEY (major_id) REFERENCES majors(major_id)\n\n-- 创建教师表\nCREATE TABLE teachers (\n                          teacher_id INT PRIMARY KEY AUTO_INCREMENT,\n                          teacher_no VARCHAR(20) UNIQUE NOT NULL COMMENT '教师编号',\n                          name VARCHAR(50) NOT NULL COMMENT '姓名',\n                          gender ENUM('男', '女', '其他') COMMENT '性别',\n                          title VARCHAR(50) COMMENT '职称',\n                          department_id INT NOT NULL COMMENT '所属院系ID',\n                          email VARCHAR(100) UNIQUE COMMENT '邮箱',\n                          phone VARCHAR(20) COMMENT '电话',\n                          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n);\n\n-- 创建考试安排表\nCREATE TABLE exam_arrangements (\n                                   exam_id INT PRIMARY KEY AUTO_INCREMENT,\n                                   course_id INT NOT NULL COMMENT '课程ID',\n                                   exam_date DATE NOT NULL COMMENT '考试日期',\n                                   start_time TIME NOT NULL COMMENT '开始时间',\n                                   end_time TIME NOT NULL COMMENT '结束时间',\n                                   exam_room VARCHAR(50) NOT NULL COMMENT '考场',\n                                   invigilator VARCHAR(100) COMMENT '监考老师',\n                                   exam_type ENUM('期中', '期末', '补考', '重修') NOT NULL COMMENT '考试类型',\n                                   academic_year VARCHAR(20) NOT NULL COMMENT '学年',\n                                   semester TINYINT NOT NULL COMMENT '学期',\n                                   created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                                   updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n)\n\n-- 创建学生信息表\nCREATE TABLE students (\n                          student_id INT PRIMARY KEY AUTO_INCREMENT,\n                          student_no VARCHAR(20) UNIQUE NOT NULL COMMENT '学号',\n                          name VARCHAR(50) NOT NULL COMMENT '姓名',\n                          gender ENUM('男', '女', '其他') COMMENT '性别',\n                          birth_date DATE COMMENT '出生日期',\n                          email VARCHAR(100) UNIQUE COMMENT '邮箱',\n                          phone VARCHAR(20) COMMENT '电话',\n                          address VARCHAR(200) COMMENT '地址',\n                          enrollment_date DATE NOT NULL COMMENT '入学日期',\n                          major_id INT NOT NULL COMMENT '专业ID',\n                          class_id INT NOT NULL COMMENT '班级ID',\n                          status ENUM('在读', '休学', '毕业', '退学') NOT NULL DEFAULT '在读' COMMENT '状态',\n                          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n);\n\n-- 创建专业表\nCREATE TABLE majors (\n                        major_id INT PRIMARY KEY AUTO_INCREMENT,\n                        major_name VARCHAR(50) UNIQUE NOT NULL COMMENT '专业名称',\n                        department_id INT NOT NULL COMMENT '院系ID',\n                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n)\n\n-- 创建院系表\nCREATE TABLE departments (\n                             department_id INT PRIMARY KEY AUTO_INCREMENT,\n                             department_name VARCHAR(50) UNIQUE NOT NULL COMMENT '院系名称',\n                             dean VARCHAR(50) COMMENT '院长',\n                             phone VARCHAR(20) COMMENT '联系电话',\n                             email VARCHAR(100) COMMENT '邮箱',\n                             created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                             updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n);\n\n-- 创建班级表\nCREATE TABLE classes (\n                         class_id INT PRIMARY KEY AUTO_INCREMENT,\n                         class_name VARCHAR(50) NOT NULL COMMENT '班级名称',\n                         major_id INT NOT NULL COMMENT '专业ID',\n                         grade INT NOT NULL COMMENT '年级',\n                         head_teacher VARCHAR(50) COMMENT '班主任',\n                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                         updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n);\n\n-- 创建课程表\nCREATE TABLE courses (\n                         course_id INT PRIMARY KEY AUTO_INCREMENT,\n                         course_code VARCHAR(20) UNIQUE NOT NULL COMMENT '课程代码',\n                         course_name VARCHAR(100) NOT NULL COMMENT '课程名称',\n                         credit TINYINT NOT NULL COMMENT '学分',\n                         course_type ENUM('必修课', '选修课', '公共课') NOT NULL COMMENT '课程类型',\n                         department_id INT NOT NULL COMMENT '开课院系ID',\n                         teacher_id INT NOT NULL COMMENT '授课教师ID',\n                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                         updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'\n)"
  13.   } ],
  14.   "stream" : false
  15. }
  16. [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
  17. - status code: 200
  18. - headers: [:status: 200], [content-length: 2644], [content-type: application/json], [date: Wed, 11 Jun 2025 02:20:11 GMT], [req-arrive-time: 1749608380626], [req-cost-time: 31767], [resp-start-time: 1749608412394], [server: istio-envoy], [set-cookie: acw_tc=c98b1f3b-b6e6-9271-a000-d232726b3d2c47d53afe44ffd30275d4f0a5315ca361;path=/;HttpOnly;Max-Age=1800], [vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers, Accept-Encoding], [x-dashscope-call-gateway: true], [x-envoy-upstream-service-time: 31762], [x-request-id: c98b1f3b-b6e6-9271-a000-d232726b3d2c]
  19. - body: {"choices":[{"message":{"role":"assistant","content":"根据提供的DDL和问题“张铁牛上学期语文考试成绩是多少”,我们需要从多个表中提取相关信息。以下是解决问题的步骤:\n\n1. **确定学生ID**:通过`students`表找到“张铁牛”的`student_id`。\n2. **确定课程ID**:通过`courses`表找到“语文”课程的`course_id`。\n3. **确定考试ID**:通过`exam_arrangements`表找到与“语文”课程相关的考试ID,并确保是“上学期”的考试(可以通过`semester`字段判断)。\n4. **查询成绩**:通过`scores`表找到该学生在该考试中的成绩。\n\n以下是完整的SQL语句:\n\n```sql\n-- Step 1: 获取张铁牛的学生ID\nSELECT student_id \nFROM students \nWHERE name = '张铁牛';\n\n-- Step 2: 获取语文课程的课程ID\nSELECT course_id \nFROM courses \nWHERE course_name = '语文';\n\n-- Step 3: 获取上学期语文考试的考试ID\nSELECT exam_id \nFROM exam_arrangements \nWHERE course_id = (SELECT course_id FROM courses WHERE course_name = '语文') \n  AND semester = (SELECT MAX(semester) FROM exam_arrangements WHERE academic_year = (SELECT academic_year FROM exam_arrangements ORDER BY exam_date DESC LIMIT 1));\n\n-- Step 4: 查询张铁牛上学期语文考试的成绩\nSELECT s.score \nFROM scores s\nJOIN students st ON s.student_id = st.student_id\nJOIN exam_arrangements ea ON s.exam_id = ea.exam_id\nJOIN courses c ON ea.course_id = c.course_id\nWHERE st.name = '张铁牛'\n  AND c.course_name = '语文'\n  AND ea.semester = (SELECT MAX(semester) FROM exam_arrangements WHERE academic_year = (SELECT academic_year FROM exam_arrangements ORDER BY exam_date DESC LIMIT 1));\n```\n\n### 解释:\n1. **Step 1**:通过`students`表查找“张铁牛”的`student_id`。\n2. **Step 2**:通过`courses`表查找“语文”课程的`course_id`。\n3. **Step 3**:通过`exam_arrangements`表查找与“语文”课程相关且属于“上学期”的考试ID。\n   - 使用`MAX(semester)`确保获取的是最近学年的上学期考试。\n4. **Step 4**:将以上结果联合查询,从`scores`表中提取张铁牛在上学期语文考试中的成绩。\n\n请确保数据库中已存在相关数据以供查询。如果需要进一步调整或补充信息,请告知!"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","usage":{"prompt_tokens":1498,"completion_tokens":558,"total_tokens":2056,"prompt_tokens_details":{"cached_tokens":0}},"created":1749608412,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-c98b1f3b-b6e6-9271-a000-d232726b3d2c"}
  20. [main] INFO com.genuai.langchaintest.rag.AiRagTest -- call ai q:张铁牛上学期语文考试成绩是多少
  21. a: 根据提供的DDL和问题“张铁牛上学期语文考试成绩是多少”,我们需要从多个表中提取相关信息。以下是解决问题的步骤:
  22. 1. **确定学生ID**:通过`students`表找到“张铁牛”的`student_id`。
  23. 2. **确定课程ID**:通过`courses`表找到“语文”课程的`course_id`。
  24. 3. **确定考试ID**:通过`exam_arrangements`表找到与“语文”课程相关的考试ID,并确保是“上学期”的考试(可以通过`semester`字段判断)。
  25. 4. **查询成绩**:通过`scores`表找到该学生在该考试中的成绩。
  26. 以下是完整的SQL语句:
  27. ```sql
  28. -- Step 1: 获取张铁牛的学生ID
  29. SELECT student_id
  30. FROM students
  31. WHERE name = '张铁牛';
  32. -- Step 2: 获取语文课程的课程ID
  33. SELECT course_id
  34. FROM courses
  35. WHERE course_name = '语文';
  36. -- Step 3: 获取上学期语文考试的考试ID
  37. SELECT exam_id
  38. FROM exam_arrangements
  39. WHERE course_id = (SELECT course_id FROM courses WHERE course_name = '语文')
  40.   AND semester = (SELECT MAX(semester) FROM exam_arrangements WHERE academic_year = (SELECT academic_year FROM exam_arrangements ORDER BY exam_date DESC LIMIT 1));
  41. -- Step 4: 查询张铁牛上学期语文考试的成绩
  42. SELECT s.score
  43. FROM scores s
  44. JOIN students st ON s.student_id = st.student_id
  45. JOIN exam_arrangements ea ON s.exam_id = ea.exam_id
  46. JOIN courses c ON ea.course_id = c.course_id
  47. WHERE st.name = '张铁牛'
  48.   AND c.course_name = '语文'
  49.   AND ea.semester = (SELECT MAX(semester) FROM exam_arrangements WHERE academic_year = (SELECT academic_year FROM exam_arrangements ORDER BY exam_date DESC LIMIT 1));
  50. ```
  51. ### 解释:
  52. 1. **Step 1**:通过`students`表查找“张铁牛”的`student_id`。
  53. 2. **Step 2**:通过`courses`表查找“语文”课程的`course_id`。
  54. 3. **Step 3**:通过`exam_arrangements`表查找与“语文”课程相关且属于“上学期”的考试ID。
  55.    - 使用`MAX(semester)`确保获取的是最近学年的上学期考试。
  56. 4. **Step 4**:将以上结果联合查询,从`scores`表中提取张铁牛在上学期语文考试中的成绩。
  57. 请确保数据库中已存在相关数据以供查询。如果需要进一步调整或补充信息,请告知!
复制代码
小结

本章通过使用RAG特性实现一个简单的text-sql功能实现, 直观的感受了一下RAG的强大之处, 下一章我们将使用SpringBoot实现快速集成LangChain4J, 通过简单的配置即可实现AI的调用。
student_ddl.sql
  1. -- 创建学生信息表
  2. CREATE TABLE students (
  3.       student_id INT PRIMARY KEY AUTO_INCREMENT,
  4.       student_no VARCHAR(20) UNIQUE NOT NULL COMMENT '学号',
  5.       name VARCHAR(50) NOT NULL COMMENT '姓名',
  6.       gender ENUM('男', '女', '其他') COMMENT '性别',
  7.       birth_date DATE COMMENT '出生日期',
  8.       email VARCHAR(100) UNIQUE COMMENT '邮箱',
  9.       phone VARCHAR(20) COMMENT '电话',
  10.       address VARCHAR(200) COMMENT '地址',
  11.       enrollment_date DATE NOT NULL COMMENT '入学日期',
  12.       major_id INT NOT NULL COMMENT '专业ID',
  13.       class_id INT NOT NULL COMMENT '班级ID',
  14.       status ENUM('在读', '休学', '毕业', '退学') NOT NULL DEFAULT '在读' COMMENT '状态',
  15.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  16.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  17. );
  18. -- 创建专业表
  19. CREATE TABLE majors (
  20.       major_id INT PRIMARY KEY AUTO_INCREMENT,
  21.       major_name VARCHAR(50) UNIQUE NOT NULL COMMENT '专业名称',
  22.       department_id INT NOT NULL COMMENT '院系ID',
  23.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  24.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  25. );
  26. -- 创建院系表
  27. CREATE TABLE departments (
  28.       department_id INT PRIMARY KEY AUTO_INCREMENT,
  29.       department_name VARCHAR(50) UNIQUE NOT NULL COMMENT '院系名称',
  30.       dean VARCHAR(50) COMMENT '院长',
  31.       phone VARCHAR(20) COMMENT '联系电话',
  32.       email VARCHAR(100) COMMENT '邮箱',
  33.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  34.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  35. );
  36. -- 创建班级表
  37. CREATE TABLE classes (
  38.       class_id INT PRIMARY KEY AUTO_INCREMENT,
  39.       class_name VARCHAR(50) NOT NULL COMMENT '班级名称',
  40.       major_id INT NOT NULL COMMENT '专业ID',
  41.       grade INT NOT NULL COMMENT '年级',
  42.       head_teacher VARCHAR(50) COMMENT '班主任',
  43.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  44.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  45. );
  46. -- 创建课程表
  47. CREATE TABLE courses (
  48.       course_id INT PRIMARY KEY AUTO_INCREMENT,
  49.       course_code VARCHAR(20) UNIQUE NOT NULL COMMENT '课程代码',
  50.       course_name VARCHAR(100) NOT NULL COMMENT '课程名称',
  51.       credit TINYINT NOT NULL COMMENT '学分',
  52.       course_type ENUM('必修课', '选修课', '公共课') NOT NULL COMMENT '课程类型',
  53.       department_id INT NOT NULL COMMENT '开课院系ID',
  54.       teacher_id INT NOT NULL COMMENT '授课教师ID',
  55.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  56.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  57. );
  58. -- 创建教师表
  59. CREATE TABLE teachers (
  60.       teacher_id INT PRIMARY KEY AUTO_INCREMENT,
  61.       teacher_no VARCHAR(20) UNIQUE NOT NULL COMMENT '教师编号',
  62.       name VARCHAR(50) NOT NULL COMMENT '姓名',
  63.       gender ENUM('男', '女', '其他') COMMENT '性别',
  64.       title VARCHAR(50) COMMENT '职称',
  65.       department_id INT NOT NULL COMMENT '所属院系ID',
  66.       email VARCHAR(100) UNIQUE COMMENT '邮箱',
  67.       phone VARCHAR(20) COMMENT '电话',
  68.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  69.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  70. );
  71. -- 创建考试安排表
  72. CREATE TABLE exam_arrangements (
  73.       exam_id INT PRIMARY KEY AUTO_INCREMENT,
  74.       course_id INT NOT NULL COMMENT '课程ID',
  75.       exam_date DATE NOT NULL COMMENT '考试日期',
  76.       start_time TIME NOT NULL COMMENT '开始时间',
  77.       end_time TIME NOT NULL COMMENT '结束时间',
  78.       exam_room VARCHAR(50) NOT NULL COMMENT '考场',
  79.       invigilator VARCHAR(100) COMMENT '监考老师',
  80.       exam_type ENUM('期中', '期末', '补考', '重修') NOT NULL COMMENT '考试类型',
  81.       academic_year VARCHAR(20) NOT NULL COMMENT '学年',
  82.       semester TINYINT NOT NULL COMMENT '学期',
  83.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  84.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  85. );
  86. -- 创建成绩表
  87. CREATE TABLE scores (
  88.       score_id INT PRIMARY KEY AUTO_INCREMENT,
  89.       student_id INT NOT NULL COMMENT '学生ID',
  90.       exam_id INT NOT NULL COMMENT '考试ID',
  91.       score DECIMAL(5,2) COMMENT '成绩',
  92.       score_type ENUM('原始分', '平时分', '卷面分', '最终分') NOT NULL DEFAULT '最终分' COMMENT '成绩类型',
  93.       remark VARCHAR(200) COMMENT '备注',
  94.       created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  95.       updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  96.       UNIQUE KEY unique_student_exam (student_id, exam_id)
  97. );
  98. -- 创建索引
  99. CREATE INDEX idx_students_student_no ON students(student_no);
  100. CREATE INDEX idx_students_major_id ON students(major_id);
  101. CREATE INDEX idx_students_class_id ON students(class_id);
  102. CREATE INDEX idx_majors_department_id ON majors(department_id);
  103. CREATE INDEX idx_classes_major_id ON classes(major_id);
  104. CREATE INDEX idx_courses_department_id ON courses(department_id);
  105. CREATE INDEX idx_courses_teacher_id ON courses(teacher_id);
  106. CREATE INDEX idx_teachers_department_id ON teachers(department_id);
  107. CREATE INDEX idx_exam_arrangements_course_id ON exam_arrangements(course_id);
  108. CREATE INDEX idx_scores_student_id ON scores(student_id);
  109. CREATE INDEX idx_scores_exam_id ON scores(exam_id);
  110. -- 添加外键约束
  111. ALTER TABLE students ADD CONSTRAINT fk_students_major_id FOREIGN KEY (major_id) REFERENCES majors(major_id);
  112. ALTER TABLE students ADD CONSTRAINT fk_students_class_id FOREIGN KEY (class_id) REFERENCES classes(class_id);
  113. ALTER TABLE majors ADD CONSTRAINT fk_majors_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);
  114. ALTER TABLE classes ADD CONSTRAINT fk_classes_major_id FOREIGN KEY (major_id) REFERENCES majors(major_id);
  115. ALTER TABLE courses ADD CONSTRAINT fk_courses_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);
  116. ALTER TABLE courses ADD CONSTRAINT fk_courses_teacher_id FOREIGN KEY (teacher_id) REFERENCES teachers(teacher_id);
  117. ALTER TABLE teachers ADD CONSTRAINT fk_teachers_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);
  118. ALTER TABLE exam_arrangements ADD CONSTRAINT fk_exam_arrangements_course_id FOREIGN KEY (course_id) REFERENCES courses(course_id);
  119. ALTER TABLE scores ADD CONSTRAINT fk_scores_student_id FOREIGN KEY (student_id) REFERENCES students(student_id);
  120. ALTER TABLE scores ADD CONSTRAINT fk_scores_exam_id FOREIGN KEY (exam_id) REFERENCES exam_arrangements(exam_id);
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册