txtai:全能AI框架

txtai是一个全能的AI框架,提供构建自主代理、检索增强生成(RAG)流程、多模型工作流等更多功能。

txtai:全能AI框架

人工智能正在快速发展,有许多新的发展。大规模生成式语言模型是一项令人兴奋的新能力,使我们能够添加令人惊叹的功能。创新仍在继续,每周似乎都有新模型和进步出现。

很难过滤掉噪音,知道哪些是现实可行的。虽然我们还没有达到完全的AI自动化,但有很多方法可以将AI集成到业务工作流中。

本文介绍了txtai,一个用于语义搜索、大型语言模型编排和语言模型工作流的全能AI框架。

1、txtai简介

tetxtai 是一个用于语义搜索、大型语言模型编排和语言模型工作流的全能AI框架。

txtai 的关键组件是一个嵌入数据库,它结合了向量索引(稀疏和密集)、图网络和关系数据库。

这个基础使得向量搜索成为可能,或者作为大型语言模型(LLM)应用程序的强大知识源。

构建自主代理、检索增强生成(RAG)流程、多模型工作流等更多功能。

以下是关键特性的摘要:

  • 🔎 带有SQL、对象存储、主题建模、图分析和多模态索引的向量搜索
  • 📄 为文本、文档、音频、图像和视频创建嵌入
  • 💡 由语言模型驱动的管道,运行LLM提示、问答、标记、转录、翻译、摘要等功能
  • ↪️️ 将管道连接在一起并聚合业务逻辑的工作流。txtai 处理可以是简单的微服务或多模型工作流。
  • 🤖 智能地将嵌入、管道、工作流和其他代理连接在一起以自主解决复杂问题的代理
  • ⚙️ Web 和 Model Context Protocol (MCP) API。支持 JavaScriptJavaRustGo 绑定。
  • 🔋 带有默认值的电池包含,快速启动
  • ☁️ 本地运行或通过容器编排扩展

txtai 使用 Python 3.10+、Hugging Face TransformersSentence TransformersFastAPI 构建。txtai 在 Apache 2.0 许可证下开源。

2、安装和运行txtai

可以通过 pipDocker 安装txtai。以下是如何通过pip安装的示例。

pip install txtai

3、语义搜索

嵌入数据库是实现语义搜索的核心引擎。数据被转换为嵌入向量,其中相似的概念会产生相似的向量。这些向量用于构建大小不同的索引。索引用于查找具有相同含义的结果,而不仅仅是相同的关键词。

嵌入数据库的基本用例是为语义搜索构建近似最近邻(ANN)索引。以下示例对少量文本条目进行索引,以展示语义搜索的价值。

from txtai import Embeddings
# Works with a list, dataset or generator
data = [
  "US tops 5 million confirmed virus cases",
  "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg",
  "Beijing mobilises invasion craft along coast as Taiwan tensions escalate",
  "The National Park Service warns against sacrificing slower friends in a bear attack",
  "Maine man wins $1M from $25 lottery ticket",
  "Make huge profits without work, earn up to $100,000 a day"
]
# Create an embeddings
embeddings = Embeddings(path="sentence-transformers/nli-mpnet-base-v2")
# Create an index for the list of text
embeddings.index(data)
print("%-20s %s" % ("Query", "Best Match"))
print("-" * 50)
# Run an embeddings search for each query
for query in ("feel good story", "climate change", 
    "public health story", "war", "wildlife", "asia",
    "lucky", "dishonest junk"):
  # Extract uid of first result
  # search result format: (uid, score)
  uid = embeddings.search(query, 1)[0][0]
  # Print text
  print("%-20s %s" % (query, data[uid]))

上面的例子显示,对于所有查询,查询文本不在数据中。这就是Transformer模型相对于基于令牌的搜索的强大之处。开箱即用的就是🔥🔥🔥!

4、更新和删除

嵌入支持更新和删除。upsert 操作会插入新数据并更新现有数据。

以下部分运行查询,然后更新一个值更改顶级结果,最后删除更新后的值以恢复到原始查询结果。

