Langraph高级用法

1、图的基础API

1.1、从节点更新图状态

更新节点状态有三种分别是
1、自定义添加消息函数

# 直接导入add运算符
from typing_extensions import TypedDict, Annotated
from operator import add
class State(TypedDict):
    messages: Annotated[list[AnyMessage], add]
    extra_field: int
# 或者自定义
def add(left, right):
    return left + right

2、直接导入API的添加消息函数(内置规约器使用)

# 内置规约器使用
from langgraph.graph.message import add_messages
class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]
    extra_field: int

3、继承消息系统状态管理类 MessagesState

# 状态管理
from langgraph.graph import MessagesState
class State(MessagesState):
    extra_field: int

使用状态管理的完整代码

"""加载环境变量"""
from dotenv import load_dotenv
load_dotenv()

"""定义状态"""
from langchain_core.messages import AnyMessage, HumanMessage, 
AIMessage
# 此处可以用上面三种方式替换
from langgraph.graph import MessagesState
class State(MessagesState):
    extra_field: int

def node(state: State):
    new_message = AIMessage("Hello!")
    """注意点"""
    return {"messages": [new_message], "extra_field": 10}

# 构建图
from langgraph.graph import START, StateGraph
graph = StateGraph(State).add_node(node).add_edge(START, "node").compile()

result = graph.invoke({"messages": [HumanMessage("Hi")]})
print(result)

1.2、创建节点序列

如果是顺序创建图,可以使用add_sequence,代替原来的add_node、add_edge

from typing_extensions import TypedDict

class State(TypedDict):
    value_1: str
    value_2: int

def step_1(state: State):
    return {"value_1": "a"}

def step_2(state: State):
    current_value_1 = state["value_1"]
    return {"value_1": f"{current_value_1} + b"}

def step_3(state: State):
    return {"value_2": 10}

from langgraph.graph import START, StateGraph
# graph_builder = StateGraph(State)
# Add nodes
# graph_builder.add_node(step_1)
# graph_builder.add_node(step_2)
# graph_builder.add_node(step_3)
#
# # Add edges
# graph_builder.add_edge(START, "step_1")
# graph_builder.add_edge("step_1", "step_2")
# graph_builder.add_edge("step_2", "step_3")
# # 编译
# graph = graph_builder.compile()

#  add_sequence替换上面的add_edge和add_node
graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3])
graph_builder.add_edge(START, "step_1")
graph = graph_builder.compile()
print(graph.invoke({"value_1": "c"}))

1.3、并行序列创建

多分支节点需要设置thread_id,这种并行化是通过扇出和扇入机制实现的,利用标准边和条件边。

import operator
import time
from typing import Annotated, Any
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    aggregate: Annotated[list, operator.add]

def a(state: State):
    print(f'Adding "A" to {state["aggregate"]}')
    return {"aggregate": ["A"]}

def b(state: State):
    print(f'Adding "B" to {state["aggregate"]}')
    return {"aggregate": ["B"]}

def c(state: State):
    print(f'Adding "C" to {state["aggregate"]}')
    return {"aggregate": ["C"]}

def d(state: State):
    print(f'Adding "D" to {state["aggregate"]}')
    return {"aggregate": ["D"]}

def b_2(state: State):
    print(f'Adding "B_2" to {state["aggregate"]}')
    return {"aggregate": ["B_2"]}

builder = StateGraph(State)
builder.add_node(a)
builder.add_node(b)
builder.add_node(b_2)
builder.add_node(c)
builder.add_node(d)

builder.add_edge(START, "a")
builder.add_edge("a", "b")
builder.add_edge("a", "c")
builder.add_edge("b", "b_2")
builder.add_edge(["b_2", "c"], "d")
builder.add_edge("d", END)
graph = builder.compile()
// 配置thread_id实现并行执行
print(graph.invoke({"aggregate": []}, {"configurable": {"thread_id": "10086"}}))

1.4、条件分支

import operator
from typing import Annotated, Sequence
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    aggregate: Annotated[list, operator.add]
    which: str

