从零开始学LangGraph指南(10):利用Runtime自定义实现Model和System Prompt的灵活切换

大模型向量数据库机器学习

关注我~第一时间学习如何更好地使用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={}contextNone),则会使用默认值"chat"。因为在后面我们invoke Graph的时候,会向context参数传入形如{"model\_type": "chat"}{"model\_type": "reasoner"}的键值对。

然后,根据获取到的model\_typeMODELS字典中选择对应的模型,调用模型的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={}contextNone),则会使用默认值"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教程

零基础搞定!萌新的 Coze 开源版保姆级本地部署指南

AI工作流编排手把手指南之一: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步做到清晰表达需求?

打工人看了流泪的Prompt设计原理,如何用老板思维让AI一次听懂需求?

不会Prompt还敢说自己会用DeepSeek?别怕!10分钟让你成为提示大神!

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动 XR 技术的探索与实践
火山引擎开发者社区技术大讲堂第二期邀请到了火山引擎 XR 技术负责人和火山引擎创作 CV 技术负责人,为大家分享字节跳动积累的前沿视觉技术及内外部的应用实践,揭秘现代炫酷的视觉效果背后的技术实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论