☰
NeMo Guardrails 自定义 Embedding Search Provider 与知识库(Knowledge Base)接入实战
2026/10/8 7:54:32 网站建设 项目流程
  • 人工智能
  • 大模型
  • AI 安全治理
  • 模型安全
  • 内容安全
  • 提示词注入防护
  • RAG

【免费下载链接】Guardrails

NeMo Guardrails is an open-source toolkit for easily adding programmable guardrails to LLM-based conversational systems.

项目地址:https://gitcode.com/gh_mirrors/ne/Guardrails
点击查看免费下载

导读

NeMo Guardrails 的「知识库(Knowledge Base)」机制允许你为对话系统注入自定义文档语料,并通过 embedding 检索为 LLM 提供事实依据。本文以仓库测试配置tests/test_configs/with_custom_embedding_search_provider为完整案例,讲解如何在配置中为**核心(core)与知识库(knowledge_base)**分别指定 embedding 搜索 Provider、如何用 Python 实现一个不依赖任何向量模型的自定义 Provider(纯字符串匹配),并通过测试与源码(kb.py)揭示知识库从文档分块到索引构建、检索的完整链路。读完本文,你将能够在自己的 NeMo Guardrails 项目中接入任意检索后端(向量库、BM25、外部 API 等),并理解知识库内部的工作机制。


一、案例全景:一个带自定义知识库的完整测试配置

tests/test_configs/with_custom_embedding_search_provider目录是仓库中一个端到端可运行的最小示例,用于验证「自定义 embedding 搜索 Provider + 知识库」的组合能力,其文件结构如下:

tests/test_configs/with_custom_embedding_search_provider/ ├── kb/ │ └── kb.md # 示例知识库文档(检索语料) ├── config.co # Colang 对话流定义 ├── config.py # 自定义 Provider 实现与注册(init 钩子) └── config.yml # 主配置:模型、core 与 knowledge_base 的 Provider 指定

整个测试由 tests/test_with_custom_embedding_search_provider.py 驱动:它用RailsConfig.from_path()加载该目录,构造一个TestChat(传入预置的 LLM 补全结果" express greeting"),随后模拟用户输入"hi"并断言机器人回复"Hello there!"。这个测试同时覆盖了配置加载、自定义 Provider 注册、Colang 流执行三个层面,是理解本文主题的最佳入口。


二、示例知识库文档:kb.md 在知识库机制中的角色

关联文档 kb/kb.md 是一个典型的 Markdown 知识库文档,内容为 NeMo Guardrails 项目的自我介绍。它在整个机制中扮演**检索语料(corpus)**的角色:当用户问题与 kb.md 内容相关时,知识库会将命中的文本块作为上下文提供给 LLM。

从 nemoguardrails/kb/kb.py 的源码可以看到,知识库初始化阶段会将每个文档按 Markdown 标题切分为「主题分块(topic chunks)」:

def init(self): """Initialize the knowledge base. The initial data is loaded from the `$kb_docs` context key. The key is populated when the model is loaded. Currently, only markdown format is supported. """ if not self.documents: return # Start splitting every doc into topic chunks for doc in self.documents: chunks = split_markdown_in_topic_chunks(doc) self.chunks.extend(chunks)

这里有两个关键事实值得注意:

  1. 当前仅支持 Markdown 格式的知识库文档(源码 docstring 明确说明Currently, only markdown format is supported)。
  2. 分块以标题为边界:split_markdown_in_topic_chunks()(实现位于 nemoguardrails/kb/utils.py)会把 kb.md 按##等标题拆成若干块。本例中 kb.md 包含标题NeMo Guardrails及一段介绍,因此会被切分为一个「标题 + 正文」的检索单元。

随后在build()阶段,每个块会被封装为一个IndexItem(text, meta)并写入索引(kb.py):

for chunk in self.chunks: text = f"# {chunk['title']}\n\n{chunk['body'].strip()}" all_text_items.append(text) index_items.append(IndexItem(text=text, meta=chunk))

也就是说,kb.md 中的文字最终以# 标题\n\n正文的形式进入检索索引,meta中保留了分块信息(标题、正文等),检索命中后由search_relevant_chunks()把result.meta直接返回给上层使用(kb.py)。

实操提示:想让知识库承载什么领域知识,就把对应的 Markdown 文档放入配置目录下的kb/子目录(如本例的kb/kb.md),NeMo Guardrails 会自动完成加载与分块。


三、配置 core 与 knowledge_base 的搜索 Provider

config.yml 是本案例的配置骨架,全文如下:

models: - type: main engine: openai model: gpt-3.5-turbo-instruct # Use the simple embedding search provider for the core logic. core: embedding_search_provider: name: simple # And for the knowledge base. knowledge_base: embedding_search_provider: name: simple

