langgraph入门
使用langgraph框架搭建一个简易agent。
最近想学习一下agent相关知识,langgraph似乎挺好的,于是就来试一试。langgraph。看了官网,起核心思想是将agent中的角色和工具都当作是图的Node,整个agent流程通过增加Node之间的边来设定。
官网用的是claude的api,我这里用的OPENAI的api。
import json
import operator
from typing import TypedDict, Annotated, Sequence
from langchain_core.messages import BaseMessage
from langchain.tools.render import format_tool_to_openai_function
from langgraph.prebuilt import ToolExecutor,ToolInvocation
from langchain_core.messages import FunctionMessage
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage
from langchain_core.tools import toolimport os
from langchain.chat_models import ChatOpenAI# 用于创建一个LLM大模型对象, .1版本langchain的调用方法
from langchain.schema import HumanMessage# 用于区别是user发的消息
os.environ['OPENAI_API_KEY'] = "sk-....."
model_name="gpt-3.5-turbo"
model = ChatOpenAI(model_name=model_name,temperature=0)# 自定义工具
# @tool
# def search(query: str) -> str:
# """Look up things online."""
# print(f"search: {query}")
# return "sunny"@tool
def search(query: str):"""Call to surf the web."""# This is a placeholder, but don't tell the LLM that...if "sf" in query.lower() or "san francisco" in query.lower():return "It's 60 degrees and foggy."return "It's 90 degrees and sunny."@tool
def multiply(a: int, b: int) -> int:"""Multiply two numbers."""return a * b tools = [search,multiply]tool_executor = ToolExecutor(tools)# 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)functions = [format_tool_to_openai_function(t) for t in tools]
model = model.bind_functions(functions)class AgentState(TypedDict):messages: Annotated[Sequence[BaseMessage], operator.add]# 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 finishif "function_call" not in last_message.additional_kwargs:return "end"# Otherwise if there is, we continueelse: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 listreturn {"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 calllast_message = messages[-1]# We construct an ToolInvocation from the function_callaction = 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 responseresponse = tool_executor.invoke(action)# print(f"response:{response}")# We use the response to create a FunctionMessagefunction_message = FunctionMessage(content=str(response), name=action.tool)# print(f"function_message:{function_message}")# We return a list, because this will get added to the existing listreturn {"messages": [function_message]} # 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() #inputs = {"messages": [HumanMessage(content="what is the weather in Beijing?")]}
# inputs = {"messages": [HumanMessage(content="3乘以5等于多少,输出最终的结果")]}
response = app.invoke(# {"messages": [HumanMessage(content="3乘以5等于多少,输出最终的结果")]},{"messages": [HumanMessage(content="what is the weather in sf")]},config={"configurable": {"thread_id": 42}}
)
# print(type(response))
# print(f"last result:{response}")
# 输出如下信息:
# {'messages': [HumanMessage(content='3乘以5等于多少'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\n "a": 3,\n "b": 5\n}', 'name': 'multiply'}}, response_metadata={'finish_reason': 'function_call'}, id='run-bbf18160-747f-48ac-9a81-6c1ee3b70b07-0'), FunctionMessage(content='15', name='multiply'), AIMessage(content='3乘以5等于15。', response_metadata={'finish_reason': 'stop'}, id='run-0d1403cf-4ddb-4db2-8cfa-d0965666e62d-0')]}
print(response['messages'][-1].content)
输出结果为
The weather in San Francisco is currently 60 degrees and foggy.
整体的代码分为,调用模型call_model,调用工具call_tool,定义工具,定义终止条件,以及定义workflow。
这里主要是这个workflow,workflow = StateGraph(AgentState)
,langgraph里面核心的概念是state
,这个state代表着整个环境的变量,message,状态信息,我认为是理解为一种agent的信息中心,每个agent要采取下一步的动作要根据这个信息中心来决定下一步的动作。
Limiation
1、这个只是一个很简单的agent框架,那么对于论文中那种复杂的包含rag等组件的agent,该如何使用langgraph进行搭建?
2、这里是用的是gpt的接口,如果要使用本地模型呢?如何把本地模型接入到langgraph框架里面?
相关文章:
langgraph入门
使用langgraph框架搭建一个简易agent。 最近想学习一下agent相关知识,langgraph似乎挺好的,于是就来试一试。langgraph。看了官网,起核心思想是将agent中的角色和工具都当作是图的Node,整个agent流程通过增加Node之间的边来设定。…...

【Python】爬虫程序打包成exe
上一篇写了爬虫获取汽车之家配置表,师父要更方便使用甚至推广(?),反正就是他们没有环境也能用嘛,我就直接打包了,界面不会做也懒得学了、、 1、下载pyinstaller(清华镜像)…...

【力扣专题栏】两两交换链表中的节点,如何实现链表中两两相邻节点的交换?
这里写目录标题 1、题目描述解释2、算法原理解析3、代码编写 1、题目描述解释 2、算法原理解析 3、代码编写 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int…...
埋点采集的日志数据常见的格式简介
埋点采集的日志数据通常以结构化或半结构化的格式进行记录,以便于分析和处理。常见的格式包括: 1. JSON(JavaScript Object Notation) 特点:JSON 格式是一种轻量级的数据交换格式,具有良好的可读性和兼容…...

基于SSM高考志愿辅助填报系统设计与实现
前言 近年来,由于计算机技术和互联网技术的飞速发展,所以各企事业单位内部的发展趋势是数字化、信息化、无纸化,随着这一趋势,而各种决策系统、辅助系统也就应运而生了,其中,信息管理系统是其中重要的组成…...

