LangGraph智能体设计模式与多智能体开发(人工智能技术丛书)【行情 报价 价格 评测】-京东
王晓华LangGraph开发入门书《LangGraph智能体设计模式与多智能体开发》全文试读~_langgraph智能体设计模式与多智能体开发 pdf 下载-CSDN博客
目录
[3.5.1 初识反应式智能体](#3.5.1 初识反应式智能体)
[3.5.2 反应式室内清扫智能体设计](#3.5.2 反应式室内清扫智能体设计)
[3.5.4 仿真场景下的清扫智能体可视化演示](#3.5.4 仿真场景下的清扫智能体可视化演示)
前面我们已系统梳理LangGraph的核心组件、输入输出逻辑及工具调用基础知识,完成了理论层面的认知铺垫。技术的生命力在于落地,本节将以"自动导航"这一连续决策的典型场景为切入点,带领大家实现从理论到实操的跨越。自动导航的核心诉求是动态响应环境反馈、闭环完成路径规划,这与LangGraph的状态管理、节点流转优势高度匹配。
本节实战将聚焦"组件协同实现导航决策链",从需求拆解、节点功能定义,到流转规则设计、工具适配,逐步演示智能体如何自主完成目标定位、路径计算、障碍规避等核心任务。通过实操,既能深化对LangGraph框架的理解,也能掌握智能体在连续决策场景中的设计逻辑,为后续复杂应用开发夯实基础。
3.5.1 初识反应式智能体
反应式智能体是一种贴近生活直觉的智能范式,其核心逻辑藏在我们日常的本能反应中:手不慎碰到滚烫的杯子会瞬间缩回,医生用小锤轻敲膝盖时小腿会自动弹起,这些无需大脑深思熟虑、由外界刺激直接触发的快速响应,正是它最生动的写照。这种智能模式不依赖复杂的内部规划,而是以"即时反馈"为核心,就像自然界中采蜜的蜜蜂------看到鲜艳的花朵便主动靠近,遇到阻碍便即刻转向,全程流畅且专注于当下的环境信号,无需提前设想后续路径。
与一般智能体相比,反应式智能体的差异尤为明显。如果说前者像严谨的棋手下棋,每一步都要权衡所有可能性、推演后续走向,后者则更像灵活的舞者,顺着环境的"节奏"即时调整动作。我们定义一个简单的规则:
规则集=[
"如果(前方有障碍物)那么(向左转)",
"如果(电量低于20%)那么(返回充电)",
"如果(检测到目标)那么(向前移动)",
"如果(无特殊情况)那么(随机探索)"
]
它的工作模式可以概括为简洁的"感知→反应"循环:首先通过传感器捕捉实时环境信息,比如是否存在障碍物、剩余电量多少、是否检测到目标等;接着匹配预设的"如果−那么"规则;最后直接执行对应的动作,整个过程没有任何中间思考或记忆环节,高效且直接。
这些"如果−那么"规则就像生物的反射弧,单个规则看似简单,组合起来却能产生"涌现"效应。例如,一套基础规则集可以是:"如果前方有障碍物,那么向左转""如果电量低于20%,那么返回充电""如果检测到目标,那么向前移动""如果无特殊情况,那么随机探索"。扫地机器人正是凭借这类简单规则的组合,仅凭"遇障转向、见尘清扫、默认直行",就能自主完成房间清扫任务,展现出看似复杂的智能行为。更重要的是,它不依赖复杂的内部模型,无需记忆"过去发生了什么",只专注于"现在该做什么",这使得它的设计既简洁又可靠。
反应式智能体示意如图3-5所示。
当然,反应式智能体也有其固有的局限。它无法完成需要长期记忆和复杂规划的任务,比如下棋、制定旅行路线等;在特定环境下还可能陷入局部循环,例如在两个障碍物之间来回摆动。但这并不影响它的价值------其"即时响应"的特性让它在诸多场景中占据优势:反应速度极快,适合避障、自动驾驶等对响应时效要求高的场景;鲁棒性强,即便部分传感器失效,剩余规则仍能保障基本功能正常运行;模块化的设计也让它易于测试、调试和维护,因此成为机器人学、工业自动化等领域的基石技术,用最简洁的"刺激−反应"模式,在动态变化的环境中发挥着不可替代的作用。

图3-5 反应式智能体
下面是我们完成的一个简单的反应式智能体示例:
from pydantic import BaseModel
from typing import Literal
from langgraph.graph import StateGraph,END
#1.极简状态与动作模型
class SimpleAction(BaseModel):
"""智能体动作:仅保留核心参数"""
move:float#移动速度(0-3,0为停止)
turn:float#转向(-1左转,1右转,0直行)
behavior:Literal"避障","回充","漫游"#仅3种核心行为
class SimpleState(BaseModel):
"""智能体状态:仅保留关键传感器数据"""
obs_dist:float#障碍物距离(米)
battery:float#电量(0-100)
action:SimpleAction=None#输出动作
#2.简化决策逻辑(用硬编码代替大模型,更直观)
def decide(state:SimpleState)->dict:
"""决策函数:按优先级返回动作"""
#最高优先级:避障(距离<2米)
if state.obs_dist<2:
return{
"action":SimpleAction(move=0.5,turn=1,behavior="避障")#减速右转
}
#次优先级:回充(电量<20%)
elifstate.battery<20:
return{
"action":SimpleAction(move=2,turn=0,behavior="回充")#直行回充
}
#默认行为:漫游
else:
return{
"action":SimpleAction(move=1.5,turn=0,behavior="漫游")#正常漫游
}
#3.构建最简单的LangGraph图
graph_builder=StateGraph(SimpleState)
graph_builder.add_node("decide",decide)#唯一决策节点
graph_builder.set_entry_point("decide")#入口即决策
graph_builder.add_edge("decide",END)#一次决策完成
#编译图
simple_graph=graph_builder.compile()
#4.测试(3种核心场景)
if__name__=="main":
test_scenes=[
{"obs_dist":1.5,"battery":80},#有障碍→避障
{"obs_dist":5,"battery":15},#低电量→回充
{"obs_dist":5,"battery":60}#正常→漫游
]
for i,sceneinenumerate(test_scenes,1):
print(f"场景{i}数据:{scene}")
res=simple_graph.invoke(scene)
print(f"动作:{res'action'.dict()}\n")
可以看到,上面的代码实现了一个极简的反应式智能体,核心在于用结构化设计和优先级逻辑模拟"环境感知−即时响应"的行为模式。代码先定义了两个基础模型:SimpleState用于存储智能体的关键状态数据,仅保留障碍物距离(obs_dist)和电量(battery)两个核心传感器信息,确保输入简洁且聚焦;SimpleAction则规范了输出动作的格式,包含移动速度(move)、转向方向(turn)和当前行为类型(behavior),让决策结果可直接被执行模块使用。中间的decide函数承担决策核心,按"避障优先于回充,回充优先于漫游"的规则处理状态数据------当障碍物距离小于2米时,触发减速右转的"避障"行为;电量低于20%且无紧急障碍时,执行直行的"回充"行为;其他情况则默认"漫游",整个过程无需复杂计算,完全基于实时状态即时反应。
LangGraph框架在这里的作用是构建高效的决策流程闭环,通过StateGraph创建仅含一个"decide"节点的图结构,将状态输入、决策计算、动作输出串联成完整链路。节点作为决策入口,接收SimpleState格式的环境数据后,调用decide函数生成SimpleAction动作,随后流程结束,形成"一次感知−一次决策"的简洁循环。测试场景通过三组典型数据验证了逻辑的有效性:有近距离障碍时优先避障,低电量时触发回充,正常状态下保持漫游,直观展现了反应式智能体"不依赖记忆,只响应当下"的核心特性,用最少的代码实现了从环境输入到行为输出的完整响应机制。
3.5.2 反应式室内清扫智能体设计
看似能自主避障、清扫灰尘还会主动充电的室内清扫机器人,其"聪明"的核心并非复杂的思考能力,而是一套高效的行为组合机制------这正是LangGraph框架与经典反应式架构的完美契合。作为专门用于构建智能体的图结构工具,LangGraph天然适配"并行行为层+优先级仲裁"的核心逻辑:它的每个节点对应一个独立的"条件−动作"模块,边则承担着行为间的抑制与切换功能,就像为机器人搭建了一个精准的"行为指挥中心",让多个基础行为有序协作,最终涌现出复杂的自主作业能力。反应式室内清扫智能体设计规则如图3-6所示。

图3-6 反应式室内清扫智能体设计规则
反应式架构的核心思想,是将智能体拆解为多个相互独立的行为层,每个行为层本质上都是一个简单的"条件−动作"模块。这些行为层并非串行执行,而是并行运行,且通过双重抑制机制实现协调:高优先级行为不仅可以覆盖低优先级行为的输出信号,还能直接阻断低优先级行为接收输入信号,从而确保关键场景下的核心行为优先执行。以室内清扫机器人为例,其行为层按优先级从低到高精心设计:最低优先级是充电行为,当电量低于10%时自动触发,机器人会启动导航向充电桩移动;中优先级是清扫行为,当传感器检测到灰尘时,立即启动刷子并匀速前进;最高优先级是避障行为,一旦探测到前方近距离存在障碍物,便瞬间停止当前动作并转向规避。
在实际运行中,这种设计的优势尤为明显:若机器人正在执行清扫任务,突然遇到障碍物,避障行为会立刻抑制清扫行为的输出,让机器人优先完成转向避障;待障碍解除后,避障行为的触发条件不再满足,清扫行为便重新夺回控制权,既保证了作业的连续性,也最大限度保障了机器人的安全。
这套机制的工作流程清晰且高效,全程围绕"并行感知−条件判断−抑制仲裁−动作执行"展开。传感器捕捉到的环境信息会同步传递给所有行为层,各层独立判断自身的触发条件是否满足;随后抑制机制开始生效:当需要避障时,避障层会同时抑制清扫层和充电层的运行;当需要清扫且无避障需求时,清扫层仅抑制充电层;只有当所有高优先级行为的触发条件均不满足时,充电层才会启动。最终通过行为仲裁机制,从所有满足条件的行为中筛选出优先级最高的一个,确保任何时刻都只有一个动作被输出执行,彻底避免行为冲突。其核心优势正在于三层并行处理、优先级抑制与行为仲裁的协同作用:三个行为层同步接收传感器信息,保证了判断的时效性;高优先级行为始终拥有执行优先权,确保了核心需求的满足;而仲裁机制则为行为执行提供了唯一出口,让整个系统运行有序。
构建这类反应式清扫智能体,需要遵循一套完整且严谨的逻辑链条。首先要明确智能体的核心任务与目标,例如"在房间内安全漫游并高效完成全面清扫";接着需要全面识别执行任务过程中可能遇到的关键情境,比如"前方有障碍物""左侧有障碍物""检测到高密度灰尘区域""电量不足""已到达充电桩附近"等;随后为每个情境设计简单直接的行为模式,例如前方有近距离障碍时"后退+转向"、左侧有适中距离障碍物时"沿墙平行前进"、无特殊输入时"随机漫游"、检测到灰尘时"启动清扫组件+直线前进";之后根据行为的重要性设定优先级,通常与生存和安全相关的避障行为优先级最高,作业相关的清扫行为次之,保障续航的充电行为最低;再通过LangGraph框架实现仲裁机制,借助节点的条件判断功能与边的权重设置,将各类行为有机组合,确保任何时刻最高优先级的有效行为都能主导机器人的行动;最后在模拟环境或真实场景中进行反复测试,不断调整探测距离阈值、转向速度、清扫强度等参数,持续优化机器人的运行性能,让其在复杂多变的室内环境中也能稳定高效地完成清扫任务。
3.5.3 反应式室内清扫智能体的实现
下面我们将使用LangGraph完成一个基本的反应式清扫智能体。与前面分析的相同,对于反应式智能体,我们首先需要设置其规则,根据输入的传感器数据对其行动做出规范处理。我们首先将反应式智能体的"感知−反应"逻辑从抽象概念转化为可执行的结构化框架,而通过LangGraph为这套框架提供了高效的运行载体。
class ActionState(BaseModel):
speed:float
angle:float
current_behavior:Literal"紧急避障","预防避障","沿墙跟随","随机探索"
我们首先定义AgentState类:它像智能体的"感官记忆库",专门存储传感器采集的关键环境数据------min_front记录前方最近障碍物距离,avg_left和avg_right反映左右两侧环境的平均状态,min_left和min_right则捕捉左右两侧的最近障碍信息。这些数据是所有"条件−动作"规则的判断依据,对应着反应式架构中"感知"环节的具象化:智能体不需要记住过去的环境,只需通过AgentState实时持有"此刻的世界模样",就能让各行为层独立判断是否满足触发条件。
class AgentState(BaseModel):
min_front:float
avg_left:float
avg_right:float
min_left:float
min_right:float
action_step:ActionState={}
ActionState的作用是智能体的"动作输出器",通过speed(速度)和angle(转向角度)定义具体行为,并用current_behavior标注当前执行的行为类型(如"紧急避障""随机探索")。这个设计直接对应"反应"环节的规范化------无论哪个行为层触发(比如避障层或清扫层),最终都要通过ActionState输出统一格式的动作,避免不同行为的输出冲突,这正是行为仲裁机制在代码层面的体现。
下面我们通过提示词将反应式智能体的决策逻辑、行为优先级、输入输出规范进行结构化固化,让大模型直接充当"行为决策核心",无需额外编写复杂的条件判断代码,即可实现传感器数据到动作指令的快速映射。
#设置反应式智能体提示词
system_prompt_template="""
你是一个机器人行为决策系统,基于传感器数据决定机器人的运动行为。请根据输入的传感器距离参数,按照优先级顺序选择并执行相应的行为。
输入传感器数据:
-前方最小距离:{min_front}
-左侧平均距离:{avg_left}
-右侧平均距离:{avg_right}
-左侧最小距离:{min_left}
-右侧最小距离:{min_right}
行为优先级(从高到低):
1.紧急避障(最高优先级)
2.预防避障(中等优先级)
3.沿墙跟随(低优先级)
4.随机探索(默认行为)
决策规则:
1.紧急避障:
-条件:min_front<30
-速度:1.0
-转向策略:转向更开阔的一侧,使用60度转向角度
-方向逻辑:如果avg_left>avg_right则向右转,否则向左转
2.预防避障:
-条件:min_front<80
-速度:1.5
-转向策略:转向更开阔的一侧,使用30度转向角度
-方向逻辑:如果avg_left>avg_right则向右转,否则向左转
3.沿墙跟随:
-条件:min_left<50或min_right<50
-速度:2.0
-墙壁跟随逻辑:
*左侧墙壁:
-距离<30:向右远离墙壁(角度-0.15)
-距离≥30:向左温和靠近墙壁(角度+0.05)
*右侧墙壁:
-距离<30:向左远离墙壁(角度+0.15)
-距离≥30:向右温和靠近墙壁(角度-0.05)
4.随机探索:
-条件:以上条件均不满足
-速度:2.5
-转向策略:基础随机转向(-0.1到0.1弧度),1%概率大幅转向(-0.5到0.5弧度)
输出要求:
请严格按照优先级顺序检查条件,一旦满足某个行为条件就立即执行并返回结果,不再检查后续条件。
请以纯JSON格式输出结果,不要包含任何Markdown标记或代码块。包含以下字段:
-speed:设置的速度值
-angle:角度变化值(正数表示左转,负数表示右转)
-current_behavior:执行的行为名称
基于以上规则和输入数据,请做出决策并输出纯JSON,参考格式如下:
classActionState(BaseModel):
speed:float
angle:float
current_behavior:Literal"紧急避障","预防避障","沿墙跟随","随机探索"
你要注意输出要最快的速度输出,在最短时间做出决定。
"""
另外需要注意,提示词模板的设计特意呼应了前文的AgentState和ActionState类------输入字段完全对应AgentState的传感器数据,输出JSON格式则与ActionState的结构完全对齐,这意味着大模型的决策结果可以直接被LangGraph节点接收和执行,无需额外的数据格式转换,实现了"感知−决策−动作"的无缝衔接。同时,模板中明确了优先级顺序和规则细节,相当于将反应式架构的"优先级抑制""行为仲裁"逻辑直接注入决策过程,让大模型成为灵活且易调整的"规则执行器",后续若需修改行为参数(如调整避障阈值、转向角度),只需修改提示词模板,无需重构代码框架。
我们使用LangGraph的提示词模版创建可用的结果如下:
#创建PromptTemplate
system_prompt=PromptTemplate(
input_variables="min_front","avg_left","avg_right","min_left","min_right",
template=system_prompt_template
)
而LangGraph的价值,就在于将这两个类串联成动态运行的"行为协作网络"。在LangGraph中,每个行为(如紧急避障、沿墙跟随)可以被定义为独立节点,节点的输入是AgentState中的传感器数据,节点的输出则是候选的ActionState;通过设置节点间的"边条件",可以实现优先级抑制------比如当"紧急避障"节点检测到min_front小于安全阈值时,LangGraph会自动阻断"随机探索"等低优先级节点的输出,让"紧急避障"的ActionState成为最终执行动作。
import bigmodel
agent=system_prompt|bigmodel.llm.with_structured_output(ActionState)
而动作的执行则通过agent_action函数与LangGraph的图结构形成闭环,将决策逻辑与智能体的状态流转紧密绑定。
agent_action函数作为核心执行节点,首先从当前AgentState中提取所有传感器数据------包括前方最小距离、左右侧平均距离及最小距离,这些数据正是决策的"原材料"。随后,通过agent.invoke将传感器数据以键值对形式传入(对应提示词模板中的占位符),触发大模型基于预设的行为规则(紧急避障、预防避障等优先级逻辑)进行快速决策,生成符合ActionState格式的动作指令(速度、角度、当前行为)。最后,函数将生成的action_step返回,用于更新智能体的状态,完成"从环境感知到动作输出"的一次完整响应。
def agent_action(state:AgentState):
min_front=state.min_front
avg_left=state.avg_left
avg_right=state.avg_right
min_left=state.min_left
min_right=state.min_right
action_step=agent.invoke(input={"min_front":min_front,"avg_left":avg_left, "avg_right":avg_right,"min_left":min_left,"min_right":min_right})
return{"action_step":action_step}
LangGraph的StateGraph构建了极简却高效的执行流:将agent_action设为图的入口节点,意味着智能体启动后会优先执行该节点;通过add_edge("agent_action",END)设置流程终点,形成单次"感知−决策−动作"的闭环。
在实际运行中,这套图结构可配合循环调用(如在仿真环境中持续传入新的传感器数据),让智能体不断根据实时环境更新动作------比如前方出现障碍物时,agent_action会触发"紧急避障"并输出转向指令;障碍物消失后,又会自动切换为"随机探索"或"沿墙跟随",完美贴合反应式智能体"即时响应、动态调整"的核心特性。
graph_builder=StateGraph(AgentState)
graph_builder.add_node(agent_action)
graph_builder.set_entry_point("agent_action")
graph_builder.add_edge("agent_action",END)
graph=graph_builder.compile()
最终编译生成的graph对象,便是这套反应式逻辑的"运行容器",只需向其输入包含传感器数据的AgentState,即可输出对应的动作指令,让智能体的行为决策从代码定义落地为可交互的实际动作。
完整的反应式室内清扫智能体的实现代码如下所示:
from pydantic import BaseModel
from typing import Optional,List,Literal
from langchain_core.prompts import PromptTemplate
#将系统提示转换为字符串模板
system_prompt_template="""
你是一个机器人行为决策系统,基于传感器数据决定机器人的运动行为。请根据输入的传感器距离参数,按照优先级顺序选择并执行相应的行为。
输入传感器数据:
-前方最小距离:{min_front}
-左侧平均距离:{avg_left}
-右侧平均距离:{avg_right}
-左侧最小距离:{min_left}
-右侧最小距离:{min_right}
行为优先级(从高到低):
1.紧急避障(最高优先级)
2.预防避障(中等优先级)
3.沿墙跟随(低优先级)
4.随机探索(默认行为)
决策规则:
1.紧急避障:
-条件:min_front<30
-速度:1.0
-转向策略:转向更开阔的一侧,使用60度转向角度
-方向逻辑:如果avg_left>avg_right则向右转,否则向左转
2.预防避障:
-条件:min_front<80
-速度:1.5
-转向策略:转向更开阔的一侧,使用30度转向角度
-方向逻辑:如果avg_left>avg_right则向右转,否则向左转
3.沿墙跟随:
-条件:min_left<50或min_right<50
-速度:2.0
-墙壁跟随逻辑:
*左侧墙壁:
-距离<30:向右远离墙壁(角度-0.15)
-距离≥30:向左温和靠近墙壁(角度+0.05)
*右侧墙壁:
-距离<30:向左远离墙壁(角度+0.15)
-距离≥30:向右温和靠近墙壁(角度-0.05)
4.随机探索:
-条件:以上条件均不满足
-速度:2.5
-转向策略:基础随机转向(-0.1到0.1弧度),1%概率大幅转向(-0.5到0.5弧度)
输出要求:
请严格按照优先级顺序检查条件,一旦满足某个行为条件就立即执行并返回结果,不再检查后续条件。
请以纯JSON格式输出结果,不要包含任何Markdown标记或代码块。包含以下字段:
-speed:设置的速度值
-angle:角度变化值(正数表示左转,负数表示右转)
-current_behavior:执行的行为名称
基于以上规则和输入数据,请做出决策并输出纯JSON,参考格式如下:
class ActionState(BaseModel):
speed:float
angle:float
current_behavior:Literal"紧急避障","预防避障","沿墙跟随","随机探索"
你要注意输出要最快的速度输出,在最短时间做出决定。
"""
#创建PromptTemplate
system_prompt=PromptTemplate(
input_variables="min_front","avg_left","avg_right","min_left","min_right",
template=system_prompt_template
)
class ActionState(BaseModel):
speed:float
angle:float
current_behavior:Literal"紧急避障","预防避障","沿墙跟随","随机探索"
class AgentState(BaseModel):
min_front:float
avg_left:float
avg_right:float
min_left:float
min_right:float
action_step:ActionState={}
import bigmodel
agent=system_prompt|bigmodel.llm.with_structured_output(ActionState)
from langgraph.graph import StateGraph,MessagesState,START,END
from pydantic import BaseModel
def agent_action(state:AgentState):
min_front=state.min_front
avg_left=state.avg_left
avg_right=state.avg_right
min_left=state.min_left
min_right=state.min_right
action_step=agent.invoke(input={"min_front":min_front,"avg_left":avg_left, "avg_right":avg_right,"min_left":min_left,"min_right":min_right})
return{"action_step":action_step}
graph_builder=StateGraph(AgentState)
graph_builder.add_node(agent_action)
graph_builder.set_entry_point("agent_action")
graph_builder.add_edge("agent_action",END)
graph=graph_builder.compile()
#使用示例
if__name__=="main":
#测试PromptTemplate
test_input={
"min_front":25,
"avg_left":120,
"avg_right":80,
"min_left":45,
"min_right":60
}
reply=graph.invoke(test_input)
action_step=reply"action_step"
print(action_step)
print(action_step.speed)
print(action_step.angle)
print(action_step.current_behavior)
首先上面代码的开始部分先完成核心依赖导入与基础结构定义,为反应式智能体搭建"规则框架"和"数据容器"。代码通过PromptTemplate将详细的行为决策规则(包括四大行为的优先级、触发条件、速度与转向策略)固化为system_prompt_template,模板中预留了min_front、avg_left等传感器数据占位符,确保大模型能精准接收环境信息。接着定义两个Pydantic模型:ActionState明确了智能体的动作输出格式,包含速度、转向角度和当前行为名称,严格对应反应式架构的"反应"环节;AgentState则专门存储传感器采集的实时环境数据(如各方距离参数)和动作状态,成为"感知"数据的结构化载体,这两个模型的设计实现了"输入−输出"的格式规范化,避免数据混乱。
代码的核心逻辑集中在智能体决策链路的构建,实现了"感知数据→动作指令"的转化。通过system_prompt|bigmodel.llm.with_structured_output(ActionState)的链式调用,将Prompt模板与大模型绑定,且指定大模型输出直接遵循ActionState结构,省去了手动解析JSON的步骤,让决策结果可直接使用。agent_action函数作为LangGraph的核心执行节点,其作用是衔接"感知"与"决策":它从AgentState中提取所有传感器实时数据,以键值对形式传入绑定好的大模型agent,触发大模型按模板中的优先级规则快速判断(如示例中min_front=25会触发最高优先级的"紧急避障"),最终生成符合规范的动作指令action_step并返回,完成一次"环境感知→规则匹配→动作决策"的闭环。
最后通过LangGraph构建可执行的工作流,并以示例验证整个系统的可用性。利用StateGraph创建图结构,将agent_action设为唯一节点和入口点,同时连接至END形成极简且高效的执行流------这种设计完美契合反应式智能体"即时响应"的特点,无需复杂分支,只需持续输入实时传感器数据即可循环运行。代码末尾的使用示例中,传入了min_front=25的测试数据(满足"紧急避障"条件),通过graph.invoke(test_input)触发整个流程,最终输出并打印结构化的动作结果(速度、角度、当前行为),直观展示了从传感器数据输入到具体动作输出的完整链路,证明了这套代码将反应式架构从理论落地为可运行系统的完整性。
代码运行结果如下所示:
speed=1.0angle=-1.0471975511965976current_behavior='紧急避障'
1.0
-1.0471975511965976
紧急避障
读者可以尝试更多的结果。
3.5.4 仿真场景下的清扫智能体可视化演示
下面我们将使用上面完成的清扫智能体结合仿真场景对过程进行可可视化演示,读者首先需要安装仿真框架:
pip install pygame
演示使用的完整代码如下所示:
import pygame
import sys
import math
import random
from pygame.locals import *
import lg #lg.py文件在当前目录下
#初始化pygame
pygame.init()
#屏幕尺寸
WIDTH,HEIGHT=1280,720
NUM_OBSTACLES=7
screen=pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('基于包容式架构的反应式机器人模拟')
#颜色定义
WHITE=(255,255,255)#白色
BLACK=(0,0,0)#黑色
RED=(255,0,0)#红色
GREEN=(0,255,0)#绿色
BLUE=(0,0,255)#蓝色
GRAY=(200,200,200)#灰色
YELLOW=(255,255,0)#黄色
ORANGE=(255,165,0)#橙色
PURPLE=(128,0,128)#紫色
CYAN=(0,255,255)#青色
#行为颜色映射
BEHAVIOR_COLORS={
"紧急避障":RED,
"预防避障":ORANGE,
"沿墙跟随":YELLOW,
"随机探索":GREEN,
"直线运动":BLUE#新增直线运动行为
}
#基于包容式架构的机器人类yx1
class Robot:
def init(self, x, y, radius=20, sensor_count=7, sensor_length=150):
机器人位置和物理属性
self.x = x
self.y = y
self.radius = radius
self.angle = random.uniform(0, 2 * math.pi) # 随机初始角度
self.speed = 2.0 # 默认速度
self.sensor_length = sensor_length # 传感器探测距离
self.sensor_count = sensor_count # 传感器数量
self.sensor_spread = math.pi * 1.2 # 传感器展开角度(216度)
行为状态
self.current_behavior = "直线运动" # 初始行为改为直线运动
self.behavior_history = \[\] # 行为历史记录
self.cleaning_progress = 0 # 清洁进度
轨迹跟踪
self.trajectory = \[\] # 轨迹点列表
self.max_trajectory_length = 200 # 最大轨迹长度
控制参数
self.obstacle_memory = \[\] # 记忆最近遇到的障碍物
self.obstacle_threshold = sensor_length * 0.5 # 障碍物检测阈值
初始化智能体
self.graph = lg.graph
def draw(self, surface):
"""在屏幕上绘制机器人及其传感器"""
根据当前行为状态绘制机器人身体
behavior_color = BEHAVIOR_COLORS.get(self.current_behavior, GREEN)
pygame.draw.circle(surface, behavior_color, (int(self.x), int(self.y)), self.radius)
绘制方向指示器
end_x = self.x + math.cos(self.angle) * self.radius
end_y = self.y + math.sin(self.angle) * self.radius
pygame.draw.line(surface, BLACK, (self.x, self.y), (end_x, end_y), 3)
绘制传感器,根据距离进行颜色编码
readings = self.get_sensor_readings(\[\]) # 空障碍物列表仅用于绘制
for i in range(self.sensor_count):
计算每个传感器的角度
sensor_angle = self.angle - self.sensor_spread / 2 + i * self.sensor_spread / (self.sensor_count - 1)
计算传感器终点
end_x = self.x + math.cos(sensor_angle) * self.sensor_length
end_y = self.y + math.sin(sensor_angle) * self.sensor_length
根据距离进行颜色编码
reading = readingsi if i < len(readings) else self.sensor_length
if reading > self.sensor_length * 0.6:
color = GREEN # 安全距离 - 绿色
elif reading > self.sensor_length * 0.3:
color = ORANGE # 警告距离 - 橙色
else:
color = RED # 危险距离 - 红色
绘制传感器线
pygame.draw.line(surface, color, (self.x, self.y), (end_x, end_y), 2)
def get_sensor_readings(self, obstacles):
"""获取所有传感器的读数"""
readings = \[\]
for i in range(self.sensor_count):
计算传感器角度
sensor_angle = self.angle - self.sensor_spread / 2 + i * self.sensor_spread / (self.sensor_count - 1)
计算传感器终点
end_x = self.x + math.cos(sensor_angle) * self.sensor_length
end_y = self.y + math.sin(sensor_angle) * self.sensor_length
寻找最近的障碍物交点
closest_dist = self.sensor_length # 初始化为最大传感器长度
检查障碍物
for obstacle in obstacles:
检查线段是否与障碍物(矩形)相交
dist = self.line_rect_intersection(
self.x, self.y, end_x, end_y,
obstacle.rect
)
if dist and dist < closest_dist:
closest_dist = dist
检查屏幕边界交点(改进的检测)
border_dist = self.improved_border_intersection(self.x, self.y, end_x, end_y)
if border_dist and border_dist < closest_dist:
closest_dist = border_dist
readings.append(closest_dist)
return readings
def improved_border_intersection(self, x1, y1, x2, y2):
"""改进的边界检测,考虑机器人半径"""
定义边界线(左、右、上、下)
borders = [
(0, 0, 0, HEIGHT), # 左边界
(WIDTH, 0, WIDTH, HEIGHT), # 右边界
(0, 0, WIDTH, 0), # 上边界
(0, HEIGHT, WIDTH, HEIGHT) # 下边界
]
closest_dist = None
for bx1, by1, bx2, by2 in borders:
计算线段交点
intersection = self.line_line_intersection(x1, y1, x2, y2, bx1, by1, bx2, by2)
if intersection:
ix, iy = intersection
dist = math.sqrt((ix - x1) ** 2 + (iy - y1) ** 2)
调整机器人半径以更早检测障碍物
adjusted_dist = dist - self.radius
if adjusted_dist < 0:
adjusted_dist = 0
if closest_dist is None or adjusted_dist < closest_dist:
closest_dist = adjusted_dist
return closest_dist
def line_rect_intersection(self, x1, y1, x2, y2, rect):
"""计算线段与矩形的交点"""
获取矩形边作为线段
left = (rect.left, rect.top, rect.left, rect.bottom)
right = (rect.right, rect.top, rect.right, rect.bottom)
top = (rect.left, rect.top, rect.right, rect.top)
bottom = (rect.left, rect.bottom, rect.right, rect.bottom)
rect_edges = left, right, top, bottom
closest_dist = None
检查与矩形每条边的交点
for rx1, ry1, rx2, ry2 in rect_edges:
intersection = self.line_line_intersection(x1, y1, x2, y2, rx1, ry1, rx2, ry2)
if intersection:
ix, iy = intersection
dist = math.sqrt((ix - x1) ** 2 + (iy - y1) ** 2)
调整机器人半径以更早检测障碍物
adjusted_dist = dist - self.radius
if adjusted_dist < 0:
adjusted_dist = 0
if closest_dist is None or adjusted_dist < closest_dist:
closest_dist = adjusted_dist
return closest_dist
def line_border_intersection(self, x1, y1, x2, y2):
"""计算线段与屏幕边界的交点"""
定义边界线(左、右、上、下)
borders = [
(0, 0, 0, HEIGHT), # 左边界
(WIDTH, 0, WIDTH, HEIGHT), # 右边界
(0, 0, WIDTH, 0), # 上边界
(0, HEIGHT, WIDTH, HEIGHT) # 下边界
]
closest_dist = None
for bx1, by1, bx2, by2 in borders:
计算线段交点
intersection = self.line_line_intersection(x1, y1, x2, y2, bx1, by1, bx2, by2)
if intersection:
ix, iy = intersection
dist = math.sqrt((ix - x1) ** 2 + (iy - y1) ** 2)
if closest_dist is None or dist < closest_dist:
closest_dist = dist
return closest_dist
def line_line_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4):
"""计算两条线段的交点"""
计算行列式
den = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
如果行列式为零,则线段平行
if den == 0:
return None
ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / den
ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / den
检查交点是否在两条线段内
if 0 <= ua <= 1 and 0 <= ub <= 1:
计算交点坐标
x = x1 + ua * (x2 - x1)
y = y1 + ua * (y2 - y1)
return (x, y)
return None
def reactive_control(self, sensor_readings):
"""反应式控制核心 - 仅在检测到障碍物时使用agent进行决策"""
检查是否有障碍物接近
min_reading = min(sensor_readings) if sensor_readings else self.sensor_length
如果没有检测到障碍物,保持直线运动
if min_reading > self.obstacle_threshold:
self.speed = 2.0 # 保持正常速度
self.current_behavior = "直线运动"
return # 不调用agent,直接返回
只有在检测到障碍物时才调用agent进行决策
传感器分组:左、前左、前、前右、右
left_sensors = sensor_readings:2 # 前2个传感器(左侧)
front_sensors = sensor_readings2:5 # 中间3个传感器(前方)
right_sensors = sensor_readings5: # 后2个传感器(右侧)
计算最小距离
min_left = min(left_sensors) if left_sensors else self.sensor_length
min_front = min(front_sensors) if front_sensors else self.sensor_length
min_right = min(right_sensors) if right_sensors else self.sensor_length
计算平均距离以获得更平滑的行为
avg_left = sum(left_sensors) / len(left_sensors) if left_sensors else self.sensor_length
avg_right = sum(right_sensors) / len(right_sensors) if right_sensors else self.sensor_length
准备agent输入数据
sensor_data = {
"min_front": min_front,
"avg_left": avg_left,
"avg_right": avg_right,
"min_left": min_left,
"min_right": min_right
}
try:
使用agent进行决策
reply = self.graph.invoke(sensor_data)
action_step = reply"action_step"
应用agent的决策结果
self.speed = action_step.speed
self.angle += action_step.angle # 注意:这里是累加角度变化
self.current_behavior = action_step.current_behavior
except Exception as e:
如果agent调用失败,使用备用策略
print(f"Agent决策失败: {e}, 使用备用策略")
简单的避障策略
if min_front < self.obstacle_threshold * 0.7:
前方有障碍物,转向
if min_left > min_right:
self.angle -= math.pi / 4 # 向左转
else:
self.angle += math.pi / 4 # 向右转
self.current_behavior = "紧急避障"
self.speed = 1.5 # 减速
def update(self, obstacles):
"""更新机器人状态"""
模拟清洁进度(仅在探索和沿墙行为时增加)
if self.current_behavior in "直线运动", "随机探索", "沿墙跟随":
self.cleaning_progress += 0.1
获取传感器读数
readings = self.get_sensor_readings(obstacles)
应用基于包容式架构的反应式控制
self.reactive_control(readings)
移动机器人
self.x += math.cos(self.angle) * self.speed
self.y += math.sin(self.angle) * self.speed
保持机器人在边界内,改进碰撞处理
collision = False
if self.x < self.radius:
self.x = self.radius
collision = True
elif self.x > WIDTH - self.radius:
self.x = WIDTH - self.radius
collision = True
if self.y < self.radius:
self.y = self.radius
collision = True
elif self.y > HEIGHT - self.radius:
self.y = HEIGHT - self.radius
collision = True
如果发生碰撞,改变方向
if collision:
self.angle += math.pi / 2 # 90度转向
self.current_behavior = "紧急避障"
记录轨迹
self.trajectory.append((self.x, self.y))
if len(self.trajectory) > self.max_trajectory_length:
self.trajectory.pop(0) # 移除最旧的轨迹点
记录行为历史
self.behavior_history.append(self.current_behavior)
if len(self.behavior_history) > 50:
self.behavior_history.pop(0) # 移除最旧的行为记录
#障碍物类
class Obstacle:
def init(self,x,y,width,height):
self.x=x
self.y=y
self.width=width
self.height=height
#创建矩形对象用于碰撞检测
self.rect=pygame.Rect(x-width//2,y-height//2,width,height)
def draw(self,surface):
"""在屏幕上绘制障碍物"""
pygame.draw.rect(surface,RED,self.rect)
#获取支持中文字体的函数
def get_chinese_font(size):
"""尝试获取支持中文的字体"""
#尝试常见的中文字体
chinese_fonts='SimHei','MicrosoftYaHei','SimSun','KaiTi','FangSong'
for font_nameinchinese_fonts:
try:
return pygame.font.SysFont(font_name,size)
except:
continue
#如果没有可用的中文字体,回退到Arial
return pygame.font.SysFont('Arial',size)
#主函数
def main():
clock=pygame.time.Clock()
#创建机器人(放置在屏幕中央)
robot=Robot(WIDTH//2,HEIGHT//2)
#创建障碍物(战略性放置)
obstacles=\[\]
for _inrange(NUM_OBSTACLES):
width=random.randint(40,100)
height=random.randint(40,100)
#确保障碍物不会太靠近边界
x=random.randint(width//2+50,WIDTH-width//2-50)
y=random.randint(height//2+50,HEIGHT-height//2-50)
obstacles.append(Obstacle(x,y,width,height))
#UI字体-使用中文字体
font=get_chinese_font(16)#普通字体
title_font=get_chinese_font(24)#标题字体
#主游戏循环
running=True
while running:
#处理事件
for event in pygame.event.get():
if event.type==QUIT:
running=False
elif event.type==KEYDOWN:
if event.key==K_ESCAPE:
running=False
elif event.key==K_r:#重置机器人位置
robot.x=WIDTH//2
robot.y=HEIGHT//2
robot.angle=random.uniform(0,2*math.pi)
robot.cleaning_progress=0
robot.trajectory=\[\]
robot.behavior_history=\[\]
elifevent.key==K_n:#生成新障碍物
obstacles=\[\]
for_inrange(NUM_OBSTACLES):
width=random.randint(40,100)
height=random.randint(40,100)
x=random.randint(width//2+50,WIDTH-width//2-50)
y=random.randint(height//2+50,HEIGHT-height//2-50)
obstacles.append(Obstacle(x,y,width,height))
#更新机器人状态
robot.update(obstacles)
#绘制所有内容
screen.fill(WHITE)#清空屏幕为白色
#绘制轨迹
if len(robot.trajectory)>1:
for i in range(1,len(robot.trajectory)):
start_pos=robot.trajectoryi-1
end_pos=robot.trajectoryi
pygame.draw.line(screen,GRAY,start_pos,end_pos,1)
#绘制所有障碍物
for obstacle in obstacles:
obstacle.draw(screen)
#绘制机器人
robot.draw(screen)
#绘制UI面板
pygame.draw.rect(screen,(240,240,240),(10,10,300,160))
#显示标题
title=title_font.render("反应式机器人控制系统",True,BLACK)
screen.blit(title,(20,20))
#显示当前行为
behavior_text=font.render(f"当前行为:{robot.current_behavior}",True,
BEHAVIOR_COLORS.get(robot.current_behavior,BLACK))
screen.blit(behavior_text,(20,60))
#显示清洁进度
cleaning_text=font.render(f"清洁进度:{robot.cleaning_progress:.1f}%",True,BLACK)
screen.blit(cleaning_text,(20,85))
#显示行为统计
if robot.behavior_history:
behavior_stats={}
for behavior in robot.behavior_history:
behavior_statsbehavior=behavior_stats.get(behavior,0)+1
y_pos=110
for behavior,count in behavior_stats.items():
percentage=(count/len(robot.behavior_history))*100
stat_text=font.render(f"{behavior}:{percentage:.1f}%", True,BEHAVIOR_COLORS.get(behavior,BLACK))
screen.blit(stat_text,(20,y_pos))
y_pos+=20
#显示操作说明
instructions=[
"R:重置机器人位置和状态",
"N:生成新障碍物",
"ESC:退出模拟"
]
for i,text in enumerate(instructions):
text_surface=font.render(text,True,BLACK)
screen.blit(text_surface,(WIDTH-250,20+i*25))
#显示行为颜色图例
legend_title=font.render("行为颜色说明:",True,BLACK)
screen.blit(legend_title,(WIDTH-250,100))
y_offset=125
for behavior,color in BEHAVIOR_COLORS.items():
pygame.draw.rect(screen,color,(WIDTH-250,y_offset,15,15))
legend_text=font.render(behavior,True,BLACK)
screen.blit(legend_text,(WIDTH-230,y_offset))
y_offset+=25
#更新显示
pygame.display.flip()
clock.tick(60)
#退出pygame
pygame.quit()
sys.exit()
if__name__=="main":
main()
在这段代码中,我们首先将之前定义的LangGraph反应式智能体(通过import lg导入,即lg.graph)深度融入PyGame模拟环境,使其成为机器人的"决策大脑"。该智能体并非独立运行,而是被封装在Robot类中,作为行为决策的核心逻辑载体------它承接了此前通过Prompt模板固化的"紧急避障、预防避障、沿墙跟随、随机探索"优先级规则,将抽象的"条件−动作"逻辑转化为模拟环境中可执行的速度、转向指令,成为连接传感器数据与机器人动作的关键桥梁。不同于传统硬编码决策,LangGraph智能体的注入让机器人的行为规则可通过Prompt灵活调整,无需重构模拟代码,极大提升了系统的扩展性和调试效率。
可以看到LangGraph智能体在模拟中承担着"环境判断−行为选择"的核心职责,其作用机制高效且贴合反应式架构特性。首先,它并非时刻调用,而是采用"按需激活"策略:当机器人通过传感器检测到障碍物(min_reading≤obstacle_threshold)时才启动,无障碍物时机器人默认执行"直线运动",避免不必要的计算消耗。其次,它对传感器数据进行精准处理------将7个传感器按"左、前、右"分组,计算各组的最小距离(min_left/min_front/min_right)和平均距离(avg_left/avg_right),这些结构化数据与智能体的输入格式完全对齐,无需额外转换即可直接传入graph.invoke(sensor_data)。接着,智能体按预设优先级规则快速决策,返回包含speed(速度)、angle(转向角度)、current_behavior(当前行为)的结构化结果,机器人直接应用该结果更新运动状态。此外,智能体还具备容错机制:若调用失败,会自动触发备用避障策略(前方障碍时转向更开阔侧),确保模拟不中断,提升了系统的鲁棒性。反应式智能体仿真演示示意如图3-7所示。
演示代码整体可分为"基础配置、核心类定义、主循环运行"三大模块,逻辑清晰且各司其职。基础配置部分通过PyGame初始化设置屏幕尺寸、颜色定义、行为−颜色映射等,为模拟提供可视化基础;同时导入lg模块关联LangGraph智能体,确保决策逻辑的复用。核心类包括Robot和Obstacle:Robot类是整个系统的核心,封装了机器人的物理属性(位置、半径、角度、速度)、传感器功能(get_sensor_readings获取障碍物距离)、决策逻辑(reactive_control调用智能体)、状态更新(update处理移动、碰撞、轨迹记录)和绘制方法(draw渲染机器人、传感器、方向指示器);Obstacle类则负责生成随机位置和尺寸的障碍物,为机器人提供真实的环境交互场景。主循环(main函数)则串联起所有模块,处理用户交互事件、更新机器人状态、绘制所有可视化元素(机器人、障碍物、轨迹、UI面板),确保模拟的实时运行。
这段代码的核心价值在于实现了"反应式架构理论→LangGraph智能体→PyGame可视化模拟"的完整落地。它没有停留在抽象的规则定义或孤立的代码片段,而是构建了一个可交互、可观察的闭环系统:LangGraph智能体保证了决策逻辑的灵活性和规范性,PyGame模拟提供了真实的环境交互场景,二者结合让"感知−决策−动作"的反应式循环变得直观可见。

图3-7 反应式智能体仿真演示
对于学习者而言,该代码可直观理解反应式智能体的优先级抑制、即时响应特性;对于开发者而言,它提供了"规则定义→智能体封装→模拟测试"的完整开发流程,后续可通过修改LangGraph的Prompt模板新增行为(如"回充导航"),或调整传感器参数、障碍物数量优化模拟场景,无需大幅改动核心框架,兼具学习价值和实用价值。
