当前位置: 首页 > news >正文

FastGPT 源码:如何实现 “问题优化“

文章目录

    • FastGPT 源码:如何实现 "问题优化"
      • 一、前言
      • 二、源码分析
        • 2.1 queryExtension.ts 提示词
        • 2.2 queryExtension.ts 核心逻辑
        • 2.3 queryExtension 引用位置
      • 三、流程总结

FastGPT 源码:如何实现 “问题优化”

一、前言

  1. 问题优化的背景和目的
  • 在 RAG (检索增强生成) 系统中,用户的问题需要去数据库中执行向量搜索来找到相关内容
  • 但在连续对话中,用户的后续问题往往是不完整的,比如"第二点是什么"这样的问题
  • 如果直接用这样的问题去搜索,由于缺乏上下文,很难找到相关内容
  • 因此需要问题优化模块来补全用户的问题,使其变得完整和明确
  1. 核心实现方式
    FastGPT 主要通过 queryExtension 函数来实现问题优化,核心代码在 packages/service/core/ai/functions/queryExtension.ts 中:
  • 输入参数包括:

    • chatBg: 对话背景
    • query: 用户当前问题
    • histories: 历史对话记录
    • model: 使用的模型
  • 实现流程:

    1. 构建提示词模板,包含示例和要求
    2. 结合历史记录和当前问题生成完整的提示词
    3. 调用 AI 模型生成多个检索词
    4. 返回原始问题和扩展后的问题列表
  1. 提示词设计
    FastGPT 使用了精心设计的提示词模板来引导 AI 模型生成高质量的检索词:
const defaultPrompt = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为"原问题"生成个不同版本的"检索词",从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确,并与"原问题语言相同"。`;
  1. 实际效果
  • 原问题:“第二点是什么”
  • 历史记录中包含了 “FastGPT 的优势” 的上下文
  • 优化后的检索词会变成:
    • “介绍下 FastGPT 简便的优势”
    • “FastGPT 为什么使用起来简便?”
    • “FastGPT的有哪些简便的功能?”
  1. 搜索结果处理
    优化后的问题会用于知识库搜索,搜索结果还会经过重排序(ReRank)来进一步提高相关性:
const reRankSearchResult = async ({data,query
}: {data: SearchDataResponseItemType[];query: string;
}) => {// 使用 rerank 模型对搜索结果重新排序const results = await reRankRecall({query,documents: data.map((item) => ({id: item.id,text: `${item.q}\n${item.a}`}))});
};

这样的问题优化机制确保了即使在用户提出不完整或指代性问题时,系统也能准确理解用户意图并找到相关的知识库内容。

二、源码分析

2.1 queryExtension.ts 提示词
作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为“原问题”生成个不同版本的“检索词”,从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确,并与“原问题语言相同”。例如:
历史记录: 
"""
"""
原问题: 介绍下剧情。
检索词: ["介绍下故事的背景和主要人物。","故事的主题是什么?","剧情是是如何发展的?"]
----------------
历史记录: 
"""
Q: 对话背景。
A: 当前对话是关于 Nginx 的介绍和使用等。
"""
原问题: 怎么下载
检索词: ["Nginx 如何下载?","下载 Nginx 需要什么条件?","有哪些渠道可以下载 Nginx?"]
----------------
历史记录: 
"""
Q: 对话背景。
A: 当前对话是关于 Nginx 的介绍和使用等。
Q: 报错 "no connection"
A: 报错"no connection"可能是因为……
"""
原问题: 怎么解决
检索词: ["Nginx报错"no connection"如何解决?","造成'no connection'报错的原因。","Nginx提示'no connection',要怎么办?"]
----------------
历史记录: 
"""
Q: 护产假多少天?
A: 护产假的天数根据员工所在的城市而定。请提供您所在的城市,以便我回答您的问题。
"""
原问题: 沈阳
检索词: ["沈阳的护产假多少天?"]
----------------
历史记录: 
"""
Q: 作者是谁?
A: FastGPT 的作者是 labring。
"""
原问题: Tell me about him
检索词: ["Introduce labring, the author of FastGPT." ," Background information on author labring." "," Why does labring do FastGPT?"]
----------------
历史记录:
"""
Q: 对话背景。
A: 关于 FatGPT 的介绍和使用等问题。
"""
原问题: 你好。
检索词: ["你好"]
----------------
历史记录:
"""
Q: FastGPT 如何收费?
A: FastGPT 收费可以参考……
"""
原问题: 你知道 laf 么?
检索词: ["laf是什么?","如何使用laf?","laf的介绍。"]
----------------
历史记录:
"""
Q: FastGPT 的优势
A: 1. 开源2. 简便3. 扩展性强
"""
原问题: 介绍下第2点。
检索词: ["介绍下 FastGPT 简便的优势", "FastGPT 为什么使用起来简便?","FastGPT的有哪些简便的功能?"]。
----------------
历史记录:
"""
Q: 什么是 FastGPT?
A: FastGPT 是一个 RAG 平台。
Q: 什么是 Laf?
A: Laf 是一个云函数开发平台。
"""
原问题: 它们有什么关系?
检索词: ["FastGPT和Laf有什么关系?","FastGPT的RAG是用Laf实现的么?"]
----------------
历史记录:
"""
{{histories}}
"""
原问题: {{query}}
检索词: `
2.2 queryExtension.ts 核心逻辑

