LLM App SDK:LangChain vs. LlamaIndex

在Why RAG is big中,我表示支持检索增强生成(RAG)作为私有、离线、去中心化 LLM 应用程序的关键技术。 当你建造一些东西供自己使用时,你就是在孤军奋战。 你可以从头开始构建,但在现有框架上构建会更有效。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包

AFAIK,存在两种选择,针对不同的范围:

  • LangChain,一个使用LLM开发东西的通用框架。
  • LlamaIndex,一个专门用于构建 RAG 系统的框架。

选择一个框架是一项巨大的投资。 你想要一个拥有强大维护者和充满活力的社区的产品。 幸运的是,这两种选择在去年都已合并,因此规模是相当可量化的。 以下是这些数字的比较:

从财务数据来看,LlamaIndex 表现强劲,融资金额接近LangChain,但其目标市场要小得多(以 GitHub 星数作为社区兴趣的近似值)。 这可能表明 LlamaIndex 有更好的生存机会。 话虽这么说,LangChain提供了更多面向企业的、可以产生收入的产品(LangServe、LangSmith……),所以这个论点可能会颠倒过来。 从货币角度来看,这是一个艰难的决定。

我的财务 101只能带我到此为止。 让我们谈谈我真正擅长的领域并用 Python 进行讨论。 在本文中,我将使用这两个框架并行完成一些基本任务。 通过并排呈现代码片段,我希望它可以帮助你做出更明智的决定,决定在你自己的 RAG 聊天机器人中使用哪些代码片段。

1、用本地LLM创建聊天机器人

对于要实现的第一个任务,我选择制作一个仅限本地的聊天机器人。 这是因为我不想在学习使用这些框架时为模拟聊天消息支付云服务费用。

我选择让 LLM 在独立的推理服务器中运行,而不是让框架在每次运行脚本时将数 GB 模型加载到内存中。 这样可以节省时间并避免磁盘磨损。

虽然 LLM 推理有多种 API 模式,但我选择了一种与 OpenAI 兼容的模式,因此如果你愿意的话,它与官方 OpenAI 端点最相似。

这是使用 LlamaIndex 的方法:

from llama_index.llms import ChatMessage, OpenAILike  llm = OpenAILike(  api_base="http://localhost:1234/v1",  timeout=600,  # secs  api_key="loremIpsum",  is_chat_model=True,  context_window=32768,  
)  
chat_history = [  ChatMessage(role="system", content="You are a bartender."),  ChatMessage(role="user", content="What do I enjoy drinking?"),  
]  
output = llm.chat(chat_history)  
print(output)

下面是LangChain:

from langchain.schema import HumanMessage, SystemMessage  
from langchain_openai import ChatOpenAI  llm = ChatOpenAI(  openai_api_base="http://localhost:1234/v1",  request_timeout=600,  # secs, I guess.  openai_api_key="loremIpsum",  max_tokens=32768,  
)  
chat_history = [  SystemMessage(content="You are a bartender."),  HumanMessage(content="What do I enjoy drinking?"),  
]  
print(llm(chat_history))

对于这两种情况,API 密钥可以是任意的,但必须存在。 我猜想这是在两个框架中运行的 OpenAI SDK 的要求。

  • LangChain 区分可聊天的 LLM (ChatOpenAI) 和仅完成的 LLM (OpenAI),而 LlamaIndex 通过构造函数中的 is_chat_model 参数控制它。
  • LlamaIndex 区分官方 OpenAI 端点和 OpenAILike 端点,而 LangChain 通过 openai_api_base 参数确定将请求发送到哪里。
  • LlamaIndex 使用角色参数标记聊天消息,而 LangChain 使用单独的类。

到目前为止,这两个框架的情况看起来并没有太大不同。 让我们继续吧。

2、为本地文件构建RAG系统

有了LLM的联系,我们就可以开始做生意了。 现在让我们构建一个简单的 RAG 系统,该系统从本地文件夹中的文本文件中读取数据。 以下是如何使用 LlamaIndex 实现这一目标,主要取自本文档:

from llama_index import ServiceContext, SimpleDirectoryReader, VectorStoreIndexservice_context = ServiceContext.from_defaults(  embed_model="local",  llm=llm, # This should be the LLM initialized in the task above.
)  
documents = SimpleDirectoryReader(input_dir="mock_notebook/",
).load_data()  
index = VectorStoreIndex.from_documents(  documents=documents,service_context=service_context,
)
engine = index.as_query_engine(  service_context=service_context,  
)
output = engine.query("What do I like to drink?")  
print(output)