def a(state: State):
    print(f'Adding "A" to {state["aggregate"]}')
    return {"aggregate": ["A"]}

def b(state: State):
    print(f'Adding "B" to {state["aggregate"]}')
    return {"aggregate": ["B"]}

def c(state: State):
    print(f'Adding "C" to {state["aggregate"]}')
    return {"aggregate": ["C"]}

def d(state: State):
    print(f'Adding "D" to {state["aggregate"]}')
    return {"aggregate": ["D"]}

def e(state: State):
    print(f'Adding "E" to {state["aggregate"]}')
    return {"aggregate": ["E"]}

builder = StateGraph(State)
builder.add_node(a)
builder.add_node(b)
builder.add_node(c)
builder.add_node(d)
builder.add_node(e)
builder.add_edge(START, "a")

"""可自定义路由走单边"""
def route_bc_or_cd(state: State) -> Sequence[str]:
    if state["which"] == "cd":
        return ["c", "d"]
    return ["b", "c"]

intermediates = ["b", "c", "d"]
# 添加a到b、c、d节点的边, 并添加条件边
builder.add_conditional_edges(
    "a",
    route_bc_or_cd,
    path_map = intermediates,
)

# 添加到e节点的边
for node in intermediates:
    print('intermediates',intermediates)
    builder.add_edge(node, "e")

builder.add_edge("e", END)
graph = builder.compile()

# print(graph.invoke({"aggregate": [], "which": "bc"}))
print(graph.invoke({"aggregate": [], "which": "cd"}))

1.5、创建用于并行执行的MapReduce分支

Map-reduce 操作对于高效的任务分解和并行处理至关重要。这种方法涉及将任务分解成更小的子任务,并行处理每个子任务,然后汇总所有已完成子任务的结果。


代码示例图
import operator
from typing import Annotated
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict
from langgraph.types import Send
from langgraph.graph import END, StateGraph, START
from pydantic import BaseModel, Field

"""
生成与{topic}相关的1到3个示例的逗号分隔列表。
生成一个关于{subject}的笑话
下面是一些关于{topic}的笑话。选择最好的一个!返回最佳的ID。
{jokes}
"""
subjects_prompt = """Generate a comma separated list of between 1 and 3 examples related to: {topic}."""
joke_prompt = """Generate a joke about {subject}"""
best_joke_prompt = """Below are a bunch of jokes about {topic}. Select the best one! Return the ID of the best one.
{jokes}"""

class Subjects(BaseModel):
    subjects: list[str]

class Joke(BaseModel):
    joke: str

class BestJoke(BaseModel):
    id: int = Field(description="Index of the best joke, starting with 0")

from dotenv import load_dotenv
load_dotenv()
model = ChatOpenAI(model="gpt-4o-mini")

class OverallState(TypedDict):
    topic: str
    subjects: list
    jokes: Annotated[list, operator.add]
    best_selected_joke: str

class JokeState(TypedDict):
    subject: str

def generate_topics(state: OverallState):
    prompt = subjects_prompt.format(topic=state["topic"])
    # 结构化输出
    response = model.with_structured_output(Subjects).invoke(prompt)
    return {"subjects": response.subjects}

def generate_joke(state: JokeState):
    prompt = joke_prompt.format(subject=state["subject"])
    response = model.with_structured_output(Joke).invoke(prompt)
    return {"jokes": [response.joke]}

def continue_to_jokes(state: OverallState):
    return [Send("generate_joke", {"subject": s}) for s in state["subjects"]]
"""for循环与列表推导式 以时间换空间的方式   python没有真正意义上的多线程"""

def best_joke(state: OverallState):
    jokes = "\n\n".join(state["jokes"])
    prompt = best_joke_prompt.format(topic=state["topic"], jokes=jokes)
    response = model.with_structured_output(BestJoke).invoke(prompt)
    return {"best_selected_joke": state["jokes"][response.id]}

graph = StateGraph(OverallState)
graph.add_node("generate_topics", generate_topics)
graph.add_node("generate_joke", generate_joke)
graph.add_node("best_joke", best_joke)

