LangChain系列文章
- LangChain 60 深入理解LangChain 表达式语言23 multiple chains链透传参数 LangChain Expression Language (LCEL)
- LangChain 61 深入理解LangChain 表达式语言24 multiple chains链透传参数 LangChain Expression Language (LCEL)
- LangChain 62 深入理解LangChain 表达式语言25 agents代理 LangChain Expression Language (LCEL)
- LangChain 63 深入理解LangChain 表达式语言26 生成代码code并执行 LangChain Expression Language (LCEL)
- LangChain 64 深入理解LangChain 表达式语言27 添加审查 Moderation LangChain Expression Language (LCEL)
- LangChain 65 深入理解LangChain 表达式语言28 余弦相似度Router Moderation LangChain Expression Language (LCEL)
- LangChain 66 深入理解LangChain 表达式语言29 管理prompt提示窗口大小 LangChain Expression Language (LCEL)
- LangChain 67 深入理解LangChain 表达式语言30 调用tools搜索引擎 LangChain Expression Language (LCEL)
- LangChain 68 LLM Deployment大语言模型部署方案
- LangChain 69 向量数据库Pinecone入门
- LangChain 70 Evaluation 评估、衡量在多样化数据上的性能和完整性
- LangChain 71 字符串评估器String Evaluation衡量在多样化数据上的性能和完整性
- LangChain 72 reference改变结果 字符串评估器String Evaluation
- LangChain 73 给结果和参考评分 Scoring Evaluator
- LangChain 74 有用的或者有害的helpful or harmful Scoring Evaluator
- LangChain 75 打造你自己的OpenAI + LangChain网页应用
- LangChain 76 LangSmith 从入门到精通一
- LangChain 77 LangSmith 从入门到精通二
- LangChain 78 LangSmith 从入门到精通三
1. LangGraph
LangGraph是一个用于构建具有LLMs的有状态多角色应用程序的库,建立在LangChain之上(并打算与之一起使用)。它通过扩展LangChain表达语言,使其能够以循环方式协调多个链(或者角色)在计算的多个步骤之间。它的灵感来自Pregel和Apache Beam。当前提供的接口受NetworkX的启发。
主要用途是为您的LLM应用程序添加循环。关键是,这不是一个DAG框架。如果您想要构建DAG,您应该只使用LangChain表达语言。
循环对于类似Agent-like代理的行为非常重要,您在循环中调用LLM,询问它下一步应该采取什么行动。
安装 LangGraph
pip install langgraph
2. 快速开始
在这里,我们将介绍一个创建简单代理的示例,该代理使用聊天模型和函数调用。这个代理将把所有状态表示为消息列表。
我们需要安装一些LangChain软件包,以及Tavily作为示例工具。
pip install -U langchain langchain_openai tavily-python
我们还需要导出一些我们代理程序所需的环境变量。
export OPENAI_API_KEY=sk-...
export TAVILY_API_KEY=tvly-...
我们可以选择性地为最佳可观察性设置LangSmith。
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY=ls__...
3. 设置工具
我们首先要定义我们想要使用的工具。对于这个简单的例子,我们将使用Tavily内置的搜索工具。然而,创建自己的工具非常容易 - 可以查看这里的文档来了解如何操作。
from langchain_community.tools.tavily_search import TavilySearchResults
tools = [TavilySearchResults(max_results=1)]
现在我们可以将这些工具封装在一个简单的ToolExecutor
中。这是一个非常简单的类,它接收一个ToolInvocation
并调用该工具,返回输出。ToolInvocation
是任何具有tool
和tool_input
属性的类。
from langgraph.prebuilt import ToolExecutor
tool_executor = ToolExecutor(tools)
4. 建立模型
现在我们需要加载我们想要使用的聊天模型。重要的是,这应该满足两个标准:
- 它应该能够处理消息。我们将以消息的形式表示所有代理状态,因此它需要能够很好地处理它们。
- 它应该能够与OpenAI函数调用一起使用。这意味着它应该是一个OpenAI模型或者是一个暴露类似接口的模型。
注意:这些模型要求不是使用LangGraph的要求 - 它们只是这个例子的要求。
from langchain_openai import ChatOpenAI
# We will set streaming=True so that we can stream tokens
# See the streaming section for more information on this.
model = ChatOpenAI(temperature=0, streaming=True)
5. 定义代理agent状态
Langgraph
中的主要图形类型是StatefulGraph
。该图形是由一个状态对象参数化的,它将该对象传递给每个节点。然后,每个节点返回操作以更新该状态。这些操作可以是在状态上设置特定属性(例如,覆盖现有值)或者添加到现有属性。是设置还是添加由您构建图形时注释的状态对象来表示。
对于此示例,我们要跟踪的状态只是一个消息列表。我们希望每个节点只是向该列表添加消息。因此,我们将使用一个TypedDict
,其中只有一个key(messages
),并对其进行注释,以便messages属性始终被添加到其中。
from typing import TypedDict, Annotated, Sequence
import operator
from langchain_core.messages import BaseMessage
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
6. 定义节点nodes
我们现在需要在我们的图中定义一些不同的节点。在langgraph
中,一个节点可以是一个函数或runnable 接口。对于这个我们需要两个主要的节点:
- Agent 代理:负责决定要采取什么(如果有的话)行动。
- 调用工具的函数:如果代理决定采取行动,那么这个节点将执行该行动。
我们还需要定义一些边。其中一些边可能是有条件的。它们有条件的原因是根据节点的输出,可能会采取几条路径中的一条。采取的路径在运行该节点之前是未知的(LLM决定)。
- Conditional Edge 有条件边:代理被调用后,我们应该要么:a. 如果代理说要采取行动,那么应该调用调用工具的函数 b. 如果代理说它已经完成了,那么它应该完成
- Normal Edge 正常边:在调用工具后,它应该总是回到代理那里决定下一步要做什么
让我们定义这些节点,以及一个决定采取什么条件边的函数。
from langgraph.prebuilt import ToolInvocation
import json
from langchain_core.messages import FunctionMessage
# Define the function that determines whether to continue or not
def should_continue(state):
messages = state['messages']
last_message = messages[-1]
# If there is no function call, then we finish
if "function_call" not in last_message.additional_kwargs:
return "end"
# Otherwise if there is, we continue
else:
return "continue"
# Define the function that calls the model
def call_model(state):
messages = state['messages']
response = model.invoke(messages)
# We return a list, because this will get added to the existing list
return {"messages": [response]}
# Define the function to execute tools
def call_tool(state):
messages = state['messages']
# Based on the continue condition
# we know the last message involves a function call
last_message = messages[-1]
# We construct an ToolInvocation from the function_call
action = ToolInvocation(
tool=last_message.additional_kwargs["function_call"]["name"],
tool_input=json.loads(last_message.additional_kwargs["function_call"]["arguments"]),
)
# We call the tool_executor and get back a response
response = tool_executor.invoke(action)
# We use the response to create a FunctionMessage
function_message = FunctionMessage(content=str(response), name=action.tool)
# We return a list, because this will get added to the existing list
return {"messages": [function_message]}
7. 定义图表 State graph
现在我们可以把所有内容放在一起,定义图表了!
from langgraph.graph import StateGraph, END
# Define a new graph
workflow = StateGraph(AgentState)
# Define the two nodes we will cycle between
workflow.add_node("agent", call_model)
workflow.add_node("action", call_tool)
# Set the entrypoint as `agent`
# This means that this node is the first one called
workflow.set_entry_point("agent")
# We now add a conditional edge
workflow.add_conditional_edges(
# First, we define the start node. We use `agent`.
# This means these are the edges taken after the `agent` node is called.
"agent",
# Next, we pass in the function that will determine which node is called next.
should_continue,
# Finally we pass in a mapping.
# The keys are strings, and the values are other nodes.
# END is a special node marking that the graph should finish.
# What will happen is we will call `should_continue`, and then the output of that
# will be matched against the keys in this mapping.
# Based on which one it matches, that node will then be called.
{
# If `tools`, then we call the tool node.
"continue": "action",
# Otherwise we finish.
"end": END
}
)
# We now add a normal edge from `tools` to `agent`.
# This means that after `tools` is called, `agent` node is called next.
workflow.add_edge('action', 'agent')
# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable
app = workflow.compile()
8. 使用它!
我们现在可以使用它! 这样就可以暴露与所有其他LangChain可运行程序相同的接口 same interface。 这个可运行程序接受消息列表。
from langchain_core.messages import HumanMessage
inputs = {"messages": [HumanMessage(content="what is the weather in sf")]}
app.invoke(inputs)
这可能需要一点时间 - 它在幕后进行了几次调用。为了开始看到一些中间结果,我们可以使用流式传输 - 有关更多信息,请参见下文。
代码
https://github.com/zgpeace/pets-name-langchain/tree/develop
参考
https://python.langchain.com/docs/langsmith/walkthrough