使用 LangChain,代码量会增加一倍,但仍然是可以管理的:

from langchain_community.document_loaders import DirectoryLoader  # pip install "unstructured[md]"  
loader = DirectoryLoader("mock_notebook/", glob="*.md")  
docs = loader.load()  from langchain.text_splitter import RecursiveCharacterTextSplitter  text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)  
splits = text_splitter.split_documents(docs)  from langchain_community.embeddings.fastembed import FastEmbedEmbeddings  
from langchain_community.vectorstores import Chroma  vectorstore = Chroma.from_documents(documents=splits, embedding=FastEmbedEmbeddings())  
retriever = vectorstore.as_retriever()  from langchain import hub  # pip install langchainhub  
prompt = hub.pull("rlm/rag-prompt")  def format_docs(docs):  return "\n\n".join(doc.page_content for doc in docs)  from langchain_core.runnables import RunnablePassthrough  rag_chain = (  {"context": retriever | format_docs, "question": RunnablePassthrough()}  | prompt  | llm # This should be the LLM initialized in the task above.
)  
print(rag_chain.invoke("What do I like to drink?"))

这些片段清楚地说明了这两个框架的不同抽象级别。 LlamaIndex 使用一个名为“查询引擎”的便捷包包装 RAG 管道,而 LangChain 则向您展示内部组件。 它们包括检索文档的串联器、“基于 X 请回答 Y”的提示模板以及链本身(如上面的 LCEL 所示)。

这种抽象的缺乏对学习者有影响:当使用 LangChain 进行构建时,你必须在第一次尝试时准确地知道你想要什么。 例如,比较 from_documents 的调用位置。 LlamaIndex 允许您在不显式选择存储后端的情况下使用向量存储索引,而 LangChain 似乎建议您立即选择一个实现。 (每个人在使用 LangChain 从文档创建向量索引时似乎都明确选择了后端。)在遇到可扩展性问题之前,我不确定在选择数据库时是否做出了明智的决定。

更有趣的是,虽然LangChain和LlamaIndex都提供类似Hugging Face Hub的云服务(即LangSmith Hub和LlamaHub),但拨到11的是LangChain。注意LangChain的hub.pull调用。 它只下载一个简短的文本模板,内容如下:

You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don’t know the answer, just say that you don’t know. Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:

虽然这确实鼓励与社区分享雄辩的提示,但我觉得这是一种矫枉过正。 存储约 1kB 的文本并不能真正证明拉取所涉及的网络调用是合理的。 我希望下载的工件被缓存。

3、将两者结合起来:支持 RAG 的聊天机器人

到目前为止,我们一直在构建不太智能的东西。 在第一个任务中,我们构建了一个可以保持对话但不太了解你的东西; 第二个,我们构建了一些了解您但不保留聊天记录的东西。 让我们将这两者结合起来。

使用 LlamaIndex,就像将 as_query_engine 与 as_chat_engine 交换一样简单:

# Everything from above, till and including the creation of the index.
engine = index.as_chat_engine()
output = engine.chat("What do I like to drink?")  
print(output) # "You enjoy drinking coffee."
output = engine.chat("How do I brew it?")  
print(output) # "You brew coffee with a Aeropress."

对于LangChain,我们需要把很多事情说清楚。 按照官方教程,我们先来定义一下内存:

# Everything above this line is the same as that of the last task.
from langchain_core.runnables import RunnablePassthrough, RunnableLambda  
from langchain_core.messages import get_buffer_string  
from langchain_core.output_parsers import StrOutputParser  
from operator import itemgetter  
from langchain.memory import ConversationBufferMemory  
from langchain.prompts.prompt import PromptTemplate  
from langchain.schema import format_document  
from langchain_core.prompts import ChatPromptTemplate  memory = ConversationBufferMemory(  return_messages=True, output_key="answer", input_key="question"  
)

计划如下:

1、在LLM开始时,我们从内存中加载聊天记录。

load_history_from_memory = RunnableLambda(memory.load_memory_variables) | itemgetter(  "history"  
)  
load_history_from_memory_and_carry_along = RunnablePassthrough.assign(  chat_history=load_history_from_memory  
)

2、我们要求LLM用上下文来丰富问题:“考虑到聊天记录,我应该在笔记中寻找什么来回答这个问题?”

