构建关系型上下文引擎的背景与目标

Unblocked公司致力于为企业和AI代理提供组织级上下文服务。演讲者Peter Werry指出,当前的工程团队数据分散在PR、代码审查评论、Issue(如Linear和Jira)、文档(Notion、Confluence)以及Slack等沟通工具中。这些数据不仅具有语义可查询性,更拥有作者、日期、状态、标签和关系等结构化属性。传统的做法是将所有数据倒入向量存储库进行检索,但这种方法忽略了数据的结构关系。演讲者强调,上下文引擎不仅仅是代码连接,更是将分散的语境转化为与任务相关的结构化数据。我们需要关注的不仅是语义关系,还有连接所有数据的结构关系。

"Context is scattered across every tool that engineering teams live in... And all of that is semantically queryable, but it's also has structure."

传统RAG的局限性与结构化查询的需求

在Unblocked的定义中,RAG主要指语义相似度搜索或向量检索。虽然它在处理“找到修改了某代码的PR”这类内容性问题时表现良好,并能作为智能体递归遍历文件系统的起点,但在实际工程场景中往往失效。工程师常问的问题如“我上周合并的PR有哪些”,这类问题包含明确的结构性和时间性约束。如果仅靠语义检索,系统可能会返回语义相关但合并于三年前的PR,因为向量搜索无法理解“上周”这一时间状态或作者身份。因此,检索必须包含两半:语义搜索和结构化查询。在这个新范式中,大模型不再直接回答问题,而是负责编写访问数据库的结构化查询语句。这要求系统能够处理自然语言到真实数据库查询的转换,这是构建完整检索系统的另一半。

"RAG is only half of retrieval and today we're going to build the other half which is turning natural language into real database queries."

可靠生成结构化查询的四大关键要素

要可靠地生成数据库查询并解决潜在的安全问题,必须满足四个核心条件。首先,语言模型必须在创建查询前了解数据库模式(Schema);其次,它需要理解数据中的关系映射。例如,当用户询问“Peter的开放PR”时,模型若不知道GitHub登录名可能与显示名称不同(如Rashab),就会生成错误的查询。第三,必须处理来自模型输出的不可信查询,防止类似JavaScript执行器等安全风险;最后,系统需要具备在初次尝试出错时的自我修正能力。Unblocked的目标是构建一个能映射用户名称、状态及排序限制符的系统,从而将自然语言准确转化为可执行的结构化查询。这一过程旨在确保模型输出的安全性与准确性,避免直接执行危险操作。

"The LM needs to be aware of what the schema is... It needs to understand some of the relationships in the data."

案例演示:从自然语言到结构化查询的映射

演讲者通过具体案例展示了理想的工作流程。假设同事Rashine的用户登录名为“Rashab”,系统需要能够正确映射这一关系。当用户提出包含状态(如Open/Closed)和排序限制的需求时,模型应能生成相应的数据库查询语句。这一过程不仅仅是简单的关键词匹配,而是对数据结构的深度理解与转换。通过这种方式,Unblocked旨在解决传统RAG在处理具有明确结构约束的问题时的不足,为工程团队提供一个更精准、可靠的上下文检索解决方案。这种关系型上下文引擎能够结合语义搜索的广度与结构化查询的深度,从而更有效地服务于复杂的工程协作场景。

构建初始UX与Schema动态发现

项目启动时,我们首先搭建一个全功能的用户界面(UX)存根引擎。这个初始版本在功能上是受限的,无法直接处理复杂查询,但它是逐步构建的基础。正如演讲者所述:“going to start with a full UX, a stubbed engine... that is not capable to start with”。为了快速上手,用户可以直接指向一个仓库让代理自动设置环境。在数据摄入阶段,系统需要构建Schema以存储内容。然而,在实际场景中,我们往往不知道预定义的Schema结构。因此,核心挑战在于构建一个完全动态的 Schema 发现引擎。由于不像 PostgreSQL 那样可以直接读取表定义,我们需要通过采样文档来推断 Schema。具体做法是抽取 300 个文档样本,将其折叠合并为单一树状结构。在此过程中,空值(nulls)会被跳过,因为缺失字段无法约束类型。对于枚举类型,我们采用过程性代码而非依赖 LLM 进行提取,因为后者具有非确定性。最终,系统成功生成了 GitHub Pull Request 的 Schema 结构并创建了索引。

身份解析与模糊匹配机制

在数据查询中,实体身份的歧义性是一个关键问题。例如,用户的 GitHub 登录名可能与显示名称相似但不完全一致(如 "Peter" vs "puery")。如果直接进行字符串匹配,查询将失败。因此,系统必须实现身份解析(Identity Resolution)机制来解决这一问题。虽然理论上可以让 LLM 动态合成映射关系,但在本实现中,我们采用了更简单且高效的方法:基于 Levenshtein 距离的模糊匹配。这种过程性技巧能够准确地将自然语言中的名字映射到数据库中的唯一标识符。尽管这看似与 Schema 耦合,但通过简单的模糊匹配算法,我们能够在不增加复杂性的前提下解决身份映射问题。这一环节确保了当用户询问“Peter 的所有 PR”时,系统能正确识别目标实体。

LLM 查询计划与安全沙箱设计