graph.add_edge(START, "generate_topics")
graph.add_conditional_edges("generate_topics", continue_to_jokes, ["generate_joke"])
graph.add_edge("generate_joke", "best_joke")
graph.add_edge("best_joke", END)
app = graph.compile()

# Call the graph: here we call it to generate a list of jokes
for s in app.stream({"topic": "animals"}):
    print(s)

1.6、循环

1、设置最大循环次数recursion_limit(默认24次退出循环)
2、添加循环条件add_conditional_edges

import operator
from typing import Annotated, Literal
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    aggregate: Annotated[list, operator.add]

def a(state: State):
    print(f'Node A sees {state["aggregate"]}')
    return {"aggregate": ["A"]}

def b(state: State):
    print(f'Node B sees {state["aggregate"]}')
    return {"aggregate": ["B"]}

def c(state: State):
    print(f'Node C sees {state["aggregate"]}')
    return {"aggregate": ["C"]}

def d(state: State):
    print(f'Node D sees {state["aggregate"]}')
    return {"aggregate": ["D"]}

builder = StateGraph(State)
builder.add_node(a)
builder.add_node(b)
builder.add_node(c)
builder.add_node(d)

def route(state: State) -> Literal["b", END]:
    print(len(state["aggregate"]))
    if len(state["aggregate"]) < 100:
        return "b"
    else:
        return END

builder.add_edge(START, "a")
builder.add_conditional_edges("a", route)
builder.add_edge("b", "c")
builder.add_edge("b", "d")
builder.add_edge(["c", "d"], "a")
graph = builder.compile()

from langgraph.errors import GraphRecursionError
try:
    # 不设置默认最多24步退出循环,需要try catch
    print(graph.invoke({"aggregate": []}, {"recursion_limit": 25}))
except GraphRecursionError:
    print("Recursion Error")

1.7、可视化图

1、使用 Pyppeteer(在线方式,需要翻墙)
2、安装 Graphviz库(不同系统需要版本适配)

from IPython.display import Image, display
from langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeStyles

display(
    Image(
        builder.compile().get_graph().draw_png(
            draw_method=MermaidDrawMethod.API,
            output_file_path='./img/示例6.png'
        )
    )
)

2、对图的细粒度控制

2.1、在图和子图中更新状态

可以将控制流(边)和状态更新(节点)结合起来使用,可以通过 Command 对象来实现

import random
from typing_extensions import TypedDict, Literal
from langgraph.graph import StateGraph, START
from langgraph.types import Command
import operator
from typing_extensions import Annotated

class State(TypedDict):
    foo: Annotated[str, operator.add]

def node_a(state: State):
    print("Called A")
    value = random.choice(["a", "b"])
    print(value)
    if value == "a":
        goto = "node_b"
    else:
        goto = "node_c"

    return Command(
        update={"foo": value},
        goto=goto,
        graph=Command.PARENT, # parent 返回到父图继续执行
    )

subgraph = StateGraph(State).add_node(node_a).add_edge(START, "node_a").compile()

def node_b(state: State):
    print("Called B")
    return {"foo": "b"}

def node_c(state: State):
    print("Called C")
    return {"foo": "c"}

builder = StateGraph(State)
builder.add_edge(START, "subgraph")
builder.add_node("subgraph", subgraph)
builder.add_node(node_b)
builder.add_node(node_c)
graph = builder.compile()

print(graph.invoke({"foo": "bc"}))

2.2、对图添加配置

1、不同能力模型的配置
2、不同提示词面板的配置

import operator
from typing import Annotated, Sequence
from typing_extensions import TypedDict, Optional
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import END, StateGraph, START
from langchain_openai import ChatOpenAI
from langchain_core.runnables.config import RunnableConfig

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], operator.add]
# 模型配置
openai_model = ChatOpenAI(model="gpt-4o-mini",api_key='',base_url="")
Anthropic_model = ChatOpenAI(model="claude-3-5-sonnet-latest",api_key="")
models = {
    "anthropic": Anthropic_model,
    "openai": openai_model,
}