rephrase_the_question = (  {  "question": itemgetter("question"),  "chat_history": lambda x: get_buffer_string(x["chat_history"]),  }  | PromptTemplate.from_template(  """You're a personal assistant to the user.  
Here's your conversation with the user so far:  
{chat_history}  
Now the user asked: {question}  
To answer this question, you need to look up from their notes about """  )  | llm  | StrOutputParser()  
)

我们不能只是将两者连接起来,因为话题可能在对话过程中发生了变化,使得聊天日志中的大多数语义信息变得无关紧要。

3、我们运行 RAG 管道。 请注意,我们如何通过暗示“我们作为用户将自己查找注释”来欺骗LLM,但实际上我们现在要求LLM承担繁重的工作。 我心情不好。

retrieve_documents = {  "docs": itemgetter("standalone_question") | retriever,  "question": itemgetter("standalone_question"),  
}

4、我们问LLM:“以检索到的文档作为参考(以及可选的迄今为止的对话),您对用户最新问题的回应是什么?”

def _combine_documents(docs):  prompt = PromptTemplate.from_template(template="{page_content}")  doc_strings = [format_document(doc, prompt) for doc in docs]  return "\n\n".join(doc_strings)  
compose_the_final_answer = (  {  "context": lambda x: _combine_documents(x["docs"]),  "question": itemgetter("question"),  }  | ChatPromptTemplate.from_template(  """You're a personal assistant.  
With the context below:  
{context}  
To the question "{question}", you answer:"""  )  | llm  
)

5、我们将最终回复附加到聊天记录中。

# Putting all 4 stages together...
final_chain = (  load_history_from_memory_and_carry_along  | {"standalone_question": rephrase_the_question}  | retrieve_documents  | compose_the_final_answer  
)  
# Demo.
inputs = {"question": "What do I like to drink?"}  
output = final_chain.invoke(inputs)  
memory.save_context(inputs, {"answer": output.content})  
print(output) # "You enjoy drinking coffee."
inputs = {"question": "How do I brew it?"}  
output = final_chain.invoke(inputs)  
memory.save_context(inputs, {"answer": output.content})  
print(output) # "You brew coffee with a Aeropress."

这真是一段旅程! 我们了解了很多关于LLM支持的应用程序通常是如何构建的。 特别是,我们多次利用了LLM,让它呈现不同的角色:查询生成器、总结检索到的文档的人,最后是我们对话的参与者。 我也希望您现在已经充分熟悉 LCEL。

4、升级为智能代理

如果将与你交谈的 LLM 角色视为一个人,那么 RAG 管道可以被视为该人使用的工具。 一个人可以使用多种工具,LLM也可以。 你可以给它提供搜索谷歌、查找维基百科、检查天气预报等的工具。通过这种方式,你的聊天机器人可以回答有关其直接知识之外的问题。

它不一定是信息工具。 通过为我们的LLM提供搜索网络、下购物订单、回复电子邮件等工具,您可以使其能够影响现实并改变世界。

工具很多,需要决定使用哪些工具以及使用顺序。 这种能力被称为Agent或智能体。 因此,具有代理权的LLM的角色被称为“代理”。

有多种方法可以为 LLM 申请提供代理权。 最模型通用(因此对自托管友好)的方式可能是 ReAct 范例,我在上一篇文章中对此进行了更多介绍。

在 LlamaIndex 中做到这一点,代码如下:

# Everything above this line is the same as in the above two tasks,  
# till and including where `notes_query_engine` is defined.  
# Let's convert the query engine into a tool.  
from llama_index.tools import ToolMetadata  
from llama_index.tools.query_engine import QueryEngineTool  notes_query_engine_tool = QueryEngineTool(  query_engine=notes_query_engine,  metadata=ToolMetadata(  name="look_up_notes",  description="Gives information about the user.",  ),  
)  
from llama_index.agent import ReActAgent  agent = ReActAgent.from_tools(  tools=[notes_query_engine_tool],  llm=llm,  service_context=service_context,  
)  
output = agent.chat("What do I like to drink?")  
print(output) # "You enjoy drinking coffee."
output = agent.chat("How do I brew it?")  
print(output) # "You can use a drip coffee maker, French press, pour-over, or espresso machine."

请注意,对于我们的后续问题“如何煮咖啡”,代理的回答与仅作为查询引擎时的回答不同。 这是因为代理可以自行决定是否从我们的笔记中查找。 如果他们有足够的信心回答问题,代理可能会选择根本不使用任何工具。 我们的“我如何……”的问题可以有两种解释:要么是关于通用选项,要么是关于事实回忆。 显然,代理选择以前一种方式理解它,而我们的查询引擎(负责从索引中查找文档)必须选择后者。