elasticsearch 8.x 插件安装(六)之Hanlp插件
elasticsearch 8.x 插件安装(六)之Hanlp插件 elasticsearch插件安装合集 elasticsearch插件安装(一)之ik分词器安装(含MySQL更新) elasticsearch 8.x插件(二)之同义词安装如何解决…...
排序算法简记
列举几种基本的排序算法和排序思想 排序就是将一组对象按照某种逻辑顺序重新排列的过程。 一、选择排序 1、基本原理 最基本的排序,每次都从原有数据中选择最小或最大的数组放入新数据集中 2、步骤(以从小到大为例) 首先, 找到数组中最小的那个元素…...
Stable diffusion inference 多卡并行
stable diffusion 推理过程 多卡并行 注意事项 以SDXL为例,指定GPU,添加device_map参数信息 device_map {add_embedding: 1,decoder: 1,encoder: 1,conv_in: 1,conv_out: 1,post_quant_conv: 1,text_model: 6,conv_norm_out: 1,quant_conv: 1,time_em…...

Docker:namespace环境隔离 CGroup资源控制
Docker:namespace环境隔离 & CGroup资源控制 Docker虚拟机容器 namespace相关命令ddmkfsdfmountunshare 进程隔离文件隔离 CGroup相关命令pidstatstresscgroup控制 内存控制CPU控制 Docker 在开发中,经常会遇到环境问题,比如程序依赖某个…...

鼠标增强工具 MousePlus v5.3.9.0 中文绿色版
MousePlus 是一款功能强大的鼠标增强工具,它可以帮助用户提高鼠标操作效率和精准度。该软件可以自定义鼠标的各种功能和行为,让用户根据自己的习惯和需求来调整鼠标的表现。 详细功能 自定义鼠标按钮功能:可以为鼠标的各个按钮设置不同的功能…...

Android 圆形进度条CircleProgressView 基础版
一个最基础的自定义View 圆形进度条,可设置背景色、进度条颜色(渐变色)下载进度控制;可二次定制度高; 核心代码: Overrideprotected void onDraw(NonNull Canvas canvas) {super.onDraw(canvas);int mW g…...

理解磁盘结构---CHS---LAB---文件系统
1,初步了解磁盘 机械磁盘是计算机中唯的一个机械设备, 特点是慢,容量大,价格便宜。 磁盘上面的光面,由数不清的小磁铁构成,我们知道磁铁是有n/s极的,这刚好与二进制的&…...
我在1024谈华为
华为的发展历程与技术创新 华为自成立以来,一直是通信技术领域的重要参与者。让我们回顾一下华为的一些关键发展里程碑: 1987年,华为在深圳成立,起初专注于电话交换网络的研发和销售。 进入1990年代,华为转型为通信…...

NVR小程序接入平台/设备EasyNVR多品牌NVR管理工具/设备视频监控解决方案
随着科技的飞速发展,安防视频监控已成为维护公共安全、提升管理效率的重要手段。在这一领域中,NVR小程序接入平台/设备EasyNVR凭借其灵活的部署方式、强大的功能以及卓越的性能表现,脱颖而出,引领着安防视频监控的新纪元。 NVR小程…...
二叉树前序遍历的 Java 实现,包括递归和非递归两种方式
二叉树前序遍历是一种遍历树节点的方式,遵循特定的顺序。其基本过程可以总结为以下几个步骤: 前序遍历的顺序 访问根节点:首先处理当前节点。 递归遍历左子树:然后依次访问左子树。 递归遍历右子树:最后访问右子树。 …...
QT开发:构建现代UI的利器:深入详解QML和Qt Quick基础开发技术
目录 引言 目录 1. 什么是QML和Qt Quick QML的优势 2. QML基础语法 组件 属性 JavaScript表达式 3. 数据绑定 直接绑定 双向绑定 4. 属性和属性别名 属性 属性别名 5. 信号与槽机制 定义信号 处理信号 6. 动画与过渡效果 简单动画 过渡效果 7. 构…...

vue前端使用pdfjs与pdfdist-mergeofd 实现预览pdf并翻页,同时解决预览pdf显示模糊的问题
vue前端使用pdfjs与pdfdist-mergeofd 实现预览pdf并翻页,同时解决预览pdf显示模糊的问题 插件介绍 pdfdist-mergeofd插件的作用可查看这篇文章,同时使用ofdjs和pdfjs遇到的问题,和解决方法——懒加载 该插件主要是为了解决pdfjs和ofdjs同时…...
C语言——回调函数
1、回调函数 在学习了函数之后,我发现了一个比较难的函数——回调函数 回调函数 (Callback Function) 指的是一种函数,它被作为参数传递给另一个函数,并在满足特定条件或事件发生后被调用执行。 它允许你将一段代码延迟执行,或者…...

2016年ATom-1飞行活动期间以10秒间隔进行的一氧化碳(CO)观测数据
目录 简介 摘要 代码 引用 网址推荐 知识星球 机器学习 ATom: Observed and GEOS-5 Simulated CO Concentrations with Tagged Tracers for ATom-1 简介 该数据集包含2016年ATom-1飞行活动期间以10秒间隔进行的一氧化碳(CO)观测数据,…...

MLM之Emu3:Emu3(仅需下一个Token预测)的简介、安装和使用方法、案例应用之详细攻略
MLM之Emu3:Emu3(仅需下一个Token预测)的简介、安装和使用方法、案例应用之详细攻略 导读:这篇论文介绍了Emu3,一个基于单一Transformer架构,仅使用下一个token预测进行训练的多模态模型。 >> 背景痛点: 多模态任…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...