from langchain_core.messages import SystemMessage
# We can define a config schema to specify the configuration options for the graph
# A config schema is useful for indicating which fields are available in the configurable dict inside the config
class ConfigSchema(TypedDict):
    model: Optional[str]
    system_message: Optional[str]

def _call_model(state: AgentState, config: RunnableConfig):
    model_name = config["configurable"].get("model", "anthropic")
    model = models[model_name]
    messages = state["messages"]
    if "system_message" in config["configurable"]:
        messages = [SystemMessage(content=config["configurable"]["system_message"])] + messages
    response = model.invoke(messages)
    return {"messages": [response]}
# Define a new graph
builder = StateGraph(AgentState)
builder.add_node("model", _call_model)
builder.add_edge(START, "model")
builder.add_edge("model", END)

graph = builder.compile()
# anthropic模型
config = {"configurable": {"model": "anthropic"}}
print(graph.invoke({"messages": [HumanMessage(content="who are you?")]}, config=config))
# openai模型
config = {"configurable": {"system_message": "respond in Chinese","model": "openai"}}
print(graph.invoke({"messages": [HumanMessage(content="who are you?")]}, config=config))

2.3、节点重试策略

一般调用API超时,或者查询数据库失败,或者调用大模型失败的情况下使用
正对决策性的节点有几种处理方式
1、针对不同的场景,需要评估哪个模型更适用这个任务
2、通过写提示词的方式进行干预(需要多次迭代)
3、对已经获得的结果进行过滤(正则)
4、重试策略

import operator
import sqlite3
from typing import Annotated, Sequence
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import END, StateGraph, START
from langchain_community.utilities import SQLDatabase
from langchain_core.messages import AIMessage
from langgraph.pregel import RetryPolicy
# 重试策略
RetryPolicy()

db = SQLDatabase.from_uri("sqlite:///:memory:")
# 创建表
db.run("CREATE TABLE Artist (ArtistId INTEGER PRIMARY KEY, Name NVARCHAR(120));")
# 表中添加数据
db.run("""
INSERT INTO Artist (Name) VALUES 
('Louis Armstrong'),
('Duke Ellington'),
('Ella Fitzgerald'),
('Charlie Parker'),
""")
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()
model = ChatOpenAI(model_name="gpt-4o-mini")
class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], operator.add]
# 正则表达式兜底
def query_database(state):
    query_result = db.run(state['messages'][1].content)
    print('query_result', query_result)
    '''```sql\nSELECT * FROM Artist LIMIT 10;\n```
    正则表达式的形式
    '''
    # query_result = db.run("SELECT * FROM Artist LIMIT 5;")
    print('query_result:', query_result)
    return {"messages": [AIMessage(content=query_result)]}

def call_model(state):
    response = model.invoke(state["messages"])
    print('response:', response)
    return {"messages": [response]}
builder = StateGraph(AgentState)
#重试策略
builder.add_node("query_database", query_database, retry=RetryPolicy(retry_on=sqlite3.OperationalError))
builder.add_node("model", call_model, retry=RetryPolicy(max_attempts=5))

builder.add_edge(START, "model")
builder.add_edge("model", "query_database")
builder.add_edge("query_database", END)
graph = builder.compile()
# 提示词要完善
result = graph.invoke({"messages": [HumanMessage(
    content="查询Artist表格中前十位艺术家,我的表名叫Artist,表结构信息只有ArtistId与Name两个字段,只返回SQL查询语句,不要返回其他内容,特别是TOP、```sql\n不要出现")]})
print('result:', result)

3、持久化

3.1、图添加持久化

编译图时添加检查点来设置其状态的持久化
关键代码

from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
# 添加检查点实现持久化
graph = builder.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "1"}}
# 待验证持久化
input_message = {"role": "user", "content": "hi! I'm Cat"}
for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
     # 打印出每部执行的的内容
    chunk["messages"][-1].pretty_print()

3.2、子图持久化

只需要在编译父图时传递一个检查点器。LangGraph 会自动将检查点器传播到子子图中。
关键代码

class SubgraphState(TypedDict):
    foo: str  # note that this key is shared with the parent graph state
    bar: str

