state
,承担着接收当前状态、执行特定计算任务,并返回更新后状态的重要职责。通过这种方式,节点实现了数据处理和状态的传递,是构建复杂逻辑的基础单元。
START
节点专门用于接收用户输入,它是图执行的起点,其主要功能是确定后续首先调用的节点,引导整个图的执行流程。END
节点作为终端节点,当图中相关边完成任务且不再需要进行其他操作时,它指示图的执行结束,标志着一次完整的图计算过程完成。from typing_extensions import TypedDict
# 定义输入状态,question字段用于接收用户问题
class InputState(TypedDict):
question: str
# 定义输出状态,answer字段用于存储回答内容
class OutputState(TypedDict):
answer: str
# OverallState整合输入输出状态,方便数据传递与处理
class OverallState(InputState, OutputState):
pass
TypedDict
是Python中用于定义类型化字典的工具,通过它可以明确字典中键的类型和值的类型。在LangGraph中,利用TypedDict
定义输入输出状态,能够清晰地规定数据的格式,这不仅有助于代码的可读性和可维护性,还方便在图的构建和执行过程中进行数据校验,确保数据的一致性和正确性。# agent_node函数,处理输入状态并传递数据
def agent_node(state: InputState):
print("我是一个AI Agent。")
return {"question": state["question"]}
# action_node函数,处理问题并生成回答
def action_node(state: InputState):
print("我现在是一个执行者。")
# 增加state存储,也可以选择不存储
step = state["question"]
return {"answer": f"我接收到的问题是:{step},读取成功了!"}
state
,其类型与之前定义的输入状态类型一致。在函数内部,可以根据业务逻辑对state
进行处理。这种设计使得每个节点专注于特定的任务,通过对state
的操作实现数据的处理和传递,多个节点协同工作就能完成复杂的业务流程。from langgraph.graph import StateGraph, START, END
# 创建StateGraph对象,指定状态模式、输入输出类型
builder = StateGraph(OverallState, input=InputState, output=OutputState)
# 添加agent_node节点到图中
builder.add_node("agent_node", agent_node)
# 添加action_node节点到图中
builder.add_node("action_node", action_node)
# 添加边,确定节点执行顺序:从START到agent_node
builder.add_edge(START, "agent_node")
# 添加边,从agent_node到action_node
builder.add_edge("agent_node", "action_node")
# 添加边,从action_node到END
builder.add_edge("action_node", END)
# 编译图,检查结构完整性,为调用做准备
graph = builder.compile()
StateGraph
类是LangGraph中用于构建图结构的核心类。创建StateGraph
对象时,指定OverallState
作为状态模式,明确了图中数据的整体结构,同时指定InputState
和OutputState
作为输入输出类型,规范了数据的输入和输出格式。
add_node
方法用于向图中添加节点,第一个参数为节点的名称,方便在图中进行标识和引用,第二个参数为节点函数,定义了节点的具体逻辑。add_edge
方法用于添加边,确定节点之间的连接关系和执行顺序,从START
节点开始,按照添加边的顺序依次执行各个节点,直到END
节点。compile
方法对图进行编译,在编译过程中会检查图的结构是否完整,例如是否存在孤立节点等问题,只有编译通过的图才能被正确调用。# 调用图,传入问题,获取并打印回答结果
result = graph.invoke({"question": "今天的天气怎么样?"})
print(result)
invoke
方法是用于触发图执行的关键方法。在调用invoke
方法时,需要传入符合InputState
定义格式的数据,这里传入了包含question
字段的字典。图会按照之前定义的节点和边的顺序依次执行,将输入数据在各个节点间传递,最终返回END
节点对应的结果,即整个图执行后的输出。通过这种方式,实现了从用户输入到最终输出的完整计算过程。LangGraph具备强大的模型兼容性,对主流的在线和开源模型均提供接入支持。借助LangChain框架,开发者能够便捷地集成各类模型。本次以OpenAI的GPT模型为例,使用ChatOpenAI方法进行演示。若需接入其他模型,可查阅LangChain官方文档获取详细接入方式。
from langgraph.graph import StateGraph
from typing_extensions import TypedDict
from langgraph.graph import START, END
# 定义输入的模式
class InputState(TypedDict):
question: str
# 定义输出的模式
class OutputState(TypedDict):
answer: str
# 将InputState和OutputState这两个TypedDict类型合并成一个更全面的字典类型。
class OverallState(InputState, OutputState):
pass
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
import getpass
import os
# 检查并获取OpenAI API Key
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
langchain_openai
中的ChatOpenAI
是LangChain框架中用于与OpenAI的Chat模型进行交互的类。ChatPromptTemplate
用于构建提示模板,虽然在当前代码中未实际使用,但在更复杂的场景下,可通过它灵活构建向模型发送的提示信息,以引导模型生成符合需求的回答。getpass
和os
模块用于获取用户输入的OpenAI API Key并设置到环境变量中。在实际应用中,API Key是访问OpenAI模型的凭证,通过这种方式可以安全地获取和使用API Key,避免在代码中硬编码导致的安全问题。# llm_node函数,调用GPT模型获取回答
def llm_node(state: InputState):
messages = [
("system", "你是一位乐于助人的智能小助理"),
("human", state["question"])
]
llm = ChatOpenAI(model="gpt-40", temperature=0)
response = llm.invoke(messages)
return {"answer": response.content}
llm_node
函数中,构建了一个包含系统提示和用户问题的消息列表messages
。系统提示用于设定模型的角色和行为,这里将模型设定为乐于助人的智能小助理。然后使用ChatOpenAI
创建语言模型对象llm
,指定模型为gpt-40
,temperature
参数设置为0,temperature
控制模型输出的随机性,值越低输出越确定性。通过调用llm.invoke(messages)
方法,将消息列表发送给模型并获取回答,最后返回包含回答内容的字典。这个过程展示了如何在LangGraph的节点函数中调用外部大模型,利用模型的能力处理用户输入并生成回答。# 创建StateGraph对象,配置状态模式和输入输出类型
builder = StateGraph(OverallState, input=InputState, output=OutputState)
# 添加llm_node节点到图中
builder.add_node("llm_node", llm_node)
# 添加边,确定执行顺序:从START到llm_node再到END
builder.add_edge(START, "llm_node")
builder.add_edge("llm_node", END)
# 编译图,确保结构正确
graph = builder.compile()
StateGraph
对象,添加llm_node
节点,并通过add_edge
方法确定图的执行顺序,从START
节点开始,调用llm_node
节点,最后到END
节点。编译图的目的同样是检查图的结构完整性,确保图在执行前没有错误,为后续的调用做好准备。from typing_extensions import TypedDict, Optional
# 扩展输入状态,新增llm_answer字段接收前一节点回答
class InputState(TypedDict):
question: str
llm_answer: Optional[str]
# 定义输出状态
class OutputState(TypedDict):
answer: str
# 整合输入输出状态
class OverallState(InputState, OutputState):
pass
TypedDict
重新定义InputState
,新增llm_answer
字段,用于接收前一个节点(这里是llm_node
)的输出结果。Optional[str]
表示该字段的值可以是字符串类型,也可以是None
,增加了数据处理的灵活性。重新定义OverallState
以整合新的输入输出状态,确保图中数据的一致性和连贯性。# llm_node函数,将模型回答存入共享状态
def llm_node(state: InputState):
messages = [
("system", "你是一位乐于助人的智能小助理"),
("human", state["question"])
]
llm = ChatOpenAI(model="gpt-40", temperature=0)
response = llm.invoke(messages)
return {"llm_answer": response.content}
llm_node
函数相比,这里的返回值发生了变化,将模型的回答存入llm_answer
字段并返回,这样后续节点就可以通过访问state
中的llm_answer
字段获取该回答,实现了节点之间的数据共享和传递,为构建更复杂的功能提供了基础。# action_node函数,提取回答并翻译
def action_node(state: InputState):
messages = [
("system", "无论你接收到什么语言的文本,请翻译成法语"),
("human", state["llm_answer"])
]
llm = ChatOpenAI(model="gpt-40", temperature=0)
response = llm.invoke(messages)
return {"answer": response.content}
action_node
函数接收包含llm_answer
字段的state
,从state
中提取llm_answer
作为翻译的输入文本。构建新的消息列表,设置系统提示为将文本翻译成法语,然后调用ChatOpenAI
模型进行翻译,最后返回翻译后的结果。这个过程展示了如何基于前一个节点的输出进行进一步的处理,通过在不同节点中定义不同的逻辑,实现复杂的业务功能。# 创建StateGraph对象,配置相关参数
builder = StateGraph(OverallState, input=InputState, output=OutputState)
# 添加llm_node和action_node节点到图中
builder.add_node("llm_node", llm_node)
builder.add_node("action_node", action_node)
# 添加边,构建执行链路:从START到llm_node,再到action_node,最后到END
builder.add_edge(START, "llm_node")
builder.add_edge("llm_node", "action_node")
builder.add_edge("action_node", END)
# 编译图,准备调用
graph = builder.compile()
llm_node
和action_node
两个节点,并通过add_edge
方法定义它们的执行顺序。从START
节点开始,首先调用llm_node
处理用户问题并生成回答,然后将回答传递给action_node
进行翻译,最后到达END
节点结束图的执行。编译图的操作同样是为了检查图结构的正确性,确保图在执行时不会出现错误。LangGraph框架展现出卓越的自主性和扩展性。节点函数支持灵活定义,开发者可根据实际需求定制功能。在模型接入方面,既可以借助LangChain集成模型,也能够运用原生API或本地开源模型权重,即便脱离LangChain框架,依然能够独立构建AI Agent程序。
import os
import openai
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
# 定义输入的模式
class InputState(TypedDict):
question: str
# 定义输出的模式
class OutputState(TypedDict):
answer: str
# 将InputState和OutputState这两个TypedDict类型合并成一个更全面的字典类型。
class OverallState(InputState, OutputState):
pass
openai.api_key = os.environ.get("OPENAI_API_KEY")
# 定义节点函数,使用OpenAI原生API调用GPT模型
def llm_node(state: InputState):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一位乐于助人的智能小助理"},
{"role": "user", "content": state["question"]}
]
)
return {"answer": response['choices'][0]['message']['content']}
builder = StateGraph(OverallState, input=InputState, output=OutputState)
builder.add_node("llm_node", llm_node)
builder.add_edge(START, "llm_node")
builder.add_edge("llm_node", END)
graph = builder.compile()
result = graph.invoke({"question": "请介绍一下Python语言"})
print(result)
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
from transformers import LlamaForCausalLM, LlamaTokenizer
# 定义输入的模式
class InputState(TypedDict):
question: str
# 定义输出的模式
class OutputState(TypedDict):
answer: str
# 将InputState和OutputState这两个TypedDict类型合并成一个更全面的字典类型。
class OverallState(InputState, OutputState):
pass
# 加载本地Llama 2模型和分词器
model_path = "/path/to/your/llama-2-model"
tokenizer = LlamaTokenizer.from_pretrained(model_path)
model = LlamaForCausalLM.from_pretrained(model_path)
# 定义节点函数,使用本地Llama 2模型
def llm_node(state: InputState):
inputs = tokenizer.encode(state["question"], return_tensors="pt")
output = model.generate(inputs)
result = tokenizer.decode(output[0], skip_special_tokens=True)
return {"answer": result}
builder = StateGraph(OverallState, input=InputState, output=OutputState)
builder.add_node("llm_node", llm_node)
builder.add_edge(START, "llm_node")
builder.add_edge("llm_node", END)
graph = builder.compile()
result = graph.invoke({"question": "请介绍一下Python语言"})
print(result)