有趣的是,代理是 LangChain 决定提供高级抽象的一个用例:

# Everything above is the same as in the 2nd task, till and including where we defined `rag_chain`.  
# Let's convert the chain into a tool.  
from langchain.agents import AgentExecutor, Tool, create_react_agent  tools = [  Tool(  name="look_up_notes",  func=rag_chain.invoke,  description="Gives information about the user.",  ),
]
react_prompt = hub.pull("hwchase17/react-chat")  
agent = create_react_agent(llm, tools, react_prompt)  
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools)  result = agent_executor.invoke(  {"input": "What do I like to drink?", "chat_history": ""}  
)  
print(result) # "You enjoy drinking coffee."
result = agent_executor.invoke(  {  "input": "How do I brew it?",  "chat_history": "Human: What do I like to drink?\nAI: You enjoy drinking coffee.",  }
)
print(result) # "You can use a drip coffee maker, French press, pour-over, or espresso machine."

虽然我们仍然需要手动管理聊天记录,但与制作 RAG 链相比,制作代理要容易得多。 create_react_agent 和 AgentExecutor 涵盖了底层的大部分接线工作。

5、结束语

LlamaIndex 和 LangChain 是构建 LLM 应用程序的两个框架。 虽然 LlamaIndex 专注于 RAG 用例,但 LangChain 似乎被更广泛地采用。 但它们在实践中有何不同? 在这篇文章中,我比较了这两个框架完成四个常见任务的情况:

  • 连接到本地 LLM 实例并构建聊天机器人。
  • 索引本地文件并构建 RAG 系统。
  • 将以上两者结合起来,制作一个具有 RAG 功能的聊天机器人。
  • 将聊天机器人转变为代理,使其可以使用更多的工具并进行简单的推理。

我希望它们能帮助你为 LLM 申请做出明智的选择。 另外,祝你在构建自己的聊天机器人的过程中一切顺利!


原文链接:LangChain vs. LlamaIndex - BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/662299.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java多线程--避免同步机制带来的死锁问题及用Lock锁解决线程安全问题

