如何使用 LangChain 构建基于LLMs的应用——入门指南

news2024/11/19 10:33:51

大型语言模型(LLMs)是非常强大的通用推理工具,在各种情况下都非常有用。

但是,与构建传统软件不同,使用LLMs存在一些挑战:

  • 调用往往是长时间运行的,并且随着可用输出而逐步生成输出。
  • 与固定参数的结构化输入(例如JSON)不同,它们采用非结构化和任意的自然语言作为输入。它们能够“理解”该语言的微妙之处。
  • 它们是非确定性的。即使是相同的输入,您也可能获得不同的输出。

LangChain 是一个流行的框架,用于创建基于LLMs的应用程序。它考虑到了这些因素以及其他因素,并提供了与封闭源模型提供商(如OpenAI、Anthropic和[Google、开源模型以及向量存储等其他第三方组件)的广泛集成。

本文将介绍使用LLMs和LangChain的Python库构建基础知识。唯一的要求是对Python有基本的了解——不需要机器学习经验!

(本文视频讲解:java567.com)

你将学到:

  • 基本项目设置
  • 使用聊天模型和其他基本的LangChain组件
  • 使用LangChain表达语言创建链
  • 在生成后立即流式输出
  • 传递上下文来引导模型的输出(基本的RAG概念)
  • 调试和追踪链的内部情况

让我们开始吧!

项目设置

我们建议使用 Jupyter 笔记本来运行本教程中的代码,因为它提供了一个清晰、交互式的环境。请参阅此页面以获取在本地设置的说明,或者查看 [Google Colab 以获得基于浏览器的体验。

首先,您需要选择要使用的聊天模型。如果您以前使用过类似 ChatGPT 的界面,那么聊天模型的基本概念对您来说应该很熟悉——模型将消息作为输入,并返回消息作为输出。不同之处在于我们将在代码中完成这些操作。

本指南默认使用Anthropic及其Claude 3聊天模型,但LangChain还有[广泛的其他集成可供选择,包括像GPT-4这样的OpenAI模型。

pip install langchain_core langchain_anthropic

如果您在Jupyter笔记本中工作,您需要在pip之前加上%符号,像这样:%pip install langchain_core langchain_anthropic

您还需要一个Anthropic API密钥,您可以从他们的控制台中获取。一旦获取到,将其设置为一个名为ANTHROPIC_API_KEY的环境变量:

export ANTHROPIC_API_KEY="..."

如果您愿意,也可以直接将密钥传递给模型。

第一步

您可以像这样初始化您的模型:

from langchain_anthropic import ChatAnthropic

chat_model = ChatAnthropic(
    model="claude-3-sonnet-20240229",
    temperature=0
)

# 如果您更喜欢显式传递您的密钥
# chat_model = ChatAnthropic(
#   model="claude-3-sonnet-20240229",
#   temperature=0,
#   api_key="YOUR_ANTHROPIC_API_KEY"
# )

model参数是一个字符串,匹配Anthropic支持的模型之一。在撰写本文时,Claude 3 Sonnet在速度、成本和推理能力之间达到了良好的平衡。

temperature是模型用于生成响应的随机性量度。为了保持一致,在本教程中,我们将其设置为0,但您可以尝试使用更高的值来进行创意用途的实验。

现在,让我们尝试运行它:

chat_model.invoke("Tell me a joke about bears!")

这是输出:

AIMessage(content="Here's a bear joke for you:\\n\\nWhy did the bear dissolve in water?\\nBecause it was a polar bear!")

您可以看到输出是一个称为AIMessage的东西。这是因为聊天模型使用聊天消息作为输入和输出。

**注意:**在前面的示例中,您能够将简单的字符串作为输入,因为LangChain接受一些方便的简写形式,它会自动将其转换为正确的格式。在这种情况下,一个单一的字符串被转换为一个带有单个HumanMessage的数组。

LangChain还包含纯文本完成LLMs的抽象,这些LLMs具有字符串输入和字符串输出。但在撰写本文时,针对聊天的变体已经在流行度上超过了LLMs。例如,GPT-4和Claude 3都是聊天模型。

为了说明正在发生的事情,您可以使用更明确的消息列表调用上述内容:

from langchain_core.messages import HumanMessage

chat_model.invoke([
    HumanMessage("Tell me a joke about bears!")
])

您将获得类似的输出:

AIMessage(content="Here's a bear joke for you:\\n\\nWhy did the bear bring a briefcase to work?\\nHe was a business bear!")

提示模板

模型本身非常有用,但通常将输入参数化以避免重复非常方便。LangChain为此提供了提示模板

在这里插入图片描述
LangChain中的提示模板

一个简单的例子可能是这样的:

from langchain_core.prompts import ChatPromptTemplate

joke_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a world class comedian."),
    ("human", "Tell me a joke about {topic}")
])

您可以使用与聊天模型相同的.invoke()方法应用模板:

joke_prompt.invoke({"topic": "beets"})

这是结果:

ChatPromptValue(messages=[
    SystemMessage(content='You are a world class comedian.'),
    HumanMessage(content='Tell me a joke about beets')
])

让我们逐步进行:

  • 您使用from_messages构建了一个包含SystemMessageHumanMessage模板的提示模板。
  • 您可以将SystemMessages视为不是当前对话的一部分,而是纯粹引导输入的元指令。
  • 提示模板包含花括号中的{topic}。这表示一个名为"topic"的必需参数。
  • 您使用一个带有名为"topic"和值"beets"的字典调用提示模板。
  • 结果包含格式化的消息。

接下来,您将学习如何将此提示模板与您的聊天模型一起使用。

链式操作

您可能已经注意到,无论是提示模板还是聊天模型,它们都实现了.invoke()方法。在LangChain术语中,它们都是可运行实例

您可以使用管道(|)操作符将可运行对象组合成“链”,在其中您可以使用上一个对象的输出调用下一个步骤。以下是一个示例:

chain = joke_prompt | chat_model

结果的chain本身就是一个可运行对象,并自动实现了.invoke()(以及后面将会看到的几个其他方法)。这是LangChain表达语言(LCEL)的基础。

让我们调用这个新的链:

chain.invoke({"topic": "beets"})

链返回了一个以甜菜为主题的笑话:

AIMessage(content="Here's a beet joke for you:\\n\\nWhy did the beet blush? Because it saw the salad dressing!")

现在,假设您只想处理消息的原始字符串输出。LangChain有一个称为输出解析器的组件,顾名思义,它负责将模型的输出解析为更易访问的格式。由于组合的链也是可运行的,您可以再次使用管道操作符:

from langchain_core.output_parsers import StrOutputParser

str_chain = chain | StrOutputParser()

# 相当于:
# str_chain = joke_prompt | chat_model | StrOutputParser()

很酷!现在让我们调用它:

str_chain.invoke({"topic": "beets"})

结果现在是我们希望的字符串:

"Here's a beet joke for you:\\n\\nWhy did the beet blush? Because it saw the salad dressing!"

您仍然将 {"topic": "beets"} 作为输入传递给新的 str_chain,因为序列中的第一个可运行对象仍然是您之前声明的提示模板。

在这里插入图片描述
提示模板和输出解析器

流式处理

使用LCEL组合链的最大优势之一是流式处理体验。

所有可运行对象都实现了.stream()方法(以及在异步环境中的.astream()),包括链。该方法返回一个生成器,将随时生成输出,这使我们能够尽快获得输出。

虽然每个可运行对象都实现了.stream(),但并非所有对象都支持多个块。例如,如果您在提示模板上调用.stream(),它将只生成一个与.invoke()相同的输出块。

您可以使用 for ... in 语法遍历输出。尝试使用您刚刚声明的 str_chain

for chunk in str_chain.stream({"topic": "beets"}):
    print(chunk, end="|")

您将获得多个字符串作为输出(在print函数中,块由|字符分隔):

Here|'s| a| beet| joke| for| you|:|

Why| did| the| beet| blush|?| Because| it| saw| the| salad| dressing|!|

str_chain 这样组合的链将尽早开始流式处理,这在这种情况下是链中的聊天模型。

一些输出解析器(如此处使用的StrOutputParser)和许多LCEL原语能够处理来自前面步骤生成的流式块,实际上充当转换流或透传,并不会中断流式处理。

如何使用上下文指导生成

LLMs是在大量数据上训练的,并且在各种主题上具有一些固有的“知识”。然而,通常在回答问题时传递模型私有或更具体的数据作为上下文,以获取有用的信息或洞察力。如果您以前听过“RAG”或“检索增强生成”,这就是它背后的核心原理。

其中一个最简单的例子是告诉LLM当前日期是什么。因为LLMs是在训练时的快照,它们不能自然地确定当前时间。以下是一个例子:

chat_model = ChatAnthropic(model_name="claude-3-sonnet-20240229")

chat_model.invoke("What is the current date?")

响应是:

AIMessage(content="Unfortunately, I don't actually have a concept of the current date and time. As an AI assistant without an integrated calendar, I don't have a dynamic sense of the present date. I can provide you with today's date based on when I was given my training data, but that may not reflect the actual current date you're asking about.")

现在,让我们看看当您将当前日期作为上下文提供给模型时会发生什么:

from datetime import date

prompt = ChatPromptTemplate.from_messages([
    ("system", 'You know that the current date is "{current_date}".'),
    ("human", "{question}")
])

chain = prompt | chat_model | StrOutputParser()

chain.invoke({
    "question": "What is the current date?",
    "current_date": date.today()
})

然后您可以看到,模型生成了当前日期:

"The current date is 2024-04-05."

好极了!现在,让我们再进一步。语言模型是在大量数据上训练的,但它们并不知道所有事情。如果您直接向聊天模型询问有关当地餐厅的非常具体的问题,情况会怎样:

chat_model.invoke(
    "What was the Old Ship Saloon's total revenue in Q1 2023?"
 )

该模型本质上不知道答案,甚至不知道我们可能在谈论世界上的哪个老船酒馆:

AIMessage(content="I'm sorry, I don't have any specific financial data about the Old Ship Saloon's revenue in Q1 2023. As an AI assistant without access to the saloon's internal records, I don't have information about their future projected revenues. I can only provide responses based on factual information that has been provided to me.")

但是,如果我们能够给模型更多上下文,我们就可以引导它给出一个很好的答案:

SOURCE = """
Old Ship Saloon 2023 quarterly revenue numbers:
Q1: $174782.38
Q2: $467372.38
Q3: $474773.38
Q4: $389289.23
"""

rag_prompt = ChatPromptTemplate.from_messages([
    ("system", 'You are a helpful assistant. Use the following context when responding:\n\n{context}.'),
    ("human", "{question}")
])

rag_chain = rag_prompt | chat_model | StrOutputParser()

rag_chain.invoke({
    "question": "What was the Old Ship Saloon's total revenue in Q1 2023?",
    "context": SOURCE
})

这次,结果如下:

"According to the provided context, the Old Ship Saloon's revenue in Q1 2023 was $174,782.38."

结果看起来不错!请注意,使用额外上下文增强生成是一个非常深入的主题——在现实世界中,这通常会采取从其他数据源检索的更长的财务文件或文档的形式。 RAG是一种强大的技术,可以回答关于大量信息的问题。

您可以查看LangChain的检索增强生成(RAG)文档以了解更多信息。

调试

由于LLMs是非确定性的,随着您的链变得越来越复杂,查看内部发生的事情变得越来越重要。

LangChain有一个 set_debug() 方法,它将返回链内部更细粒度的日志:让我们用上面的示例看一下:

from langchain.globals import set_debug

set_debug(True)

from datetime import date

prompt = ChatPromptTemplate.from_messages([
    ("system", 'You know that the current date is "{current_date}".'),
    ("human", "{question}")
])

chain = prompt | chat_model | StrOutputParser()

chain.invoke({
    "question": "What is the current date?",
    "current_date": date.today()
})

有更多的信息!

[chain/start] [1:chain:RunnableSequence] Entering Chain run with input:
[inputs]
[chain/start] [1:chain:RunnableSequence > 2:prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [1:chain:RunnableSequence > 2:prompt:ChatPromptTemplate] [1ms] Exiting Prompt run with output:
[outputs]
[llm/start] [1:chain:RunnableSequence > 3:llm:ChatAnthropic] Entering LLM run with input:
{
  "prompts": [
    "System: You know that the current date is \\"2024-04-05\\".\\nHuman: What is the current date?"
  ]
}
...
[chain/end] [1:chain:RunnableSequence] [885ms] Exiting Chain run with output:
{
  "output": "The current date you provided is 2024-04-05."
}

您可以在这里查看有关调试的更多信息。

您还可以使用 astream_events() 方法返回此数据。如果您想在应用程序逻辑中使用中间步骤,这很有用。请注意,这是一个 async 方法,需要一个额外的 version 标志,因为它仍然处于 beta 版:

# 为了清晰起见,关闭调试模式
set_debug(False)

stream = chain.astream_events({
    "question": "What is the current date?",
    "current_date": date.today()
}, version="v1")

async for event in stream:
    print(event)
    print("-----")
{'event': 'on_chain_start', 'run_id': '90785a49-987e-46bf-99ea-d3748d314759', 'name': 'RunnableSequence', 'tags': [], 'metadata': {}, 'data': {'input': {'question': 'What is the current date?', 'current_date': datetime.date(2024, 4, 5)}}}
-----
{'event': 'on_prompt_start', 'name': 'ChatPromptTemplate', 'run_id': '54b1f604-6b2a-48eb-8b4e-c57a66b4c5da', 'tags': ['seq:step:1'], 'metadata': {}, 'data': {'input': {'question': 'What is the current date?', 'current_date': datetime.date(2024, 4, 5)}}}
-----
{'event': 'on_prompt_end', 'name': 'ChatPromptTemplate', 'run_id': '54b1f604-6b2a-48eb-8b4e-c57a66b4c5da', 'tags': ['seq:step:1'], 'metadata': {}, 'data': {'input': {'question': 'What is the current date?', 'current_date': datetime.date(2024, 4, 5)}, 'output': ChatPromptValue(messages=[SystemMessage(content='You know that the current date is "2024-04-05".'), HumanMessage(content='What is the current date?')])}
-----
{'event': 'on_chat_model_start', 'name': 'ChatAnthropic', 'run_id': 'f5caa4c6-1b51-49dd-b304-e9b8e176623a', 'tags': ['seq:step:2'], 'metadata': {}, 'data': {'input': {'messages': [[SystemMessage(content='You know that the current date is "2024-04-05".'), HumanMessage(content='What is the current date?')]]}}}
-----
...
{'event': 'on_chain_end', 'name': 'RunnableSequence', 'run_id': '90785a49-987e-46bf-99ea-d3748d314759', 'tags': [], 'metadata': {}, 'data': {'output': 'The current date is 2024-04-05.'}}
-----

最后,您可以使用像LangSmith这样的外部服务来添加跟踪。这里是一个例子:

# 在 <https://smith.langchain.com/> 注册
# 设置环境变量

# import os

# os.environ["LANGCHAIN_TRACING_V2"] = "true"
# os.environ["LANGCHAIN_API_KEY"] = "YOUR_KEY"
# os.environ["LANGCHAIN_PROJECT"] = "YOUR_PROJECT"

chain.invoke({
  "question": "What is the current date?",
  "current_date": date.today()
})
"The current date is 2024-04-05."

LangSmith将在每个步骤捕获内部情况,为您提供这样的结果。

您还可以在游乐场中调整提示并重新运行模型调用。由于LLMs的非确定性性质,您还可以在游乐场中调整提示并重新运行模型调用,以及创建数据集和测试用例来评估对应用程序的更改并捕获回归。

(本文视频讲解:java567.com)

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

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

相关文章

力扣面试150 整数转罗马数字 打表 + 贪心

Problem: 12. 整数转罗马数字 文章目录 思路&#x1f496; 打表 贪心 思路 &#x1f468;‍&#x1f3eb; 参考&#xff1a;打表贪心 &#x1f496; 打表 贪心 class Solution {public String intToRoman(int num) {int[] value {1000, 900, 500, 400, 100, 90, 50, 40, …

微服务之Consul 注册中心介绍以及搭建

一、微服务概述 1.1单体架构 单体架构&#xff08;monolithic structure&#xff09;&#xff1a;顾名思义&#xff0c;整个项目中所有功能模块都在一个工程中开发&#xff1b;项目部署时需要对所有模块一起编译、打包&#xff1b;项目的架构设计、开发模式都非常简单。 当项…

MGRE环境下运行OSPF

一、分析要求 自行定义公网网段和私有网段&#xff0c;ISP设备仅配置IP地址R1/R4/R5构建Full-Mesh结构R1/R2/R3构建Hub-Spoke结构&#xff0c;R1为NHS除ISP设备&#xff0c;其余路由器运行OSPF 二、实施过程 1. 配置IP及环回地址 R1 [R1]int g 0/0/0 [R1-GigabitEthernet0/…

运筹说 第112期 | M/M/s等待制排队模型

通过上期学习&#xff0c;大家已经了解了排队论中的一些基本概念&#xff0c;以及生灭过程和Poisson过程。 那么本期小编将基于这些基本原理&#xff0c;为大家介绍M/M/s混合制排队模型&#xff0c;包括单服务台模型和多服务台模型&#xff0c;介绍模型的概念以及推导过程等内容…

全栈的自我修养 ———— react实现滑动验证

实现滑动验证 展示依赖实现不借助create-puzzle借助create-puzzle 展示 依赖 npm install rc-slider-captcha npm install create-puzzleapi地址 实现 不借助create-puzzle 需要准备两张图片一个是核验图形&#xff0c;一个是原图------> 这个方法小编试了后感觉比较麻烦…

1panel更新系统

准备两个软件包 名为dist的前端包 以.jar为后缀的后端jar包 更新后端 进去1Panel管理页面(浏览器收藏里有) http://127.0.0.1:42689/f2a8a874bd 点击容器&#xff0c;将名为app的容器直接删除掉 打开软件electerm 点击书签&#xff0c;连接2222 连接成功后长这样&#xff…

咸鱼之王_手游_开服搭建架设_内购修复无bug运营版

视频演示 咸鱼之王_手游_开服 游戏管理后台界面 源码获取在文章末尾 源码获取在文章末尾 源码获取在文章末尾 或者直接下面 https://githubs.xyz/y28.html 1.安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh &…

软考之零碎片段记录(十五)+复习巩固(十)

一、学习 1. 多对多关系模式 举例&#xff1a;学生和课程。顾客和商品等。 多对多关系的确立需要有中间表&#xff0c;需要使用两个外键确认表中的唯一数据。 2. 数据库范式 1nf 表中每个字段都是原子性不可查分的。在关系&#xff08;或表&#xff09;中&#xff0c;每一行…

软件设计师——软件工程基础知识

软件工程基础知识 软件过程软件过程模型软件测试方法进度管理软件复杂性度量环路复杂度耦合聚合和组合 软件过程 软件过程模型 软件测试方法 黑盒测试和白盒测试 白盒测试中&#xff0c;语句覆盖对程序执行逻辑的覆盖很低&#xff0c;因此一般认为它是很弱的逻辑覆盖。 进度管…

微服务架构使用和docker部署方法(若依)

这里以若依官方网站开源的微服务框架为例子记录使用方法过程。 开源地址&#xff1a;RuoYi-Cloud: &#x1f389; 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统&#xff0c;同时提供了 Vue3 的版本 下载后&#xff0c;用IDEA社区版开发工具打…

Vol.41 SEO基本术语解释

1.TDK TDK&#xff1a;即标题、描述、关键词&#xff1b;TDK是网站的基本属性&#xff0c;对SEO非常重要&#xff0c;可以帮助搜索引擎了解你的网站&#xff1b; T&#xff1a;title 谷歌建议不超过70个字符 D&#xff1a;description 谷歌建议不超过150个字符 K&#xff1…

麒麟服务器操作系统安装DHCP服务02

原文链接&#xff1a;麒麟服务器操作系统安装DHCP服务02 Hello&#xff0c;大家好啊&#xff01;继昨天介绍了在麒麟服务器操作系统上部署DHCP服务并演示了终端自动获取IP地址的过程之后&#xff0c;今天我们将进一步探讨如何通过绑定终端的MAC地址来为其分配固定的IP地址。这种…

【一竞技DOTA2】ESL ONE伯明翰站分组名单出炉

1、ESL ONE伯明翰站即将在4月22号开战,小组赛分组名单也正式出炉。 A组队伍名单:BB、G2xiG、SR、Talon、Falcons、Liquid B组队伍名单:GG、Heroic、OG、Spirit、Tundra、XG 小组赛是常规Bo2循环赛(4月22日-4月24日)各个小组的前两名晋级胜者组,三四名晋级败者组,五六名被淘汰…

二十款好用的屏幕录制,绿色绿色好用软件工具,云盘下载

本人收藏多年的屏幕录制工具&#xff0c;绿色的&#xff0c;你懂得的。。。。 二十款好用的屏幕录制&#xff0c;绿色绿色好用软件工具&#xff0c;值得收藏 下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1RPTlFfeap4TGMnDPgCEo-w?pwdmaky 提取码&#xff1…

【Linux】进程基础铺垫(三)软件基础:系统调用接口

系统调用接口 前言&#xff1a;系统调用接口的引入例子理解 总结小图一、系统调用接口二、底层封装 系统调用接口1. printf && scanf 重新理解 三、库函数 与 系统调用函数 前言&#xff1a;系统调用接口的引入 例子理解 就好比 去银行取钱&#xff0c;银行不可能让你…

硬件开发相关的流程文件介绍

学习目的&#xff1a;前面文章有简要介绍硬件开发的基本过程&#xff0c;本文会细分硬件开发的流程&#xff0c;然后分作5个步骤&#xff0c;详细介绍开发全过程&#xff0c;包括立项-实施项目-软件开发-测试-验收 这几个过程&#xff0c;然后&#xff0c;再分解对每一个步骤进…

二叉树(二)———链式二叉树结构

链式结构 链式二叉树的存储结构是指用链表来表示一棵二叉树&#xff0c;即用链表来指示元素的逻辑关系。通常的方法是链表中的每个节点由三个域组成&#xff0c;数据域与左右指针域&#xff0c;左右指针分别用来存储该节点左孩子和右孩子所在链节点的存储位置&#xff0c;这种…

用html写一个有趣的鬼魂动画

<!DOCTYPE html> <html lang"en" > <head><meta charset"UTF-8"><title>一个有趣的鬼魂动画</title><link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.m…

性能测试-数据库优化一(配置参数)

性能测试-数据库优化 数据库的优化方向 1、数据库是安装在操作系统中&#xff0c;是非常追求磁盘的稳定性。MySQL的库是磁盘文件夹&#xff0c;表是磁盘文件所以数据库的优化&#xff1a; 磁盘的性能 1、磁盘的速度&#xff0c;减少同时读写&#xff0c;考虑读写分离&#x…

【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【Spring Boot】深入解密Spring Boot日志&#xff1a;最佳实践与策略解析 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 Spring Boot 日志一. 日志的概念?…