本教程系统性地讲解 GREP(Global Regular Expression Print)全局正则表达式搜索的核心概念及其在 RAG(检索增强生成)系统中的应用价值。教程从正则表达式基础出发,分析纯向量检索的局限性,阐述正则表达式搜索如何与语义搜索形成互补,并通过一个基于 FastAPI 的混合检索服务示例展示完整的实现路径。

前置教程

如想快速开始学习本教程,你可能需要先完成以下前置教程:

资源下载

1. GREP 与正则表达式核心概念

1.1 什么是 GREP

GREPGlobal Regular Expression Print 的缩写,源自 Unix 系统中的一个经典命令行工具。它的核心功能是在文本文件中搜索符合特定模式的行,并将匹配的行打印出来:

# Unix grep 命令的基本用法
grep "pattern" filename         # 在文件中搜索包含 pattern 的行
grep -r "pattern" directory/    # 递归搜索目录
grep -E "[0-9]+\.[0-9]+" file   # 使用扩展正则表达式

GREP 工具的名称本身就概括了它的三个核心特征:

组成部分 含义 说明
Global 全局搜索 在整个范围内(如整个文档、整个输入文本)进行检索
Regular Expression 正则表达式 使用模式匹配,而非固定字符串
Print 打印输出 将匹配的内容输出

在更广泛的意义上,GREP 泛指一切基于正则表达式的文本模式匹配与搜索技术,这也是本教程讨论的核心:如何将这种精确的、基于规则的搜索能力整合到 RAG 系统中。

1.2 正则表达式基础

正则表达式(Regular Expression,简称 regex) 是一种描述字符串模式的表达式语言。它由普通字符(如字母、数字)和元字符(具有特殊含义的符号)组成。

核心元字符

元字符 含义 示例 匹配结果
. 匹配任意单个字符(除换行符) p.th "path"、"pyth"、"p5th"
* 匹配前一个字符零次或多次 ab*c "ac"、"abc"、"abbc"
+ 匹配前一个字符一次或多次 ab+c "abc"、"abbc"(不匹配"ac")
? 匹配前一个字符零次或一次 ab?c "ac"、"abc"
^ 匹配行首 ^Hello 行首的"Hello"
$ 匹配行尾 end$ 行尾的"end"
[abc] 字符集,匹配任意一个列出的字符 [Pp]ython "Python"、"python"
[^abc] 排除型字符集 [^0-9] 任何非数字字符
| 逻辑或 cat|dog "cat"或"dog"
() 分组,捕获匹配的子串 (ab)+ "ab"、"abab"
\d 匹配数字,等价于 [0-9] \d+ "123"、"42"
\w 匹配字母、数字、下划线 \w+ "hello"、"test_1"
\s 匹配空白字符 \s+ 空格、制表符

数量词匹配模式

正则表达式的数量词有三种匹配模式,理解它们的区别对于精确检索至关重要:

模式 含义 正则 对 "aabab" 的匹配结果 核心区别
贪婪(Greedy) 尽可能多匹配(回溯) a.*b "aabab" 最长的,从第一个 a 到最后一个 b
懒惰(Lazy) 尽可能少匹配(回溯) a.*?b "aab" 最短的,匹配到第一个 b 即停止
占有(Possessive) 不回溯 a.*+b 匹配失败 一口气吞完所有字符,绝不吐出,导致末尾无 b 可匹配

这里的回溯可以简单理解为后悔模式。

在 RAG 应用中,正则搜索通常使用贪婪模式,因为我们需要找出所有可能的匹配项,而非追求最小匹配。

1.3 正则表达式在检索中的优势

正则表达式搜索相对于纯文本搜索具有以下独特优势:

精确性:正则表达式可以描述精确的字符串模式,不会因为语义相似度而产生"近似但错误"的匹配。

结构化数据检索:可以匹配特定格式的数据,如版本号(\d+\.\d+(\.\d+)?)、日期(\d{4}-\d{2}-\d{2})、邮箱地址等。

