LangChain的介绍
LangChain是用来开发由大预言模型驱动的应用程序的框架。
LangChain简化了LLM应用的生命周期的每个阶段:
开发:使用LangChain的开源组件和第三方集成构建您的应用程序。使用LangGraph构建具有一流流媒体和人在循环支持的有状态代理。也就是说LangChain 是基础开发工具包,而 LangGraph 是高阶流程引擎,两者共同覆盖从简单到复杂的所有开发需求。
产品化:使用LangSmith检查、监控和评估您的应用程序,以便您可以放心地持续优化和部署。
部署:使用LangGraph平台将您的LangGraph应用程序转换为生产就绪的API和助手。
使用示例
首先安装LangChain和选择的LLM依赖库,这里以deepseek为例
pip install -qU langchain-deepseek
编写代码
import getpass
import os
from langchain_deepseek import ChatDeepSeek
if not os.environ.get("DEEPSEEK_API_KEY"): # 获取环境变量 “GOOGLE_API_KEY”
os.environ["DEEPSEEK_API_KEY"] = getpass.getpass("Enter DeepSeek API key: ")
# 初始化模型
model = ChatDeepSeek(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/beta",
temperature=0.5
)
response = model.invoke("Who are you?")
print(response.content)
LangChain的组成
LangChain由多个开源库组成。
1.langchain-core:
Base abstractions for chat models and other components.
2.Integration packages (e.g. langchain-openai
, langchain-anthropic
, etc.):重要的集成已被拆分为轻量级包,由langchain团队和集成开发人员共同维护。
3.langchain
: Chains, agents, and retrieval strategies that make up an application's cognitive architecture.
4.langchain-community
: Third-party integrations that are community maintained.
5.langgraph
: Orchestration framework for combining LangChain components into production-ready applications with persistence, streaming, and other key features.
LangChain的官方使用示例
Build a simple LLM application with chat models and prompt templates
这个应用的业务是将英文翻译为其他语言。
1.安装LangChain库
pip install langchain
2.注册一个LangSmith账户用来调试应用,网址为LangSmith;使用教程:如何注册LangSmith并取得开发用Key_langsmith注册-CSDN博客
3.编写代码
import getpass
import os
try:
# 从.env文件中加载环境变量,也就是把LLM的key和url配置在.env文件中
from dotenv import load_dotenv,find_dotenv
_ = load_dotenv(find_dotenv())
except ImportError:
pass
os.environ["LANGSMITH_TRACING"] = "true"
if "LANGSMITH_API_KEY" not in os.environ: # 这里我已经将LANGSMITH_API_KEY添加到了windows的用户环境变量中了
os.environ["LANGSMITH_API_KEY"] = getpass.getpass(
prompt="Enter your LangSmith API key:"
)
if "LANGSMITH_PROJECT" not in os.environ:
os.environ["LANGSMITH_PROJECT"] = getpass.getpass(
prompt="Enter your LangSmith Project Name:"
)
if not os.environ.get("LANGSMITH_PROJECT"):
os.environ["LANGSMITH_PROJECT"] = "default"
from langchain_deepseek import ChatDeepSeek
model = ChatDeepSeek(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/beta"
)
from langchain_core.messages import HumanMessage, SystemMessage
message = [
SystemMessage("Translate the following from English into Chinese"),
HumanMessage("hi!"),
]
# 同步方式接收生成的内容
# response = model.invoke(message)
# print(response.content)
# 流式加异步的方式接收生成的内容,也就是常见网页中大模型生成文本的方式
# for token in model.stream(message):
# print(token.content, end="|")
# 目前传递给模型的方式是采用将message列表直接传递给模型, message是由用户输入和应用程序逻辑的组合构成的。
# 应用程序逻辑通常接受原始用户输入,并将其转换为准备传递给模型的message列表。
# prompt template的作用是帮助进行这种转换。它接受原始用户输入,并返回准备传递到语言模型的数据。
# 接下来展示prompt template的用法
from langchain_core.prompts import ChatPromptTemplate
system_template = "Translate the following from English into {language}"
prompt_template = ChatPromptTemplate.from_messages(
[("system", system_template), ("user","{text}")]
)
# 这里对上面的格式化内容 以字典格式传递了参数
prompt = prompt_template.invoke({"language": "Chinese", "text": "hi!"})
response = model.invoke(prompt)
print(response.content)
可以在LangSmith上查看每一次调用模型时的具体细节。
Build a Chatbot
设计和实现LLM驱动的聊天机器人的示例。这个聊天机器人将能够进行对话,并记住之前与聊天模型的交互。
这个案列会用到LangGraph,它实现了一个持久层,使其成为了实现多个回合聊天的理想选择。
import asyncio
import getpass
import os
# 从.env文件中加载环境变量,也就是需要把LLM的key和url配置在.env文件中
from dotenv import load_dotenv,find_dotenv
_ = load_dotenv(find_dotenv())
os.environ["LANGSMITH_TRACING"] = "true"
if "LANGSMITH_API_KEY" not in os.environ: # 这里我已经将LANGSMITH_API_KEY添加到了windows的用户环境变量中了
os.environ["LANGSMITH_API_KEY"] = getpass.getpass(
prompt="Enter your LangSmith API key:"
)
if "LANGSMITH_PROJECT" not in os.environ:
os.environ["LANGSMITH_PROJECT"] = "default"
from langchain_deepseek import ChatDeepSeek
model = ChatDeepSeek(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/beta"
)
from langchain_core.messages import HumanMessage
# print(model.invoke([HumanMessage(content="Hi! I'm Bob")]).content)
# # 你会发现,模型根本没有记住上下文信息,也就不知道你叫上面名字
# print(model.invoke([HumanMessage(content="What's my name?")]).content)
from langchain_core.messages import AIMessage
# 当把前面的来哦天记录一同交给模型时,模型便能识别到你的名字,这就是实现机器人对话的基本理念
# response = model.invoke(
# [
# HumanMessage(content="Hi! I'm Bob"),
# AIMessage(content="Hi Bob! Nice to meet you. How can I help you today? "),
# HumanMessage(content="What's my name?")
# ]
# )
# print(response.content)
# 一个内存检查点存储器,用于在图的执行过程中保存和恢复状态
# 它会将图的运行状态保存在内存中,适用于短期场景
from langgraph.checkpoint.memory import MemorySaver
# START表示图执行的起始节点
# MessagesState是一个状态类,专门用于管理消息队列传递的状态,默认会在message列表中追加新的message,所以它会自动保存聊天历史
# StateGraph这是用于构建状态图的主要类
from langgraph.graph import START, MessagesState, StateGraph
# # 定义一个图,也就是定义一个工作流, 这里定义MessagesState为图节点操作共享内容,所有节点的输入和输出必须都是MessagesState类型
# workflow = StateGraph(state_schema=MessagesState)
#
# # 定义调用模型的函数,返回值是一个字典
# def call_model(state: MessagesState):
# response = model.invoke(state["messages"])
# return {"messages": response}
#
# # 给图增加一个节点
# workflow.add_edge(START, "model") # 添加节点“model”
# workflow.add_node("model", call_model) # 定义“model”的执行逻辑为call_model函数
#
# # 定义好工作流的编译可执行应用
# memory = MemorySaver()
# app = workflow.compile(checkpointer=memory)
#
# # 这种定义方式可以实现多个用户同时对一个chatbot聊天,而彼此的内容不会干涉到对方,比如这里定义了一个用户abc123
# # 那么在掉用MemorySaver中的共享内容时,只会调佣用户abc123的历史内容
# config = {"configurable": {"thread_id": "abc123"}}
#
# # 输入第一段对话
# query = "Hi! I'm Bob."
# input_messages = [HumanMessage(query)]
# output = app.invoke({"messages": input_messages}, config)
# output["messages"][-1].pretty_print()
#
# # 询问名字,其已经可以记住上文对话内容
# query = "What's my name?"
# input_messages = [HumanMessage(query)]
# output = app.invoke({"messages": input_messages}, config)
# output["messages"][-1].pretty_print()
#
# # 现在修改thread_id也就是换了一个用户,模型自然无法知道你是谁了
# config = {"configurable": {"thread_id": "abc234"}}
# input_messages = [HumanMessage(query)]
# output = app.invoke({"messages": input_messages}, config)
# output["messages"][-1].pretty_print()
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# prompt_template = ChatPromptTemplate.from_messages(
# [
# (
# "system",
# "You talk like a pirate. Answer all questions to the best of your ability in {language}.",
# ),
# MessagesPlaceholder(variable_name="messages") # 在提示模板中预留位置,用于动态插入对话历史消息
# ]
# )
#
# workflow = StateGraph(state_schema=MessagesState)
#
# def call_model(state: MessagesState):
# prompt = prompt_template.invoke(state) # 这里会将state填充到MessagesPlaceholder中
# response = model.invoke(prompt)
# return {"messages": response}
#
# workflow.add_edge(START, "model")
# workflow.add_node("model", call_model)
#
# memory = MemorySaver()
# app = workflow.compile(checkpointer=memory)
#
# config = {"configurable": {"thread_id": "abc123"}}
# query = "Hi! I'm Jim."
#
# input_messages = [HumanMessage(query)]
# output = app.invoke({"messages": input_messages}, config)
# output["messages"][-1].pretty_print()
#
# query = "What is my name?"
# input_messages = [HumanMessage(query)]
# output = app.invoke({"messages": input_messages}, config)
# output["messages"][-1].pretty_print()
prompt_template = ChatPromptTemplate.from_messages(
[
"system",
"You are a helpful assistant. Answer all questions to the best of your ability in {language}.",
MessagesPlaceholder(variable_name="messages"),
]
)
from typing import Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing_extensions import Annotated, TypedDict
class State(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
language: str
workflow = StateGraph(state_schema=State)
def call_model(state: State):
prompt = prompt_template.invoke(state)
response = model.invoke(prompt)
return {"messages": [response]}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "abc123"}}
query = "Hi! I'm Bob."
language = "Chinese"
input_messages = [HumanMessage(query)]
output = app.invoke(
{"messages": input_messages, "language": language},
config
)
output["messages"][-1].pretty_print()
query = "What is my name?"
input_messages = [HumanMessage(query)]
output = app.invoke(
{"messages": input_messages},
config,
)
output["messages"][-1].pretty_print()
管理对话历史
如果不断对话,那么Messages列表将会无限增长,导致溢出或者导致模型一次无法读取那么多token,所以需要管理Messages的长度。
LangChain附带了一些用于管理消息列表的内置帮助程序。将使用trim_messages助手来减少我们向模型发送的消息数量。
from langchain_core.messages import SystemMessage, trim_messages
trimmer = trim_messages(
max_tokens=65,
strategy="last",
token_counter=model,
include_system=True,
allow_partial=False,
start_on="human",
)
messages = [
SystemMessage(content="you're a good assistant"),
HumanMessage(content="hi! I'm bob"),
AIMessage(content="hi!"),
HumanMessage(content="I like vanilla ice cream"),
AIMessage(content="nice"),
HumanMessage(content="whats 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="thanks"),
AIMessage(content="no problem!"),
HumanMessage(content="having fun?"),
AIMessage(content="yes!"),
]
trimmer.invoke(messages)
Streaming
然而,聊天机器人应用程序的一个非常重要的用户体验考虑因素是流媒体。LLM有时需要一段时间才能响应,因此为了改善用户体验,大多数应用程序所做的一件事就是在生成每个令牌时将其流式传输回来。这允许用户查看进度。
config = {"configurable": {"thread_id": "abc789"}}
query = "Hi I'm Todd, please tell me a joke."
language = "English"
input_messages = [HumanMessage(query)]
for chunk, metadata in app.stream(
{"messages": input_messages, "language": language},
config,
stream_mode="messages",
):
if isinstance(chunk, AIMessage): # Filter to just model responses
print(chunk.content, end="|")
Build an Agent
LangChain支持创建Agent或使用LLM作为推理引擎的系统,以确定要采取哪些行动以及执行行动所需的输入。执行操作后,可以将结果反馈到LLM中,以确定是否需要更多操作,或者是否可以完成。这通常是通过工具调用来实现的。
在本教程中,将构建一个可以与搜索引擎交互的代理。您将能够向该代理提问,观看它调用搜索工具,并与它进行对话。
Agent调用工具的流程:
from langchain_deepseek import ChatDeepSeek
from langchain_tavily import TavilySearch
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
import os
try:
# 从.env文件中加载环境变量,也就是把LLM的key和url配置在.env文件中
from dotenv import load_dotenv,find_dotenv
_ = load_dotenv(find_dotenv())
except ImportError:
pass
# 创建Agent
memory = MemorySaver()
model = ChatDeepSeek(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/beta"
)
search = TavilySearch(
tavily_api_key="填写你自己的Key",
max_result=2
)
tools = [search] # 可选的执行工具,这里只有搜索引擎
agent_executor = create_react_agent(model, tools, checkpointer=memory)
config = {"configurable": {"thread_id": "abc123"}}
input_message = {
"role": "user",
"content": "你好,我住在南京。"
}
for step in agent_executor.stream(
{"messages": [input_message]}, config, stream_mode="values"
):
step["messages"][-1].pretty_print()
input_message = {
"role": "user",
"content": "我居住地方的今天天气如何?"
}
for step in agent_executor.stream(
{"messages": [input_message]}, config, stream_mode="values"
):
step["messages"][-1].pretty_print()