好的,我来帮您逐行分析 queryExtension.ts 的代码实现:

  1. 导入依赖
import { replaceVariable } from '@fastgpt/global/common/string/tools';  // 用于替换字符串中的变量
import { getAIApi } from '../config';  // 获取 AI API 配置
import { ChatItemType } from '@fastgpt/global/core/chat/type';  // 聊天项类型定义
import { countGptMessagesTokens } from '@fastgpt/global/common/string/tiktoken';  // 计算 token 数量
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';  // OpenAI 聊天参数类型
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';  // 聊天内容转换为运行时提示词
  1. 提示词模板定义
const defaultPrompt = `作为一个向量检索助手...`;

这是一个很长的提示词模板,包含了多个示例。每个示例都遵循相同的格式:

  • 历史记录: 展示对话上下文
  • 原问题: 用户当前的问题
  • 检索词: 生成的扩展问题数组
  1. 主函数定义
export const queryExtension = async ({chatBg,         // 对话背景query,          // 用户当前问题histories = [], // 历史对话记录model          // 使用的模型
}) => {
  1. 构建系统提示词
const systemFewShot = chatBg? `Q: 对话背景。
A: ${chatBg}
`: '';

如果有对话背景,则添加到提示词中

  1. 构建历史对话
const historyFewShot = histories.map((item) => {const role = item.obj === 'Human' ? 'Q' : 'A';return `${role}: ${chatValue2RuntimePrompt(item.value).text}`;}).join('\n');

将历史对话记录转换为 Q/A 格式的文本

  1. 合并提示词
const concatFewShot = `${systemFewShot}${historyFewShot}`.trim();
  1. 初始化 AI API
const ai = getAIApi({timeout: 480000  // 8分钟超时
});
  1. 构建请求消息
const messages = [{role: 'user',content: replaceVariable(defaultPrompt, {query: `${query}`,histories: concatFewShot})}
] as ChatCompletionMessageParam[];

将用户问题和历史记录替换到提示词模板中

  1. 调用 AI 接口
const result = await ai.chat.completions.create({model: model,temperature: 0.01,  // 温度很低,保证输出稳定性messages,stream: false
});
  1. 处理返回结果
let answer = result.choices?.[0]?.message?.content || '';
if (!answer) {return {rawQuery: query,extensionQueries: [],model,tokens: 0};
}
  1. 解析结果
answer = answer.replace(/\\"/g, '"');  // 处理转义字符
try {const queries = JSON.parse(answer) as string[];  // 解析为字符串数组return {rawQuery: query,           // 原始问题extensionQueries: Array.isArray(queries) ? queries : [],  // 扩展后的问题列表model,                     // 使用的模型tokens: countGptMessagesTokens(messages)  // token 数量};
} catch (error) {// 解析失败时返回空结果console.log(error);return {rawQuery: query,extensionQueries: [],model,tokens: 0};
}

这个函数的主要作用是:

  1. 接收用户问题和历史对话
  2. 使用精心设计的提示词引导 AI 生成多个检索词
  3. 返回原始问题和扩展后的问题列表
  4. 通过低温度参数(0.01)确保输出的稳定性
  5. 完整的错误处理确保函数的健壮性

这样的设计使得即使用户提出不完整的问题,系统也能通过上下文理解用户意图并生成合适的检索词。

2.3 queryExtension 引用位置
  1. 知识库搜索中的使用
// packages/service/core/dataset/search/utils.ts
export const datasetSearchQueryExtension = async ({model,query,histories,chatBg
}) => {const result = await queryExtension({model,query,histories,chatBg});
}

这是最主要的使用场景,在知识库搜索前对用户问题进行优化。

  1. 工作流节点中的使用
// packages/service/core/workflow/dispatch/tools/queryExternsion.ts
export const dispatchQueryExtension = async ({model,params,histories,chatBg
}) => {const queryExtensionModel = getLLMModel(model);const { extensionQueries, tokens } = await queryExtension({chatBg,query: params.userChatInput,histories,model: queryExtensionModel.model});
}

作为工作流中的一个独立节点使用,可以在对话流程中进行问题优化。

  1. 搜索测试中的使用
// projects/app/src/pages/api/core/dataset/searchTest.ts
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({model: body.model,query: body.searchParams.query,histories: body.searchParams.histories || [],chatBg: body.searchParams.chatBg
});

在知识库的搜索测试功能中使用,用于测试问题优化的效果。

  1. 配置相关
    在多个配置文件中,我们可以看到 usedInQueryExtension 的配置:
{"usedInQueryExtension": true  // 标记模型是否可用于问题优化
}
  1. UI 组件中的使用
// projects/app/src/components/core/module/DatasetParamsModal.tsx
const queryExtensionModel = watch('datasetSearchExtensionModel');

在界面上提供问题优化相关的配置选项。

三、流程总结

  1. 用户发起问题
  2. 如果启用了问题优化功能:
    • 系统会调用 queryExtension 生成多个检索词
    • 这些检索词会与原始问题一起用于知识库搜索
    • 搜索结果会经过重排序和合并
  3. 最终返回最相关的知识库内容

这种设计让系统能够:

  • 更好地理解用户的意图
  • 处理不完整或指代性的问题
  • 提高知识库搜索的准确性
  • 支持连续对话中的上下文理解

相关文章:

FastGPT 源码:如何实现 “问题优化“

文章目录 FastGPT 源码:如何实现 "问题优化"一、前言二、源码分析2.1 queryExtension.ts 提示词2.2 queryExtension.ts 核心逻辑2.3 queryExtension 引用位置 三、流程总结 FastGPT 源码:如何实现 “问题优化” 一、前言 问题优化的背景和目…...

CSS—flex布局、过渡transition属性、2D转换transform属性、3D转换transform属性

​ 1.flex布局 也叫弹性布局,是浏览器提倡的布局模型,非常适合结构化布局,提供了强大的空间分布和对齐能力,不会产生浮动布局中脱标现象,布局网页更简单,更灵活。 flex容器属性: 属性描述d…...

Spring Boot Gradle 项目中使用 @Slf4j 注解

Spring Boot Gradle 项目中,如果想使用 Slf4j 注解来启用日志记录,首先需要添加 Lombok 和 SLF4J 的依赖。可以通过以下步骤来添加它们: 1. 添加 Lombok 依赖 在 build.gradle 文件中添加以下 Lombok 依赖: dependencies {impl…...

FreeRTOS系列---程序正常,但任务无法创建

实验环境 stm32F103RCT6核心板 keil5 vscode stm32cubemx 使用stm32cubemx 问题现场 void my_task_init(void) {xTaskCreate(LED1_Task, "LED1_Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);xTaskCreate(LED2_Task, "LED2_Task", configMINIMA…...

linux应用:errno、perror、open、fopen

errno errno 是一个全局变量,定义在 头文件中。当系统调用(如 open、read、write 等)或库函数执行失败时,会将一个错误码赋值给 errno。不同的错误码代表不同的错误类型,通过检查 errno 的值,可以判断具体…...

物联网中的气象监测设备具备顶级功能

物联网中的气象监测设备具备顶级功能时,通常集成GPS、数据上报和预警系统,以确保精准监测和及时响应。以下是这些功能的详细说明: 1. GPS定位 精准定位:GPS模块提供设备的精确地理位置,确保数据与具体位置关联&#…...

15-YOLOV8OBB损失函数详解

一、YOLO OBB支持的OBB 在Ultralytics YOLO 模型中,OBB 由YOLO OBB 格式中的四个角点表示。这样可以更准确地检测到物体,因为边界框可以旋转以更好地适应物体。其坐标在 0 和 1 之间归一化: class_index x1 y1 x2 y2 x3 y3 x4 y4 YOLO 在内部处理损失和输出是xywhr 格式,x…...

WHAT - 前端异步事件流处理场景梳理

目录 一、典型场景二、解决方案与技术选型1. 基础异步控制2. 状态管理方案3. 复杂任务调度4. 任务取消机制5. 微任务队列优化 三、最佳实践建议四、工具链推荐 前端异步任务流处理是现代Web开发中常见的需求,尤其在复杂业务逻辑、高交互性应用中不可或缺。以下是常见…...

计算机网络软考

1.物理层 1.两个主机之间发送数据的过程 自上而下的封装数据,自下而上的解封装数据,实现数据的传输 2.数据、信号、码元 码元就是数字通信里用来表示信息的基本信号单元。比如在二进制中,用高电平代表 “1”、低电平代表 “0”&#xff0c…...

安防监控/视频集中存储EasyCVR视频汇聚平台如何配置AI智能分析平台的接入?

EasyCVR安防视频监控平台不仅支持AI边缘计算智能硬件设备的接入,还能快速集成AI智能分析平台,接收来自智能分析平台或设备的AI告警信息,如烟火检测、周界入侵检测、危险区域闯入检测、安全帽/反光衣佩戴检测等。 本文将详细介绍如何在EasyCVR…...

做小程序开发的安全防护全方案

小程序开发安全防护方案 为了确保小程序在开发过程中的安全性,以下是一个全面的防护方案: 1. 需求分析与规划 功能模块分析:明确小程序的功能模块,识别高风险区域如用户登录和支付功能。数据分类分级:将数据分为敏感…...

在Spring Boot项目中导出复杂对象到Excel文件

在Spring Boot项目中导出复杂对象到Excel文件&#xff0c;可以利用Hutool或EasyExcel等库来简化操作。这里我们将详细介绍如何使用Hutool和EasyExcel两种方式来实现这一功能。 使用Hutool导出复杂对象到Excel 首先确保你的pom.xml中添加了Hutool的依赖&#xff1a; <depe…...

从JDBC到数据库连接池:构建高性能Java应用的基石(中篇)

推荐关联阅读&#xff1a;JDBC核心技术解析&#xff1a;从基础连接到ORM演进之路&#xff08;上&#xff09; 一、JDBC的困境与连接池的救赎 1.1 传统JDBC的致命缺陷 在Java应用与数据库交互的原始模式中&#xff0c;开发者通过DriverManager.getConnection()获取数据库连接…...

JavaWeb后端基础(6)

主键返回 例子&#xff1a; /** * 新增员工数据 */ Options(useGeneratedKeys true, keyProperty "id") Insert("insert into emp(username, name, gender, phone, job, salary, image, entry_date, dept_id, create_time, update_time) " "value…...

nio多线程版本

多线程多路复用 多线程NIO&#xff0c;&#xff0c;就是多个线程&#xff0c;每个线程上都有一个Selector&#xff0c;&#xff0c;&#xff0c;比如说一个系统中一个线程用来接收请求&#xff0c;&#xff0c;剩余的线程用来读写数据&#xff0c;&#xff0c;每个线程独立干自…...

Electron、Tauri及其它跨平台方案终极对比

Electron、Tauri及跨平台方案终极对比&#xff08;2025版&#xff09; 一、核心框架深度解析 1.1 Electron&#xff1a;Web技术的桌面霸主 技术架构 基于Chromium&#xff08;浏览器内核&#xff09; Node.js&#xff08;后端运行时&#xff09;的双进程架构&#xff0c;支持…...

蓝桥杯试题:二分查找

一、问题描述 给定 n 个数形成的一个序列 a&#xff0c;现定义如果一个连续子序列包含序列 a 中所有不同元素&#xff0c;则该连续子序列便为蓝桥序列&#xff0c;现在问你&#xff0c;该蓝桥序列长度最短为多少&#xff1f; 例如 1 2 2 2 3 2 2 1&#xff0c;包含 3 个不同的…...

MongoDB Chunks核心概念与机制

1. 基础定义‌ ‌Chunk&#xff08;块&#xff09;‌&#xff1a;MongoDB分片集群中数据的逻辑存储单元&#xff0c;由一组连续的片键&#xff08;Shard Key&#xff09;范围数据组成&#xff0c;默认大小为‌64MB‌&#xff08;可调整范围为1-1024MB&#xff09;‌。‌数据分…...

决策树(Decision Tree):机器学习中的经典算法

1. 什么是决策树&#xff1f; 决策树&#xff08;Decision Tree&#xff09;是一种基于树形结构的机器学习算法&#xff0c;适用于分类和回归任务。其核心思想是通过一系列的规则判断&#xff0c;将数据集不断划分&#xff0c;最终形成一棵树状结构&#xff0c;从而实现预测目…...

高频 SQL 50 题(基础版)_1084. 销售分析 III

高频 SQL 50 题&#xff08;基础版&#xff09;_1084. 销售分析 III 思路 思路 select t1.product_id,product_name from Product as t1 join(select product_id,min(sale_date) as min_date,max(sale_date) as max_datefrom Salesgroup by (product_id)having 2019-01-01<…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...