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

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. 最终返回最相关的知识库内容

这种设计让系统能够:

  • 更好地理解用户的意图
  • 处理不完整或指代性的问题
  • 提高知识库搜索的准确性
  • 支持连续对话中的上下文理解
<think>嗯,用户让我介绍一下FastGPT源码解读,或者用中文回答相关问题。首先,我需要确定用户的需求是什么。可能他们正在寻找如何理解FastGPT的代码结构,或者想自己部署或修改这个项目。FastGPT是基于ChatGPT的开源项目,所以用户可能对自然语言处理、AI对话系统感兴趣,或者想在自己的业务中应用类似的技术。 接下来,我需要考虑用户的背景。如果他们提到源码解读,可能有一定的技术基础,可能是开发者或者研究人员。他们可能希望了解代码的架构、关键模块的实现,或者如何二次开发。这时候,我应该提供结构化的解读,分模块介绍,比如数据处理、模型结构、API设计等,同时给出一些部署和二次开发的建议。 另外,用户可能在使用FastGPT时遇到了问题,或者想深入理解其工作原理。这时候,我需要解释其核心技术,比如如何结合向量数据库,工作流程是怎样的,以及依赖的技术栈如LangChain。可能还需要提到性能优化的部分,比如缓存机制,这对实际应用中的响应速度很重要。 还要考虑用户是否需要实际操作指南,比如如何配置环境、调整模型参数,或者处理数据。可能需要给出一些示例,比如如何添加自定义模块,或者调整提示词模板。同时,注意事项部分也很重要,比如硬件需求和数据隐私问题,避免用户在实际部署中遇到问题。 用户可能没有明确说出他们遇到的困难,但源码解读通常涉及深入的技术细节,因此需要平衡深度和易懂性。可能需要用简洁的语言解释关键点,避免过于冗长,同时提供参考资料和社区链接,方便用户进一步学习。 总结来说,用户需要一个清晰的源码结构分析,各模块的功能说明,以及如何应用或修改这些代码的指导。需要确保回答全面,覆盖主要部分,同时提供实用建议,帮助用户顺利开展工作。</think>FastGPT 是一个基于 ChatGPT 的开源项目,旨在通过模块化设计实现高效、灵活的企业级对话 AI 应用。以下是对其源码核心结构和关键技术的解读: --- ### **1. 源码核心结构** FastGPT 代码通常分为以下模块(以常见版本为例): #### **1.1 数据处理模块** - **数据预处理**:`/src/data` 目录下的脚本负责清洗、分词、向量化(如调用 OpenAI Embeddings 或本地模型)。 - **知识库管理**:支持本地/云端存储,通过 `KnowledgeBase` 类实现文档的增删改查,结合向量数据库(如 Milvus、Pinecone)进行语义检索。 #### **1.2 模型核心** - **对话引擎**:`/src/core/chat` 中的逻辑控制对话流程,整合大模型响应与知识库检索结果。 - **LLM 接口**:对接多种模型(GPT-3.5/4、本地 LLM),通过 `LLMAdapter` 抽象层实现多模型兼容。 - **缓存机制**:使用 Redis 或内存缓存高频问题,降低 API 调用成本。 #### **1.3 API 服务** - 基于 Node.js/Express 或 Python/FastAPI 构建 RESTful API,路由定义在 `/src/routes` 下,提供对话、知识库管理等接口。 - 鉴权模块通过 JWT 或 API Key 控制访问权限。 #### **1.4 前端交互** - Web 界面使用 React/Vue,通过 `/src/components` 中的组件实现对话界面、知识库管理面板。 - 实时通信可能依赖 WebSocket 或 Server-Sent Events (SSE)。 --- ### **2. 关键技术点** #### **2.1 工作流程** 1. 用户输入 → 2. 意图识别 → 3. 知识库检索 → 4. 提示词拼接 → 5. 大模型生成 → 6. 结果后处理 #### **2.2 核心代码片段示例** ```javascript // 伪代码:对话流程核心逻辑 async function generateResponse(userInput) { // 检索知识库 const relatedKnowledge = await vectorDB.search(userInput); // 构建提示词 const prompt = buildPrompt({ userInput, knowledge: relatedKnowledge, history: chatHistory }); // 调用大模型 const response = await openai.chat.completions.create({ model: "gpt-3.5-turbo", messages: [{ role: "user", content: prompt }] }); // 后处理(敏感词过滤、格式优化) return postProcess(response.choices[0].message.content); } ``` #### **2.3 依赖技术栈** - **LangChain**:用于任务链编排(检索→生成→评估) - **向量数据库**:实现相似语义检索 - **模型量化**(如使用 llama.cpp):本地模型轻量化部署 --- ### **3. 二次开发建议** 1. **自定义知识库**:修改 `KnowledgeBaseLoader` 类以支持新的文档格式(PDF/数据库)。 2. **优化检索**:调整 `/src/search` 中的混合检索策略(关键词+向量)。 3. **扩展模型支持**:在 `LLMAdapter` 中添加新模型接口(如 Claude、文心一言)。 4. **业务逻辑注入**:在 `postProcess` 阶段插入领域规则(如医疗术语校验)。 --- ### **4. 部署注意事项** - **硬件要求**:CPU 版需至少 8GB 内存,GPU 版推荐 NVIDIA T4 以上 - **环境配置**:注意 Python/Node.js 版本兼容性,推荐使用 Docker 部署 - **性能调优**:通过 `config.yaml` 调整缓存策略、并发请求数 --- ### **5. 学习资源** - 官方文档:https://github.com/labring/FastGPT - 调试技巧:通过 `DEBUG_MODE=1` 输出详细推理日志 - 社区讨论:关注项目的 GitHub Issues 和 Discord 频道 如果需要深入某个模块(如向量检索实现细节)或具体代码分析,可以进一步说明需求!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值