第三步是实际的查询计划生成阶段。这是整个流程中相对简单但至关重要的部分,主要依赖 LLM 的能力。我们将 Schema 作为工具传递给模型,并强制其调用特定工具进行名称提取和查询合成。此外,为了处理时间相关查询,我们在提示词中注入了当前日期和时间信息,因为 LLM 在处理时间逻辑方面表现良好。生成的查询计划随后被执行,实现了从自然语言到数据库结果的转化(如成功找到由 Peter 提交的 PR)。然而,直接执行 LLM 生成的查询存在严重的安全隐患。LLM 可能生成包含 where 子句中的 JavaScript 执行代码,导致提示注入攻击。同时,Schema 中可能隐藏敏感字段(如 ID 或元数据),导致内部信息泄露。因此,我们采用白名单机制(Allow-list)而非黑名单进行安全过滤:默认拒绝所有操作,仅允许预定义的操作符和字段。这种设计确保了系统的安全性,特别是在多租户隔离场景中,可以隐藏特定的元数据字段以保护租户隐私。

输出校验与安全白名单机制

在生成结构化数据后,系统必须对 LLM 的输出进行严格验证。如果输出中包含非法的元数据字段或受限制的语法,系统需要将其清除并注入合规的新字段。这一过程本质上是程序化的代码逻辑,不建议在此环节使用 LLM 进行判断。核心策略是采用“白名单”机制而非黑名单,即只允许特定的操作和字段存在。例如,系统明确禁止使用正则表达式(Regular Expressions)或全文搜索(FTS),除非显式配置。当查询试图使用被禁止的功能时,验证模块会直接拦截并返回错误。

"Allow list, don't deny or deny everything... technically this is not actually that difficult to do. You just have to write the code."

在演示中,当尝试执行包含被禁正则表达式的查询时,服务直接拒绝了该请求。这种严格的验证确保了底层数据库操作的安全性与合规性,防止 LLM 产生幻觉或执行危险操作。验证失败后产生的错误信息虽然暂时被丢弃,但为后续的自修正机制提供了关键输入。

错误重试与自我纠正循环

验证失败或执行过程中的逻辑错误(如混合使用包含/排除投影)会导致计划失败,但系统具备自我纠正能力。通过引入重试循环(Retry Loop),系统将错误信息反馈给 LLM,提示其之前的尝试有误并请求重新生成查询。这一机制允许系统在有限的最大尝试次数内,逐步优化输出结果。

在演示案例中,初始查询因违反“禁止正则表达式”的规则而失败。经过多次尝试后,LLM 在第 3 次尝试时调整为使用 match 进行精确匹配。然而,由于用户问题涉及模糊语义(如“上周是否处理过认证相关事务”),精确匹配无法覆盖所有情况。此时,系统通过动态调整策略,最终引入了全文搜索(Full Text Search)功能。

"We can self-correct. We can take that error and pass it back into the LLM... up to some number of max attempts."

这种迭代式修正解决了传统 RAG 在处理结构性、时间性或模糊语义查询时的局限性。系统不仅过滤数据,还能通过聚合和全文搜索提供精确的时间范围限定(Time-scoped)和内容检索能力。

复杂推理与可扩展性架构

最终构建的系统能够支撑复杂的聊天代理(Chat Agent),使其具备后台推理能力。例如,当用户询问“上周是否处理过认证相关事务”时,系统能生成包含全文搜索、作者过滤和时间范围限制的复合查询。这证明了该引擎不仅能处理简单的关键词匹配,还能理解上下文与时间维度。针对大规模部署的疑问(如 Schema 管理、数据摄入成本),演讲者指出可通过引入额外的 LLM 步骤来解决:为每个 Schema 集合生成简短摘要,由 LLM 根据查询意图动态加载相关 Schema,从而节省上下文窗口并优化成本。数据摄入则通过 Webhook 回调实现持续集成,确保分布式系统的高效扩展。

"The error loop is actually one of the most important things... it's validated, self-corrects, and powers a full-blown chat agent."

核心复用理念包括:Schema 感知提示(Schema-aware prompting)、动态 Schema 发现、身份解析(Identity resolution)以及关键的验证与错误重试机制。这套架构使得系统能够回答传统 RAG 无法触及的结构性问题,如状态管理和时间相关性。

核心数据源的选取与结构化查询的应用

在构建关系型上下文引擎时,选择正确的数据源至关重要。Unblocked 团队将 Slack、GitHub PR(Pull Requests)以及文档存储视为三大核心数据源。其中,Slack 被认定为回答“为什么”类问题的最关键渠道,因为大量的决策讨论和背景信息都隐含在对话中。例如,用户可以使用结构化查询功能回溯三周前与特定同事的对话记录。

结论

该关系型上下文引擎通过融合语义搜索与结构化查询,有效解决了传统RAG在处理时间约束、状态过滤及实体歧义时的局限性。其核心优势在于动态Schema发现、基于Levenshtein距离的身份解析以及严格的白名单安全沙箱,配合错误重试与自我纠正机制,确保了查询生成的准确性与安全性。系统已验证能支撑复杂推理场景下的多源数据(如Slack、PR)检索。然而,其性能与扩展性仍高度依赖LLM的上下文窗口管理及数据摄入成本优化方案的有效性,且当前架构主要聚焦于工程协作场景的结构化约束问题。