LangGraph 入门实战(5)

LangGraph 实战:连接 DeepSeek 和 GPT 大模型

LangGraph 不仅能编排普通 Python 函数,也可以把大模型调用封装成图节点。本文用一个最小示例,实现以下流程:

text 复制代码
START -> call_model -> END

程序还支持通过运行配置,在 DeepSeek 和 GPT 之间动态切换。

1. 安装依赖

bash 复制代码
python -m pip install -U langgraph langchain-deepseek langchain-openai

本文示例使用的主要版本:

text 复制代码
Python 3.14.7
LangGraph 1.2.10
langchain-deepseek 1.1.0
langchain-openai 1.4.3

2. 配置 API Key

不要把 API Key 直接写进 Python 文件,可以先在终端中设置环境变量:

bash 复制代码
export DEEPSEEK_API_KEY="你的 DeepSeek API Key"
export API_KEY="你的 GPT 服务 API Key"

代码通过 os.environ 读取密钥,并使用 SecretStr 包装:

python 复制代码
api_key=SecretStr(os.environ["DEEPSEEK_API_KEY"])

如果环境变量不存在,运行时会出现 KeyError

3. 定义消息状态

python 复制代码
class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], operator.add]

messages 用来保存对话消息。operator.add 是 reducer,节点返回的新消息会追加到原消息列表中,而不是覆盖已有消息。

4. 在节点中调用模型

python 复制代码
models = {"deepseek": deepseek, "gpt": gpt}


def call_model(state: AgentState, config: RunnableConfig):
    configurable = config.get("configurable") or {}
    model_name = configurable.get("model", "deepseek")
    model = models[model_name]
    response = model.invoke(state["messages"])
    return {"messages": [response]}

节点先读取 configurable.model,再从 models 中选择对应的大模型。没有传入模型名称时,默认使用 DeepSeek。

运行配置如下:

python 复制代码
run_config: RunnableConfig = {"configurable": {"model": "deepseek"}}

切换到 GPT 只需要改成:

python 复制代码
run_config: RunnableConfig = {"configurable": {"model": "gpt"}}

5. 完整代码

python 复制代码
import operator
import os
from typing import Annotated, Sequence

# BaseMessage 是所有 LangChain 消息类型的基类;
# HumanMessage 用于表示用户发送给大模型的消息。
from langchain_core.messages import BaseMessage, HumanMessage
# RunnableConfig 用于描述图运行时传入的配置信息。
from langchain_core.runnables.config import RunnableConfig
from langchain_deepseek import ChatDeepSeek
from langchain_openai import ChatOpenAI
# START 和 END 是 LangGraph 内置的开始、结束节点。
from langgraph.graph import END, START, StateGraph
# SecretStr 用于包装敏感字符串,避免 API Key 被直接显示在日志中。
from pydantic import SecretStr
from typing_extensions import TypedDict

# 创建 DeepSeek 聊天模型客户端。
deepseek = ChatDeepSeek(
    # 指定需要调用的 DeepSeek 模型。
    model="deepseek-v4-flash",
    # temperature 越低,回答越稳定;设置为 0 适合演示和确定性任务。
    temperature=0,
    # DeepSeek 官方 API 地址。
    base_url="https://api.deepseek.com",
    # 从环境变量读取 API Key,避免将密钥直接写进源代码。
    # 运行前需要执行:export DEEPSEEK_API_KEY="你的密钥"
    api_key=SecretStr(os.environ["DEEPSEEK_API_KEY"]),
)

# ChatOpenAI 不仅可以连接 OpenAI 官方接口,也可以连接兼容
# OpenAI API 协议的第三方服务。
gpt = ChatOpenAI(
    # 模型名称必须是当前 API 服务实际支持的模型。
    model="gpt-5.6-sol",
    temperature=0,
    # 当前示例使用自定义的 OpenAI 兼容接口地址。
    base_url="https://codex.ximuai.com",
    # 运行前需要执行:export API_KEY="你的密钥"
    api_key=SecretStr(os.environ["API_KEY"]),
)

# 使用字典统一管理模型,后面可以通过名称动态选择模型。
models = {"deepseek": deepseek, "gpt": gpt}