def subgraph_node_1(state: SubgraphState):
    return {"bar": "bar"}

def subgraph_node_2(state: SubgraphState):
    # note that this node is using a state key ('bar') that is only available in the subgraph
    # and is sending update on the shared state key ('foo')
    return {"foo": state["foo"] + state["bar"]}
subgraph_builder = StateGraph(SubgraphState)
subgraph_builder.add_node(subgraph_node_1)
subgraph_builder.add_node(subgraph_node_2)
subgraph_builder.add_edge(START, "subgraph_node_1")
subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")
subgraph = subgraph_builder.compile()
# 父图
class State(TypedDict):
    foo: str
def node_1(state: State):
    return {"foo": "hi! " + state["foo"]}

builder = StateGraph(State)
builder.add_node("node_1", node_1)
# note that we're adding the compiled subgraph as a node to the parent graph
builder.add_node("node_2", subgraph)
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
# 使用内存检查点器( MemorySaver )编译该图
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)

# 验证持久性是否生效
config = {"configurable": {"thread_id": "1"}}
for _, chunk in graph.stream({"foo": "foo"}, config, subgraphs=True):
    print(_,chunk)
# 通过使用与调用图相同的配置来查看父图状态。
print(graph.get_state(config).values)   # {'foo': 'hi! foobar'}
print('###')
# 检查父图的状态历史,找到在从node_2(包含子图的节点)返回结果之前的状态快照:
state_with_subgraph = [s for s in graph.get_state_history(config) if s.next == ("node_2",)][0]
print(state_with_subgraph)
print('####')

# 检索子图状态的配置
subgraph_config = state_with_subgraph.tasks[0].state
print(subgraph_config)
print('#####')
print(graph.get_state(subgraph_config).values)

3.3、跨线程持久化

将用户信息(例如姓名或偏好)存储在共享内存中,并在新的对话线程中重复使用这些信息。

# 加载环境变量(通常用于存储API密钥等敏感信息)
from dotenv import load_dotenv
load_dotenv()
# 导入内存存储模块和OpenAI嵌入模型
from langgraph.store.memory import InMemoryStore
from langchain_openai import OpenAIEmbeddings
# 初始化内存存储系统,配置嵌入模型和维度
in_memory_store = InMemoryStore(
    index={
        "embed": OpenAIEmbeddings(model="text-embedding-3-small"),  # 使用OpenAI的小型嵌入模型
        "dims": 1536,  # 对应text-embedding-3-small模型的输出维度
    }
)
# 导入必要模块
import uuid
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.base import BaseStore
# 初始化OpenAI聊天模型(使用最新的gpt-4o-mini模型)
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")

# 定义核心处理函数
def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore):
    """对话处理核心逻辑,包含记忆存储和检索功能"""
    # 从配置中获取用户ID,创建专属的命名空间
    user_id = config["configurable"]["user_id"]
    namespace = ("memories", user_id)
    # 在存储中搜索与当前对话相关的记忆
    memories = store.search(namespace, query=str(state["messages"][-1].content))
    # 将记忆数据转换为字符串格式
    info = "\n".join([d.value["data"] for d in memories])
    # 构建系统提示,包含用户记忆信息
    system_msg = f"You are a helpful assistant talking to the user. User info: {info}"
    # 检查是否需要存储新记忆
    last_message = state["messages"][-1]
    print('last_message:',last_message)
    if "remember" in last_message.content.lower():
        # 生成并存储新记忆(示例记忆内容)
        memory = "User name is Cat"
        store.put(namespace, str(uuid.uuid4()), {"data": memory})  # 使用UUID作为唯一键
    # 调用AI模型生成回复(结合系统提示和对话历史)
    response = model.invoke(
        [{"role": "system", "content": system_msg}] + state["messages"]
    )
    return {"messages": response}
# 构建状态图工作流
builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)  # 添加处理节点
builder.add_edge(START, "call_model")  # 设置起始节点

# 编译完整的工作流图,配置内存检查点和存储
graph = builder.compile(
    checkpointer=MemorySaver(),  # 用于保存对话状态的检查点
    store=in_memory_store  # 使用之前配置的内存存储
)

