Skip to content

tio-boot 案例 - 增强检索 ​

这篇文章主要介绍,如何使用 tio-boot 框架的其他相关框架完成下面的功能

  • 爬取数据:编写爬虫从网页上爬取数据
  • 数据入库:将爬取的数据存入数据库,这里使用 postgre 数据库
  • 向量化:将入库后的数据的部分字段进行向量化并入库,方便后续搜索
  • 定时任务:使用定时任务每 1 小时更新整体的数据

网页数据分析 ​

这里爬取的数据是是 SJSU 2024 年的 Class Schedule 数据,

-发现网页数据数据格式大致如下

html
<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>&nbsp;</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>&nbsp;</td>
    </tr>
  </tbody>
</table>

创建数据表 ​

通过对网数据的分析,创的数据表如下

sql
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,添加依赖如下

xml
<dependency>
  <groupId>nexus.io</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

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

java
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.postgresql.util.PGobject;

import nexus.io.db.activerecord.Db;
import nexus.io.db.activerecord.Row;
import nexus.io.db.utils.PgVectorUtils;
import nexus.io.jfinal.aop.Aop;
import nexus.io.open.chat.services.VectorService;
import nexus.io.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<Row> 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);
      Row row = new Row().setColumns(map);
      row.put("id", SnowflakeIdUtils.id());
      String sectionVector = vectorService.getVector(row.getStr("section"));
      PGobject pgVector = PgVectorUtils.getPgVector(sectionVector);
      row.set("section_vector", pgVector);
      saveRecords.add(row);

      // 批量保存,假设每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 方法

java
  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);
      Row saveRecord = new Row().set("t", text).set("v", pgVector).set("id", id).set("m", model);
      Db.save("rumi_embedding",saveRecord);
    }
    return v;
  }

SJSUClassScheduleService.java

java
import nexus.io.open.chat.spider.sjsu.SJSUClassSchedulePipeline;
import nexus.io.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 模型

添加依赖 ​

xml
    <dependency>
      <groupId>nexus.io</groupId>
      <artifactId>java-openai</artifactId>
      <version>1.0.1</version>
    </dependency>

创建数据表 ​

创建一张数据表,缓存向量结果,放置相同的文本多次向量化

sql
CREATE TABLE "rumi_embedding" (
  "id" "int8" PRIMARY KEY,
  "t" "text" ,
  "m" "varchar" ,
  "v" "vector(3072)",
);

向量化 ​

VectorService.java

  • 调用 OpneAi 的 text-embedding 模型进行向量
  • 将向量结果存入数据库
  • 对外提供向量结果的查询
java
import java.util.Arrays;

import org.postgresql.util.PGobject;

import nexus.io.jfinal.plugin.activerecord.Db;
import nexus.io.jfinal.plugin.activerecord.Row;
import nexus.io.jfinal.plugin.utils.PgVectorUtils;
import nexus.io.open.chat.constants.TableNames;
import nexus.io.openai.client.OpenAiClient;
import nexus.io.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);
      Row saveRecord = new Row().set("t", text).set("v", pgVector).set("id", id).set("m", model)
          .setTableName("rumi_embedding");
      Db.save(saveRecord);
    }
    return v;
  }

}

VectorController

  • 查询数据表的数据进行向量化
  • 使用异步任务进行
java
import nexus.io.jfinal.aop.Aop;
import nexus.io.jfinal.plugin.activerecord.Db;
import nexus.io.jfinal.plugin.activerecord.Row;
import nexus.io.open.chat.constants.TableNames;
import nexus.io.open.chat.services.VectorService;
import nexus.io.annotation.RequestPath;
import nexus.io.tio.utils.resp.RespBodyVo;
import nexus.io.tio.utils.string.StrUtils;
import nexus.io.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<Row> records = Db.find(selectSql);

        String updateSql = "update " + TableNames.rumi_sjsu_class_schedule_2024_fall
            + " set section_vector= ? where id=?";

        for (Row row : records) {
          Object id = row.get("id");
          String section = row.getStr("section");

          if (row.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,需要添加的依赖如下

xml
    <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 内容如下

nexus.io.open.chat.task.TaskForPerHour = 0 0 */1 * * ?

TaskForPerHour.java

import org.quartz.JobExecutionContext;

import nexus.io.jfinal.aop.Aop;
import nexus.io.open.chat.services.SJSUClassScheduleService;
import nexus.io.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 如下

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;
java
package nexus.io.open.chat.services;

import java.util.List;

import org.junit.Test;

import nexus.io.jfinal.aop.Aop;
import nexus.io.jfinal.plugin.activerecord.Db;
import nexus.io.jfinal.plugin.activerecord.Row;
import nexus.io.jfinal.plugin.template.SqlTemplates;
import nexus.io.open.chat.config.DbConfig;
import nexus.io.table.utils.MarkdownTableUtils;
import nexus.io.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<Row> classes = Db.find(classScheduleSql, vectorString);
    String table = MarkdownTableUtils.to(classes);
    System.out.println(table);
  }
}

