RAG + 语义标记

阅读其他语言: English Español 한국어 Português

文档在不同的软件项目中的重要性各不相同, 但即使是最重要的文档也具有固有的辅助角色。 它并非程序依赖于正确运行的东西。 即使文档与程序共享数据,连接仍然是单向的。 面向用户的文档反映了产品,并不改变产品的工作方式。

这主要是出于技术原因。 如果自然语言的指令能够足够具体, 并且计算机在解读这些指令时表现出色,我们就可以通过编写文档来实现新功能和修复错误。 这听起来很熟悉,不是吗?

尽管人们还在争论是否可能完全用自然语言进行编程, 但这种潜在的变化实际上更多的是关于软件开发的可访问性,而不是引入一些新的程序操作方式。 最终,我们将得到相同的二进制文件,现在只是多了一个值得赞赏的编译器。

AI能否以不同的方式驱动特性呢? 今天,我想介绍一下最近在 JetBrains IDEs中的一项AI驱动特性, 它直接利用了自然语言处理,将大量的我们的文档变成了一个极其有用的开发资产。

动作搜索

从2024.3版本开始,JetBrains IDEs中的AI助手可以访问IDE动作,并帮助你找到解决问题的正确动作。 另外,它还可以为你执行这个动作:

AI助手推荐一个IDE特性,并显示一个按钮来启动对应的动作 AI助手推荐一个IDE特性,并显示一个按钮来启动对应的动作

正如前面所述,这个特性是由文档驱动的。 虽然古老的代码肯定起到了一部分作用,但选择正确动作完全依赖于 RAG (检索增强生成)的高级形式 – 不需要大量的LLM函数或特别的映射。

在深入细节之前,让我们简单回顾一下RAG的概念。

RAG

检索增强生成(RAG) 是一种常用的技术,用于提高LLM响应的准确性和地面性。 当LLMs需要对超越他们的训练数据和频繁变化的事实给出回应时,这种技术尤其有效,这使得微调变得不切实际。

在最常见的形式中,这种技术包括在文档上维护一个索引。 在这个索引中,键反映了文档元素的意义, 由embeddings表示, 值对应于元素的内容或对元素本身的引用。 得益于键的数值表示,相应的 元素可以使用余弦相似性k-最近邻(KNN)等方法语义地比较和检索

语义索引图示 - 以embeddings为键,以内容为值 语义索引图示 - 以embeddings为键,以内容为值

当你在一个由RAG辅助的AI聊天中提问某个问题时,会发生什么:

我怎么更改字体大小?

系统首先搜索索引,如果成功的话, 附加到提示上。 例如:

我怎么更改字体大小?
这里是一些可能帮助你回答问题的补充信息:
-> 搜索结果被插入到这里 <-

使用这种方法,可以从提供的补充信息中推断出正确的答案, 使LLM的回应更有根据和准确。 从最终用户的角度看, 这可能会让人觉得模型变得更聪明了,而实际上模型并没有改变 — 唯一的区别在于提示。

现在让我们看看我们如何可以增强这个方法。

语义标记

如果你编写技术文档, 你可能已经熟悉了 语义标记的概念:

使用语义标记,你可以为每个元素指定其 “含义”

例如,Markdown就不是语义的。 在Markdown中,你操作的是常见的文档元素,如标题,段落,列表等,除了一些排版属性。 这个语法概述了一个高级的结构,并告诉处理器如何渲染文档。 但它没有告诉你文档中各种元素的目的,或者意思是什么。

这里有一个简短的例子。星号表示处理器应该将这个短语斜体化,类似于在HTML中的 <i> 的作用:

*在过马路前看两边!*

相反,MDX,Markdown的一个超集,具有语义属性。 MDX允许你定义自定义元素,这些元素通常代表UI组件,并可以传达内容的预期目的:

<Warning>
    在过马路前看两边!
</Warning>

与原生Markdown不同,这个特定的MDX例子抽象了视觉方面,更侧重于内容的目的。 作为文档编写人员,我们并不太关心 <Warning> 是否应使用斜体或以其他方式从常规文本中突出出来。 它的样式在另一个地方定义,如组件本身或CSS样式表中。 这样的关注点分离使代码库更易于维护,并让你根据内容的含义以集中的方式更改样式。

完美的匹配?

在 JetBrains,我们使用 Writerside编写我们的文档。 这个工具支持Markdown和语义XML作为文档的源格式。 下面的例子显示了一个使用Writerside的语义XML写的典型的文档结构:

<chapter title="Testing" id="testing">

    ... some content ...

    <procedure title="Navigate to tests" type="choices">
        <p>
            When at a symbol declaration, you can navigate to the corresponding
            tests by doing one of the following:
        </p>
        <step>
            From the main menu, select <ui-path>Navigate | Test</ui-path>.
        </step>
        <step>
            Press <shortcut key="GotoTest"/>.
        </step>
    </procedure>

    ... more content ...

</chapter>

你会注意到在提供的片段中有一些元信息。 它已经对于上述原因具有价值, 但没有什么能阻止我们将它用于一些额外的用途。