# 运行初始查询  
uid = embeddings.search("感人的故事", 1)[0][0]  
print("初始: ", data[uid])
# 创建一个要修改的数据副本  
udata = data.copy()# 更新数据  
udata[0] = "看这个:出生的小熊猫"  
embeddings.upsert([(0, udata[0], None)])uid = embeddings.search("感人的故事", 1)[0][0]  
print("更新后: ", udata[uid])# 从索引中移除刚刚添加的记录  
embeddings.delete([0])# 确保值与之前的值匹配  
uid = embeddings.search("感人的故事", 1)[0][0]  
print("删除后: ", udata[uid])
初始: 缅因州男子从25美元的彩票中赢得100万美元  
更新后: 看这个:出生的小熊猫  
删除后: 缅因州男子从25美元的彩票中赢得100万美元

5、持久化

嵌入可以保存到存储中并重新加载。

embeddings.save("index")
embeddings = Embeddings()  
embeddings.load("index")uid = embeddings.search("气候变化", 1)[0][0]  
print(data[uid])
加拿大的最后一个完整冰架突然崩塌,形成了一座曼哈顿大小的冰山

6、混合搜索

虽然密集向量索引无疑是语义搜索系统的最佳选择,但稀疏关键词索引仍然可以增加价值。可能会有一些情况需要找到精确匹配。

混合搜索结合了稀疏和密集向量索引的结果,兼顾两者的优势。

# 创建嵌入  
embeddings = Embeddings(  
  hybrid=True,  
  path="sentence-transformers/nli-mpnet-base-v2"  
)
# Create an index for the list of text
embeddings.index(data)
print("%-20s %s" % ("Query", "Best Match"))
print("-" * 50)
# Run an embeddings search for each query
for query in ("feel good story", "climate change", 
    "public health story", "war", "wildlife", "asia",
    "lucky", "dishonest junk"):
  # Extract uid of first result
  # search result format: (uid, score)
  uid = embeddings.search(query, 1)[0][0]
  # Print text
  print("%-20s %s" % (query, data[uid]))

与语义搜索结果相同。让我们只使用关键词索引来查看这些结果。

# 创建嵌入  
embeddings = Embeddings(keyword=True)
# 为文本列表创建索引  
embeddings.index(data)print(embeddings.search("感人的故事"))  
print(embeddings.search("彩票"))
[]  
[(4, 0.5234998733628726)]

可以看到,当嵌入实例仅使用关键词索引时,它只能找到关键词匹配,而不能找到语义匹配。

7、内容存储

到目前为止,所有示例都引用原始数据数组来检索输入文本。这在演示中很好,但如果有一百万份文档怎么办?在这种情况下,文本需要从外部数据存储中检索使用id。

内容存储添加了一个关联数据库(例如SQLite、DuckDB),该数据库与向量索引一起存储关联元数据。文档文本、附加元数据和附加对象可以与索引的向量一起存储和检索。

# 启用内容创建嵌入。  
# 默认行为是仅存储索引的向量。  
embeddings = Embeddings(  
  path="sentence-transformers/nli-mpnet-base-v2",  
  content=True,  
  objects=True  
)
# 为文本列表创建索引  
embeddings.index(data)print(embeddings.search("感人的故事", 1)[0]["text"])
缅因州男子从25美元的彩票中赢得100万美元

上面唯一的变化是将 content 标志设置为 True。这启用了与索引一起存储文本和元数据(如果提供)。注意如何直接从查询结果中提取文本!

让我们添加一些元数据。

8、使用SQL查询

启用内容后,整个字典都会被存储并可以查询。除了向量查询外,txtai 接受SQL查询。这使得可以使用向量索引和数据库后端中存储的内容进行组合查询。

# 为文本列表创建索引  
embeddings.index([{"text": text, "length": len(text)} for text in data])
# 通过 sco重  
print(embeddings.search("select text, score from txtai where similar('hiking danger') and score >= 0.15"))# 根据元数据字段 'length' 过滤  
print(embeddings.search("select text, length, score from txtai where similar('feel good story') and score >= 0.05 and length >= 40"))# 执行聚合查询  
print(embeddings.search("select count(*), min(length), max(length), sum(length) from txtai"))
[{'text': '美国国家公园管理局警告不要在熊袭击时牺牲较慢的朋友', 'score': 0.3151373863220215}]  
[{'text': '缅因州男子赢得100万美元的25美元彩票', 'length': 42, 'score': 0.08329027891159058}]  
[{'count(*)': 6, 'min(length)': 39, 'max(length)': 94, 'sum(length)': 387}]