# 测试场景1:存储记忆
config = {"configurable": {"thread_id": "1", "user_id": "1"}}  # 用户1的对话配置
input_message = {"role": "user", "content": "Hi! Remember: my name is Cat"}
print("第一次对话(存储记忆):")
for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
    chunk["messages"][-1].pretty_print()  # 格式化输出模型回复

# 查看存储的记忆
print("\n存储的记忆内容:")
for memory in in_memory_store.search(("memories", "1")):
    print(memory.value)

# 测试场景2:读取记忆
config = {"configurable": {"thread_id": "3", "user_id": "1"}}  # 用户2的对话配置
input_message = {"role": "user", "content": "what is my name?"}
print("\n第二次对话(读取记忆):")

# 跨线程读取到记忆
for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
    chunk["messages"][-1].pretty_print()

4、内存管理

1、记录消息 但是仅保留6-10轮次的会话然后清空,或者记录token数量超过某个阈值清空
2、做摘要,5-6轮做一次摘要,但是可能存在记忆污染问题,影响后序工具调用的结果,要做提示词约束迭代

4.1、历史消息管理

"""自定义只记住最后一条消息"""
def filter_messages(messages: list):
    print('messages: ',messages)
    print('-1',messages[-1:])
    return messages[-1:]
def call_model(state: MessagesState):
    messages = filter_messages(state["messages"])
    response = bound_model.invoke(messages)
    return {"messages": response}
# Define a new graph
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("action", tool_node)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges(
    "agent",
    should_continue,
    ["action", END],
)
workflow.add_edge("action", "agent")
app = workflow.compile(checkpointer=memory)

4.2、删除消息

1、手动删除

# 当前线程状态
messages = app.get_state(config).values["messages"]
print('当前',messages)
# 手动删除消息
from langchain_core.messages import RemoveMessage
app.update_state(config, {"messages": RemoveMessage(id=messages[0].id)})

messages = app.get_state(config).values["messages"]
print('现在',messages)

2、程序删除消息

'自定义删除方法'
def delete_messages(state):
    messages = state["messages"]
    print('############@',messages)
    if len(messages) > 3:
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:-3]]}
# We need to modify the logic to call delete_messages rather than end right away
def should_continue(state: MessagesState) -> Literal["action", "delete_messages"]:
    last_message = state["messages"][-1]
    if not last_message.tool_calls:
        return "delete_messages"
    return "action"
# Define a new graph
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("action", tool_node)
# This is our new node we're defining
workflow.add_node(delete_messages)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges(
    "agent",
    should_continue,
)
workflow.add_edge("action", "agent")
# This is the new edge we're adding: after we delete messages, we finish
workflow.add_edge("delete_messages", END)
app = workflow.compile(checkpointer=memory)

4.3、对历史消息做摘要

def summarize_conversation(state: State):
    summary = state.get("summary", "")
    if summary:
        summary_message = (
            f"This is summary of the conversation to date: {summary}\n\n"
            "Extend the summary by taking into account the new messages above:"
        )
    else:
        summary_message = "Create a summary of the conversation above:"

    messages = state["messages"] + [HumanMessage(content=summary_message)]
    print('messagesall:',messages)
    response = model.invoke(messages)
    delete_messages = [RemoveMessage(id=m.id) for m in state["messages"][:-2]]
    print('delete_messages',delete_messages)
    print('@@@@@',{"summary": response.content, "messages": delete_messages})
    return {"summary": response.content, "messages": delete_messages}

workflow = StateGraph(State)
workflow.add_node("conversation", call_model)
workflow.add_node(summarize_conversation)
workflow.add_edge(START, "conversation")
workflow.add_conditional_edges(
    "conversation",
    should_continue,
)

5、人工介入

5.1、InjectedState

作用:允许工具直接访问 Agent 状态图(如对话历史、用户上下文),而不暴露给大语言模型(LLM),防止模型误生成或篡改敏感状态。
机制:
工具模式(schema)生成时自动过滤该参数,LLM 调用工具时无需提供;
运行时由 ToolNode 自动从当前状态图中提取并注入对应值。

