从零开始构建 RAG

我的第一个 AI 版本告诉我我卖了一台液压挖掘机。我并不卖挖掘机。它非常自信地给出了一个虚假的价格和描述。

那一刻,我不再仅仅信任提示词(prompts)。我重建了系统,并遵循一个原则:要么根据目录回答,要么干脆不回答。

以下是我如何使用 Postgres 和 Python 构建一个可靠的 RAG(检索增强生成)系统的方法。

数据准备 大多数教程都跳过了最难的部分:数据清洗。我将过程分为两个阶段:

  • 阶段 1:将 HTML 文件下载到磁盘。我将元数据作为注释保存在每个文件的顶部。这使得过程具有幂等性(idempotent)。如果文件已存在,我会跳过它。
  • 阶段 2:离线解析这些文件。这会将 HTML 转换为干净的 JSON 目录。

解析后,我会检查字段覆盖率。如果像重量或价格这样的字段为空,我会立即发现。数据清洗才是真正的工作所在。

AI 部分 我将每个产品转换为一段文本,并使用 bge-m3 模型将其转换为向量。我使用 pgvector 扩展将这些向量存储在 Postgres 中。

我使用混合搜索方法来查找产品:

  • 语义搜索:使用向量来查找与问题含义相匹配的产品。
  • 结构化过滤:我使用 LLM 将类似“2000 欧元以下的西门子电机”的查询转换为 JSON。这允许我运行带有品牌和价格精确过滤条件的 SQL 查询。

一条 SQL 语句即可同时处理模糊搜索和硬性过滤。这保证了所有内容保持同步。

防护栏 一个好的 RAG 必须知道何时闭嘴。我使用两层机制来防止幻觉(hallucinations):

  • 相似度阈值:每个匹配项都会得到一个评分。如果评分低于设定的限制,我会丢弃结果。如果没有结果通过,系统会直接说“未找到”,甚至不会调用 LLM。如果模型根本看不到数据,它就不可能产生幻觉。
  • 严格的系统提示词:我告诉模型只能根据提供的产品进行回答。如果产品不相关,它必须拒绝回答。

阈值让错误行为变得不可能。提示词只是要求正确的行为。两者结合使用。

总结

  • 仔细收集。
  • 诚实清洗。
  • 简单嵌入。
  • 设计即拒绝。

“拒绝”机制正是让系统值得信赖的关键。信任源于架构,而不是要求模型表现得“友善”。

Source: https://dev.to/utku_catal/building-a-rag-from-scratch-collect-clean-embed-refuse-20ob

Optional learning community: https://t.me/GyaanSetuAi