非线性工作流:分支与动态路由

目录

[5.2.1 非线性工作流核心概念](#5.2.1 非线性工作流核心概念)

[1. LangGraph非线性工作流核心价值](#1. LangGraph非线性工作流核心价值)

[2. 非线性工作流关键技术](#2. 非线性工作流关键技术)

[3. DeepSeek LLM适配](#3. DeepSeek LLM适配)

[4. 路由策略](#4. 路由策略)

[5.2.2 实战案例:使用条件边路由到节点](#5.2.2 实战案例:使用条件边路由到节点)

[1. 案例场景:智能客服问题处理系统](#1. 案例场景:智能客服问题处理系统)

[2. 基础组件定义](#2. 基础组件定义)

[3. 核心节点实现](#3. 核心节点实现)

[4. 构建LangGraph工作流](#4. 构建LangGraph工作流)


LangGraph开发AI Agent实践(人工智能技术丛书)【行情 报价 价格 评测】-京东

LangGraph是LangChain生态中用于构建有状态、多参与者、非线性LLM工作流的库。它特别适合需要分支(branching)、循环(looping)或动态路由(dynamic routing)的复杂Agent编排场景。其核心思想是将工作流建模为有向图(Directed Graph),其中节点是Agent(或工具),边是条件路由逻辑。

5.2.1 非线性工作流核心概念

1. LangGraph非线性工作流核心价值

LangGraph是LangChain生态中的有状态图工作流框架,专为解决LLM应用中的非线性逻辑设计。相比传统线性流水线,它支持:

  • 分支(并行/条件执行)。
  • 循环(结果校验重试)。
  • 动态路由(根据输入/中间结果切换执行路径)。
  • 状态持久化(跨节点共享上下文)。
2. 非线性工作流关键技术
  • 条件分支:根据输入特征分流执行,如问题分类(事实问答、创意写作、代码生成)。
  • 动态路由 :基于中间结果动态切换执行路径,如答案质量校验(合格则直接输出,否则重新生成)。
  • 并行分支:多任务并发执行,实现多维度内容分析(如情感分析、关键词提取、实体识别)。
  • 循环节点:当结果不满足条件时自动重试,适用于生成内容合规性校验与格式修正。
3. DeepSeek LLM适配

DeepSeek系列模型(如deepseek-chat、deepseek-coder)支持中文优化、长上下文处理,通过LangChain的DeepSeek封装可无缝集成到LangGraph中,核心优势如下:

  • 中文理解准确率高。
  • 代码/逻辑推理能力强。
  • 支持工具调用与函数执行。
4. 路由策略

LangGraph的动态路由通过条件边(conditional edges)实现:

  • 定义路由函数:接收当前状态,返回目标节点名称。
  • 支持多分支路由:根据状态变量(如query_type、is_complete)动态切换路径。
  • 循环路由:通过END与节点名称的条件判断,实现结果不满足时的重试逻辑。

from typing import Literal

from langgraph.graph import END, StateGraph

5.2.2 实战案例:使用条件边路由到节点

1. 案例场景:智能客服问题处理系统

系统需求说明如下:

  • 接收用户咨询,先分类(订单问题/产品咨询/投诉建议)。
  • 订单问题:查询订单状态(模拟工具调用)。
  • 产品咨询:生成产品说明(直接LLM响应)。
  • 投诉建议:记录内容并转人工(并行执行两个任务)。
  • 结果校验:若回答不完整,则重新调用对应节点。
2. 基础组件定义

1)初始化DeepSeek LLM

from dotenv import load_dotenv

from langchain_openai import ChatOpenAI

import os

加载环境变量

load_dotenv()

初始化qwen-plus LLM(指定聊天模型)

llm = ChatOpenAI(

model="qwen-plus",

api_key=os.getenv("DASHSCOPE_API_KEY"), # 从 .env 读取

base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",

temperature=0

)

2)定义工作流状态

LangGraph通过StateGraph管理状态,状态变量需明确类型:

from typing import TypedDict, Optional

from langgraph.graph import StateGraph, END

定义工作流状态结构

class SupportState(TypedDict):

user_query: str # 用户原始查询

query_type: Optionalstr # 问题类型(订单/产品/投诉)

order_id: Optionalstr # 订单号(仅订单问题需要)

response: Optionalstr # 最终响应

is_complete: bool # 响应是否完整(用于动态路由)

3. 核心节点实现

节点是工作流的执行单元,每个节点接收State并返回更新后的State。

1)问题分类节点(路由入口)

根据用户查询判断问题类型,为动态路由提供依据:

def classify_query(state: SupportState) -> SupportState:

"""分类用户查询:订单问题/产品咨询/投诉建议"""

user_query = state"user_query"

调用qwen-plus 进行分类

prompt = f"""

请将用户查询分类为以下三类:订单问题、产品咨询、投诉建议

分类规则:

  • 订单问题:包含订单号、查询物流、修改订单、取消订单等关键词

  • 产品咨询:包含产品功能、使用方法、规格参数、兼容性等关键词

  • 投诉建议:包含投诉、不满、建议、改进等关键词

用户查询:{user_query}

仅返回分类结果,无须额外说明。

"""

query_type = llm.invoke(prompt).strip()

print(f"问题分类结果:{query_type}")

提取订单号(仅订单问题)

order_id = None

if query_type == "订单问题":

extract_prompt = f"""

从用户查询中提取订单号(通常为6~12位数字或字母+数字组合),

若未提及订单号,则返回"无"。

用户查询:{user_query}

仅返回订单号或"无"。

"""

order_id = llm.invoke(extract_prompt).strip()

print(f"提取订单号:{order_id}")

return {

**state,

"query_type": query_type,

"order_id": order_id,

"is_complete": False # 初始响应未完成

}

2)分支处理节点

(1)订单问题处理(模拟工具调用):

def handle_order(state: SupportState) -> SupportState:

"""处理订单问题:查询订单状态(模拟工具调用)"""

order_id = state"order_id"

user_query = state"user_query"

if order_id == "无":

response = "为了帮你查询订单状态,请提供你的订单号(6~12位数字或字母+数字组合)。"

else:

模拟调用订单查询API

mock_order_status = f"订单{order_id}当前状态:已发货,物流单号:SF123456789,预计明日送达。"

response = f"您好!{mock_order_status} 若有其他需求,请随时告知。"

print(f"订单问题响应:{response}")

return {**state, "response": response}

(2)产品咨询处理(直接LLM生成):

def handle_product(state: SupportState) -> SupportState:

"""处理产品咨询:生成产品说明"""

user_query = state"user_query"

prompt = f"""

作为产品顾问,请详细解答用户的产品咨询,语言通俗易懂,结构清晰。

用户咨询:{user_query}

回答要求:

  1. 先明确核心问题

  2. 分点说明(不超过3点)

  3. 结尾提供进一步帮助引导

"""

response = llm.invoke(prompt)

print(f"产品咨询响应:{response}")

return {**state, "response": response}

(3)投诉建议处理(并行执行):

def handle_complaint_record(state: SupportState) -> SupportState:

"""处理投诉建议:记录投诉内容(并行节点1)"""

user_query = state"user_query"

模拟写入数据库

print(f"已记录投诉建议:{user_query}")

return {**state, "complaint_recorded": True}

def handle_complaint_forward(state: SupportState) -> SupportState:

"""处理投诉建议:转人工处理(并行节点2)"""

user_query = state"user_query"

response = f"您好!您的反馈已收到,我们将在24小时内安排专属客服与您联系。感谢您的支持!"

print(f"投诉转人工响应:{response}")

return {**state, "response": response}

3)结果校验节点(动态路由判断)

def validate_response(state: SupportState) -> SupportState:

"""校验响应是否完整,决定是否重试"""

response = state"response"

user_query = state"user_query"

prompt = f"""

请判断以下响应是否完整解答了用户的查询:

  1. 若响应直接回答了核心问题,或提供了明确的下一步操作指引(如要求补充信息),则判定为完整,返回"完整"

  2. 若响应模糊、未解答核心问题、遗漏关键信息,则判定为不完整,返回"不完整"

用户查询:{user_query}

系统响应:{response}

仅返回"完整"或"不完整"。

"""

validation_result = llm.invoke(prompt).strip()

print(f"响应校验结果:{validation_result}")

return {

**state,

"is_complete": validation_result == "完整"

}

4. 构建LangGraph工作流

1)初始化图并添加节点

初始化状态图

graph = StateGraph(SupportState)

添加核心节点

graph.add_node("classify_query", classify_query) # 问题分类

graph.add_node("handle_order", handle_order) # 订单处理

graph.add_node("handle_product", handle_product) # 产品咨询处理

graph.add_node("handle_complaint_record", handle_complaint_record) # 投诉记录

graph.add_node("handle_complaint_forward", handle_complaint_forward) # 投诉转人工

graph.add_node("validate_response", validate_response) # 响应校验

2)定义边(路由规则)

(1)初始路由:分类后分流:

从分类节点根据query_type分流到对应处理节点

def route_after_classify(state: SupportState) -> str:

query_type = state"query_type"

if query_type == "订单问题":

return "handle_order"

elif query_type == "产品咨询":

return "handle_product"

elif query_type == "投诉建议":

return "handle_complaint_record" # 先执行记录,再并行转人工

else:

return "handle_product" # 默认走产品咨询

添加条件边:分类节点 -> 处理节点

graph.add_conditional_edges(

"classify_query",

route_after_classify

)

(2)投诉处理并行路由:

投诉记录后,并行执行转人工(使用add_edge实现串行,实际并行可通过LangGraph并行节点优化)

graph.add_edge("handle_complaint_record", "handle_complaint_forward")

(3)响应校验与动态重试路由:

所有处理节点都指向校验节点

graph.add_edge("handle_order", "validate_response")

graph.add_edge("handle_product", "validate_response")

graph.add_edge("handle_complaint_forward", "validate_response")

校验节点的动态路由:完整则结束,不完整则重试对应处理节点

def route_after_validation(state: SupportState) -> str:

if state"is_complete":

return END # 流程结束

else:

不完整则重试原处理节点

query_type = state"query_type"

if query_type == "订单问题":

return "handle_order"

elif query_type == "产品咨询":

return "handle_product"

elif query_type == "投诉建议":

return "handle_complaint_forward"

else:

return "handle_product"

添加条件边:校验节点 -> 结束或重试

graph.add_conditional_edges(

"validate_response",

route_after_validation

)

(4)设置入口节点:

graph.set_entry_point("classify_query")

3)编译图并运行

编译图(生成可执行工作流)

app = graph.compile()

测试运行:输入不同类型的用户查询

def test_workflow(user_query: str):

print(f"\n{'=' * 60}")

print(f"处理用户查询:{user_query}")

print(f"{'=' * 60}")

运行工作流

result = app.invoke({

"user_query": user_query,

"query_type": None,

"order_id": None,

"response": None,

"is_complete": False,

"retry_count": 0

})

print(f"\n最终响应:{result.get('response')}")

print(f"重试次数:{result.get('retry_count', 0)}")

print(f"{'=' * 60}")

return result

测试函数,运行所有测试用例

def run_all_tests():

"""运行所有测试用例"""

test_cases = [

("我的订单号是OD123456,请问什么时候发货?", "订单问题"),

("你们的智能音箱支持蓝牙5.0吗?怎么连接手机?", "产品咨询"),

("我对昨天收到的商品不满意,质量有问题,希望退款或换货。", "投诉建议"),

("我的订单什么时候能到?", "订单问题但没提供订单号"),

("我想问一下这个产品怎么用", "简短的产品咨询")

]

results = \[\]

for i, (query, description) in enumerate(test_cases, 1):

print(f"\n{'#' * 60}")

print(f"测试 {i}: {description}")

print(f"查询内容: {query}")

print(f"{'#' * 60}")

try:

result = test_workflow(query)

results.append((query, description, result))

except Exception as e:

print(f"测试失败: {e}")

import traceback

traceback.print_exc()

print(f"\n{'=' * 60}")

print(f"所有测试完成,共运行 {len(results)} 个测试用例")

print(f"{'=' * 60}")

测试用例

if name == "main":

run_all_tests()

5.2.3 实战案例:使用动态路由路由到节点

【案例5.2】非线性工作流智能客服问题处理系统LangGraph_Non-linear_workflow。

完整依赖导入(适配 LangGraph 1.0.5,纯串行非线性工作流)

from dotenv import load_dotenv

from typing import TypedDict, Optional

from langgraph.graph import StateGraph, END # LangGraph 1.0.5

import os

import requests

==================== 1. API 调用(本地模拟,确保无网络问题) ===================

load_dotenv()

api_key = os.getenv("DEEPSEEK_API_KEY")

def call_deepseek(prompt: str) -> str:

"""本地模拟 API 响应,非线性流程核心逻辑不变"""

if "分类" in prompt:

return "订单问题" if "订单" in prompt or "OD" in prompt else "产品咨询" if "产品" in prompt else "投诉建议"

elif "提取订单号" in prompt:

return "OD123456" if "OD123456" in prompt else "无"

elif "智能音箱" in prompt and "蓝牙" in prompt:

return "1. 支持蓝牙5.0;2. 手机搜设备配对;3. 兼容主流系统"

elif "校验" in prompt:

return "不完整" if "请提供订单号" in prompt else "完整"

return "系统暂时无法服务"

====================== 2. 状态定义(精简,仅保留核心控制字段) =================

class SupportState(TypedDict):

user_query: str

query_type: Optionalstr

order_id: Optionalstr

response: Optionalstr

retry_count: int

current_node: str # 唯一控制字段:指定当前要执行的节点

is_complete: bool

============= 3. 核心节点(每个节点仅处理自身逻辑,不依赖边关系分支) ==============

def classify_query(state: SupportState) -> SupportState:

"""分类节点:仅处理分类,更新下一个节点"""

print(f"节点执行 分类查询:{state'user_query'}")

query_type = call_deepseek(f"分类用户查询:{state'user_query'}")

非线性分支:根据分类指定下一个节点

next_node = "extract_order_id" if query_type == "订单问题" else "handle_product" if query_type == "产品咨询" else "handle_complaint"

return {

**state,

"query_type": query_type,

"current_node": next_node, # 明确下一个要执行的节点

"is_complete": False

}

def extract_order_id(state: SupportState) -> SupportState:

"""提取订单号节点:条件跳转"""

print(f"节点执行 提取订单号")

order_id = call_deepseek(f"提取订单号:{state'user_query'}")

条件逻辑:有订单号→处理订单,无→提示补充

next_node = "handle_order" if order_id != "无" else "prompt_order_id"

return {

**state,

"order_id": order_id,

"current_node": next_node,

"retry_count": state"retry_count" + 1

}

def prompt_order_id(state: SupportState) -> SupportState:

"""提示补充订单号节点:触发重试"""

print(f"节点执行 提示补充订单号")

response = "请提供6~12位订单号(如OD123456)"

非线性循环:提示后跳转校验,校验不通过则重试

return {

**state,

"response": response,

"current_node": "validate_response"

}

def handle_order(state: SupportState) -> SupportState:

"""处理订单节点:生成响应"""

print(f"节点执行 处理订单:{state'order_id'}")

response = f"订单{state'order_id'}已发货,物流SF123456789,明日送达"

return {**state, "response": response, "current_node": "validate_response"}

def handle_product(state: SupportState) -> SupportState:

"""处理产品咨询节点:生成响应"""

print(f"节点执行 处理产品咨询")

response = call_deepseek(f"解答产品咨询:{state'user_query'}")

return {**state, "response": response, "current_node": "validate_response"}

def handle_complaint(state: SupportState) -> SupportState:

"""处理投诉节点:生成响应"""

print(f"节点执行 处理投诉")

response = "投诉已记录,24小时内联系你"

return {**state, "response": response, "current_node": "validate_response"}

def validate_response(state: SupportState) -> SupportState:

"""校验节点:非线性核心(终止/重试)"""

print(f"节点执行 校验响应,重试次数:{state'retry_count'}")

validation = call_deepseek(f"校验响应:{state'response'}")

终止条件:响应完整或重试3次

if validation == "完整" or state"retry_count" >= 3:

return {**state, "is_complete": True, "current_node": "END"}

重试逻辑:根据问题类型跳转回对应节点

retry_node = "extract_order_id" if state"query_type" == "订单问题" else "handle_product" if state"query_type" == "产品咨询" else "handle_complaint"

return {**state, "current_node": retry_node, "retry_count": state"retry_count" + 1}

================ 4. 动态路由节点(核心修复:单一入口,串行执行) ===================

def dynamic_router(state: SupportState) -> SupportState:

"""动态路由:根据 current_node 执行对应节点逻辑,避免多节点并发"""

current_node = state"current_node"

print(f"路由执行 跳转至节点:{current_node}")

核心:路由节点内部调用目标节点,而非通过边关系触发

if current_node == "classify_query":

return classify_query(state)

elif current_node == "extract_order_id":

return extract_order_id(state)

elif current_node == "prompt_order_id":

return prompt_order_id(state)

elif current_node == "handle_order":

return handle_order(state)

elif current_node == "handle_product":

return handle_product(state)

elif current_node == "handle_complaint":

return handle_complaint(state)

elif current_node == "validate_response":

return validate_response(state)

elif current_node == "END":

return {**state, "is_complete": True}

else:

return {**state, "current_node": "classify_query"}

====================== 5. 工作流构建(极简边关系,无并发) ======================

graph = StateGraph(SupportState)

仅添加 1 个路由节点(所有逻辑都在路由内执行)

graph.add_node("dynamic_router", dynamic_router)

边关系:仅路由节点 ↔ 结束节点,完全规避多节点并发

graph.set_entry_point("dynamic_router")

graph.add_edge("dynamic_router", END)

编译工作流

app = graph.compile()

================== 6. 测试非线性工作流(串行执行,无并发错误) ==================

def test_workflow(user_query: str):

print(f"\n{'='*70}")

print(f"处理用户查询:{user_query}")

print(f"{'='*70}")

initial_state: SupportState = {

"user_query": user_query,

"query_type": None,

"order_id": None,

"response": None,

"retry_count": 0,

"current_node": "classify_query", # 初始节点

"is_complete": False

}

try:

执行工作流(循环直到流程完成)

result = initial_state

while not result"is_complete":

result = app.invoke(result)

避免无限循环(双重保障)

if result"retry_count" > 5:

result"is_complete" = True

result"response" = "系统繁忙,请稍后再试"

print(f"\n【最终响应】:{result'response'}")

print(f"【流程状态】:完成,总重试次数:{result'retry_count'}")

except Exception as e:

print(f"\n【运行错误】:{str(e)}")

finally:

print(f"{'='*70}\n")

测试核心场景

if name == "main":

test_workflow("我的订单号是OD123456,请问什么时候发货?")

test_workflow("请问我的订单什么时候发货?") # 无订单号,触发重试

test_workflow("你们的智能音箱支持蓝牙5.0吗?") # 产品咨询分支

运行输出:

======================================================================

处理用户查询:我的订单号是OD123456,请问什么时候发货?

======================================================================

路由执行 跳转至节点:classify_query

节点执行 分类查询:我的订单号是OD123456,请问什么时候发货?

路由执行 跳转至节点:extract_order_id

节点执行 提取订单号

路由执行 跳转至节点:handle_order

节点执行 处理订单:OD123456

路由执行 跳转至节点:validate_response

节点执行 校验响应,重试次数:1

【最终响应】:订单OD123456已发货,物流SF123456789,明日送达

【流程状态】:完成,总重试次数:1

======================================================================

该程序非线性工作流的特性如下。

  • 多分支:订单、产品、投诉三个独立分支,根据查询类型动态切换。
  • 条件跳转:订单号有无→不同处理路径;投诉信息完整性→不同响应。
  • 循环重试:响应不完整时自动重试对应分支,最多重试3次。
  • 状态驱动:通过current_node字段控制流程走向,符合非线性工作流定义。

项目扩展说明如下。

  • 非线性逻辑清晰:分支、条件跳转、循环重试的逻辑直观,适合教学演示。
  • 适配LangGraph 1.0.5:仅使用基础功能,读者可直接复现。
  • 可扩展性强:新增节点只需在路由内添加调用逻辑,无须修改边关系。