LangChain构建语言模型驱动应用的强大框架
LangChain
- 核心功能与组件
- 链(Chains)
- 记忆(Memory)
- 提示模板(Prompts)
- 代理(Agents)
- 数据检索(Indexes)
- 应用场景
- 文档问答
- 自动化工作流
- 知识管理系统
- 发展历程
- 总结
- LangChain Cookbook 👨🍳👩🍳
- **什么是 LangChain?**
- **为什么是 LangChain?**
- LangChain 组件
- Schema - 使用大型语言模型 (LLM) 的基本知识
- **文本**
- **聊天消息**
- **文档**
- 模型——人工智能大脑的接口
- **语言模型**
- **聊天模型**
- 函数调用模型
- **文本嵌入模型**
- 提示 - 通常用作模型说明的文本
- **提示**
- **提示模板**
- **示例选择器**
- **输出解析器方法 1:提示指令和字符串解析**
- **输出解析器方法 2:OpenAI 函数**
- 索引 - 将文档结构化为 LLM 可以使用它们
- **文档加载器**
- **文本分割器**
- **检索器**
- **VectorStores**
- 记忆
- 聊天消息历史记录
- 链 ⛓️⛓️⛓️
- 1. 简单顺序链
- 2. 总结链
- Agent 🤖🤖
- Agent
- 工具
- 工具包
LangChain 是一个开源框架,旨在帮助开发者构建基于大型语言模型(LLM)的应用程序。它通过模块化设计和灵活的组件组合,简化了语言模型的集成和应用开发过程。
核心功能与组件
LangChain 提供了一系列强大的功能和组件,以支持从简单到复杂的语言模型应用开发:
链(Chains)
链是 LangChain 的核心机制,通过将多个组件(如语言模型、提示模板、工具等)组合在一起,形成一个自动化的工作流。链可以用于文档问答、多语言翻译、内容生成等多种场景。
记忆(Memory)
LangChain 提供了多种记忆机制,如对话缓冲内存、对话摘要内存等。这些机制能够帮助语言模型在对话过程中保留上下文信息,从而生成更具连贯性和个性化的响应。
提示模板(Prompts)
提示模板是预构建的结构,用于优化语言模型的输入格式。开发者可以通过这些模板为聊天机器人、问答系统等应用提供更精准的指令。
代理(Agents)
代理是一种特殊的链,能够根据用户输入动态调用工具和执行任务。它允许语言模型自主决定完成任务的最佳步骤,适用于复杂的多步推理任务。
数据检索(Indexes)
LangChain 支持构建和操作文档索引,能够将语言模型与外部数据源(如向量数据库)连接起来。这使得模型可以访问最新的数据,而无需重新训练。
应用场景
LangChain 的灵活性使其能够支持多种应用场景,包括但不限于:
聊天机器人:通过记忆和提示模板,构建能够理解上下文的智能聊天助手。
文档问答
结合数据检索功能,实现对特定文档的智能问答。
自动化工作流
通过代理和工具集成,实现复杂的任务自动化。
知识管理系统
构建企业级的知识库,支持智能检索和内容生成。
发展历程
LangChain 由 Harrison Chase 创立,于 2022 年 10 月首次开源。其发展历程可以分为以下几个阶段:
2022 年:最初版本发布,提供基础的提示管理和链式调用功能。
2023 年:快速迭代,推出记忆、代理、RAG 支持等关键功能。
2024 年:进一步优化技术,加强社区建设,推动商业化应用。
LangChain 的成功不仅体现在其技术的先进性,还在于其强大的社区支持和快速的迭代能力。
总结
LangChain 作为语言模型应用开发的框架,凭借其模块化设计和强大的功能组件,为开发者提供了极大的便利。它不仅简化了开发流程,还通过记忆、检索和代理等功能,扩展了语言模型的应用边界。无论是简单的问答系统还是复杂的多步推理任务,LangChain 都能提供有效的解决方案。随着人工智能技术的不断发展,LangChain 有望继续引领语言模型应用开发的潮流。
下面具体的代码演示
LangChain Cookbook 👨🍳👩🍳
什么是 LangChain?
LangChain 是一个由语言模型驱动的应用程序开发框架。
LangChain 让使用 AI 模型进行工作和构建的复杂部分变得更容易。它通过两种方式帮助实现这一点:
- 集成 - 将外部数据(例如文件、其他应用程序和 API 数据)带到您的 LLM
- Agent - 允许您的 LLM 通过决策与其环境交互。使用 LLM 来帮助决定下一步要采取什么行动
为什么是 LangChain?
-
组件 - LangChain 可以轻松交换使用语言模型所需的抽象和组件。
-
定制链 - LangChain 为使用和定制“链”提供开箱即用的支持 - 一系列串联在一起的操作。
-
速度🚢 - Langchain团队的更新速度非常快,集成最新的 LLM 功能。
-
社区 👥 - 出色的 discord 和社区支持、聚会、黑客马拉松等。
虽然 LLM 可以很简单(文本输入、文本输出),但一旦你需要开发出更复杂的应用程序,很快就会遇到很多问题,此时LangChain可以很好的帮助你解决问题
需要一个 OpenAI api 密钥才能遵循本教程。读者可以将其作为环境变量,放在此 jupyter 笔记本所在的 .env 文件中,或将其插入“YourAPIKey”所在的位置下方。
!pip install -q chromadb
!pip install -q tiktoken
!pip install -qU langchain-openai
!pip install -qU langchain-community
!pip install -q unstructured
import os
from google.colab import userdata
OPENAI_API_KEY=userdata.get('OPENAI_API_KEY')
os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY
LangChain 组件
Schema - 使用大型语言模型 (LLM) 的基本知识
文本
与 LLM 交互的自然语言方式
# 你将使用简单的字符串(其复杂性很快就会增加!)
my_text = "星期五之后是星期几?"
my_text
'星期五之后是星期几?'
聊天消息
类似文本,但指定了消息类型(系统、人类、人工智能)
- 系统(System) - 有用的背景信息,告诉人工智能该做什么
- 人类(Human) - 旨在代表用户的消息
- 人工智能(AI) - 显示人工智能响应的内容的消息
有关更多信息,请参阅 OpenAI 的 文档
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage, AIMessage
# 这就是我们将要使用的语言模型。我们将在下一节中讨论我们正在做的事情
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
现在让我们创建一些消息来模拟与机器人的聊天体验
llm.invoke(
[
SystemMessage(content="你是一个很棒的人工智能机器人,能用一句话帮助用户知道该吃什么"),
HumanMessage(content="我喜欢吃西红柿,应该吃什么呢?")
]
)
AIMessage(content='你可以尝试做一个新鲜的西红柿鸡蛋汤,简单又美味!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 44, 'total_tokens': 67, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_5f20662549', 'finish_reason': 'stop', 'logprobs': None}, id='run-8b6991b4-6708-4383-8e0b-cc471bda0b47-0', usage_metadata={'input_tokens': 44, 'output_tokens': 23, 'total_tokens': 67, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
您还可以通过 AI 的回复传递更多聊天记录
llm.invoke(
[
SystemMessage(content="你是一个友好的AI机器人,可以用简短的一句话帮助用户决定去哪里旅行"),
HumanMessage(content="我喜欢海滩,我应该去哪里?"),
AIMessage(content="你应该去法国尼斯"),
HumanMessage(content="我到那里后还应该做些什么?")
]
)
AIMessage(content='除了享受海滩,你还可以探索老城、参观马蒂斯博物馆,或者沿着英国大道漫步。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 31, 'prompt_tokens': 62, 'total_tokens': 93, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_5f20662549', 'finish_reason': 'stop', 'logprobs': None}, id='run-b287edd2-fd2f-4123-b87c-4b98f0b962e5-0', usage_metadata={'input_tokens': 62, 'output_tokens': 31, 'total_tokens': 93, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
您还可以根据需要排除系统消息
llm.invoke(
[
HumanMessage(content="星期四之后是星期几?")
]
)
AIMessage(content='星期四之后是星期五。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 14, 'total_tokens': 22, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_d28bcae782', 'finish_reason': 'stop', 'logprobs': None}, id='run-985361de-1947-4a77-b4c5-7ed0e55ad56f-0', usage_metadata={'input_tokens': 14, 'output_tokens': 8, 'total_tokens': 22, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
文档
包含一段文本和元数据(有关该文本的更多信息)的对象
from langchain.schema import Document
Document(page_content="这是我的文档。它包括了我从其他地方收集的信息",
metadata={
'my_document_id' : 234234,
'my_document_source' : "LangChain 论文",
'my_document_create_time' : 1680013019
})
Document(metadata={'my_document_id': 234234, 'my_document_source': 'LangChain 论文', 'my_document_create_time': 1680013019}, page_content='这是我的文档。它包括了我从其他地方收集的信息')
metadata信息不需要被包含
Document(page_content="这是我的文档。它包括了我从其他地方收集的信息")
Document(metadata={}, page_content='这是我的文档。它包括了我从其他地方收集的信息')
模型——人工智能大脑的接口
语言模型
一个输入文本➡️输出文本的模型!
查看我如何将我使用的模型从默认模型更改为gpt-3.5-turbo。可以点击下面链接查看更多模型(https://platform.openai.com/docs/models)
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
llm.invoke("星期五之后是星期几?")
AIMessage(content='星期五之后是星期六。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 12, 'prompt_tokens': 18, 'total_tokens': 30, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-44f66e6c-9688-4662-a7d3-923ed61a58c6-0', usage_metadata={'input_tokens': 18, 'output_tokens': 12, 'total_tokens': 30, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
聊天模型
接收一系列消息并返回消息输出的模型
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage, AIMessage
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
llm.invoke(
[
SystemMessage(content="你是一个毫无帮助的人工智能机器人,无论用户说什么,它都会开玩笑"),
HumanMessage(content="我想去上海,应该怎么办?")
]
)
AIMessage(content='把右脚放在左脚前面,然后用左脚迈出一步,再重复这个动作直到到达上海。开玩笑的,哈哈!要去上海,你需要先买机票,然后安排住宿和行程。祝你旅途愉快!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 89, 'prompt_tokens': 62, 'total_tokens': 151, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-4dd072f3-6fe0-4b2f-b878-bd32e3b52667-0', usage_metadata={'input_tokens': 62, 'output_tokens': 89, 'total_tokens': 151, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
函数调用模型
函数调用模型 与聊天模型类似,但略有不同。它们经过微调,可提供结构化数据输出。
当对外部服务进行 API 调用或进行提取时,这会派上很大的用场。
llm = ChatOpenAI(model_name="gpt-4o")
messages = [
SystemMessage(content="你是一个乐于助人的人工智能机器人"),
HumanMessage(content="上海现在的天气怎么样?")
]
functions=[{
"name": "get_current_weather",
"description": "获取指定位置的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市和州,例如中国上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}]
output = llm.invoke(input=messages, functions=functions)
print(output)
content='' additional_kwargs={'function_call': {'arguments': '{"location":"中国上海","unit":"celsius"}', 'name': 'get_current_weather'}, 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 82, 'total_tokens': 104, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_5f20662549', 'finish_reason': 'function_call', 'logprobs': None} id='run-2bb2b276-beb5-4770-92d5-6c8b3396368c-0' usage_metadata={'input_tokens': 82, 'output_tokens': 22, 'total_tokens': 104, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
看到传回给我们的额外“additional_kwargs”了吗?我们可以将其传递给外部 API 以获取数据。这省去了进行输出解析的麻烦。
文本嵌入模型
将文本转换为向量(一系列包含文本语义“含义”的数字)。主要用于比较两段文本。
顺便说一句:语义意味着“与语言或逻辑中的含义相关”。
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
text = "今天艳阳高照天气很舒服!"
text_embedding = embeddings.embed_query(text)
print (f"Here's a sample: {text_embedding[:5]}...")
print (f"Your embedding is length {len(text_embedding)}")
Here's a sample: [0.017948752269148827, 0.008655799552798271, 0.006652832496911287, -0.021351424977183342, 0.006696891039609909]...
Your embedding is length 1536
提示 - 通常用作模型说明的文本
提示
需要传递给底层模型的内容
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-4o")
prompt = """
今天是星期一,明天是星期三。
这句话有什么问题?
"""
print(llm(prompt))
<ipython-input-4-8f4e450524c6>:11: LangChainDeprecationWarning: The method `BaseChatModel.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use :meth:`~invoke` instead.
print(llm(prompt))
content='这句话有一个明显的问题:逻辑上的不一致。根据常识,今天是星期一,那么明天应该是星期二,而不是星期三。正确的表述应该是:“今天是星期一,明天是星期二。”' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 52, 'prompt_tokens': 24, 'total_tokens': 76, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_50cad350e4', 'finish_reason': 'stop', 'logprobs': None} id='run-699ad99c-bb3a-45f7-9c9d-97058974f0cb-0' usage_metadata={'input_tokens': 24, 'output_tokens': 52, 'total_tokens': 76, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
提示模板
一个帮助根据用户输入、其他非静态信息和固定模板字符串的组合创建提示的对象。
将其视为 Python 中的 f-string,但用于提示
高级:查看 LangSmithHub(https://smith.langchain.com/hub) 以获取更多社区提示模板
from langchain import PromptTemplate
llm = ChatOpenAI(model_name="gpt-4o")
# 注意下面的"location",这是稍后另一个值的占位符
template = """
我真的很想去{location}旅行。我应该在那里做什么?
用一个简短的句子回复
"""
prompt = PromptTemplate(
input_variables=["location"],
template=template,
)
final_prompt = prompt.format(location='上海')
print (f"最终提示词: {final_prompt}")
print ("-----------")
print (f"LLM 输出: {llm(final_prompt)}")
最终提示词:
我真的很想去上海旅行。我应该在那里做什么?
用一个简短的句子回复
-----------
LLM 输出: content='游览外滩,品尝当地美食,探索豫园和参观东方明珠塔。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 30, 'total_tokens': 55, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_d28bcae782', 'finish_reason': 'stop', 'logprobs': None} id='run-e38b3647-a03b-46ea-b370-f18a7ead1842-0' usage_metadata={'input_tokens': 30, 'output_tokens': 25, 'total_tokens': 55, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
示例选择器
一种从一系列示例中进行选择的简单方法,可让您动态地将上下文信息放入提示中。通常在您的任务很细致或您有大量示例时使用。
可以通过下方的链接查看不同类型的示例选择器(https://python.langchain.com/docs/modules/model_io/prompts/example_selectors/)
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
from langchain.llms import OpenAI
llm = ChatOpenAI(model_name="gpt-4o")
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="示例输入: {input}\n示例输出: {output}",
)
# 名词所在位置的示例
examples = [
{"input": "海盗", "output": "船"},
{"input": "飞行员", "output": "飞机"},
{"input": "司机", "output": "汽车"},
{"input": "树", "output": "地面"},
{"input": "鸟", "output": "巢"},
]
# SemanticSimilarityExampleSelector 将通过语义含义选择与输入相似的示例
example_selector = SemanticSimilarityExampleSelector.from_examples(
# 这是可供选择的示例列表。
examples,
# 这是用于生成嵌入的嵌入类,用于测量语义相似性。
OpenAIEmbeddings(),
# 这是用于存储嵌入并进行相似性搜索的 VectorStore 类。
Chroma,
# 这是要生成的示例数量。
k=1
)
<ipython-input-6-ed2c96cfdf7f>:8: LangChainDeprecationWarning: The class `OpenAIEmbeddings` was deprecated in LangChain 0.0.9 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-openai package and should be used instead. To use it run `pip install -U :class:`~langchain-openai` and import as `from :class:`~langchain_openai import OpenAIEmbeddings``.
OpenAIEmbeddings(),
similar_prompt = FewShotPromptTemplate(
# 有助于选择示例的对象
example_selector=example_selector,
# 你的提示词
example_prompt=example_prompt,
# 将添加到提示顶部和底部的自定义内容
prefix="给出物品通常出现的位置",
suffix="Input: {noun}\nOutput:",
# 你的提示将收到什么输入
input_variables=["noun"],
)
my_noun = "老虎"
my_noun1 = "学生"
my_noun2 = "植物"
print(similar_prompt.format(noun=my_noun))
print("==============")
print(similar_prompt.format(noun=my_noun1))
print("==============")
print(similar_prompt.format(noun=my_noun2))
给出物品通常出现的位置
示例输入: 鸟
示例输出: 巢
Input: 老虎
Output:
==============
给出物品通常出现的位置
示例输入: 司机
示例输出: 汽车
Input: 学生
Output:
==============
给出物品通常出现的位置
示例输入: 树
示例输出: 地面
Input: 植物
Output:
llm(similar_prompt.format(noun=my_noun))
AIMessage(content='丛林', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 3, 'prompt_tokens': 36, 'total_tokens': 39, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_50cad350e4', 'finish_reason': 'stop', 'logprobs': None}, id='run-6fb9299a-c365-45f8-94a2-dcb611ad0274-0', usage_metadata={'input_tokens': 36, 'output_tokens': 3, 'total_tokens': 39, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
输出解析器方法 1:提示指令和字符串解析
一种格式化模型输出的有用方法。通常用于结构化输出。LangChain 在其 文档 中列出了更多输出解析器。
两个大概念:
1. 格式化指令 - 自动生成的提示,告诉 LLM 如何根据您期望的结果格式化其响应
2. 解析器 - 一种将模型的文本输出提取到所需结构(通常是 json)的方法
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
from pydantic import BaseModel, Field, model_validator
model = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0.0)
# 定义你所需的数据结构。
class Joke(BaseModel):
setup: str = Field(description="设置笑话的问题")
punchline: str = Field(description="解答笑话的答案")
# 设置解析器并将指令注入提示模板。
parser = PydanticOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="回答用户的询问。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
# 一个用于提示语言模型填充数据结构的查询。
prompt_and_model = prompt | model
output = prompt_and_model.invoke({"query": "可以讲个笑话给我听吗?"})
parser.invoke(output)
Joke(setup='为什么熊猫喜欢睡觉?', punchline='因为他们是熊猫,不是熊熊。')
输出解析器方法 2:OpenAI 函数
当 OpenAI 发布函数调用时,游戏发生了变化。这是刚开始时推荐的方法。
他们专门训练模型以输出结构化数据。指定 Pydantic(一种python中的数据验证模式) 模式并获得结构化输出变得非常容易。
有很多方法可以定义你的模式,我更喜欢使用 Pydantic 模型,因为它们的组织性很强。请参考 OpenAI 的 文档 了解其他方法。
使用此方法需要使用支持函数调用的模型。我将使用gpt4-0613
示例 1:简单
让我们从定义一个简单的模型开始,以便从中提取。
from pydantic import BaseModel, Field
from typing import Optional
class Person(BaseModel):
"""关于一个人的身份信息。"""
name: str = Field(..., description="该人的姓名")
age: int = Field(..., description="该人的年龄")
fav_food: Optional[str] = Field(None, description="该人最喜欢的食物")
然后让我们创建一个链(稍后会详细介绍),它将为我们进行提取相关的信息
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-4o")
structured_llm = llm.with_structured_output(Person)
structured_llm.invoke(
"小丽 13岁,小明刚满 12 岁并且喜欢吃菠菜。 小军比小丽大 10 岁。"
)
Person(name='小丽', age=13, fav_food=None)
注意到我们只有一个人的数据吗?那是因为我们没有指定我们想要多个。让我们更改我们的架构以指定我们想要一个人员列表。
from typing import Sequence
class People(BaseModel):
"""识别文本中所有人物的信息。"""
people: Sequence[Person] = Field(..., description="文本中的人物信息")
现在我们将调用People这个大类,而不是之前定义的person,这个People的大类里面包含了Person这个小类
structured_llm = llm.with_structured_output(People)
structured_llm.invoke(
"小丽 13岁,小明刚满 12 岁并且喜欢吃菠菜。 小军比小丽大 10 岁。"
)
People(people=[Person(name='小丽', age=13, fav_food=None), Person(name='小明', age=12, fav_food='菠菜'), Person(name='小军', age=23, fav_food=None)])
让我们用它做更多解析
示例 2:枚举
现在让我们解析列表中提及的产品
import enum
llm = ChatOpenAI(model_name="gpt-4o")
class Product(str, enum.Enum):
CRM = "CRM"
VIDEO_EDITING = "视频剪辑"
HARDWARE = "硬件"
class Products(BaseModel):
"""识别文本中提到的产品"""
products: Sequence[Product] = Field(..., description="文中提到的产品")
structured_llm = llm.with_structured_output(Products)
structured_llm.invoke(
"这个演示中的 CRM 很棒。喜欢硬件。麦克风也很酷。喜欢视频编辑"
)
Products(products=[<Product.CRM: 'CRM'>, <Product.HARDWARE: '硬件'>, <Product.VIDEO_EDITING: '视频剪辑'>])
索引 - 将文档结构化为 LLM 可以使用它们
文档加载器
从其他来源导入数据的一系列简单方法。与 OpenAI 插件 特别是检索插件 共享功能
可以访问下面的网站查看文档加载器的更多信息(https://python.langchain.com/en/latest/modules/indexes/document_loaders.html)。
Llama Index另外一个类似langchain的框架,主要是索引和提取 上还有更多的信息
网站帖子
from langchain.document_loaders import HNLoader
WARNING:langchain_community.utils.user_agent:USER_AGENT environment variable not set, consider setting it to identify your requests.
loader = HNLoader("https://news.ycombinator.com/item?id=34422627")
data = loader.load()
print (f"发现 {len(data)} 评论")
print (f"以下是一个示例:\n\n{''.join([x.page_content[:150] for x in data[:2]])}")
发现 76 评论
以下是一个示例:
Ozzie_osman on Jan 18, 2023
| next [–]
LangChain is awesome. For people not sure what it's doing, large language models (LLMs) are veOzzie_osman on Jan 18, 2023
| parent | next [–]
Also, another library to check out is GPT Index (https://github.com/jerryjliu/gpt_ind
电子书
from langchain.document_loaders import GutenbergLoader
loader = GutenbergLoader("https://www.gutenberg.org/cache/epub/2148/pg2148.txt")
data = loader.load()
print(data[0].page_content[1855:1984])
o.—_Seneca_.
At Paris, just after dark one gusty evening in the autumn of 18-,
I was enjoying the twofold l
URL 和网页
让我们在 Paul Graham 的网站 上尝试一下
from langchain.document_loaders import UnstructuredURLLoader
urls = [
"http://www.paulgraham.com/",
]
loader = UnstructuredURLLoader(urls=urls)
data = loader.load()
data[0].page_content
'New: Writes and Write-Nots | Founder Mode Want to start a startup? Get funded by Y Combinator . © mmxxv pg'
文本分割器
通常的文档对于基本上所有的LLM 来说都太长(就像一本书)。因此需要将其分割成块。文本分割器可帮助完成此操作。
可以通过多种方式将文本分割成块,可以访问下面的链接(https://python.langchain.com/v0.1/docs/modules/data_connection/document_transformers/code_splitter/) 中不同的方法来确定哪种方法最适合。
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 这是一个很长的文档
with open('/content/Xiyouji.txt') as f:
pg_work = f.read()
print (f"你有 {len([pg_work])} 个文档")
你有 1 个文档
text_splitter = RecursiveCharacterTextSplitter(
# 定义chunk size.
chunk_size = 500,
chunk_overlap = 20,
)
texts = text_splitter.create_documents([pg_work])
print (f"你有 {len(texts)} 个文档")
你有 1784 个文档
print ("前言:")
print (texts[0].page_content, "\n")
print (texts[1].page_content)
前言:
第一回 靈根育孕源流出 心性修持大道生
詩曰:
混沌未分天地亂,茫茫渺渺無人見。
自從盤古破鴻濛,開闢從茲清濁辨。
覆載群生仰至仁,發明萬物皆成善。
欲知造化會元功,須看西遊釋厄傳。
蓋聞天地之數,有十二萬九千六百歲為一元。將一元分為十二會,乃子、丑、寅
、卯、辰、巳、午、未、申、酉、戌、亥之十二支也。每會該一萬八百歲。且就
一日而論:子時得陽氣,而丑則雞鳴﹔寅不通光,而卯則日出﹔辰時食後,而巳
則挨排﹔日午天中,而未則西蹉﹔申時晡,而日落酉,戌黃昏,而人定亥。譬於
大數,若到戌會之終,則天地昏曚而萬物否矣。再去五千四百歲,交亥會之初,
則當黑暗,而兩間人物俱無矣,故曰混沌。又五千四百歲,亥會將終,貞下起元
,近子之會,而復逐漸開明。邵康節曰::「冬至子之半,天心無改移。一陽初
動處,萬物未生時。」到此,天始有根。再五千四百歲,正當子會,輕清上騰,
有日,有月,有星,有辰。日、月、星、辰,謂之四象。故曰,天開於子。又經
五千四百歲,子會將終,近丑之會,而逐漸堅實。《易》曰:「大哉乾元!至哉
坤元!萬物資生,乃順承天。」至此,地始凝結。再五千四百歲,正當丑會,重
濁下凝,有水,有火,有山,有石,有土。水、火、山、石、土,謂之五形。故
曰,地闢於丑。又經五千四百歲,丑會終而寅會之初,發生萬物。曆曰:「天氣
文本分割的方法有很多种,这取决于检索策略和应用程序设计。可以访问下面的链接查看更多分割器(https://python.langchain.com/v0.1/docs/modules/data_connection/document_transformers/)
检索器
将文档与语言模型相结合的简单方法。
检索器有很多种类型,其中最广泛支持的是 VectorStoreRetriever
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
loader = TextLoader('data/PaulGrahamEssays/worked.txt')
documents = loader.load()
# Get your splitter ready
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
# Split your docs into texts
texts = text_splitter.split_documents(documents)
# Get embedding engine ready
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
# Embedd your texts
db = FAISS.from_documents(texts, embeddings)
# Init your retriever. Asking for just 1 document back
retriever = db.as_retriever()
retriever
VectorStoreRetriever(tags=['FAISS'], vectorstore=<langchain.vectorstores.faiss.FAISS object at 0x7f8389169070>)
docs = retriever.get_relevant_documents("what types of things did the author want to build?")
print("\n\n".join([x.page_content[:200] for x in docs[:2]]))
standards; what was the point? No one else wanted one either, so
off they went. That was what happened to systems work.I wanted not just to build things, but to build things that would
last.In this di
much of it in grad school.Computer Science is an uneasy alliance between two halves, theory
and systems. The theory people prove things, and the systems people
build things. I wanted to build things.
VectorStores
用于存储向量的数据库。最流行的是 Pinecone 和 Weaviate。OpenAIs retriever 文档 上有更多示例。Chroma 和 FAISS 易于本地使用。
从概念上讲,将它们视为带有嵌入(向量)列和元数据列的表格。
示例
嵌入 | 元数据 |
---|---|
[-0.00015641732898075134, -0.003165106289088726, …] | {‘日期’ : '1/2/23} |
[-0.00035465431654651654, 1.4654131651654516546, …] | {‘日期’ : '1/3/23} |
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
loader = TextLoader('data/PaulGrahamEssays/worked.txt')
documents = loader.load()
# Get your splitter ready
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
# Split your docs into texts
texts = text_splitter.split_documents(documents)
# Get embedding engine ready
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
print (f"You have {len(texts)} documents")
You have 78 documents
embedding_list = embeddings.embed_documents([text.page_content for text in texts])
print (f"You have {len(embedding_list)} embeddings")
print (f"Here's a sample of one: {embedding_list[0][:3]}...")
You have 78 embeddings
Here's a sample of one: [-0.001058628615053026, -0.01118234211553424, -0.012874804746266883]...
你的 vectorstore 存储你的嵌入(☝️)并使它们易于搜索
记忆
帮助 LLM 记住信息。
记忆是一个有点宽泛的术语。它可以简单到记住你过去聊过的信息,也可以更复杂地检索信息。
我们将把它放在聊天消息用例中。这将用于聊天机器人。
记忆有很多种类型,可以访问下面的链接(https://python.langchain.com/v0.1/docs/modules/memory/),看看哪一种适合特定的用例。
聊天消息历史记录
from langchain.memory import ChatMessageHistory
from langchain.chat_models import ChatOpenAI
chat = ChatOpenAI(temperature=0, openai_api_key=openai_api_key)
history = ChatMessageHistory()
history.add_ai_message("hi!")
history.add_user_message("what is the capital of france?")
history.messages
[AIMessage(content='hi!'),
HumanMessage(content='what is the capital of france?')]
ai_response = chat(history.messages)
ai_response
AIMessage(content='The capital of France is Paris.')
history.add_ai_message(ai_response.content)
history.messages
[AIMessage(content='hi!'),
HumanMessage(content='what is the capital of france?'),
AIMessage(content='The capital of France is Paris.')]
链 ⛓️⛓️⛓️
自动组合不同的 LLM 调用和操作
例如:摘要 #1、摘要 #2、摘要 #3 > 最终摘要
可以查看下面的链接(https://python.langchain.com/v0.1/docs/modules/chains/),搜索看看哪个最适合你自己的用例。
我们将介绍其中两个非常有代表性的:
1. 简单顺序链
简单的链可以将 LLM 的输出用作另一个的输入。适合分解任务(让LLM一次完成一项任务)
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains import SimpleSequentialChain
llm = OpenAI(temperature=1, openai_api_key=openai_api_key)
template = """Your job is to come up with a classic dish from the area that the users suggests.
% USER LOCATION
{user_location}
YOUR RESPONSE:
"""
prompt_template = PromptTemplate(input_variables=["user_location"], template=template)
# Holds my 'location' chain
location_chain = LLMChain(llm=llm, prompt=prompt_template)
template = """Given a meal, give a short and simple recipe on how to make that dish at home.
% MEAL
{user_meal}
YOUR RESPONSE:
"""
prompt_template = PromptTemplate(input_variables=["user_meal"], template=template)
# Holds my 'meal' chain
meal_chain = LLMChain(llm=llm, prompt=prompt_template)
overall_chain = SimpleSequentialChain(chains=[location_chain, meal_chain], verbose=True)
review = overall_chain.run("Rome")
[1m> Entering new SimpleSequentialChain chain...[0m
[36;1m[1;3m
A classic dish from Rome is Spaghetti alla Carbonara, featuring egg, Parmesan cheese, black pepper, and pancetta or guanciale.[0m
[33;1m[1;3m
Ingredients:
- 8oz spaghetti
- 4 tablespoons olive oil
- 4oz diced pancetta or guanciale
- 2 cloves garlic, minced
- 2 eggs, lightly beaten
- 2 tablespoons parsley, chopped
- ½ cup grated Parmesan
- Salt and black pepper to taste
Instructions:
1. Bring a pot of salted water to a boil and add the spaghetti. Cook according to package directions.
2. Meanwhile, add the olive oil to a large skillet over medium-high heat. Add the diced pancetta and garlic, and cook until pancetta is browned and garlic is fragrant.
3. In a medium bowl, whisk together the eggs, parsley, Parmesan, and salt and pepper.
4. Drain the cooked spaghetti and add it to the skillet with the pancetta and garlic. Remove from heat and pour the egg mixture over the spaghetti, stirring to combine.
5. Serve the spaghetti alla carbonara with additional Parmesan cheese and black pepper.[0m
[1m> Finished chain.[0m
2. 总结链
轻松浏览大量长文档并获取总结。查看此视频 了解除 map-reduce 之外的其他链类型
from langchain.chains.summarize import load_summarize_chain
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
loader = TextLoader('data/PaulGrahamEssays/disc.txt')
documents = loader.load()
# Get your splitter ready
text_splitter = RecursiveCharacterTextSplitter(chunk_size=700, chunk_overlap=50)
# Split your docs into texts
texts = text_splitter.split_documents(documents)
# There is a lot of complexity hidden in this one line. I encourage you to check out the video above for more detail
chain = load_summarize_chain(llm, chain_type="map_reduce", verbose=True)
chain.run(texts)
[1m> Entering new MapReduceDocumentsChain chain...[0m
[1m> Entering new LLMChain chain...[0m
Prompt after formatting:
[32;1m[1;3mWrite a concise summary of the following:
"January 2017Because biographies of famous scientists tend to
edit out their mistakes, we underestimate the
degree of risk they were willing to take.
And because anything a famous scientist did that
wasn't a mistake has probably now become the
conventional wisdom, those choices don't
seem risky either.Biographies of Newton, for example, understandably focus
more on physics than alchemy or theology.
The impression we get is that his unerring judgment
led him straight to truths no one else had noticed.
How to explain all the time he spent on alchemy
and theology? Well, smart people are often kind of
crazy.But maybe there is a simpler explanation. Maybe"
CONCISE SUMMARY:[0m
Prompt after formatting:
[32;1m[1;3mWrite a concise summary of the following:
"the smartness and the craziness were not as separate
as we think. Physics seems to us a promising thing
to work on, and alchemy and theology obvious wastes
of time. But that's because we know how things
turned out. In Newton's day the three problems
seemed roughly equally promising. No one knew yet
what the payoff would be for inventing what we
now call physics; if they had, more people would
have been working on it. And alchemy and theology
were still then in the category Marc Andreessen would
describe as "huge, if true."Newton made three bets. One of them worked. But
they were all risky."
CONCISE SUMMARY:[0m
[1m> Finished chain.[0m
[1m> Entering new LLMChain chain...[0m
Prompt after formatting:
[32;1m[1;3mWrite a concise summary of the following:
" Biographies of famous scientists often edit out their mistakes, giving readers the wrong impression that they never faced any risks to achieve successful results. An example of this is Newton, whose smartness is assumed to have led straight him to truths without any detours into alchemy or theology - despite the fact that he spent a lot of time on both fields. Maybe the simpler explanation is that he was willing to take risks, even if it means potentially making mistakes.
In the 17th century, Newton took a risk and made three bets, one of which turned out to be a successful invention of what we now call physics. The other two bets were on less popular subjects of the time such as alchemy and theology. People did not know then what the payoff would be, but the bets still seemed relatively promising."
CONCISE SUMMARY:[0m
[1m> Finished chain.[0m
[1m> Finished chain.[0m
" Biographies tend to omit famous scientists' mistakes from their stories, but Newton was willing to take risks and explore multiple fields to make his discoveries. He placed three risky bets, one of which resulted in the creation of physics as we know it today."
Agent 🤖🤖
官方 LangChain 文档完美地描述了Agent的概念:
一些应用程序不仅需要对 LLM/其他工具的预定调用链,还可能需要依赖于用户输入的未知链。在这些类型的链中,有一个可以访问一套工具的“Agent”。根据用户输入,Agent可以**决定调用这些工具中的哪一个(如果有的话)。
基本上Agent不仅将 LLM 用于文本输出,还用于决策。此功能的酷炫和强大怎么强调都不为过。
Sam Altman 强调 LLM 是优秀的“推理引擎”。Agent的出现正是利用了这一点。
Agent
驱动决策的语言模型。
更具体地说,Agent接收输入并返回与要采取的操作以及操作输入相对应的响应。可以访问下面的链接(https://python.langchain.com/v0.1/docs/modules/agents/agent_types/) 看到不同类型的Agent(它们更适合不同的用例)。
工具
Agent的“功能”。这是函数顶部的抽象,使 LLM(和Agent)能够轻松与其交互。例如:Google 搜索。
此区域与 OpenAI 插件 有共同之处。
工具包
一个特定的Agent可以从中选择的工具组
让我们将它们整合在一起:
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.llms import OpenAI
import json
llm = OpenAI(temperature=0, openai_api_key=openai_api_key)
serpapi_api_key=os.getenv("SERP_API_KEY", "YourAPIKey")
toolkit = load_tools(["serpapi"], llm=llm, serpapi_api_key=serpapi_api_key)
agent = initialize_agent(toolkit, llm, agent="zero-shot-react-description", verbose=True, return_intermediate_steps=True)
response = agent({"input":"what was the first album of the"
"band that Natalie Bergman is a part of?"})
[1m> Entering new AgentExecutor chain...[0m
[32;1m[1;3m I should try to find out what band Natalie Bergman is a part of.
Action: Search
Action Input: "Natalie Bergman band"[0m
Observation: [36;1m[1;3m['Natalie Bergman is an American singer-songwriter. She is one half of the duo Wild Belle, along with her brother Elliot Bergman. Her debut solo album, Mercy, was released on Third Man Records on May 7, 2021. She is based in Los Angeles.', 'Natalie Bergman type: American singer-songwriter.', 'Natalie Bergman main_tab_text: Overview.', 'Natalie Bergman kgmid: /m/0qgx4kh.', 'Natalie Bergman genre: Folk.', 'Natalie Bergman parents: Susan Bergman, Judson Bergman.', 'Natalie Bergman born: 1988 or 1989 (age 34–35).', 'Natalie Bergman is an American singer-songwriter. She is one half of the duo Wild Belle, along with her brother Elliot Bergman. Her debut solo album, Mercy, ...'][0m
Thought:[32;1m[1;3m I should search for the first album of Wild Belle
Action: Search
Action Input: "Wild Belle first album"[0m
Observation: [36;1m[1;3mIsles[0m
Thought:[32;1m[1;3m I now know the final answer
Final Answer: Isles is the first album of the band that Natalie Bergman is a part of.[0m
[1m> Finished chain.[0m