配置拆解如下:

配置项含义本例取值
models主对话模型(LLM)openai引擎 +gpt-3.5-turbo-instruct
core.embedding_search_provider.name核心(core)逻辑使用的检索 Providersimple(自定义)
knowledge_base.embedding_search_provider.name知识库检索使用的 Providersimple(自定义)

这里体现了 NeMo Guardrails 的一个设计:core 与 knowledge_base 可以各自独立指定 embedding 搜索 Provider。core 侧的 Provider 服务于护栏(rails)相关的语义检索(例如 self-check、事实核查等需要召回上下文的场景),而 knowledge_base 侧的 Provider 服务于知识库文档检索。两者解耦,允许你为不同用途选择不同后端(例如核心用高精度向量库、知识库用轻量全文检索)。

配置结构对应的底层数据类型可在 nemoguardrails/rails/llm/config.py 中找到(EmbeddingSearchProvider与KnowledgeBaseConfig定义)。name字段与后续register_embedding_search_provider(name, cls)注册的名字一一对应。


四、实现并注册自定义 Provider(config.py)

config.py 是整个方案的核心:它定义了一个不使用任何 embedding 向量的检索实现,并把它注册为simple。这种实现的价值在于:无外部依赖、零成本、可离线运行,非常适合测试、演示以及中小规模语料的快速原型。

4.1 继承 EmbeddingsIndex 抽象基类

自定义 Provider 必须继承 nemoguardrails/embeddings/index.py 中定义的EmbeddingsIndex抽象类。抽象基类声明的核心接口包括:

  • embedding_size(属性):向量维度,无向量实现返回0;
  • add_item(item: IndexItem):添加单个条目;
  • add_items(items: List[IndexItem]):批量添加条目;
  • build():可选,索引构建钩子(默认空实现,pass);
  • search(text: str, max_results: int, threshold: Optional[float]):检索最相关条目。

其中IndexItem是一个 dataclass(index.py),包含text(文本)与meta(附加元数据字典,默认为空字典)。

4.2 SimpleEmbeddingSearchProvider:纯字符串检索实现

本例的SimpleEmbeddingSearchProvider以「子串包含」作为匹配逻辑:

class SimpleEmbeddingSearchProvider(EmbeddingsIndex): """A very simple implementation of an embeddings search provider. It actually does not use any embeddings, just plain string search through all items. """ @property def embedding_size(self): return 0 def __init__(self): self.items: List[IndexItem] = [] async def add_item(self, item: IndexItem): """Adds a new item to the index.""" self.items.append(item) async def add_items(self, items: List[IndexItem]): """Adds multiple items to the index.""" self.items.extend(items) async def search(self, text: str, max_results: int, threshold: Optional[float] = None) -> List[IndexItem]: """Searches the index for the closes matches to the provided text.""" results = [] for item in self.items: if text in item.text: results.append(item) return results

要点说明:

  • search()遍历全部条目,判断查询文本是否为条目文本的子串(if text in item.text),命中则返回。这是一个刻意简化的检索策略,threshold参数在此实现中被忽略;
  • max_results在示例实现中未做截断,真实场景中应results[:max_results];
  • embedding_size返回0,表明该实现不产生向量——这与build()阶段知识库的默认缓存逻辑兼容(见下文第六节,缓存仅对name == "default"的 Provider 启用,自定义 Provider 不参与向量缓存)。

4.3 init 钩子:向应用注册 Provider

自定义配置通过init(app: LLMRails)钩子把实现绑定到名字simple:

def init(app: LLMRails): app.register_embedding_search_provider("simple", SimpleEmbeddingSearchProvider)

register_embedding_search_provider(name, cls)是公开注册 API,其实现位于 nemoguardrails/rails/llm/llmrails.py,并在 nemoguardrails/guardrails/guardrails.py 中对LLMRails做了转发(IORails 引擎会抛出NotImplementedError,表明该能力当前属于 LLMRails 引擎)。这样 config.yml 中embedding_search_provider.name: simple就能在运行时解析到SimpleEmbeddingSearchProvider的实例。

从源码结构看,只要实现EmbeddingsIndex的全部抽象方法(embedding_size、add_item、add_items、search),并通过init钩子注册,就可以把任意检索后端(如 Elasticsearch、faiss、SQLite FTS、远程向量 API)接入 NeMo Guardrails。


五、Colang 对话流(config.co)

config.co 定义了一段极简的 Colang 对话流,用于驱动端到端测试:

define user express greeting "hi" "hello" define flow user express greeting bot express greeting define bot express greeting "Hello there!"

