FastGPT 源码:如何实现 “问题优化“
文章目录
- FastGPT 源码:如何实现 "问题优化"
- 一、前言
- 二、源码分析
- 2.1 queryExtension.ts 提示词
- 2.2 queryExtension.ts 核心逻辑
- 2.3 queryExtension 引用位置
- 三、流程总结
FastGPT 源码:如何实现 “问题优化”
一、前言
- 问题优化的背景和目的
- 在 RAG (检索增强生成) 系统中,用户的问题需要去数据库中执行向量搜索来找到相关内容
- 但在连续对话中,用户的后续问题往往是不完整的,比如"第二点是什么"这样的问题
- 如果直接用这样的问题去搜索,由于缺乏上下文,很难找到相关内容
- 因此需要问题优化模块来补全用户的问题,使其变得完整和明确
- 核心实现方式
FastGPT 主要通过queryExtension
函数来实现问题优化,核心代码在packages/service/core/ai/functions/queryExtension.ts
中:
-
输入参数包括:
- chatBg: 对话背景
- query: 用户当前问题
- histories: 历史对话记录
- model: 使用的模型
-
实现流程:
- 构建提示词模板,包含示例和要求
- 结合历史记录和当前问题生成完整的提示词
- 调用 AI 模型生成多个检索词
- 返回原始问题和扩展后的问题列表
- 提示词设计
FastGPT 使用了精心设计的提示词模板来引导 AI 模型生成高质量的检索词:
const defaultPrompt = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为"原问题"生成个不同版本的"检索词",从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确,并与"原问题语言相同"。`;
- 实际效果
- 原问题:“第二点是什么”
- 历史记录中包含了 “FastGPT 的优势” 的上下文
- 优化后的检索词会变成:
- “介绍下 FastGPT 简便的优势”
- “FastGPT 为什么使用起来简便?”
- “FastGPT的有哪些简便的功能?”
- 搜索结果处理
优化后的问题会用于知识库搜索,搜索结果还会经过重排序(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
的代码实现:
- 导入依赖
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'; // 聊天内容转换为运行时提示词
- 提示词模板定义
const defaultPrompt = `作为一个向量检索助手...`;
这是一个很长的提示词模板,包含了多个示例。每个示例都遵循相同的格式:
- 历史记录: 展示对话上下文
- 原问题: 用户当前的问题
- 检索词: 生成的扩展问题数组
- 主函数定义
export const queryExtension = async ({chatBg, // 对话背景query, // 用户当前问题histories = [], // 历史对话记录model // 使用的模型
}) => {
- 构建系统提示词
const systemFewShot = chatBg? `Q: 对话背景。
A: ${chatBg}
`: '';
如果有对话背景,则添加到提示词中
- 构建历史对话
const historyFewShot = histories.map((item) => {const role = item.obj === 'Human' ? 'Q' : 'A';return `${role}: ${chatValue2RuntimePrompt(item.value).text}`;}).join('\n');
将历史对话记录转换为 Q/A 格式的文本
- 合并提示词
const concatFewShot = `${systemFewShot}${historyFewShot}`.trim();
- 初始化 AI API
const ai = getAIApi({timeout: 480000 // 8分钟超时
});
- 构建请求消息
const messages = [{role: 'user',content: replaceVariable(defaultPrompt, {query: `${query}`,histories: concatFewShot})}
] as ChatCompletionMessageParam[];
将用户问题和历史记录替换到提示词模板中
- 调用 AI 接口
const result = await ai.chat.completions.create({model: model,temperature: 0.01, // 温度很低,保证输出稳定性messages,stream: false
});
- 处理返回结果
let answer = result.choices?.[0]?.message?.content || '';
if (!answer) {return {rawQuery: query,extensionQueries: [],model,tokens: 0};
}
- 解析结果
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};
}
这个函数的主要作用是:
- 接收用户问题和历史对话
- 使用精心设计的提示词引导 AI 生成多个检索词
- 返回原始问题和扩展后的问题列表
- 通过低温度参数(0.01)确保输出的稳定性
- 完整的错误处理确保函数的健壮性
这样的设计使得即使用户提出不完整的问题,系统也能通过上下文理解用户意图并生成合适的检索词。
2.3 queryExtension 引用位置
- 知识库搜索中的使用
// packages/service/core/dataset/search/utils.ts
export const datasetSearchQueryExtension = async ({model,query,histories,chatBg
}) => {const result = await queryExtension({model,query,histories,chatBg});
}
这是最主要的使用场景,在知识库搜索前对用户问题进行优化。
- 工作流节点中的使用
// 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});
}
作为工作流中的一个独立节点使用,可以在对话流程中进行问题优化。
- 搜索测试中的使用
// 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
});
在知识库的搜索测试功能中使用,用于测试问题优化的效果。
- 配置相关
在多个配置文件中,我们可以看到usedInQueryExtension
的配置:
{"usedInQueryExtension": true // 标记模型是否可用于问题优化
}
- UI 组件中的使用
// projects/app/src/components/core/module/DatasetParamsModal.tsx
const queryExtensionModel = watch('datasetSearchExtensionModel');
在界面上提供问题优化相关的配置选项。
三、流程总结
- 用户发起问题
- 如果启用了问题优化功能:
- 系统会调用
queryExtension
生成多个检索词 - 这些检索词会与原始问题一起用于知识库搜索
- 搜索结果会经过重排序和合并
- 系统会调用
- 最终返回最相关的知识库内容
这种设计让系统能够:
- 更好地理解用户的意图
- 处理不完整或指代性的问题
- 提高知识库搜索的准确性
- 支持连续对话中的上下文理解
相关文章:
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”,…...

安防监控/视频集中存储EasyCVR视频汇聚平台如何配置AI智能分析平台的接入?
EasyCVR安防视频监控平台不仅支持AI边缘计算智能硬件设备的接入,还能快速集成AI智能分析平台,接收来自智能分析平台或设备的AI告警信息,如烟火检测、周界入侵检测、危险区域闯入检测、安全帽/反光衣佩戴检测等。 本文将详细介绍如何在EasyCVR…...
做小程序开发的安全防护全方案
小程序开发安全防护方案 为了确保小程序在开发过程中的安全性,以下是一个全面的防护方案: 1. 需求分析与规划 功能模块分析:明确小程序的功能模块,识别高风险区域如用户登录和支付功能。数据分类分级:将数据分为敏感…...
在Spring Boot项目中导出复杂对象到Excel文件
在Spring Boot项目中导出复杂对象到Excel文件,可以利用Hutool或EasyExcel等库来简化操作。这里我们将详细介绍如何使用Hutool和EasyExcel两种方式来实现这一功能。 使用Hutool导出复杂对象到Excel 首先确保你的pom.xml中添加了Hutool的依赖: <depe…...
从JDBC到数据库连接池:构建高性能Java应用的基石(中篇)
推荐关联阅读:JDBC核心技术解析:从基础连接到ORM演进之路(上) 一、JDBC的困境与连接池的救赎 1.1 传统JDBC的致命缺陷 在Java应用与数据库交互的原始模式中,开发者通过DriverManager.getConnection()获取数据库连接…...
JavaWeb后端基础(6)
主键返回 例子: /** * 新增员工数据 */ 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,,就是多个线程,每个线程上都有一个Selector,,,比如说一个系统中一个线程用来接收请求,,剩余的线程用来读写数据,,每个线程独立干自…...
Electron、Tauri及其它跨平台方案终极对比
Electron、Tauri及跨平台方案终极对比(2025版) 一、核心框架深度解析 1.1 Electron:Web技术的桌面霸主 技术架构 基于Chromium(浏览器内核) Node.js(后端运行时)的双进程架构,支持…...
蓝桥杯试题:二分查找
一、问题描述 给定 n 个数形成的一个序列 a,现定义如果一个连续子序列包含序列 a 中所有不同元素,则该连续子序列便为蓝桥序列,现在问你,该蓝桥序列长度最短为多少? 例如 1 2 2 2 3 2 2 1,包含 3 个不同的…...
MongoDB Chunks核心概念与机制
1. 基础定义 Chunk(块):MongoDB分片集群中数据的逻辑存储单元,由一组连续的片键(Shard Key)范围数据组成,默认大小为64MB(可调整范围为1-1024MB)。数据分…...
决策树(Decision Tree):机器学习中的经典算法
1. 什么是决策树? 决策树(Decision Tree)是一种基于树形结构的机器学习算法,适用于分类和回归任务。其核心思想是通过一系列的规则判断,将数据集不断划分,最终形成一棵树状结构,从而实现预测目…...

高频 SQL 50 题(基础版)_1084. 销售分析 III
高频 SQL 50 题(基础版)_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的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

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

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

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