上面的例子添加了一个简单的额外字段,即文本长度。

注意第二个查询根据元数据字段 length 以及 similar 查询条件进行过滤。这为向量搜索与传统的过滤结合提供了一个很好的组合,有助于识别最佳结果。

9、对象存储

除了元数据,二进制内容也可以与文档关联。下面的例子下载一张图片,并将其与相关文本一起插入到嵌入索引中。

import urllib
from IPython.display import Image# 获取一张图片  
request = urllib.request.urlopen("https://raw.githubusercontent.com/neuml/txtai/master/demo.gif")# 插入具有文本和对象的新记录  
embeddings.upsert([("txtai", {"text": "txtai 执行机器学习工作流以转换数据并构建基于人工智能的语义搜索引擎。", "object": request.read()}, None)])# 查询 txtai 中与 "machine learning" 最相似的结果并获取相关对象  
result = embeddings.search("select object from txtai where similar('machine learning') limit 1")[0]["object"]# 显示图像  
Image(result.getvalue(), width=600)

10、主题建模

主题建模是通过语义图实现的。语义图,也称为知识图谱或语义网络,通过语义关系将节点连接起来形成一个图网络。在 txtai 中,它们可以利用嵌入索引中固有的关系。

# 创建带有图索引的嵌入  
embeddings = Embeddings(  
  path="sentence-transformers/nli-mpnet-base-v2",  
  content=True,  
  functions=[  
    {"name": "graph", "function": "graph.attribute"},  
  ],  
  expressions=[  
    {"name": "category", "expression": "graph(indexid, 'category')"},  
    {"name": "topic", "expression": "graph(indexid, 'topic')"},  
  ],  
  graph={  
    "topics": {  
      "categories": ["健康", "气候", "金融", "世界政治"]  
    }  
  }  
)
embeddings.index(data)  
embeddings.search("select topic, category, text from txtai")
[{'topic': 'confirmed_cases_us_5',  
  'category': '健康',  
  'text': '美国新冠确诊病例超过500万'},  
 {'topic': 'collapsed_iceberg_ice_intact',  
  'category': '气候',  
  'text': "加拿大最后一个完全完整的冰架突然崩塌,形成了一个曼哈顿大小的冰山"},  
 {'topic': 'beijing_along_craft_tensions',  
  'category': '世界政治',  
  'text': '北京在台湾紧张局势升级之际动员入侵船只沿岸']}

当启用图索引时,每个嵌入实例中的条目都会分配一个主题。主题是通过社区检测算法分组的图节点上的稀疏索引动态创建的。

如上所示,还可以推导出主题类别。

11、子索引

可以在嵌入中配置子索引。单个嵌入实例可以有多个子索引,每个子索引有不同的配置。

我们将构建一个同时具有关键词索引和密集索引的嵌入索引来演示。

# 创建带有子索引的嵌入  
embeddings = Embeddings(  
  content=True,  
  defaults=False,  
  indexes={  
    "keyword": {  
      "keyword": True  
    },  
    "dense": {  
      "path": "sentence-transformers/nli-mpnet-base-v2"  
    }  
  }  
)  
embeddings.index(data)
embeddings.search("feel good story", limit=1, index="keyword")
[]
embeddings.search("feel good story", limit=1, index="dense")
[{'id': '4',  
  'text': '缅因州男子赢得100万美元的25美元彩票',  
  'score': 0.08329027891159058}]

再次说明了关键词搜索和语义搜索的区别。第一个搜索调用使用定义的关键词索引,第二个使用密集向量索引。

12、大语言模型编排

txtai 是一个一体化的 AI 框架。txtai 支持构建自主代理、检索增强生成(RAG)、与数据对话、包含大型语言模型(LLM)的管道和工作流。

RAG 管道 是 txtai 对检索增强生成(RAG)的一种实现。该管道通过将提示、上下文数据存储和生成模型结合起来,从内容中提取知识。

以下示例展示了如何让大型语言模型(LLM)使用嵌入数据库作为上下文。