它定义了三件事:

  1. user express greeting:用户意图,匹配用户消息"hi"/"hello";
  2. flow:主流程,识别到问候后触发bot express greeting;
  3. bot express greeting:机器人回复文本"Hello there!"。

测试中通过预置 LLM 补全" express greeting"来模拟模型「预测出意图标签」的行为,从而不依赖真实 LLM 调用即可完成断言(chat >> "hi"后chat << "Hello there!")。


六、源码级原理:知识库的构建与检索链路

结合 nemoguardrails/kb/kb.py 可以完整还原知识库的内部工作流:

  1. init():把每个 Markdown 文档按标题切分为主题块(split_markdown_in_topic_chunks),累积到self.chunks;
  2. build():将每个块格式化为# 标题\n\n正文并构造IndexItem;然后根据配置实例化 Provider:
    • 若embedding_search_provider.name == "default",走内置的BasicEmbeddingsIndex并启用.cache目录下的持久化缓存(kb.py)——缓存文件名由文档内容哈希 + embedding 引擎/模型拼接计算,避免 embedding 模型更换后误用旧缓存;
    • 若为自定义名字(本例simple),则直接self._get_embeddings_search_instance(config.embedding_search_provider)获得 Provider 实例,add_items()后调用build(),不涉及任何向量缓存;
  3. search_relevant_chunks(text, max_results=3):调用self.index.search(text, max_results, threshold=None),把命中的IndexItem.meta(含标题、正文的分块元数据)返回给上层,作为后续 LLM 提示词的上下文。

这也解释了本案例的优雅之处:simpleProvider 的embedding_size为 0、无向量,因此 build 阶段既不会触发默认缓存、也不需要任何 embedding 模型(default_embedding_model可以缺省),让整个示例可以完全离线运行。


七、从测试到实践:如何复用到自己的项目

test_1的完整流程(tests/test_with_custom_embedding_search_provider.py)为你提供了一个可复用的「自定义 Provider 冒烟测试」模板:

config = RailsConfig.from_path(os.path.join(CONFIGS_FOLDER, "with_custom_embedding_search_provider")) chat = TestChat( config, llm_completions=[" express greeting"], ) chat >> "hi" chat << "Hello there!"

在自己的项目中接入自定义知识库检索时,按以下四步操作即可:

  1. 准备语料:在配置目录新建kb/,放入 Markdown 文档(如kb/kb.md),按##标题组织内容以便分块;
  2. 实现 Provider:继承EmbeddingsIndex,实现embedding_size、add_item、add_items、search,并在init(app)中调用app.register_embedding_search_provider("你的名字", YourProvider);
  3. 声明配置:在config.yml中分别设置core.embedding_search_provider.name与knowledge_base.embedding_search_provider.name为你的注册名(两者可不同);
  4. 验证:参考上述测试,用TestChat预置 LLM 补全,断言知识库检索与对话流行为符合预期。

八、注意事项与边界

  • Markdown 专属:知识库文档当前只支持 Markdown 格式(见 kb.py 的 docstring),其他格式需要自行预处理为 Markdown 再放入kb/。
  • 自定义 Provider 无内置缓存:向量缓存逻辑只针对name == "default"的内置BasicEmbeddingsIndex(kb.py)。如果自定义 Provider 的计算开销较大,需要自行实现持久化。
  • 引擎支持范围:register_embedding_search_provider当前属于 LLMRails 引擎能力,IORails 会直接抛出NotImplementedError(guardrails.py),使用前请确认你的引擎类型。
  • LLMRails.embedding_search_providers属性已弃用:源码中标记为 DeprecationWarning(llmrails.py),提示新增 Provider 一律走register_embedding_search_provider()注册 API。

结语

with_custom_embedding_search_provider是一个「小而全」的样板:一份两段的 Markdown 知识库(kb.md)、一个零依赖的检索 Provider(config.py)、一份声明式配置(config.yml)与一条自动化断言(test_with_custom_embedding_search_provider.py)。以此为基础,你可以将任何检索后端无缝接入 NeMo Guardrails 的知识库与核心逻辑,让护栏系统获得稳定、可控、可测试的上下文召回能力。

  • 人工智能
  • 大模型
  • AI 安全治理
  • 模型安全
  • 内容安全
  • 提示词注入防护
  • RAG

【免费下载链接】Guardrails

NeMo Guardrails is an open-source toolkit for easily adding programmable guardrails to LLM-based conversational systems.

项目地址:https://gitcode.com/gh_mirrors/ne/Guardrails
点击查看免费下载

相关推荐

上一篇:SEO优化策略:让作品集在搜索引擎中获得更好排名
下一篇:AsyncAPI消息设计:构建可扩展事件驱动架构的终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询