考虑到 <shortcut> 标签, 该标签表示一个与平台相关的键绑定。 这个标签是一个间接层的一部分,允许 以集中的方式管理和验证快捷键的提及。

要在文档中插入一个快捷键,你使用 <shortcut> 标签 和对应的动作ID,就像这样: <shortcut key="CoolAction"> 。 在构建过程中,工具验证并将 <shortcut> 元素 转化为可用键映射的实际键绑定。

这种方法使文档和产品之间的快捷键同步,并为文档用户提供了选择他们喜欢的键映射的灵活性。 为了看到这个在实践中的样子, 访问IntelliJ IDEA帮助 并注意文本中的快捷键是如何根据在Shortcuts菜单中选择的键映射发生变化的:

IntelliJ IDEA的帮助页面,页面头部有一个'快捷键'菜单 IntelliJ IDEA的帮助页面,页面头部有一个'快捷键'菜单

这就是 <shortcut> 标签的主要目的。 这个标签在增强RAG方面有什么好处呢?

最直接的用途是在AI聊天中显示平台特定的快捷键, 就像在帮助页面上一样:

AI助手显示平台的正确快捷键 AI助手显示平台的正确快捷键

然而,更有趣的部分是,类似于建立用于检索事实和指令的索引, 我们可以建立能够查找动作ID的索引。

之前讨论的简单RAG场景中,我们从用于检索的同一元素生成了嵌入。 然而,对于动作ID索引,我们将使用以下结构:

动作ID索引的图示 – 以嵌入为键,以动作ID为值 动作ID索引的图示 – 以嵌入为键,以动作ID为值

在例子中,对于 <shortcut key="GotoTest"/> 的包含元素,将是 <step> 甚至整个 <procedure>

<procedure title="Navigate to tests" type="choices">
    <p>
        When at a symbol declaration, you can navigate to the corresponding
        tests by doing one of the following:
    </p>
    <step>
        From the main menu, select <ui-path>Navigate | Test</ui-path>.
    </step>
    <step>
        Press <shortcut key="GotoTest"/>.
    </step>
</procedure>

上下文很重要

为了展示索引包含元素内容重要性的例子,让我们以 IntelliJ IDEA 的 Set Value 特性为例。 这个特性让你在调试模式中程序挂起时更新变量。

对应的动作ID是 SetValue 。 然而,这个ID本身并没有多大的描述性。 考虑到 SetValue 和许多其他ID在编程中都是相当通用的词组, 单独基于这些ID的语义搜索将会产生大量的误报和漏报。 如果不在索引中包括额外的上下文,任务实际上是不可能的。

相反,通过建立动作ID和 surrounding 内容之间的关系,我们为AI助手提供了关于各个功能、其常见用例和限制的详细信息。 这些信息大大提高了检索的准确性:

AI助手对于'更新一个变量'的请求提议了正确的功能(Set Value) AI助手对于'更新一个变量'的请求提议了正确的功能(Set Value)

客户端部分

最后,一旦AI助手有了正确的动作ID,它就可以建议这个动作,并且甚至可以显示一个按钮来从聊天中调用它:

AI助手显示一个按钮,让你可以直接从聊天中执行动作 AI助手显示一个按钮,让你可以直接从聊天中执行动作

当然,这个部分比简单地在UI中添加一个按钮要复杂一些。 考虑到IDE动作是取决于上下文的,有一些额外的检查需要实现, 但这个话题是非常特定于IntelliJ平台开发的,并且值得单独讨论。

其他标记元素

在这篇文章中,我们主要关注’动作搜索’特性,它通过从Writerside的 <shortcut> 标签中检索动作ID来工作。 然而,你可以将相同的原则应用到语义标记中的许多其他元素类型。

只是一个快速例子使用 <ui-path>元素。 这个元素表示一系列的UI元素,如菜单项, 用户需要通过这些元素来访问描述的功能。

<step>
    Open the IDE settings (<shortcut key="ShowSettings"/>),
    then navigate to <ui-path>Tools | Terminal</ui-path>.
</step>

在Web帮助中提供长路径是合理的,因为从网页直接导航到产品通常是不切实际的。 另一方面,当用户在产品中询问这个问题时,没有理由不直接显示对应的设置页面:

AI助手显示一个按钮,让你可以直接导航到文档中引用的菜单 AI助手显示一个按钮,让你可以直接导航到文档中引用的菜单

总结

不久前,许多人设想了 Web 3.0的概念。 他们的想法是将整个网络转移到语义标记, 以便机器可以像人一样使用它。 虽然这没有发生,但令人感兴趣的是同样的想法如何再次变得相关, 现在是从现代工具和技术的角度看问题。

我在这篇文章中讨论的特性只是你可以利用 你现有的良好实践,如语义标记,与Gen AI系统的一个例子。 还有更多的方法可以探索。 如果你对RAG特别感兴趣,这里有一份关于检索增强生成的精彩 教程集合

你喜欢这篇文章吗?你喜欢尝试Gen AI吗? 即使我的博客没有评论部分,我仍然会很高兴 听到你的观点和用例, 所以不要犹豫使用页脚中的联系方式告诉我。

我们很快再见!

all posts ->