# 定义整个图在节点之间传递的状态结构。
class AgentState(TypedDict):
    # messages 保存完整的对话消息。
    # Annotated 的第二个参数 operator.add 是 reducer(归并函数):
    # 当节点返回新消息时,LangGraph 会把新消息追加到原消息序列中,
    # 而不是直接覆盖原来的消息。
    messages: Annotated[Sequence[BaseMessage], operator.add]


# 图的运行时配置。
# configurable 中可以放自定义配置,这里通过 model 指定使用 DeepSeek。
# 如果需要切换到 GPT,可以把 "deepseek" 改为 "gpt"。
run_config: RunnableConfig = {"configurable": {"model": "deepseek"}}


def call_model(state: AgentState, config: RunnableConfig):
    """读取当前对话状态,调用指定模型,并返回模型生成的新消息。"""

    # RunnableConfig 中的 configurable 字段是可选字段。
    # 使用 get() 和空字典兜底,可以避免字段不存在时触发 KeyError。
    configurable = config.get("configurable") or {}

    # 读取本次运行指定的模型名称;没有指定时默认使用 DeepSeek。
    model_name = configurable.get("model", "deepseek")

    # 根据模型名称获取已经创建好的聊天模型客户端。
    model = models[model_name]

    # 把状态中保存的全部历史消息发送给大模型。
    response = model.invoke(state["messages"])

    # 节点只返回本次生成的新消息。
    # AgentState 中配置的 operator.add 会将它追加到原消息序列。
    return {"messages": [response]}


# 创建状态图,并指定图中流转的状态类型为 AgentState。
graph_builder = StateGraph(AgentState)

# 注册一个名为 call_model 的节点,并绑定同名处理函数。
graph_builder.add_node("call_model", call_model)

# 图开始后进入 call_model 节点。
graph_builder.add_edge(START, "call_model")

# 大模型调用完成后直接结束图,不再执行其他节点。
graph_builder.add_edge("call_model", END)

# 编译图。编译后得到的 graph 才能通过 invoke() 执行。
graph = graph_builder.compile()

# 执行图:
# 1. 初始状态中放入一条用户消息;
# 2. config 用于指定本次运行需要调用的模型;
# 3. 返回值包含初始消息和模型追加的回复消息。
res = graph.invoke(
    {"messages": [HumanMessage(content="你好,你是谁?")]},
    config=run_config,
)

# 依次格式化输出用户消息和 AI 回复。
for message in res["messages"]:
    message.pretty_print()

6. 运行与输出

执行程序:

bash 复制代码
python index.py

输出示例:

text 复制代码
================================ Human Message =================================

你好,你是谁?
================================== Ai Message ==================================

你好!我是一个由人工智能技术驱动的助手,可以帮助你解答问题、整理信息和编写代码。

大模型生成的内容并不固定,因此每次运行的回答可能略有不同。res["messages"] 中同时包含最初的 HumanMessage 和节点追加的 AI 回复。

总结

连接大模型的关键步骤只有三个:定义消息状态、在节点中调用模型、用边连接 STARTEND。把模型名称放进 RunnableConfig 后,同一张图就可以在不修改节点逻辑的情况下切换不同模型。

需要注意:当前代码在程序启动时会同时创建 DeepSeek 和 GPT 客户端,因此两个 API Key 都必须存在,即使本次只调用其中一个模型。

相关推荐
Python私教1 小时前
请求超时不等于失败:把写操作恢复设计成事实回读
后端·python
且听风吟02201 小时前
7.包管理工具
python
程序员杰哥2 小时前
依赖于第三方接口时,如何进行测试?
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
java修仙传2 小时前
从网页禅道到 AI 能调用的工具:我的禅道 MCP 实现思路分享
java·人工智能·python·ai应用·mcp开发
reasonsummer2 小时前
【办公类-120-01】20260812闵行区幼儿园概况表的数据统计(户籍、五类性质、优抚子女)
前端·python
董可伦2 小时前
RAG 工具怎么选:LangChain、LlamaIndex、Dify 实测对比
人工智能·ai·langchain·大模型
梦想的旅途22 小时前
企业微信API实战:Python自动化消息推送
java·前端·python·自动化·企业微信
月光船幽幽2 小时前
昆仑流形多模态融合新架构
人工智能·python
爱敲代码的憨仔2 小时前
BM25全文检索
开发语言·python·全文检索