import torch  
from txtai import RAG
def prompt(question):  
  return [{  
    "query": question,  
    "question": f"""  
回答以下问题,使用下面的上下文。  
问题:{question}  
上下文:  
"""  
}]# 创建嵌入  
embeddings = Embeddings(  
  path="sentence-transformers/nli-mpnet-base-v2",  
  content=True,  
  autoid="uuid5"  
)# 为文本列表创建索引  
embeddings.index(data)# 创建并运行 RAG 实例  
rag = RAG(  
  embeddings,  
  "google/flan-t5-large",   
  torch_dtype=torch.bfloat16,   
  output="reference"  
)  
rag(prompt("哪个国家正在经历气候变化问题?"))[0]
{'answer': '加拿大', 'reference': 'da633124-33ff-58d6-8ecb-14f7a44c042a'}

上述逻辑首先构建了一个嵌入索引。然后加载了一个 LLM,并使用嵌入索引驱动 LLM 提示。

RAG 管道可以选择返回最佳匹配记录的答案的引用。该引用可以用来解析完整的答案引用。注意上面的嵌入使用了 uuid 自动序列

uid = rag(prompt("哪个国家正在经历气候变化问题?"))[0]["reference"]  
embeddings.search(f"select id, text from txtai where id = '{uid}'")
[{'id': 'da633124-33ff-58d6-8ecb-14f7a44c042a',  
  'text': "加拿大最后一个完全完整的冰架突然崩塌,形成了一个曼哈顿大小的冰山"}]

LLM 推理也可以单独运行。

from txtai import LLM
llm = LLM("google/flan-t5-large", torch_dtype=torch.bfloat16)  
llm("华盛顿特区你会去什么地方?")
national museum of american history

13、语言模型工作流

语言模型工作流,也称为语义工作流,将语言模型连接起来以构建智能应用程序。

工作流可以与嵌入实例一起运行,类似于关系数据库中的存储过程。工作流可以用 Python 或 YAML 编写。我们将演示如何用 YAML 编写一个工作流。

# 嵌入实例  
writable: true  
embeddings:  
  path: sentence-transformers/nli-mpnet-base-v2  
  content: true  
  functions:  
    - {name: translation, argcount: 2, function: translation}
# 翻译管道  
translation:# 工作流定义  
workflow:  
  search:  
    tasks:  
      - search  
      - action: translation  
        args:  
          target: fr  
        task: template  
        template: "{text}"

上述工作流加载了一个嵌入索引,并定义了一个搜索工作流。搜索工作流运行搜索并将结果传递给翻译管道。翻译管道将结果翻译成法语。

from txtai import Application
# 构建索引  
app = Application("embeddings.yml")  
app.add(data)  
app.index()# 运行工作流  
list(app.workflow(  
  "search",   
  ["select text from txtai where similar('feel good story') limit 1"]  
))
['缅因州男子赢得100万美元的25美元彩票']

在某些情况下,SQL 函数可以完成与工作流相同的事情。下面的函数运行翻译管道作为一个函数。

app.search("select translation(text, 'fr') text from txtai where similar('feel good story') limit 1")
[{'text': '缅因州男子赢得100万美元的25美元彩票'}]

工作流中也可以使用模板的 LLM 链。工作流是自包含的,它们既可以与嵌入实例一起运行,也可以独立运行。以下工作流使用 LLM 条件翻译文本为法语,然后检测文本的语言。

sequences:  
  path: google/flan-t5-large  
  torch_dtype: torch.bfloat16
workflow:  
  chain:  
    tasks:  
      - task: template  
        template: 将 '{statement}' 翻译成 {language} 如果它是英语  
        action: sequences  
      - task: template  
        template: 下面的文本是什么语言?{text}  
        action: sequences
inputs = [  
  {"statement": "Hello, how are you", "language": "French"},  
  {"statement": "Hallo, wie geht's dir", "language": "French"}  
]
app = Application("workflow.yml")  
list(app.workflow("chain", inputs))
['French', 'German']

14、结束语

AI 正以惊人的速度发展。一年前不可能做到的事情现在已经成为可能。本文介绍了 txtai,一个一体化的 AI 框架。可能性是无限的,我们对基于 txtai 构建的内容感到兴奋!


原文链接:Introducing txtai, the all-in-one AI framework

汇智网翻译整理,转载请标明出处