代码搜索:在文档代码片段中搜索特定的函数名、API 调用模式、导入语句等结构化内容。

组合条件:通过正则的逻辑或(|)、分组和前后查找断言,可以表达复杂的组合搜索条件。

正则表达式搜索的强项在于精确的模式匹配,而向量搜索的强项在于语义的相似度匹配。两者在 RAG 系统中不是替代关系,而是互补关系。

2. 纯向量 RAG 的局限与 GREP 的补充

2.1 向量语义检索的工作原理

向量语义检索通过将文本映射到高维语义空间,实现"意思相近即匹配"的搜索。其工作流程如下:

flowchart LR A[查询文本] --> B[Embedding模型] --> C[查询向量] D[文档库] --> E[Embedding模型] --> F[向量索引] C --> G[向量相似度计算] F --> G G --> H[排序结果] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#d6eaf8,stroke:#2980b9 style C fill:#d5f5e3,stroke:#27ae60 style D fill:#fadbd8,stroke:#c0392b style E fill:#d6eaf8,stroke:#2980b9 style F fill:#d5f5e3,stroke:#27ae60 style G fill:#fdebd0,stroke:#e67e22 style H fill:#ffecd6,stroke:#d35400,stroke-width:2px

向量检索的优势在于能理解语义相近的表达,例如搜索"Python 框架"可以匹配到"FastAPI 是一个现代 Web 框架"这样的结果,即使两者没有共同的关键词。

2.2 向量检索的盲区

尽管向量检索在语义理解上表现出色,但它存在以下固有的盲区:

盲区 说明 示例场景
精确字符串匹配 向量无法区分"3.12"和"3.13"的细微差异 搜索特定版本号 v3.12
模式匹配 向量不理解正则模式 搜索所有版本号
结构化查询 无法表达"包含数字后跟版本号"的条件 搜索 \d+\.\d+ 模式的文档
边界感知 无法区分"Python"和"Python 教程"的边界 精确匹配完整单词
否定匹配 难以可靠地排除包含某些内容的文档 搜索不含"deprecated"的文档

假设 RAG 文档库中有多篇关于不同 MySQL 版本的文章。用户想要精确找到提及 "MySQL 8.4" 的文档。向量搜索可能会把 "MySQL 8.0" 和 "MySQL 5.7" 的结果也排到前面,因为语义上它们都是"MySQL 版本",但正则表达式搜索 MySQL 8\.4 可以精确定位到目标文档。

2.3 GREP 在 RAG 中的应用场景

正则表达式搜索在 RAG 系统中可以发挥独特的价值,以下列出几个典型场景:

技术文档版本过滤:在技术文档 RAG 中,使用正则匹配特定版本号,确保 LLM 基于正确的版本信息生成回答。例如,搜索 Python 3.12 相关的文档时,用 Python 3\.12 精确匹配。