这里假设的用户的问题是 "Tell me about CS 46B";将过向量查询后的数据如下,可以看到 section 字段 大多数的数据都是和 CS 46B 具有语义关系的. 下一步的任务就是编写一个提示词,用户为的问题,查询结果交给大模型处理

idtermsectionsection_urlclass_numbermode_of_instructioncourse_titlesatisfiesunitstypedaystimesinstructorinstructor_emaillocationdatesopen_seatsnotesclass_name_similarity
395424600148852861Fall 2024CS 46B (Section 02)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538244758In PersonIntroduction to Data Structures4.0LECMW03:00PM-04:15PMChung-Wen Tsaomailto:chung-wen.tsao@sjsu.eduSH10008/21/24-12/09/241130.6762653975170425
395424600148852860Fall 2024CS 46B (Section 01)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538242278In PersonIntroduction to Data Structures4.0LECMW04:30PM-05:45PMFaranak Abrimailto:faranak.abri@sjsu.eduSCI14208/21/24-12/09/24390.6732165996511937
395424600148852846Fall 2024CS 46A (Section 02)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46A&cur_cat_oid=15&navoid=538248991In PersonIntroduction to Programming4.0LECTR09:00AM-10:15AMStaffWSQ10908/21/24-12/09/24820.6337127744473038
395424600148852845Fall 2024CS 46A (Section 01)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46A&cur_cat_oid=15&navoid=538242277In PersonIntroduction to Programming4.0LECTR12:00PM-01:15PMSayma Akthermailto:sayma.akther@sjsu.eduSH10008/21/24-12/09/24580.6254571229764769
395424600148852868Fall 2024CS 46B (Section 16)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538244759In PersonIntroduction to Data Structures4.0LABF11:00AM-01:50PMChung-Wen Tsaomailto:chung-wen.tsao@sjsu.eduDH45008/21/24-12/09/24220.6088166832924062
395424600148852869Fall 2024CS 46B (Section 17)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538244760In PersonIntroduction to Data Structures4.0LABF02:00PM-04:50PMStaffDH45008/21/24-12/09/24290.6062307700560641
395424600148852863Fall 2024CS 46B (Section 11)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538242301In PersonIntroduction to Data Structures4.0LABF11:00AM-01:50PMFaranak Abrimailto:faranak.abri@sjsu.eduMH22208/21/24-12/09/2400.6008694350978779
395424600148852867Fall 2024CS 46B (Section 15)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538242305In PersonIntroduction to Data Structures4.0LABF02:00PM-04:50PMChung-Wen Tsaomailto:chung-wen.tsao@sjsu.eduMH22308/21/24-12/09/24310.5992897155017519
395424600148852864Fall 2024CS 46B (Section 12)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538242302In PersonIntroduction to Data Structures4.0LABF02:00PM-04:50PMFaranak Abrimailto:faranak.abri@sjsu.eduMH22208/21/24-12/09/24150.5917533198579384
395424600148852865Fall 2024CS 46B (Section 13)https://catalog.sjsu.edu/content.php?filter[27]=CS&filter[29]=46B&cur_cat_oid=15&navoid=538242303In PersonIntroduction to Data Structures4.0LABF11:00AM-01:50PMFaranak Abrimailto:faranak.abri@sjsu.eduMH22308/21/24-12/09/24130.5900395405245417

大模型推理 ​

编写代码,进行推理 ​

java

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import nexus.io.jfinal.aop.Aop;
import nexus.io.jfinal.plugin.activerecord.Db;
import nexus.io.jfinal.plugin.activerecord.Row;
import nexus.io.jfinal.plugin.template.SqlTemplates;
import nexus.io.open.chat.config.DbConfig;
import nexus.io.openai.chat.ChatMessage;
import nexus.io.openai.chat.ChatResponseVo;
import nexus.io.openai.chat.OpenAiChatRequestVo;
import nexus.io.openai.client.OpenAiClient;
import nexus.io.openai.consts.OpenAiModels;
import nexus.io.table.utils.MarkdownTableUtils;
import nexus.io.tio.utils.environment.EnvUtils;
import nexus.io.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<Row> 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));
  }
}

推理结果 ​

json
{
  "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:

  1. Chung-Wen Tsao - Teaching Section 02 (LEC), Section 16 (LAB), and Section 15 (LAB).

  2. 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'.

MIT Licensed