tio-boot 案例 - 增强检索
这篇文章主要介绍,如何使用 tio-boot 框架的其他相关框架完成下面的功能
- 爬取数据:编写爬虫从网页上爬取数据
- 数据入库:将爬取的数据存入数据库,这里使用 postgre 数据库
- 向量化:将入库后的数据的部分字段进行向量化并入库,方便后续搜索
- 定时任务:使用定时任务每 1 小时更新整体的数据
网页数据分析
这里爬取的数据是是 SJSU 2024 年的 Class Schedule 数据,
- 网页地址https://www.sjsu.edu/classes/schedules/fall-2024.php
- 数据字段 Section,Class Number,Mode of Instruction,Course Title,Satisfies,Units,Type,Days,Times,Instructor,Location,Dates,Open Seats,Notes
-发现网页数据数据格式大致如下
<table id="classSchedule" class="responsive display o-table o-table--class-schedule hide" style="width:100%">
<thead>
<tr>
<th class="all" width="10%" data-priority="1">Section</th>
<th>Class Number</th>
<th>Mode of Instruction</th>
<th class="all" data-priority="2">Course Title</th>
<th>Satisfies</th>
<th>Units</th>
<th>Type</th>
<th data-priority="3">Days</th>
<th data-priority="4">Times</th>
<th>Instructor</th>
<th>Location</th>
<th data-priority="3">Dates</th>
<th data-priority="2">Open Seats</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a href="https://catalog.sjsu.edu/content.php?filter%5B27%5D=AAS&filter%5B29%5D=1&cur_cat_oid=15&navoid=5382"
>AAS 1 (Section 04)</a
>
</td>
<td>47610</td>
<td>In Person</td>
<td>Introduction to Asian American Studies</td>
<td>GE: F</td>
<td>3.0</td>
<td>LEC</td>
<td>TR</td>
<td>12:00PM-01:15PM</td>
<td><a href="mailto:saugher.nojan@sjsu.edu">Saugher Nojan</a></td>
<td>HGH122</td>
<td>08/21/24-12/09/24</td>
<td>0</td>
<td> </td>
</tr>
<tr>
<td>
<a href="https://catalog.sjsu.edu/content.php?filter%5B27%5D=AAS&filter%5B29%5D=1&cur_cat_oid=15&navoid=5382"
>AAS 1 (Section 05)</a
>
</td>
<td>47611</td>
<td>In Person</td>
<td>Introduction to Asian American Studies</td>
<td>GE: F</td>
<td>3.0</td>
<td>LEC</td>
<td>MW</td>
<td>03:00PM-04:15PM</td>
<td><a href="mailto:wayne.jopanda@sjsu.edu">Wayne Jopanda</a></td>
<td>HGH122</td>
<td>08/21/24-12/09/24</td>
<td>0</td>
<td> </td>
</tr>
</tbody>
</table>
创建数据表
通过对网数据的分析,创的数据表如下
CREATE TABLE "public"."rumi_sjsu_class_schedule_2024_fall" (
"id" "int8" NOT NULL,
"term" "varchar",
"section" "varchar",
"section_url" "varchar",
"class_number" "varchar",
"mode_of_instruction" "varchar",
"course_title" "varchar",
"satisfies" "varchar",
"units" "varchar",
"type" "varchar",
"days" "varchar",
"times" "varchar",
"instructor" "varchar",
"instructor_email" "varchar",
"location" "varchar",
"dates" "varchar",
"open_seats" "varchar",
"notes" "text",
"source_url" "varchar",
"section_vector" vector(3072),
"remark" "varchar",
"creator" "varchar" DEFAULT ''::character varying,
"create_time" "timestamp" NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" "varchar" DEFAULT ''::character varying,
"update_time" "timestamp" NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" "int2" NOT NULL DEFAULT 0,
"tenant_id" "int8" NOT NULL DEFAULT 0,
)
- section_vector 用于存储向量,设置维度为 3072,这是目前 openai 的 text-text-embedding-3-large 将文本向量化后的文档
爬取数据并入库
添加依赖库
笔者使用的爬虫框架是 webmagic,数据库操作框架是 api-table,添加依赖如下
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>api-table</artifactId>
<version>${table-to-json.version}</version>
</dependency>
<!-- Apache HTTP Client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.9.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
编辑代码
SJSUClassScheduleProcessor.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import lombok.extern.slf4j.Slf4j;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;
@Slf4j
public class SJSUClassScheduleProcessor implements PageProcessor {
private Site site = Site.me().setRetryTimes(3).setSleepTime(5000);
@Override
public void process(Page page) {
Elements rows = page.getHtml().getDocument().select("#classSchedule > tbody > tr");
String sourceUrl = page.getUrl().toString();
List<Map<String, Object>> dataList = new ArrayList<>();
int rowSize = rows.size();
// log.info("row size:{}", rowSize);
for (int i = 0; i < rowSize; i++) {
Element row = rows.get(i);
Elements columns = row.select("td");
int size = columns.size(); // 正常请求下值是14
// log.info("size:{}", size);
if (size == 14) {
getDataList(i, row, columns, sourceUrl, dataList);
} else {
log.error("columns size is not 14");
}
}
page.putField("dataList", dataList);
log.info("dataList size:{}", dataList.size());
}
/**
* can be Enrolled columns is 15
*/
private void getDataList(int i, Element row, Elements columns, String sourceUrl, List<Map<String, Object>> dataList) {
// get data
String section = columns.get(0).text().trim();
String class_number = columns.get(1).text().trim();
String mode_of_instruction = columns.get(2).text().trim();
String course_title = columns.get(3).text().trim();
String satisfies = columns.get(4).text().trim();
String units = columns.get(5).text().trim();
String type = columns.get(6).text().trim();
String days = columns.get(7).text().trim();
String times = columns.get(8).text().trim();
String instructor = columns.get(9).text().trim();
String location = columns.get(10).text().trim();
String dates = columns.get(11).text().trim();
String open_seats = columns.get(12).text().trim();
String notes = columns.get(13).text().trim();
String section_url = columns.get(0).select("a").attr("href").trim();
String instructor_email = columns.get(9).select("a").attr("href").trim();
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("section", section);
dataMap.put("class_number", class_number);
dataMap.put("mode_of_instruction", mode_of_instruction);
dataMap.put("course_title", course_title);
dataMap.put("satisfies", satisfies);
dataMap.put("units", units);
dataMap.put("type", type);
dataMap.put("days", days);
dataMap.put("times", times);
dataMap.put("instructor", instructor);
dataMap.put("location", location);
dataMap.put("dates", dates);
dataMap.put("open_seats", open_seats);
dataMap.put("notes", notes);
dataMap.put("section_url", section_url);
dataMap.put("instructor_email", instructor_email);
dataMap.put("source_url", sourceUrl);
dataMap.put("term", "Fall 2024");
dataList.add(dataMap);
}
@Override
public Site getSite() {
return site;
}
}
SJSUClassSchedulePipeline.java
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.postgresql.util.PGobject;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Record;
import com.litongjava.db.utils.PgVectorUtils;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.open.chat.services.VectorService;
import com.litongjava.tio.utils.snowflake.SnowflakeIdUtils;
import lombok.extern.slf4j.Slf4j;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;
@Slf4j
public class SJSUClassSchedulePipeline implements Pipeline {
@Override
public void process(ResultItems resultItems, Task task) {
VectorService vectorService = Aop.get(VectorService.class);
List<Map<String, Object>> dataList = resultItems.get("dataList");
List<Record> saveRecords = new ArrayList<>();
Db.delete("delete from rumi_sjsu_class_schedule_2024_fall");
for (int i = 0; i < dataList.size(); i++) {
Map<String, Object> map = dataList.get(i);
Record record = new Record().setColumns(map);
record.put("id", SnowflakeIdUtils.id());
String sectionVector = vectorService.getVector(record.getStr("section"));
PGobject pgVector = PgVectorUtils.getPgVector(sectionVector);
record.set("section_vector", pgVector);
saveRecords.add(record);
// 批量保存,假设每10条数据进行一次批量保存
if (saveRecords.size() >= 10 || i == dataList.size() - 1) {
Db.batchSave("rumi_sjsu_class_schedule_2024_fall", saveRecords, saveRecords.size());
log.info("Batch saved records up to index: {}", i);
saveRecords.clear();
}
}
}
}
getVector 方法
public String getVector(String text) {
String v = null;
String sql = "select v from " + TableNames.rumi_embedding + " where t=? and m=?";
PGobject pGobject = Db.queryFirst(sql, text, OpenAiModels.text_embedding_3_large);
if (pGobject != null) {
v = pGobject.getValue();
} else {
String model = "text-embedding-3-large";
float[] embeddingArray = OpenAiClient.embeddingArray(text, model);
String string = Arrays.toString(embeddingArray);
long id = SnowflakeIdUtils.id();
v = (String) string;
PGobject pgVector = PgVectorUtils.getPgVector(v);
Record saveRecord = new Record().set("t", text).set("v", pgVector).set("id", id).set("m", model);
Db.save("rumi_embedding",saveRecord);
}
return v;
}
SJSUClassScheduleService.java
import com.litongjava.open.chat.spider.sjsu.SJSUClassSchedulePipeline;
import com.litongjava.open.chat.spider.sjsu.SJSUClassScheduleProcessor;
import us.codecraft.webmagic.Spider;
public class SJSUClassScheduleService {
public void index() {
String url = "https://www.sjsu.edu/classes/schedules/fall-2024.php";
Spider.create(new SJSUClassScheduleProcessor())
// url
.addUrl(url) // Add the url you want to scrape
.addPipeline(new SJSUClassSchedulePipeline())
//
.thread(5).run();
}
}
上面的代码非常简单,这就不过多解释了
向量化
向量模型
向量模型可以将 文本
转为具有语义信息的向量,这里使用的向量模型是 openai 的 text-text-embedding-3-large,截止了 2024 年 7 月 17 日,这个模型还需要联网调用.笔者使用 java-openai 库调用 openai 的 text-embedding 模型
添加依赖
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>java-openai</artifactId>
<version>1.0.1</version>
</dependency>
创建数据表
创建一张数据表,缓存向量结果,放置相同的文本多次向量化
CREATE TABLE "rumi_embedding" (
"id" "int8" PRIMARY KEY,
"t" "text" ,
"m" "varchar" ,
"v" "vector(3072)",
);
向量化
VectorService.java
- 调用 OpneAi 的 text-embedding 模型进行向量
- 将向量结果存入数据库
- 对外提供向量结果的查询
import java.util.Arrays;
import org.postgresql.util.PGobject;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.jfinal.plugin.utils.PgVectorUtils;
import com.litongjava.open.chat.constants.TableNames;
import com.litongjava.openai.client.OpenAiClient;
import com.litongjava.table.utils.SnowflakeIdUtils;
public class VectorService {
public String getVector(String text) {
String v = null;
String sql = "select v from " + TableNames.rumi_embedding + " where t=?";
PGobject pGobject = Db.queryFirst(sql, text);
if (pGobject != null) {
v = pGobject.getValue();
} else {
String model = "text-embedding-3-large";
Float[] embeddingArray = OpenAiClient.embeddingArray(text, model);
String string = Arrays.toString(embeddingArray);
long id = SnowflakeIdUtils.id();
v = (String) string;
PGobject pgVector = PgVectorUtils.getPgVector(v);
Record saveRecord = new Record().set("t", text).set("v", pgVector).set("id", id).set("m", model)
.setTableName("rumi_embedding");
Db.save(saveRecord);
}
return v;
}
}
VectorController
- 查询数据表的数据进行向量化
- 使用异步任务进行
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.open.chat.constants.TableNames;
import com.litongjava.open.chat.services.VectorService;
import com.litongjava.annotation.RequestPath;
import com.litongjava.tio.utils.resp.RespBodyVo;
import com.litongjava.tio.utils.string.StrUtils;
import com.litongjava.tio.utils.thread.ThreadUtils;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/vector")
@Slf4j
public class VectorController {
public RespBodyVo rumi_sjsu_class_schedule_2024_fall() {
// 使用ExecutorService异步执行任务
ThreadUtils.getFixedThreadPool().submit(() -> {
try {
String selectSql = "select id,section from " + TableNames.rumi_sjsu_class_schedule_2024_fall
+ " where section_vector is null";
List<Record> records = Db.find(selectSql);
String updateSql = "update " + TableNames.rumi_sjsu_class_schedule_2024_fall
+ " set section_vector= ? where id=?";
for (Record record : records) {
Object id = record.get("id");
String section = record.getStr("section");
if (record.get("section_vector") != null) {
log.info("skip:{},{}", id, section);
continue;
}
if (StrUtils.isEmpty(section)) {
log.error("please check the data:{},{},{}", TableNames.rumi_sjsu_class_schedule_2024_fall, id, section);
continue;
}
String sectionVector = Aop.get(VectorService.class).getVector(section);
int update = Db.update(updateSql, sectionVector, id);
if (update < 1) {
log.error("update fail:{},{},{}", TableNames.rumi_sjsu_class_schedule_2024_fall, id, sectionVector);
}
}
log.info("finished");
} catch (Exception e) {
log.error("expetion", e);
}
});
return RespBodyVo.ok();
}
}
触发向量任务
访问 endping:/vector/rumi_sjsu_class_schedule_2024_fall 即可触发向量
定时任务
添加库
笔者使用的定时任务框架是 quartz,需要添加的依赖如下
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
定时任务
src\main\resources\config\tio-quartz.properties 内容如下
com.litongjava.open.chat.task.TaskForPerHour = 0 0 */1 * * ?
TaskForPerHour.java
import org.quartz.JobExecutionContext;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.open.chat.services.SJSUClassScheduleService;
import com.litongjava.tio.utils.quartz.AbstractJobWithLog;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TaskForPerHour extends AbstractJobWithLog {
@Override
public void run(JobExecutionContext context) throws Exception {
log.info("run (SJSUClassScheduleService.class).index()");
Aop.get(SJSUClassScheduleService.class).index();
}
}
TaskForPerHour 是一个定时任务,一个小时执行一次
向量查询
向量查询的 SQl 如下
--# rumi_sjsu_class_schedule_2024_fall.vector_search
SELECT
id,term,section,section_url,class_number,mode_of_instruction,course_title,satisfies,units,type,days,times,instructor,instructor_email,
location,dates,open_seats,notes,
(1-(section_vector <=> input_vector)) AS class_name_similarity
FROM
rumi_sjsu_class_schedule_2024_fall,
LATERAL (
VALUES (
?::VECTOR(3072)
)
) AS input(input_vector)
where
(1-(section_vector <=> input_vector)) >0.3
ORDER BY
(1-(section_vector <=> input_vector)) DESC
LIMIT 10;
package com.litongjava.open.chat.services;
import java.util.List;
import org.junit.Test;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.jfinal.plugin.template.SqlTemplates;
import com.litongjava.open.chat.config.DbConfig;
import com.litongjava.table.utils.MarkdownTableUtils;
import com.litongjava.tio.utils.environment.EnvUtils;
public class VectorDatabaseSerchServiceTest2 {
@Test
public void test() {
EnvUtils.load();
new DbConfig().config();
String question = "Tell me about CS 46B";
String vectorString = Aop.get(VectorService.class).getVector(question);
String classScheduleSql = SqlTemplates.get("rumi_sjsu_class_schedule_2024_fall.vector_search");
List<Record> classes = Db.find(classScheduleSql, vectorString);
String table = MarkdownTableUtils.to(classes);
System.out.println(table);
}
}
这里假设的用户的问题是 "Tell me about CS 46B";将过向量查询后的数据如下,可以看到 section 字段 大多数的数据都是和 CS 46B 具有语义关系的. 下一步的任务就是编写一个提示词,用户为的问题,查询结果交给大模型处理
id | term | section | section_url | class_number | mode_of_instruction | course_title | satisfies | units | type | days | times | instructor | instructor_email | location | dates | open_seats | notes | class_name_similarity |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
395424600148852861 | Fall 2024 | CS 46B (Section 02) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 44758 | In Person | Introduction to Data Structures | 4.0 | LEC | MW | 03:00PM-04:15PM | Chung-Wen Tsao | mailto:chung-wen.tsao@sjsu.edu | SH100 | 08/21/24-12/09/24 | 113 | 0.6762653975170425 | ||
395424600148852860 | Fall 2024 | CS 46B (Section 01) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 42278 | In Person | Introduction to Data Structures | 4.0 | LEC | MW | 04:30PM-05:45PM | Faranak Abri | mailto:faranak.abri@sjsu.edu | SCI142 | 08/21/24-12/09/24 | 39 | 0.6732165996511937 | ||
395424600148852846 | Fall 2024 | CS 46A (Section 02) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46A&cur_cat_oid=15&navoid=5382 | 48991 | In Person | Introduction to Programming | 4.0 | LEC | TR | 09:00AM-10:15AM | Staff | WSQ109 | 08/21/24-12/09/24 | 82 | 0.6337127744473038 | |||
395424600148852845 | Fall 2024 | CS 46A (Section 01) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46A&cur_cat_oid=15&navoid=5382 | 42277 | In Person | Introduction to Programming | 4.0 | LEC | TR | 12:00PM-01:15PM | Sayma Akther | mailto:sayma.akther@sjsu.edu | SH100 | 08/21/24-12/09/24 | 58 | 0.6254571229764769 | ||
395424600148852868 | Fall 2024 | CS 46B (Section 16) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 44759 | In Person | Introduction to Data Structures | 4.0 | LAB | F | 11:00AM-01:50PM | Chung-Wen Tsao | mailto:chung-wen.tsao@sjsu.edu | DH450 | 08/21/24-12/09/24 | 22 | 0.6088166832924062 | ||
395424600148852869 | Fall 2024 | CS 46B (Section 17) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 44760 | In Person | Introduction to Data Structures | 4.0 | LAB | F | 02:00PM-04:50PM | Staff | DH450 | 08/21/24-12/09/24 | 29 | 0.6062307700560641 | |||
395424600148852863 | Fall 2024 | CS 46B (Section 11) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 42301 | In Person | Introduction to Data Structures | 4.0 | LAB | F | 11:00AM-01:50PM | Faranak Abri | mailto:faranak.abri@sjsu.edu | MH222 | 08/21/24-12/09/24 | 0 | 0.6008694350978779 | ||
395424600148852867 | Fall 2024 | CS 46B (Section 15) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 42305 | In Person | Introduction to Data Structures | 4.0 | LAB | F | 02:00PM-04:50PM | Chung-Wen Tsao | mailto:chung-wen.tsao@sjsu.edu | MH223 | 08/21/24-12/09/24 | 31 | 0.5992897155017519 | ||
395424600148852864 | Fall 2024 | CS 46B (Section 12) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 42302 | In Person | Introduction to Data Structures | 4.0 | LAB | F | 02:00PM-04:50PM | Faranak Abri | mailto:faranak.abri@sjsu.edu | MH222 | 08/21/24-12/09/24 | 15 | 0.5917533198579384 | ||
395424600148852865 | Fall 2024 | CS 46B (Section 13) | https://catalog.sjsu.edu/content.php?filter%5B27%5D=CS&filter%5B29%5D=46B&cur_cat_oid=15&navoid=5382 | 42303 | In Person | Introduction to Data Structures | 4.0 | LAB | F | 11:00AM-01:50PM | Faranak Abri | mailto:faranak.abri@sjsu.edu | MH223 | 08/21/24-12/09/24 | 13 | 0.5900395405245417 |
大模型推理
编写代码,进行推理
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.jfinal.plugin.template.SqlTemplates;
import com.litongjava.open.chat.config.DbConfig;
import com.litongjava.openai.chat.ChatMessage;
import com.litongjava.openai.chat.ChatResponseVo;
import com.litongjava.openai.chat.OpenAiChatRequestVo;
import com.litongjava.openai.client.OpenAiClient;
import com.litongjava.openai.constants.OpenAiModels;
import com.litongjava.table.utils.MarkdownTableUtils;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.json.JsonUtils;
public class ClassLLMAskTest {
@Test
public void test() {
String question = "Who is the professor in CS 46B?";
EnvUtils.load();
new DbConfig().config();
String vectorString = Aop.get(VectorService.class).getVector(question);
String classScheduleSql = SqlTemplates.get("rumi_sjsu_class_schedule_2024_fall.vector_search");
List<Record> classes = Db.find(classScheduleSql, vectorString);
String table = MarkdownTableUtils.to(classes);
List<ChatMessage> messages = new ArrayList<>();
// 初始提示词
messages.add(new ChatMessage("system", "你是一个课程助手"));
// 数据
messages.add(new ChatMessage("system", table));
// 用户问题
messages.add(new ChatMessage("user", question));
OpenAiChatRequestVo openAiChatRequestVo = new OpenAiChatRequestVo();
openAiChatRequestVo.setModel(OpenAiModels.gpt_4_turbo_2024_04_09);
openAiChatRequestVo.setMessages(messages);
ChatResponseVo chatResponseVo = OpenAiClient.chatCompletions(openAiChatRequestVo);
System.out.println(JsonUtils.toJson(chatResponseVo));
}
}
推理结果
{
"object": "chat.completion",
"id": "chatcmpl-9ltBXMJtcf0ZZAT2BMrWMIj4PMs09",
"system_fingerprint": "fp_595e3bc347",
"model": "gpt-4-turbo-2024-04-09",
"choices": [
{
"message": {
"content": "For the CS 46B course sections in Fall 2024 at San Jose State University, there are two professors teaching different sections:\n\n1. **Chung-Wen Tsao**\n - Teaching Section 02 (LEC), Section 16 (LAB), and Section 15 (LAB).\n\n2. **Faranak Abri**\n - Teaching Section 01 (LEC), Section 11 (LAB), Section 13 (LAB), and Section 12 (LAB).\n\nSome laboratories (e.g., Section 17) have not specified their instructor and are listed as 'Staff'.",
"role": "assistant",
"tool_calls": null
},
"index": 0,
"logprobs": null,
"delta": null,
"finish_reason": "stop"
}
],
"usage": {
"completion_tokens": 118,
"prompt_tokens": 1634,
"total_tokens": 1752
},
"created": "1721200575"
}
格式化后的推理结果,通过分析可以返现,大模型精准的给出了回复
For the CS 46B course sections in Fall 2024 at San Jose State University, there are two professors teaching different sections:
Chung-Wen Tsao - Teaching Section 02 (LEC), Section 16 (LAB), and Section 15 (LAB).
Faranak Abri - Teaching Section 01 (LEC), Section 11 (LAB), Section 13 (LAB), and Section 12 (LAB).
Some laboratories (e.g., Section 17) have not specified their instructor and are listed as 'Staff'.