在这个快速入门 demo 中,我们将会使用 langGraph 构建一个基本的对话机器人和可是使用网络搜索的机器人。通过这个 demo 我们来快速对 langgraph 有一定感知。
概念补充
顾名思义langGraph是基于图(Graph Theory)的,如果你学过图论那么你一定很熟悉,没学过也没关系,只需要能看懂下面的简单的示意图即可。
-
Node(a.k.a. vertex ): 图上的顶点
-
Edge: 图上点与点之间的关系
-
State: Nodes 通过在 Edge 上传递 State 来通讯。
-
Graph: 图内容的总和
开发前准备
llm
这个 demo 我们使用豆包的 function call 优化的模型,同时它也兼容 openAI 的 api,开发上更便利。如果使用其他家 AI,你需要确定模型是否支持 function call。
创建 api key
创建推理点
模型名称 ep-xxxxx
依赖安装
pip install -U langgraph langchain-openai
配置 api key
import os
# 配置你的api key
os.environ["OPENAI_API_KEY"] = os.environ.get("DOUBAO_API_KEY", "your api key")
创建 llm 对象
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="your id", # 你的推理点id,ep-开头
base_url="https://ark.cn-beijing.volces.com/api/v3",
)
llm.invoke("你好")
AIMessage(content='你好!有什么我可以帮助你的吗?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 10, 'total_tokens': 21, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'doubao-pro-32k-functioncall-240815', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-946e93a3-b8fd-4cc9-aaf4-12489983e79c-0', usage_metadata={'input_tokens': 10, 'output_tokens': 11, 'total_tokens': 21, 'input_token_details': {}, 'output_token_details': {}})
构建一个简单对话机器人(chat bot)
我们将构建一个简单的对话机器人,通过这个样例来熟悉最基本的概念。
graph 构建
首先我们从创建一个Stategraph
开始,我们使用 StateGraph 来定义 chat bot 的结构,它就相当于我们 bot 的状态机(state machine)。我们添加 nodes 来表示增加 llm 或者功能调用,通过添加 edges 来指定 node 之间跳转。
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class State(TypedDict): # 继承TypedDict就可以用dict表示了State了
# 还记得上吗怎么介绍State的么?对,它是传递信息的载体。
#
# 这里我们定一个list类型State
# 并为它添加元数据add_messages function,框架将通过这个function来reduce数据,
# add_messages作用是将多个message list合并为一个
messages: Annotated[list, add_messages]
# 定义graph
graph_builder = StateGraph(State)
# 使用一个function在node运行时调用
def chatbot(state: State):
# 返回一个State dict
return {"messages": [llm.invoke(state["messages"])]}
# 添加node
# 第一个参数为node的名称,第二个参数为node被使用时调用的方法
graph_builder.add_node("chatbot", chatbot)
# 添加一个入口
graph_builder.add_edge(START, "chatbot")
# 添加一个出口,任何node都可以指向出口
graph_builder.add_edge("chatbot", END)
# 最后如果想让我们的graph运行,我们需要将graph进行编译。它将返回一个compiledGraph
graph = graph_builder.compile()
注意 ⚠️: State 可以是 dytanic 也可以是 TypeDict,推荐继承 TypeDict,这样我们直接返回 dict 就可以,类型,编程会更加方便。
可视化 graph
langgraph 提供了很多可视化输出的工具,可以是 ascii 也可以是 png。
from IPython.display import Image, display
display(Image(graph.get_graph().draw_mermaid_png()))
运行
终于可以运行试一试了,graph 是继承了 runnable 的。这意味着我们可以,使用同步、异步的 invoke、stream 等操作。
需要注意的是 langgraph 的 stream 和 langchain 的 stream 并不等同。
langgraph 的 stream 每次 yield 产生的是一个 event,而 langchain 产生的是一个 message chunk。如果不了解 runnable,可以看一眼我之前的 langchain 的文章。
for event in graph.stream({"messages": [("user", "你好")]}):
print(event)
{'chatbot': {'messages': [AIMessage(content='你好!有什么我可以帮助你的吗?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 10, 'total_tokens': 21, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'doubao-pro-32k-functioncall-240815', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-16bda42e-7ca1-4cab-a8d9-6597c5987259-0', usage_metadata={'input_tokens': 10, 'output_tokens': 11, 'total_tokens': 21, 'input_token_details': {}, 'output_token_details': {}})]}}
恭喜 🎉 我们的第一个 langgraph 应用运行起来了。后续我们将为我们的 bot 增加更多功能。
让 bot 具有使用工具能力
为了让 bot 回答原始训练数据并未包含的问题,我们将 web search 工具内嵌到 bot 中。
可以使用魔法以及英语英语场景较多的朋友,可以试试 tavily。中文环境没有太好的免费AI搜索API,我这使用coze平台的插件,它可以使用 bing 搜索、百度搜索等,单需要自己封装一下。两种方式我都会给出 demo。
tavily
tavily 是专为 LLMs 设计的 AI 搜索工具,每月有 1000 次免费搜索次数,官网。
langchain 体系对 tavily 有着较好的支持,我们可以之间引入对应 sdk。在注册账号后需要配置 API KEY。
pip install -U tavily-python langchain_community
from langchain_community.tools.tavily_search import TavilySearchResults
os.environ["TAVILY_API_KEY"] = os.environ["TAVILY_API_KEY"] or "your api key"
tool = TavilySearchResults(max_results=2)
tool.invoke("北京值得逛的景点?")
[{'url': 'https://www.visitbeijing.com.cn/article/4JN1YW7zzzc',
'content': '如果你第一来北京旅游,不知道去哪儿里玩,这篇旅游攻略将为你提供最全面的指南,帮助你了解北京最值得去的20大景点。从故宫博物院到长城,从颐和园到天坛公园,这些景点将带你领略北京的历史、文化和风光。让我们开始这次北京之旅吧! 01故宫'},
{'url': 'https://zhuanlan.zhihu.com/p/656674369',
'content': '假期周末闲暇时间总要来场说走就走的旅行吧!有机会要来趟北京游玩吧!北京好玩的地方有哪些值得打卡的标志性景点呢?今天整理了10个北京值得逛的热门景点合集,附五日游路线攻略! 1、颐和园(5a) 门票:30 时间…'}]
coze
coze 是字节旗下低代码 AI 平台,官网。coze 本身也非常好用,提供一定的可视化 agent 开发能力,非常适合有一定动手能力的个人,但是如果希望开发用于生产的 agent,我们最好还是基于代码来进行开发。这里我们使用 coze 提供的工具来进行搜索。
这是我在 coze 上构建的一个 bing 搜索的工作流。
coze api 的使用方链接🔗。
我们可以定义一个 python 的 function 来作为工具。
import httpx
import json
async def search_web(
query: Annotated[str, "The query to search for"], count: int = 2
) -> list:
"""使用搜索引擎获取资料"""
headers = {
"Authorization": f"Bearer {os.environ.get("COZE_KEY", "your api key")}",
"Content-Type": "application/json",
"Accept": "*/*",
"Host": "api.coze.cn",
"Connection": "keep-alive",
}
data = {
"workflow_id": "your workflow_id",# 使用你自己的workflow
"parameters": {"query": query, "count": count},
}
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.coze.cn/v1/workflow/run", headers=headers, json=data
)
response.raise_for_status()
data = response.json()
data = json.loads(data["data"])["data"]
return json.loads(data)
await search_web("北京值得逛的景点?")
['北京最值得去的12个地方,你去过几个?认为值得吗?-手机网易网\n天安门广场天安门广场位于北京市中心,可容纳100万人举行盛大集会,不仅是中国的政治和文化象征,也是许多重要历史事件的发生地,广场上的主要建筑和景点包括:天安门城楼、人民英雄纪念碑、人民大会堂、中国国家博物馆、毛主席纪念堂。八达岭长城八达岭长城是万里长城的一部分,古称“居庸关之险不在关而在八达岭”,八达岭是明长城的一个隘口,为居庸关的重要前哨,八达岭长城是明长城居庸关八景之一,全国十大风景名胜之首,世界新七大奇迹之首。天坛公园天坛,是明清时期皇帝祭天、祈谷、和祈雨的场所,作为中国现存最大的祭天建筑群,天坛承载着丰富的历史和文化内涵。天坛不仅是一座建筑,更是中华民族文化传承的重要载体,正所谓冬至祭天,孟春祈谷,孟夏祭雨,都是我们需要从内心深处去传承的文化苦旅。4.故宫博物院 推开一扇窗,两朝三世六百年,故宫博物馆作为中国历史文化遗产的重要代表之一,经历了数百年的历史变迁,是在明清皇宫及其收藏基础上建立起来的大型综合性博物馆,也是中国最大的古代文化艺术博物馆,是中国现存规模最大、保存最完整的古代宫殿建筑群。5.圆明园 中国清代大型皇家园林,不仅汇集了江南若干名园胜景,还移植了西方园林建筑,集当时古今中外造园艺术之大成,堪称人类文化的宝库之一,是当时世界上最大的一座博物馆。圆明园遗址所承载的内涵是任何东西都无法替代的,具有极其特殊的历史文化价值,既是中华文明无比辉煌的象征,又是中华民族近代屈辱的纪念遗址,更是值得全人类深刻检讨和反思的文化遗存。6.什刹海 什刹海是中国级别的重点文物保护单位,也是国家AAAA级旅游景区,这里保存着大量的文物和历史建筑,同时也是京津冀地区最具文化和历史价值的景区之一,作为老北京胡同文化的重要代表,什刹海保留了许多传统建筑和街巷,展现了老北京人民的生活方式和生活习惯,它是感受北京传统文化和探索胡同历史的绝佳场所,游客可以在这里感受到浓厚的历史氛围和传统文化的魅力。\nlink:http://m.163.com/dy/article/JBLMIG1U05567KVH.html\n',
'北京有哪些值得去玩的地方?推荐10大旅游景点,让你一次玩个痛快\n1、故宫博物院、2、天安门广场、3、长城、4、颐和园、5、圆明园:圆明园是清代的皇家园林,被誉为“万园之园”,6、北京欢乐谷、7、北京鸟巢和水立方、8、798艺术区:79...北京有哪些值得去玩的地方?推荐10大旅游景点,让你一次玩个痛快 北京有哪些值得去玩的地方?北京值得去的地方有:1、故宫博物院、 2、天安门广场、 3、长城、 4、颐和园、 5、圆明园:圆明园是清代的皇家园林,被誉为“万园之园”,6、北京欢乐谷、 7、北京鸟巢和水立方、 8、798艺术区:798艺术区是北京的一个当代艺术中心,9、南锣鼓巷:南锣鼓巷是北京的一条传统胡同,保存了很多传统的建筑和文化元素,10、北海公园:保存最完整的皇家园林之一。说到玩,咱们得直击重点:北京,这地儿,有哪些让你一去就舍不得回来的好玩之处?甭急,我这就给你细细道来。先得说说那故宫。你听说过紫禁城没?对,就是那儿!走进去,满眼都是金碧辉煌,仿佛穿越回了明清时代。那红墙黄瓦,简直就是古装的“大片现场”。还有那珍宝馆,哇塞,里面的宝贝看得你眼花缭乱。什么玉如意、金麒麟,简直比电影里的还要炫!接下来,咱得到天安门广场逛逛。站在这儿,你就站在了中国的“心脏”。每天早上看升旗仪式,那场面,比任何电影都要震撼人心。还有毛主席纪念堂,去瞻仰一下伟人的风采,也是一种别样的体验。长城呢?咱不能忘了长城!这可是咱们中国的骄傲。去爬一次长城,感受一下那种“不到长城非好汉”的豪情。站在长城上,放眼望去,山川壮美,心里那个美呀,简直无法用语言形容。颐和园也值得一去。那里面的湖光山色、亭台楼阁,简直就是一幅活生生的中国画卷。特别是那十七孔桥和昆明湖,简直就是摄影爱好者的天堂。还有圆明园遗址公园。虽然历史给它留下了些许遗憾,但正是这种残缺美,更让人感慨万分。\nlink:http://m.163.com/dy/article/IU8GCHRG0552M20P.html\n']
```
### 再次构建 graph
```python
from typing import Annotated
from langgraph.prebuilt import ToolNode
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
# 注意,这里一定要将llm和tool绑定,赋予llm工具调用能力
llm_with_tools = llm.bind_tools([search_web])
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
# 增加route节点
# 判断如果最新message含有tool_calls,那么将指向tool node
async def route(state: State):
if getattr(state["messages"][-1],"tool_calls"):
return "toolnode"
else:
return END
# 这里使用官方提供的ToolNode,主要封装了state内容提取,返回封装为ToolMessage等功能
tool_node = ToolNode([search_web])
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("toolnode", tool_node)
graph_builder.add_edge(START, "chatbot")
# tools调用完之后回到chatbot node
graph_builder.add_edge("toolnode", "chatbot")
graph_builder.add_conditional_edges("chatbot", route)
graph = graph_builder.compile()
# 打印出来看看
display(Image(graph.get_graph().draw_mermaid_png()))
这种结构是最基础、最常见的 agent,实际上如果只是这种结构,我们并不需要自己写,langgraph 提供了封装,使用langgraph.prebuilt.create_react_agent
即可创建 graph。
异步运行
这里使用 astream 会产生 AsyncIterator,我们可以异步运行。让我们看看每每个 node 的运行结果。
async for event in graph.astream({"messages": [("user", "查一下北京的天气")]}):
print(event)
{'chatbot': {'messages': [AIMessage(content='\n', additional_kwargs={'tool_calls': [{'id': 'call_heh7r4f5btcv486jxcu2wqx8', 'function': {'arguments': '{"query": "北京的天气"}', 'name': 'search_web'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 24, 'prompt_tokens': 78, 'total_tokens': 102, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'doubao-pro-32k-functioncall-240815', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-932e6a29-f804-4ccd-9cb8-c3728632685e-0', tool_calls=[{'name': 'search_web', 'args': {'query': '北京的天气'}, 'id': 'call_heh7r4f5btcv486jxcu2wqx8', 'type': 'tool_call'}], usage_metadata={'input_tokens': 78, 'output_tokens': 24, 'total_tokens': 102, 'input_token_details': {}, 'output_token_details': {}})]}}
{'toolnode': {'messages': [ToolMessage(content='["【北京天气预报15天_北京天气预报15天查询】-中国天气网\\n15天预报 北京 昨天 16日 阴 18° 阴 14° <3级 <3级 今天 17日 小雨 18° 小雨 14° <3级 <3级 周五 18日 小雨 18° 晴 5° 3-4级 3-4级 周六 19日 晴 12° 多云 3° <3级 <3级 周日 20日 小雨 10° 多云 3° <3级 <3级 周一 21日 多云 15° 晴 6° <3级 <3级 周二 22日 晴 17° 晴 5° <3级 <3级 周三 23日 晴 19° 晴 6° <3级 <3级 周四 24日 晴 19° 晴 8° <3级 <3级 周五 25日 晴 18° 晴 7° <3级 <3级 周六 26日 晴 18° 阴 9° <3级 <3级 周日 27日 阴 15° 雨 8° <3级 <3级 周一 28日 雨 15° 晴 7° <3级 <3级 周二 29日 阴 14° 阴 8° <3级 <3级 周三 30日 雨 10° 雨 8° <3级 <3级 周四 31日 雨 10° 雨 8° <3级 <3级 昨天 16日 今天 17日 周五 18日 周六 19日 周日 20日 周一 21日 周二 22日 周三 23日 周四 24日 周五 25日 周六 26日 周日 27日 周一 28日 周二 29日 周三 30日 周四 31日 <3级 <3级 3-4级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级\\nlink:https://m.weather.com.cn/mweather15d/999999999.shtml\\n", "翻出厚衣服!北京降雨大风和强降温天气将至 雨后最低气温仅有3℃\\n中国天气网讯 受冷空气影响,10月17日至20日,北京市将先后出现降雨、大风和强降温天气。预计明显降雨时段在17日傍晚至18日早晨,雨后大风降温将至,降温后20日最低气温仅有3℃左右,需及时添衣保暖。\\n降雨预报:预计17日下午至18日下午,北京自西向东将出现中雨天气,西部、北部局地中到大雨,高海拔山区可能出现雨夹雪。明显降雨时段在17日傍晚至18日早晨,18日下午降雨逐渐结束。\\n大风预报:18日下午至夜间,北京将有3、4级偏北风,阵风6、7级,山区局地可达8级。19日早晨风力减弱。\\n降温预报:18日夜间开始,北京市气温明显下降,气温降幅达8℃至10℃。预计20日白天,北京最高气温将降至10℃左右,夜间最低气温降至3℃左右,山区最低气温降至0℃以下。21日起气温缓慢回升。\\n中国天气网提醒,雨后道路湿滑、能见度下降,建议公众出行提前关注路况信息,特别是17日晚高峰和18日早高峰期间,提前安排出行时间,行车需减速慢行,注意交通安全。此外,雨后北风较大,外出时需注意防风,远离临时搭建设施和高大树木。气温较前期明显下降,18日夜间至20日气温偏低、体感偏冷,需及时添衣保暖,山区户外活动建议配备棉衣或羽绒服等御寒衣物,谨防感冒和心脑血管疾病。\\nlink:https://m.weather.com.cn/news/2024/10/3924847.shtml\\n"]', name='search_web', id='e56ccd74-c134-4a76-8c86-dddfabdb45fa', tool_call_id='call_heh7r4f5btcv486jxcu2wqx8')]}}
{'chatbot': {'messages': [AIMessage(content='北京的天气情况如下:\n|日期|天气状况|最高温度|最低温度|风力等级|\n|---|---|---|---|---|\n|10月17日|小雨|18℃|14℃|<3级|\n|10月18日|小雨转晴|18℃|5℃|3-4级转<3级|\n|10月19日|晴|12℃|3℃|<3级|\n|10月20日|小雨转多云|10℃|3℃|<3级|\n|10月21日|多云转晴|15℃|6℃|<3级|\n|10月22日|晴|17℃|5℃|<3级|\n|10月23日|晴|19℃|6℃|<3级|\n|10月24日|晴|19℃|8℃|<3级|\n|10月25日|晴|18℃|7℃|<3级|\n|10月26日|晴转多云|18℃|9℃|<3级|\n|10月27日|多云|15℃|8℃|<3级|\n|10月28日|晴|18℃|8℃|<3级|\n|10月29日|多云|14℃|8℃|<3级|\n|10月30日|小雨|10℃|8℃|<3级|\n|10月31日|小雨|10℃|8℃|<3级|', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 359, 'prompt_tokens': 1130, 'total_tokens': 1489, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'doubao-pro-32k-functioncall-240815', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-54a61e28-1be2-400a-9d58-26e659215a39-0', usage_metadata={'input_tokens': 1130, 'output_tokens': 359, 'total_tokens': 1489, 'input_token_details': {}, 'output_token_details': {}})]}}
总结 🎉
这次我们通过,两个比较基础的 chatbot 对 langgraph 有了基础的了解,之后我会对一些核心概念进行比较详细的解释,,最后我们会通过构建一个生产级别的 agent来对整个langgraph框架一窥门径。