langgraph基础概念

#聊聊Agent开发#

1. 状态(State)

代表应用程序当前快照的共享数据结构

可以使用 TypedDict 或 Pydantic 模型定义

包含作为所有节点和边输入模式的模式(schema)

通过减速器函数(reducer functions)进行更新,指定如何应用更改

1. TypedDict 和 Pydantic 是什么?

它们都是定义状态结构的方式:、

简单说:都是用来定义你的数据结构,就像数据库的字段定义。

#pydantic方式
from pydantic import BaseModel

class State(BaseModel):
    messages: list
    llm_calls: int

#typedDict方式
from typing_extensions import TypedDict, Annotated
import operator

class State(TypedDict):
    messages: list
    llm_calls: int

2. 减速器函数(Reducer Function)是什么?

减速器函数定义了如何合并状态更新

比如,节点可能返回 {"messages": [new_msg]},但你想追加到消息列表,而不是覆盖它。

from typing_extensions import TypedDict, Annotated
import operator

# ❌ 没有减速器 - 会被覆盖
class State1(TypedDict):
    messages: list

# ✅ 有减速器 - 会追加
class State2(TypedDict):
    messages: Annotated[list, operator.add]  # 使用 add 函数追加

2. 节点(Nodes)

编码代理逻辑的函数

接收当前状态作为输入

执行计算或副作用

返回更新后的状态

代表图中执行的实际工作

3. 边(Edges)

基于当前状态确定接下来执行哪个节点的函数

可以是条件分支或固定转换

告诉图接下来要做什么

启用图中的路由和控制流

4. 状态图(StateGraph)

构建LangGraph应用程序的主要抽象

允许你定义节点和边的图

编译时自动创建Pregel应用程序

使得组合复杂的循环工作流变得简单

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

# 定义状态
class State(TypedDict):
    messages: Annotated[list, operator.add]
    topic: str
    essay: str

# 定义节点
def write_essay(state: State):
    """写文章"""
    essay_content = f"关于 {state['topic']} 的文章内容..."
    print(f"[write_essay 节点] 写了文章: {essay_content}")
    return {"essay": essay_content}

def review_essay(state: State):
    """审阅文章"""
    review = f"审阅意见:文章讲的是 {state['topic']}"
    print(f"[review_essay 节点] 添加审阅: {review}")
    return {"messages": [review]}

# 构建图
builder = StateGraph(State)
builder.add_node("write", write_essay)
builder.add_node("review", review_essay)
builder.add_edge(START, "write")
builder.add_edge("write", "review")

# 编译
graph = builder.compile()

# 运行
print("=== 开始执行图 ===\n")
result = graph.invoke({
    "messages": ["开始"],
    "topic": "AI",
    "essay": ""
})

print("\n=== 最终输出 ===")
print(f"messages: {result['messages']}")
print(f"topic: {result['topic']}")
print(f"essay: {result['essay']}")

实际输出会是这样=== 开始执行图 === [write_essay 节点] 写了文章: 关于 AI 的文章内容... [review_essay 节点] 添加审阅: 审阅意见:文章讲的是 AI === 最终输出 === messages: ['开始', '审阅意见:文章讲的是 AI'] topic: AI essay: 关于 AI 的文章内容... 

关键点解释

1.messages 追加了:从 ['开始'] 变成 ['开始', '审阅意见:文章讲的是 AI']因为我们用了 Annotated[list, operator.add] 减速器

2.essay 被设置:从空字符串变成 "关于 AI 的文章内容..."因为 essay 没有减速器,所以直接被覆盖

3.topic 保持不变:因为没有节点修改它,所以还是 "AI"

如果没有减速器会怎样?

class State(TypedDict):
    messages: list  # 没有 operator.add
# 输出会是:
messages: ['审阅意见:文章讲的是 AI']  # 原来的 '开始' 被覆盖了!

关键原则

节点做工作,边决定做什么。 这种设计使控制流显式且可追踪——你总是可以通过查看当前节点和状态来理解代理接下来会做什么。

通过组合节点和边,你可以创建复杂的代理工作流,使状态随时间演变。节点和边仅仅是函数,所以它们可以包含LLM或任何其他代码逻辑。

参考资料:

https://docs.langchain.com/oss/python/langgraph/use-graph-api

https://docs.langchain.com/oss/python/langgraph/graph-api

全部评论
点赞 回复 分享
发布于 02-12 11:47 北京

相关推荐

02-08 15:15
已编辑
中国科学技术大学 Java
兄弟姐妹们,今天不聊技术栈,单纯聊聊刚入职时脑子里进的水。回想我刚进公司那会儿,简直就是一部行走的《踩坑百科全书》。本以为技术过硬就能横着走,结果现实反手就是一个大逼兜。最深刻的一个坑,叫“我以为我听懂了”。 记得入职第一周,导师给我讲核心业务逻辑,语速飞快,还夹杂着一堆公司内部的缩写黑话。我当时脑子已经宕机了,但为了维持“这届实习生素质不错”的人设,我全程疯狂点头,嘴里不停说着“嗯嗯,明白了,好的”。 结果mt一走,我对着屏幕两眼一抹黑。最要命的是,因为脸皮薄、怕被嫌弃菜,我硬是憋了一下午没敢去问,就在那儿瞎琢磨。等到下班要交日报了,才发现自己理解的方向完全是反的!那天晚上我硬着头皮去请教,导师反而一脸懵:“下午怎么不早问?这块逻辑老员工都容易晕。”那一刻我才意识到,“不懂装懂”才是新人最大的死穴。还有一个坑是“迷之自信的操作”。 以前在学校写代码随心所欲,没有分支管理的概念。入职后有次修了个小bug,没切分支,直接在dev上改完就push了,结果正好赶上发版,把别人的代码给冲了。那天整个组都在回滚代码,我坐在工位上汗流浃背,真想当场把键盘吃了。给刚入职的牛友们两个带血的建议:入职第一个月,是你职业生涯中“提问成本”最低的时候。这时候你问多傻的问题,大家都会觉得“他是新人嘛,正常”。过了这个保护期,你再问基础问题就是事故了。所以,脸皮厚一点,不懂就拿小本本记下来去问!敬畏流程!敬畏线上环境! 任何涉及数据库修改、代码合并的操作,如果不确定,哪怕拉着mt看一眼再回车,也别自己盲目自信。大家入职时有没有干过啥让脚趾扣地的蠢事?或者有没有被“坑爹”文档折磨过?
刚入职的你踩过哪些坑
点赞 评论 收藏
分享
评论
3
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务