2024-11-14 19:28:00
Genkit 是一个 Google Firebase 团队开发的 AI Agent 开发框架,用于构建现代、高效的 AI 应用。它目前包含一个 Node.js 的实现 和一个 Go 语言的实现。之所以注意到这个框架是因为 Go 团队在他们的十五周年博客中提到了它。Go 团队在博客中提到,他们正在努力使 Go 成为构建生产 AI 系统的优秀语言,并且他们正在为流行的 Go 语言框架提供支持,包括 LangChainGo 和 Genkit。如果你了解过 AI 开发,那么 LangChain 一定并不陌生。LangChainGo 就是 LangChain 的 Go 语言实现。但是对应的,我们还需要一个单独的 AI Agent 开发框架,帮助我们组织 AI Agent 的开发。LangChain 的生态位中,对应的是 LangGraph,在 Go 语言的生态位中,你可以选择 Genkit 或者 LangGraphGo。
但是,值得注意的是,目前 Genkit 仍旧是在 Alpha 的状态,所以,如果你希望将 Genkit 用于生产环境,在 2024 年这个时间点请注意未来可能的 API 变动。 目前 Genkit 官方支持的模型功能仍旧是以 Google 家的产品为主,如果你需要使用其他 AI 模型,你可能需要暂时先使用第三方 plugin(比如 OpenAI)。
2024-06-03 19:15:00
检索增强生成(Retrieval Augmented Generation,简称RAG)技术正逐渐成为提升大型语言模型(LLM)性能的关键。OpenAI 的 John Allard 和 Colin Jarvis 在一场分享中,以实际案例为基础,深入浅出地讲解了如何将 RAG 技术应用于实际工程问题,并分享了他们的最佳实践经验。这些内容对于 RAG 技术的应用者来说,无疑是非常有价值的。事实上,OpenAI 开放了很多的他们的最佳实践内容,包括不限于 Prompt Engineering、Data Augmentation、Fine-tuning、Inference Optimization 等等。有空除了多看一下相关论文,OpenAI 的 Github 和 Youtube 频道都值得看一下。比如说这次分享就是来源于 OpenAI 的 Youtube 频道。
优化 LLM 性能并非易事,哪怕丁点的区别都可能导致结果上出现很大的差异。目前优化性能主要存在以下挑战:
为了解决上述挑战,OpenAI 提出了一个二维优化框架,将优化方向分为__内容优化__和__LLM优化__两个部分。
内容优化指的是优化模型可访问的知识内容。如果模型缺乏必要的知识或信息质量较差,那么即使模型本身的能力很强,也难以给出令人满意的答案。
为了补全这部分的知识内容,可以采用以下方法进行:
通过优化模型本身提升效果。如果模型本身存在缺陷,例如容易产生幻觉或难以理解复杂指令,那么即使有充足的知识,也难以给出合理的答案。
在这个优化方案中,可以采用下面的方式进行:
在这个框架下,OpenAI 建议开发者采用以下流程进行优化:
2024-04-10 17:15:00
在开始看这篇文章之前,推荐你阅读前一篇概念介绍文章。这篇文章之内不会再额外介绍概念上的内容,仅仅从实践角度演示如何是用 HyDE 和 RAG 结合。正如同前文提到的那样,我们会是用 LangChain 构建这个演示 Demo。当然,如果你熟悉 LlamaIndex 或者其他框架,其实主要流程大致差不多,只不过是用的 API 和库有一些区别。这些就不再赘述。
让我们假设你在为麦当劳制作一个问答系统。这个问答系统的目的是帮助用户解决一些常见问题,比如:麦当劳的营业时间,麦当劳的菜单,麦当劳的优惠活动等等。这些问题的答案通常是可以在麦当劳的官方网站上找到的,但是用户可能并不知道这些信息,或者他们可能会用自己的方式提出问题。这时候,我们就需要一个 RAG 模型来帮助我们回答这些问题。但是麦当劳实际上有非常多的商品,那么它的知识库可能会非常大,而且用户提出的问题可能并不在知识库中。这时候 HyDE 就可以帮助我们处理这些问题。
我们会是用一些基本的 Python 相关工具(pdm 等等)、Ollama和chroma。关于 Ollama 的介绍,可以查看我的之前的文章。出于成本考虑,这里使用了开源的本地模型实现,请确保你的 Ollama 是在运行的状态。当然,如果你是用 GPT-4 Turbo(今天凌晨刚刚发布了新版本),效果要远比开源模型好很多。
mkdir hydemo && cd hydemo
pdm init
pdm add langchain chromadb beautifulsoup4
source .venv/bin/activate
请注意,在本文编写时,使用的 LangChain 的版本为 0.1.14,如果你的版本较低或者过高,可能存在部分 API 出入,请根据具体情况调整引用的模块和对应的 API。
from langchain.chains.hyde.base import HypotheticalDocumentEmbedder
from langchain.chains.llm import LLMChain
from langchain.embeddings.ollama import OllamaEmbeddings
from langchain.globals import set_debug
from langchain.llms.ollama import Ollama
from langchain.vectorstores.chroma import Chroma
def main():
set_debug(True) # 设置 langchain 的调试模式,后面我们会看到具体的效果
llm = Ollama(model="qwen:7b") # 使用通义千问 7b 模型
olemb = OllamaEmbeddings(
model="nomic-embed-text"
) # 嵌入模型我们是使用的 nomic-embed-text
embeddings = HypotheticalDocumentEmbedder.from_llm(
llm,
base_embeddings=olemb,
prompt_key="web_search", # 加载预置的 Web Search Prompt
)
print(embeddings.llm_chain.prompt)
if __name__ == "__main__":
main()
让我们先执行一下,看一下 Prompt 会是什么样子的:
2024-04-09 22:15:00
最近看到了一篇新的论文,关于评估 RAG 中的各种技术的评估。这篇《ARAGOG: Advanced RAG Output Grading》提供了一个对比多种 RAG 技术的评估。其中提到 HyDE 和 LLM re-rank 能够很好的提高检索精度。不过似乎 HyDE 其实讨论并不是很多,那么咱就对这块内容添砖加瓦一下,让我们从零开始进行一段 HyDE 的冒险。
大语言模型(LLM)虽然在过去的一段时间中成为大家眼中的香馍馍,但它们也存在一些关键的局限性:
要解决这些问题,那么就需要是用 RAG 来帮助解决。那么,什么是 RAG 呢?
Retrieval-Augmented Generation (RAG) 是一种基于检索的生成式模型,它结合了检索式和生成式的优点,可以在生成式模型(Generative Model)中引入外部知识。RAG 模型由两部分组成:一个是检索器,另一个是生成器。检索器负责从知识库中检索相关信息,生成器则负责生成答案。与传统的大语言模型(LLM)仅依靠自身有限的知识进行文本生成不同,RAG 模型能够动态地从海量的外部知识库中检索相关信息,并将这些信息融入到生成过程中,产生更加准确、连贯的输出内容。
换句话说,当用户提出一个查询或者需要生成某种类型的文本时,RAG 模型首先会利用一个信息检索子模块,从知识库中检索出与查询相关的信息。然后,RAG 的生成子模块会结合这些检索到的相关信息,生成最终的输出文本。这种结合检索和生成的方式,使 RAG 模型能够弥补传统 LLM 的局限性,提高生成内容的准确性和可靠性。
简单点来说,RAG 的基本流程如下:
但是在是用 RAG 的时候,你往往也会遇到一些问题。
让我们设想一个场景:我们有一个知识库,里面存储了大量的文档,我们希望使用 RAG 模型来回答用户的问题。但是,原始的 RAG 只能处理知识库中包含的数据,但是对于知识库中不存在的数据,RAG 无法处理。比较常见的场景包含用户的问答系统,帮助中心,对话系统等。用户通常对你的系统或者专有名词并不了解,他们通常会提出一些根据自己理解或者自己熟悉概念中的名词问题,这些表达方式可能并不在你的知识库中。这时候 RAG 就无法从知识库中检索到相关信息。
2024-03-18 18:11:00
在前几周的时候,Python的允许禁用 GIL PR 正式合并进入了 Python 3.13 的master分支。这是一个非常重要的 PR,因为它在未来将会对 Python 的并发性能产生非常大的影响。在即将到来的 Python 3.13 中,这个允许禁用 GIL和包含了 Copy and Paste JIT 技术,这些同时都对 Python 的性能产生了非常大的影响。
GIL,即全局解释器锁,是 Python 语言中一个技术术语。官方实现的 CPython 中包含了 GIL 的实现,同时也是最广泛使用的实现。GIL 的主要目的是在任何时候只允许一个线程执行 Python 字节码,这意味着即使你的程序在多核处理器上运行,也无法实现真正的并行执行。GIL 的存在主要是为了简化 CPython 的内存管理。Python 的对象,如列表、字典等,不是线程安全的,这意味着如果多个线程同时从不同的核心修改同一个对象,可能会导致数据不一致或者程序崩溃。GIL 通过限制同时执行的线程数来避免这种情况。
换句话说:如果一个系统线程想要执行 Python 的字节码,必须要获取到 GIL 锁,然后才可以执行 Python 字节码。而如果没有获取到锁,那么线程就会休眠,直到获得信号而被唤醒。为了保证 Python 的效率,Python 也会自动切换线程,比如 IO 阻塞时,或者执行了指定数量的 Python 字节码时。这样就尽量保证了 Python 的效率。当然,你也可以选择手工释放 GIL 锁,比如使用 C/C++/Rust 扩展,或者使用 Cython 等开发高性能扩展时。
但是这个 GIL 也是 Python 的一个瓶颈,因为它限制了 Python 的并发性能。在多核处理器上,Python 的并发性能并不是很好。这也是为什么很多人选择使用多进程而不是多线程来提高 Python 的并发性能。在现代的计算机上,多核处理器已经是标配,因此 Python 的并发性能成为了一个非常重要的问题。这也是为什么这么多年来,Python 的 GIL 一直是一个非常热门的话题。