代码仓库 RAG:在代码库的 RAG 系统中,正则表达式可以搜索函数定义(def \w+\()、导入语句(from \w+ import)、API 端点定义、错误日志模式等规范化文本。

日志分析 RAG:在运维文档和日志 RAG 中,正则表达式可以匹配特定的日志级别(ERROR|CRITICAL)、时间戳格式、错误码(ERR-\d{4})等。

合规审查:在合规文档 RAG 中,正则表达式可以搜索敏感数据模式,如身份证号、电话号码、IP 地址等,辅助 LLM 进行合规判断。

精确引用检索:当需要严格按照引用格式查找文献时,正则表达式可以匹配特定的引用标记,如 \[1\]\[RFC\d+\] 等。

3. 混合检索架构设计

3.1 三种检索模式

在 GREP + RAG 的混合检索系统中,我们可以定义三种基础检索模式:

flowchart LR subgraph 混合搜索 direction LR A1[查询+模式] --> A2[混合策略] --> A3[先正则过滤] --> A5[结果合并] A2 --> A4[后语义排序] --> A5 end subgraph 纯正则搜索 direction LR B1[正则模式] --> B2[模式编译] --> B3[文本匹配] --> B4[精确结果] end subgraph 纯向量搜索 direction LR C1[查询文本] --> C2[语义编码] --> C3[向量相似度] --> C4[语义结果] end style 混合搜索 fill:#fdebd0,stroke:#e67e22 style 纯正则搜索 fill:#fadbd8,stroke:#c0392b style 纯向量搜索 fill:#d6eaf8,stroke:#2980b9
模式 输入 输出 适用场景
vector 自然语言查询 语义相似文档 概念查询、宽泛搜索
regex 正则表达式 模式匹配文档 精确查找、结构化数据检索
hybrid 查询 + 正则 排序后的匹配文档 精确约束下的语义搜索

3.2 混合检索策略

三种实用的混合策略:

策略一、正则初筛 + 语义重排序(Rerank)

先用正则表达式做第一轮过滤,只保留模式匹配的文档,再对这些文档做向量语义排序。适用于需要在精确定位的基础上进行语义理解的场景。

flowchart LR A[正则模式] --> C[正则匹配过滤] B[文档库] --> C C --> D[匹配文档子集] E[用户查询] --> F[语义编码] D --> G[相似度排序] F --> G G --> H[最终结果] style A fill:#fadbd8,stroke:#c0392b style B fill:#d5f5e3,stroke:#27ae60 style C fill:#fdebd0,stroke:#e67e22 style D fill:#d5f5e3,stroke:#27ae60 style E fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style F fill:#d6eaf8,stroke:#2980b9 style G fill:#fdebd0,stroke:#e67e22 style H fill:#ffecd6,stroke:#d35400,stroke-width:2px

策略二、分别搜索 + 去重合并(Union)

分别执行向量搜索和正则搜索,然后将结果合并去重。适用于希望尽可能覆盖更多相关文档的场景。

flowchart LR A[查询] --> B[向量搜索] A --> C[正则模式] --> D[正则搜索] B --> E[合并去重] D --> E E --> F[排序输出] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#d6eaf8,stroke:#2980b9 style C fill:#fadbd8,stroke:#c0392b style D fill:#fadbd8,stroke:#c0392b style E fill:#fdebd0,stroke:#e67e22,stroke-width:2px style F fill:#ffecd6,stroke:#d35400,stroke-width:2px

策略三、取交集(Intersect)

同时满足语义相似和模式匹配的文档才是有效结果。适用于要求严格的场景,如合规审查中既要语义相关又必须包含特定格式信息的文档。

3.3 架构决策

在设计 GREP + RAG 混合检索系统时,以下架构决策值得关注:

嵌入模型选择:混合检索的语义部分需要合适的嵌入模型,本教程示例使用 bge-small-zh-v1.5,在 CPU 上即可运行。

正则引擎能力:Python 的 re 模块提供了完整的正则支持,生产环境建议使用 regex 库作为替代以获得更强大的能力。

性能考量

维度 向量搜索 正则搜索
硬件依赖 需 CPU/GPU 推理 纯 CPU 计算
索引构建 启动时需编码所有文档 无需预处理
增量更新 需追加编码 无需额外操作

推荐策略:对于文档数量较小(千级以下)的 RAG 系统,正则搜索的性能开销完全可以接受。对于大规模系统,可以在正则搜索前先用倒排索引或向量检索做初步剪枝。

4. FastAPI 示例:GREP + RAG 混合检索服务

本节通过一个完整的 FastAPI 应用,演示如何将向量语义搜索与正则表达式搜索整合到一个统一的服务中。

完整代码请前往网盘下载查看:示例项目grep-rag源码下载地址

4.1 项目结构

grep-rag/
    ├── main.py               # FastAPI 应用入口和 API 路由
    ├── config.py             # 配置文件(模型、检索参数)
    ├── rag_core.py           # 核心检索模块(DocumentStore
    ├── requirements.txt      # 依赖清单
    ├── docs/                 # 预设文档目录
       ├── docs_meta.json    # 文档元数据
       ├── 01-python.txt
       ├── 02-fastapi.txt
       ├── 03-regex.txt
       ├── ...
       └── 09-gpu.txt
    └── static/
        └── index.html        # 前端交互页面

4.2 环境准备与安装

# 创建并激活 Conda 虚拟环境
conda create -n grep-rag python=3.10
conda activate grep-rag

# 安装依赖
pip install -r requirements.txt

依赖说明:

包名 版本 用途
fastapi 0.115.0 Web 框架
uvicorn 0.30.0 ASGI 服务器
sentence-transformers 3.0.1 语义嵌入模型
numpy <2 向量运算
python-multipart 0.0.12 表单数据解析

模型文件请从上方资源下载链接获取,下载后解压到 C:\models\bge-small-zh-v1.5 目录下,config.py 会自动从该路径加载模型,请确保与下图目录结构一致:

安装依赖示意图

4.3 配置文件

config.py 定义了嵌入模型名称、设备类型和检索参数:

# 嵌入模型本地路径(从资源下载链接获取后解压到此目录)
EMBEDDING_MODEL_PATH = r"C:\models\bge-small-zh-v1.5"
EMBEDDING_DIMENSION = 512  # 模型输出维度
DEVICE = "cpu"

# 检索参数
DEFAULT_TOP_K = 5

4.4 核心检索模块

rag_core.py 中的 DocumentStore 类是检索系统的核心,它封装了文档管理、向量索引和三种检索模式。

文档存储与向量索引

class DocumentStore:
    def __init__(self):
        self.documents = []     # 文档列表,每项包含 id、text、metadata
        self.embeddings = None  # numpy 矩阵,形状为 (n_docs, dim)
        self.model = SentenceTransformer(EMBEDDING_MODEL_PATH, device=DEVICE)

    def add_documents(self, docs):
        """添加文档并构建向量索引"""
        start_index = len(self.documents)

        for i, doc in enumerate(docs):
            self.documents.append({
                "id": start_index + i,
                "text": doc["text"],
                "metadata": doc.get("metadata", {})
            })

        texts = [doc["text"] for doc in docs]
        new_embeddings = self.model.encode(texts, normalize_embeddings=True)

        if self.embeddings is None:
            self.embeddings = new_embeddings
        else:
            self.embeddings = np.vstack([self.embeddings, new_embeddings])

        return len(docs)

向量语义搜索:将查询文本编码为向量后,通过点积计算余弦相似度(已归一化),返回最相似的文档。

正则表达式搜索:使用 Python 的 re.compile 编译模式,对每个文档执行 finditer 获取所有匹配位置和上下文片段:

def regex_search(self, pattern, flags=0):
    """正则表达式搜索(GREP 模式)"""
    compiled = re.compile(pattern, flags)
    results = []

    for doc in self.documents:
        matches = list(compiled.finditer(doc["text"]))
        if not matches:
            continue

        match_positions = []
        for m in matches:
            start = max(0, m.start() - 30)
            end = min(len(doc["text"]), m.end() + 30)
            snippet = doc["text"][start:end]
            match_positions.append({
                "start": m.start(),
                "end": m.end(),
                "matched": m.group(),
                "context": f"...{snippet}..."
            })

        results.append({
            "id": doc["id"],
            "text": doc["text"],
            "metadata": doc["metadata"],
            "match_count": len(matches),
            "matches": match_positions,
            "method": "regex"
        })

    return sorted(results, key=lambda x: x["id"])

三种混合策略的实现

  • _rerank_hybrid:先执行正则搜索获取匹配文档 ID,过滤向量索引后只对子集做向量排序
  • _union_hybrid:分别执行两种搜索,合并结果后按 ID 去重
  • _intersect_hybrid:取两种搜索结果 ID 的交集

4.5 FastAPI 应用入口

main.py 通过 load_docs_from_directory() 函数从 docs/ 目录加载 9 篇预设的中文技术文档(涵盖 Python、FastAPI、正则表达式、向量数据库、RAG、Transformer、Docker、MySQL、GPU 等主题)构建初始文档库。文档文本存储在 .txt 文件中,元数据(来源、分类、版本)统一记录在 docs/docs_meta.json 中,方便增删和修改。

支持以下 API 端点:

端点 方法 功能
/ GET 返回前端交互页面
/api/search GET 执行检索(支持五种模式)
/api/documents GET 查看所有文档
/api/documents POST 添加新文档
/api/documents DELETE 清空文档库
/api/stats GET 服务统计信息

/api/search 端点是检索的核心入口,支持以下参数:

  • query:语义查询文本
  • pattern:正则表达式模式
  • mode:检索模式(vector / regex / rerank / union / intersect)
  • top_k:返回结果数量

4.6 运行与测试

# 启动服务(在 examples/grep-rag 目录下)
conda activate grep-rag
python main.py

启动后访问 http://localhost:8000 即可打开交互界面。

测试用例举例

测试场景 模式 查询 正则模式 预期效果
语义搜索 vector 编程语言 (空) 返回 Python 相关文档
版本号搜索 regex (空) \d+\.\d+ 返回所有含版本号的文档
版本号限定语义 rerank 数据库 \d+\.\d+ 在数据库相关文档中找含版本号的
多关键词并集 union Python Docker\|MySQL 包含 Python 语义或 Docker/MySQL 关键词的文档
严格交集 intersect 数据库 8\.4 仅返回语义上关于数据库且含 "8.4" 的文档

4.7 前端界面

前端页面提供了一个简洁的搜索界面,支持切换五种检索模式,并直观展示搜索结果(得分、匹配位置、元数据等)。你可以前往 grep-rag/static/index.html 查看完整的示例代码。

前端界面示意图

5. 总结

5.1 核心内容回顾

  • GREP(Global Regular Expression Print) 是一种基于正则表达式的文本模式匹配技术,与向量语义搜索形成互补关系。
  • 纯向量 RAG 的盲区包括精确字符串匹配、模式匹配、结构化查询、边界感知和否定匹配,这些正是正则搜索的强项。
  • 三种检索模式——纯向量搜索、纯正则搜索、混合搜索——各自适用于不同的应用场景。
  • 三种混合策略——正则初筛+语义重排序(Rerank)、分别搜索后合并去重(Union)、取交集(Intersect)——覆盖了精确约束下语义搜索的常见需求。
  • GREP 在 RAG 中的典型应用包括技术文档版本过滤、代码仓库搜索、日志分析、合规审查和精确引用检索。

5.2 常见问题与解答

问:正则表达式搜索和关键词搜索有什么区别?

关键词搜索只能匹配完全相同的字符串,而正则表达式可以匹配符合某种模式的所有字符串。例如,正则 v\d+\.\d+ 可以匹配 v1.0、v2.12、v3.44 等所有版本号格式,关键词搜索则需要逐一列举每个版本号。在 RAG 系统中,正则搜索比关键词搜索更适合处理结构化的、有规律可循的文本内容。

问:混合检索会影响搜索性能吗?

正则搜索的时间复杂度与文档数量和文档长度成正比。对于千级以下的小型文档库,正则搜索的开销可以忽略不计。对于大规模文档库,建议先通过向量检索或倒排索引缩小搜索范围,再对候选子集执行正则过滤。本教程示例采用内存存储,适用于演示和中小规模场景,生产环境可嵌入向量数据库(如 Milvus、Chroma)使用。

问:正则搜索能替代向量搜索吗?

不能。两者解决的是不同类型的问题:正则搜索擅长精确的模式匹配,向量搜索擅长语义相似度匹配。在 RAG 系统中最有效的做法是将两者结合——先用正则搜索确保结果的精确性,再用向量搜索补充语义相关性,或反之用向量搜索扩大召回范围后再用正则过滤。

问:非技术人员如何设计正则表达式?

对于非技术人员,可以使用 LLM 辅助生成正则表达式——向大语言模型描述你想要匹配的模式,让它为你生成对应的正则表达式。另外,在线正则测试工具(如 regex101.com)可以帮助可视化和调试正则表达式。