from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
# 通过InjectedState自动注入当前状态
@tool
def get_history(query: str, state: Annotated[dict, InjectedState("messages")]) -> str:
    return f"历史消息数:{len(state)}"
tools = [get_history]
tool_node = ToolNode(tools)
注解 注入内容 生命周期 典型场景
InjectedState 当前图状态(会话级) 单次图执行周期 多轮对话上下文、临时变量
InjectedStore 持久化存储对象 跨会话/全局 用户档案、长期记忆
InjectedToolArg 任意运行时标量值 按需注入 动态 Token、用户 ID

5.2、Interrupt

作用:Interrupt 是 LangGraph 内置的一个函数,用于在节点执行过程中主动暂停整个工作流。
用法:
1、暂停执行:立即停止当前图的运行,不继续执行后续节点
2、保存现场:与 Checkpointer 配合,将当前 State 和断点位置持久化
3、等待恢复:无限期等待外部输入,直到收到 Command(resume=...) 指令

from langgraph.types import interrupt
def my_node(state: State):
    # 图会在这里暂停,并向外抛出 "请问您的年龄是?"
    user_input = interrupt("请问您的年龄是?")
    # 当用户输入恢复后,程序从这里继续执行
    return {"age": user_input}
#  手动使用用户输入更新我们的图状态
for event in graph.stream(
    Command(resume="go to my_node!"), thread, stream_mode="updates"
):
    print(event)
    print("\n")
print(graph.get_state(thread).values)

5.3、编辑图状态(update)

# Set up memory
memory = MemorySaver()
# Add
graph = builder.compile(checkpointer=memory, interrupt_before=["step_2"])
# Input
initial_input = {"input": "hello world"}
# Thread
thread = {"configurable": {"thread_id": "1"}}
# Run the graph until the first interruption
for event in graph.stream(initial_input, thread, stream_mode="values"):
    print(event)
print("Current state!")
print(graph.get_state(thread).values)
# 使用update方法来更新图状态
graph.update_state(thread, {"input": "hello universe!"})
print("---\n---\nUpdated state!")
print(graph.get_state(thread).values)
# Continue the graph execution
for event in graph.stream(None, thread, stream_mode="values"):
    print(event)

5.4、ToolNode(工具使用)

@tool
def get_weather(location: str):
    """Call to get the current weather."""
    if location.lower() in ["sf", "san francisco"]:
        return "It's 60 degrees and foggy."
    else:
        return "It's 90 degrees and sunny."
@tool
def get_coolest_cities():
    """Get a list of coolest cities"""
    return "nyc, sf"
tools = [get_weather, get_coolest_cities]
tool_node = ToolNode(tools)
message_with_single_tool_call = AIMessage(
    content="",
    tool_calls=[
        {
            "name": "get_weather",
            "args": {"location": "sf"},
            "id": "tool_call_id",
            "type": "tool_call",
        }
    ],
)
print(tool_node.invoke({"messages": [message_with_single_tool_call]}))

预构建使用

tool_node = ToolNode([get_weather])
from langchain_openai import ChatOpenAI
# 绑定大模型
model_with_tools = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools([get_weather])

6、多智能体

6.1、多智能体架构

多智能体架构图

1、Network:每个代理可以与其他所有代理通信。任何代理都可以决定下一步呼叫哪个其他代理。
2、Supervisor:每个代理与一个单一的监督者代理通信。监督者代理决定下一步应该呼叫哪个代理。
3、Supervisor (tool-calling):这是一种监督者架构的特殊情况。单个代理可以表示为工具。在这种情况下,监督者代理使用一个调用工具的 LLM 来决定要调用哪个代理工具,以及传递给这些代理的参数。
4、Hierarchical:您可以定义一个具有监督者监督的多代理系统。这是监督者架构的泛化,并允许更复杂的控制流程。
5、Custom multi-agent workflow:每个智能体仅与部分智能体通信。流程的部分是确定的,只有某些智能体可以决定下一步调用哪些其他智能体。

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容