关注我~第一时间学习如何更好地使用AI。
重要的不是我们是否会被AI替代,
而是我们要比被替代的人更懂AI。
前期导览:
从零开始学LangGraph(1):Chat Model --- 如何通过代码与模型对话(上)
从零开始学LangGraph(2):Message和Template —— 如何通过代码与模型对话(下)
从零开始学LangGraph(3):Tools --- 如何实现大模型与外部系统对接
从零开始学LangGraph(4):用Excel表格来理解LangGraph的基本工作原理
从零开始学LangGraph(5):手把手教你搭建简易Graph
从零开始学LangGraph(6):轻松玩转Conditional Edges
从零开始学LangGraph(7):手把手教你手搓带Memory的Chat Bot
从零开始学LangGraph(8):Tools + Subgraph实战,手把手教你构建ReAct Agent
从零开始学LangGraph(9):详解State的定义、更新与管理
番外:Deep Agent入门,60行代码实现网络检索小助手
大家好,上上期我们拓展补充了关于State的一些重要知识点,从定义、更新、管理三个角度丰富了State的可玩性。今天,我们学习一个实用知识点,即自定义Runtime Configuration 。
为什么需要自定义Runtime Configuration
在实际应用中,我们经常会遇到这样的需求:同一个Graph,在不同的调用场景下,需要使用不同的配置参数 。
比如:
- • 有时候我们希望Graph使用Claude模型,有时候希望使用GPT模型
- • 有时候我们希望给模型设置不同的System Prompt,以适应不同的任务场景
- • 有时候我们希望根据不同的用户或环境,动态调整Graph的行为参数
如果每次都要重新定义Graph或者修改代码,那显然太麻烦了。这时候,Runtime Configuration(运行时配置) 就派上用场了。
Runtime Configuration允许我们在调用Graph时 (而不是在定义Graph时)传入配置参数,让同一个Graph能够根据不同的配置表现出不同的行为。这样,我们就可以在不修改Graph定义的情况下,灵活地控制Graph的运行方式。
Runtime Configuration的基本设置方法
增加运行时设置的基本步骤包括:
- • 1.指定一个关于运行时设置配置的schema;
- • 2.将这个schema写入节点函数或者条件边的路由函数中;
- • 3.在搭建Graph与state一起传入StateGraph()。
下面我们结合官方示例代码来看看具体的操作过程。
1.定义ContexSchema
from langgraph.graph import END, StateGraph, START
from typing\_extensions import TypedDict
# 1. Specify config schema
class ContextSchema(TypedDict):
my\_runtime\_value: str
# 2. Define a graph that accesses the config in a node
class State(TypedDict):
my\_state\_value: str
首先,除了老朋友State,我们再定义一个ContextSchema,它负责定义运行时配置的结构,告诉LangGraph,我们在调用Graph时可以传入哪些配置参数,比如这里我们定义了一个my\_runtime\_value字段,类型为str。
注意,对于ContextSchema定义的配置内容,节点可以读取,但不能直接修改。
2.在节点函数中配置runtime参数
接下来,我们定义节点函数。
from langgraph.runtime import Runtime
def node(state: State, runtime: Runtime[ContextSchema]):
if runtime.context["my\_runtime\_value"] == "a":
return {"my\_state\_value": 1}
elif runtime.context["my\_runtime\_value"] == "b":
return {"my\_state\_value": 2}
else:
raise ValueError("Unknown values.")
首先,我们需要从langgraph.runtime中导入Runtime类型。由于Runtime是一个泛型类型,我们在指定自定义的ContextSchema时,需要使用方括号,即Runtime[ContextSchema]。
然后,在节点函数的参数中添加runtime参数,将它的类型设定为Runtime[ContextSchema]。这样,节点函数就可以访问运行时配置了。
接下来看函数体内部:
首先,我们通过runtime.context["my\_runtime\_value"]来访问配置值。这里runtime.context是一个字典,我们可以通过指定键名来获取对应的配置值。
然后,根据配置值的不同,节点会返回不同的state更新结果。如果传入的是"a",就返回{"my\_state\_value": 1};如果传入的是"b",就返回{"my\_state\_value": 2};如果传入其他值,则抛出异常。
换言之,根据上述代码,我们可以通过观察state值的变化,确定runtime信息是否被读取。
3.向Graph传入contetx_schema
接着,我们来构建Graph:
builder = StateGraph(State, context\_schema=ContextSchema)
builder.add\_node(node)
builder.add\_edge(START, "node")
builder.add\_edge("node", END)
graph = builder.compile()
这里的关键点在于StateGraph的初始化。除了传入State(这是我们之前就熟悉的),我们还需要传入context\_schema=ContextSchema参数。这个参数告诉LangGraph,这个Graph支持运行时配置,并且配置的格式由ContextSchema定义。
接下来的步骤就和之前一样了:添加节点、添加边、编译Graph。
4.Runtime Configuration的使用
然后我们来看看如何在调用Graph时传入Runtime Configuration:
# 3. Pass in configuration at runtime:
print(graph.invoke({}, context={"my\_runtime\_value": "a"}))
print(graph.invoke({}, context={"my\_runtime\_value": "b"}))
在调用graph.invoke()时,除了传入初始state(这里传入的是空字典{}),我们可以通过context参数传入运行时配置。
context参数接收一个字典,字典的键必须与ContextSchema中定义的字段名一致。这里我们传入{"my\_runtime\_value": "a"}或{"my\_runtime\_value": "b"},Graph会根据不同的配置值,按照我们在节点函数中的设定,执行不同的逻辑。
实际效果:
- • 第一次调用
graph.invoke({}, context={"my\_runtime\_value": "a"})时,节点会返回{"my\_state\_value": 1} - • 第二次调用
graph.invoke({}, context={"my\_runtime\_value": "b"})时,节点会返回{"my\_state\_value": 2}
这样,我们就实现了同一个Graph,根据不同的运行时配置,产生不同的行为的效果。
下面我们来看两个比较常用的runtime configuration的应用。
Runtime Configuration的实际应用
1.指定运行时使用的Model
在实际开发中,我们经常会遇到这样的场景:同一个AI Agent,有时候需要使用Claude模型(比如处理复杂推理任务),有时候需要使用GPT模型(比如处理代码生成任务)。如果每次都重新定义Graph,显然太麻烦了。
这时候,我们可以通过事先配置runtime相关schema,指定不同情况下使用的模型,实现在运行时动态切换模型的效果。
鉴于官方示例代码使用了Claude和OpenAI这些国外模型,为了方便大家实践,我做了些调整,把整个代码改为在DeepSeek的chat和reasoner之间切换。
(1)ContextSchema与模型库构建
from dotenv import load\_dotenv
from langchain.chat\_models import init\_chat\_model
from langgraph.graph import MessagesState, END, StateGraph, START
from langgraph.runtime import Runtime
from typing\_extensions import TypedDict
# 导入API Key
load\_dotenv()
classContextSchema(TypedDict):
model\_type: str
MODELS = {
"chat": init\_chat\_model("deepseek-chat", model\_provider="deepseek"),
"reasoner": init\_chat\_model("deepseek-reasoner", model\_provider="deepseek"),
}
模型初始化相关内容不再赘述,不熟悉的同学请复习前期内容。
这里我们首先定义了一个ContextSchema,它包含一个model\_type字段,类型为str。
然后,我们定义一个MODELS字典作为模型库,存储不同提供商对应的模型实例(已通过init\_chat\_model完成初始化)。这样,我们就可以根据配置的提供商名称,快速获取对应的模型。
需要注意的是,这里可以看到我们没有定义state,那是因为我们引用了LangGraph的一个内置组件MessagesState。
(2)模型切换的节点函数定义
接下来,我们定义节点函数,参数传递方面同前文,不再赘述。
def call\_model(state: MessagesState, runtime: Runtime[ContextSchema]):
# 使用 get 方法提供默认值 "chat"
model\_type = (runtime.context or {}).get("model\_type", "chat")
model = MODELS[model\_type]
response = model.invoke(state["messages"])
return {"messages": [response]}
在函数体内,我们使用(runtime.context or {}).get("model\_type", "chat")获取配置的模型类型。这里使用(runtime.context or {})是为了处理runtime.context可能为None的情况,如果为None则使用空字典{}。然后使用get方法的好处是,如果没有提供配置(即context={}或context为None),则会使用默认值"chat"。因为在后面我们invoke Graph的时候,会向context参数传入形如{"model\_type": "chat"}或{"model\_type": "reasoner"}的键值对。
然后,根据获取到的model\_type从MODELS字典中选择对应的模型,调用模型的invoke方法处理消息,并将响应添加到消息列表中返回。
(3)搭建Graph传入context_schema
builder = StateGraph(MessagesState, context\_schema=ContextSchema)
builder.add\_node("model", call\_model)
builder.add\_edge(START, "model")
builder.add\_edge("model", END)
graph = builder.compile()
这些步骤和之前一样,不再赘述。
(4)在graph.invok中指定contetx的参数
这段代码展示了如何使用配置了Runtime Configuration的Graph:
# Usage
input\_message = {"role": "user", "content": "hi"}
# With no configuration, uses default ("chat")
response\_1 = graph.invoke({"messages": [input\_message]}, context={})["messages"][-1]
# Or, can set reasoner
response\_2 = graph.invoke({"messages": [input\_message]}, context={"model\_type": "reasoner"})["messages"][-1]
print(response\_1.response\_metadata["model\_name"])
print(response\_2.response\_metadata["model\_name"])
首先,我们准备一个输入消息。然后,我们进行了两次调用:
第一次调用
:
context={}
,由于我们在节点函数中使用了
(runtime.context or {}).get("model\_type", "chat")
,当配置为空字典时,会使用默认值
"chat"
,所以这次调用会使用DeepSeek的chat模型。
第二次调用
:
context={"model\_type": "reasoner"}
。我们显式地传入配置,指定使用DeepSeek的reasoner模型。
最后,我们打印两次调用的模型名称,可以看到它们确实使用了不同的模型。
- •
response\_1会使用DeepSeek Chat模型处理消息 - •
response\_2会使用DeepSeek Reasoner模型处理消息
这样,我们就实现了同一个Graph,根据运行时配置使用不同的模型 。在实际应用中,你可以根据任务类型、用户偏好、成本考虑等因素,动态选择最合适的模型。
2.指定运行时使用的System Prompt
除了动态切换模型,Runtime Configuration的另一个常见应用场景是动态指定System Prompt 。在实际应用中,我们可能需要根据不同的任务类型、用户角色或业务场景,使用不同的System Prompt来指导模型的行为。
比如,同一个客服Agent,在处理技术问题时需要使用技术支持的System Prompt,在处理退款问题时需要使用客服的System Prompt。如果每次都重新定义Graph,显然不够灵活。
下面我们结合代码来看看具体的实现过程。
(1)ContextSchema定义
from langchain.chat\_models import init\_chat\_model
from langgraph.graph import MessagesState, END, StateGraph, START
from langgraph.runtime import Runtime
from langchain\_core.messages import SystemMessage
from typing\_extensions import TypedDict
class ContextSchema(TypedDict):
system\_prompt: str
这里我们定义了一个ContextSchema,它包含一个system\_prompt字段,类型为str。
(2)处理System Prompt的节点函数定义
接下来,我们定义节点函数:
def call\_model\_with\_prompt(state: MessagesState, runtime: Runtime[ContextSchema]):
model = init\_chat\_model("deepseek-chat", model\_provider="deepseek")
# 获取运行时配置的System Prompt,如果没有提供则使用默认值
system\_prompt = (runtime.context or {}).get("system\_prompt", "You are a helpful assistant.")
system\_message = SystemMessage(content=system\_prompt)
# 将System Message添加到消息列表的开头
messages = [system\_message] + state["messages"]
response = model.invoke(messages)
return {"messages": [response]}
在函数体内,我们首先初始化模型。然后使用(runtime.context or {}).get("system\_prompt", "You are a helpful assistant.")获取配置的System Prompt,如果没有提供配置(即context={}或context为None),则会使用默认值"You are a helpful assistant."。
接下来,我们创建一个SystemMessage对象,并将其添加到消息列表的开头。这样,模型在处理用户消息时,会首先看到System Prompt,从而按照指定的角色和风格来响应。
最后,调用模型的invoke方法处理消息,并将响应添加到消息列表中返回。
(3)搭建Graph传入context_schema
builder = StateGraph(MessagesState, context\_schema=ContextSchema)
builder.add\_node("model", call\_model\_with\_prompt)
builder.add\_edge(START, "model")
builder.add\_edge("model", END)
graph = builder.compile()
这些步骤和之前一样,不再赘述。
(4)在graph.invoke中指定context的参数
# Usage
input\_message = {"role": "user", "content": "What is Python?"}
# 使用默认的System Prompt
response\_1 = graph.invoke(
{"messages": [input\_message]},
context={}
)
# 使用自定义的System Prompt
response\_2 = graph.invoke(
{"messages": [input\_message]},
context={"system\_prompt": "You are a Python expert. Provide detailed technical explanations."}
)
首先,我们准备一个输入消息。然后,我们进行了两次调用:
第一次调用
:
context={}
,这时Graph会使用默认值
"You are a helpful assistant."
,所以这次调用会使用默认的System Prompt。
第二次调用
:
context={"system\_prompt": "You are a Python expert. Provide detailed technical explanations."}
。我们显式地传入配置,指定使用自定义的System Prompt,让模型以Python专家的身份来回答问题。
这样,我们就可以在运行时动态指定System Prompt,而不需要修改Graph的定义。在实际应用中,你可以根据任务类型、用户角色、业务场景等因素,动态选择最合适的System Prompt。
总结
本期我们学习了如何在LangGraph中对Runtime Configuration进行自定义。通过自定义Runtime Configuration,我们可以:
动态配置Graph行为 :在调用Graph时传入配置参数,而不是在定义Graph时硬编码
提高代码复用性 :同一个Graph可以根据不同的配置表现出不同的行为
灵活应对不同场景 :根据任务类型、用户需求、环境等因素,动态调整Graph的配置
掌握了自定义Runtime Configuration,我们就可以构建更加灵活、可复用的AI Agent了。好了,以上就是本期的主要内容,希望对大家有帮助,喜欢的朋友别忘了点赞、收藏、转发祝大家玩的开心
—— END——
往期精华:
1.COZE教程
AI工作流编排手把手指南之一:Coze智能体的创建与基本设置
AI工作流编排手把手指南之二:Coze智能体的插件添加与调用
Agent | 工作流编排指南4:萌新友好的Coze选择器节点原理及配置教程
Agent | 工作流编排指南5:长文扩写自由 — Coze循环节点用法详解
Coze工作流编排指南6:聊天陪伴类智能体基本工作流详解-快来和玛奇玛小姐姐谈心吧~
PPT自由!Coze工作流 X iSlide插件-小白也能看懂的节点参数配置原理详解
2.MCP探索
Excel-MCP应用 | 自动提取图片数据到Excel的极简工作流手把手教程
markitdown-mcp联动Obsidian-mcp | 一个极简知识管理工作流
【15合1神器】不会代码也能做高级图表!这个MCP工具让我工作效率翻了不止三倍!
【效率翻倍】Obsidian自动待办清单实现:MCP联动Prompt保姆级教程(萌新3分钟上手)
萌新靠MCP实现RPA、爬虫自由?playwright-mcp实操案例分享!
高德、彩云MCP全体验:让Cherry Studio化身私人小助理的喂饭版指南!
3.Prompt设计
干货分享 | Prompt设计心法 - 如何3步做到清晰表达需求?