文章目录 一、死锁(1)说明(2)案例1、案例12、案例23、案例3 (3)诱发死锁的原因及解决方案1、诱发死锁的原因2、避免死锁 二、JDK5.0新特性:Lock(锁)(1)介绍(2…

小白水平理解面试经典题目_数组类LeetCode 118 Pascal‘s Triangle【回归解法】

LeetCode 118 生成杨辉三角(Pascal’s Triangle) 小白渣翻译 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。 在杨辉三角中,每个数是它左上方和右上方的数的和。 例子 这里是小白理解 那么这种题目一上来看&#xf…

4-树-合并两个有序链表

这是树的第4篇算法,力扣链接。 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2: 输入&#xf…

Linux下gcc的使用与程序的翻译

gcc和程序的翻译过程 gcc介绍程序的翻译过程预编译编译汇编链接 命令行式宏定义 gcc介绍 gcc是一款编译C语言编译器,可以把我们用vim写的代码编译成可执行程序。编译C用g进行编译,C的文件后缀是test.cc或test.cpp或test.cxx 如果要安装g就执行以下命令 …

C# winform 多语言(json)方式实现

前后对比 使用nuget json工具包1.总体思路 创建对应的json字典对照表 { "测试":"Test", "语言":"Language", "设置":"Set", "中文(默认)":"Chinese (default)", "英文":"E…

【HarmonyOS应用开发】ArkTS 属性动画的使用(十二)

一、概述 属性动画,是最为基础的动画,其功能强大、使用场景多,应用范围较广。常用于如下场景中: 一、页面布局发生变化。例如添加、删除部分组件元素。二、页面元素的可见性和位置发生变化。例如显示或者隐藏部分元素&#xff0…

CANoe学习笔记——窗口类型

CANoe中的窗口类型,共分为三种 1:MDI windows 2:Standard Windows 3:Docking Windows 窗口有多种类型,每种类型都定义了特定的窗口行为。通过点击窗口顶部的区域,可以更改窗口类型。 如下图&#xff0…

Python的requests库与HTTP代理的使用:魔法般的网络探险之旅

嘿,各位魔法探险家们!今天我们要一起探索Python的requests库与HTTP代理的神奇组合,开启一段魔法般的网络探险之旅! 首先,我们要明白什么是requests库。简单说,requests库就是Python中的魔法飞毯&#xff0…

Linux基础知识合集

整理了一下学习的一些关于Linux的一些基础知识,同学们也可以通过公众号菜单栏查看! 一、基础知识 Linux基础知识 Linux命令行基础学习 Linux用户与组概念初识 Linux文件与目录权限基础 Linux中文件内容的查看 Linux系统之计划任务管理 二、服务器管理 Vm…

MySql主从同步,同步SQL_ERROR 1032解决办法

1.登录从库 mysql -u root -p 2.输入命令查看状态 SHOW SLAVE STATUS\G; 3.找到对应的错误数据位置 Slave_IO_Running: YesSlave_SQL_Running: NoReplicate_Do_DB: app_push_centerReplicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Tabl…

“欢天喜地迎新春”下姜村邻里守望写对联活动

卯兔追冬去,辰龙报春来。空谷幽香谱佳期,红联金句寄吉祥。春联是我国特有的文学形式,贴春联是继承传统习俗的一种方式,是对祖先的尊敬,对传统的继承。春节前夕,家家户户贴上红红的春联,一副副透…

西圣Olite开放式耳机持续100+天霸榜:品质优势再掀数码狂潮

随着开放式耳机的市场竞争加剧,用户对耳机的音质和配置要求越来越高。而西圣开放式耳机的不断推陈出新,正是对客户需求的完美回应!西圣开放式耳机,在现在鱼龙混杂的市场上,能够获得着卓越的研发成果并且还在不断的追求…

qt学习:停车场管理系统+摄像头+http识别车牌+sqlite3数据库

目录 参考前面发的几篇文章http识别车牌,sqlite3数据库、摄像头的文章 步骤 部分代码 新建一个项目,加入前面用到的http和image两个文件,和加入用到的模块和头函数和成员,加入前面用到的全局变量 配置ui界面 在构造函数中初…

Qt5 基于OpenGL实现六轴机械臂三维仿真

需求 在Qt中通过OPenGL方式加载三维模型STL文件,然后将多个结构的STL文件类型的模型进行组装,形成6轴机械臂三维模型的显示,并且可以对每个关节进行关节角度的控制。 新建一个C类STLFileLoader,用于加载STL文件,并进…

IP协议(2) 和 数据链路层协议基础

IP协议续 1.路由选择 在复杂的网络结构中,我们需要找到一个通往终点的路线,这就是路由选择 举个例子:我们在没有手机导航之前,想去一个地方得是到一个地方问一下路的方式最终找到目的地 路由的过程,其实就是样子问路的过程 1.当IP数据包到达路由器的时候,会查看目的IP 2.路由器…

Jmeter高级使用

文章目录 JMeter之计数器JMeter之集合点JMeter之断言JMeter之动态关联后置处理器:正则表达式提取器 JMeter之分布式测试JMeter之组件执行顺序元件的作用域元件的执行顺序配置元件Http Cookie管理器 多协议接口的性能测试Debug采样器Http请求中文乱码的解决Post参数设…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)

专属领域论文订阅 关注{晓理紫|小李子},每日更新论文,如感兴趣,请转发给有需要的同学,谢谢支持 如果你感觉对你有所帮助,请关注我,每日准时为你推送最新论文。 为了答谢各位网友的支持,从今日起…

Java_简单模拟实现ArrayList_学习ArrayList

文章目录 一、 了解线性表和顺序表区别1.线性表2.顺序表 二、模拟实现1.定义接口2.定义MyArrayList3.成员变量以及构造方法4.实现打印数组5.实现add方法6.实现查找某个数是否存在contains或者某个数的下标indexOf7.获取或更改pos位置的值 get和set8.获取数组大小 size9.删除某个…

Git版本管理工具(实战进阶):零基础到起飞实战项目完整篇 →Git学习一篇就够 从基本指令、到本地仓库、远程仓库、实战项目开发演练介绍超详细!

heima 李师傅最新版 Git的讲解 文章目录 Git在实战项目开发使用功能学习01.Git 初识02.Git 仓库03.Git 的三个区域04.Git 文件状态05.Git 暂存区作用06.练习-登录页面07.Git-切换版本08.删除文件09.忽略文件10.分支的概念11.练习-登录 bug 修复12.分支-合并与删除13.分支-合并与…

循环神经网络RNN专题(01/6)

一、说明 RNN用于处理序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。例如,你要预测句子的下一个单词是什么&a…