线性工作流设计

目录

5.1.1  线性工作流核心概念

1. 线性工作流设计概念解释

2. 工具调用

3. LangGraph框架定位

5.1.2  实战案例:线性工作流的实现

1. 定义状态结构(State)

2. 实现节点逻辑(Node)

3. 构建线性工作流图(StateGraph)

4. 执行工作流与调试

5. 流程执行解析

5.1.3  实战案例:文档摘要工作流的实现

【示例5.1】文档摘要工作流完整程序实现(LangGraph_linear_workflow.py)。


5.1.1  线性工作流核心概念

工作流是智能体系统运转的核心骨架,本小节立足LangGraph框架,系统讲解智能体工作流的设计方法。从基础的线性工作流入手,帮助读者掌握按序执行任务的标准化搭建流程。

1. 线性工作流设计概念解释

线性工作流是最基础的工作流形式,任务按预定义顺序依次执行,前一步的输出作为后一步的输入。其特点是结构清晰、易于调试,适用于流程固定、依赖明确的场景,如“用户提问→调用工具获取数据→生成回答”。

在LangGraph中,线性工作流通过StateGraph定义状态,并通过add_edge指定节点之间的单向依赖关系,形成类似A→B→C→END的流水线。

2. 工具调用

(1)LangGraph核心组件:

  • StateGraph:工作流图的核心容器,负责定义节点、边和状态结构。
  • State:状态载体,存储流程中所有数据(输入、中间结果、输出),支持动态修改。
  • Node:工作流的执行单元,可以是函数、LLM调用、工具调用等逻辑。
  • Edge:节点间的连接关系,在线性流程中为“顺序边”(从节点A直接指向节点B)。
  • Graph:编译后的工作流实例,通过graph.invoke()触发执行。

(2)常用工具:如RequestsAPI调用)、TavilySearch(搜索工具)、PythonREPLTool(在安全沙箱中执行 Python 代码)、SQLDatabaseToolkit(封装了 SQL 查询、表查看等操作)、自定义数据处理函数等。

3. LangGraph框架定位

LangGraph是LangChain生态下的状态机工作流框架,专为大语言模型(LLM)应用的复杂流程编排设计。其核心优势在于:

  • 显式的状态管理(State Management):通过可修改的状态对象串联整个工作流。
  • 可组合性(Composability):支持节点(Node)的灵活组合与复用。
  • 可观测性(Observability):内置日志、追踪能力,便于调试。
  • 循环与分支支持:相比线性脚本,天然支持复杂逻辑跳转(本书聚焦线性流程)。

线性工作流是LangGraph最基础的应用场景,核心特征是节点按固定顺序执行,无分支、无循环,适用于“数据预处理→LLM调用→结果后处理”等确定性流程。

5.1.2  实战案例:线性工作流的实现

线性工作流的设计遵循“定义状态→实现节点→构建图→执行与调试”4步流程,本小节以文档摘要工作流状态为例进行讲解。此工作流的完整代码参看配套资源中的代码文件5.1.2.py。

1. 定义状态结构(State)

状态是工作流的数据总线,需明确存储“输入数据→中间结果→输出结果”。LangGraph支持以下两种状态定义方式:

  • 简单场景:使用TypedDict定义强类型状态(推荐)。
  • 复杂场景:使用pydantic.BaseModel定义带校验的状态。

from typing import TypedDict, Optional

# 定义状态结构:存储原始文档、预处理后的文本、LLM摘要结果

class SummaryState(TypedDict):

    raw_document: str  # 输入:原始文档

    processed_text: Optional[str]  # 中间结果:预处理后的文本

    summary: Optional[str]  # 输出:LLM生成的摘要

2. 实现节点逻辑(Node)

节点是工作流的执行单元,每个节点接收State作为输入,修改并返回新的State。在线性流程中,节点按“预处理→LLM调用→后处理”的顺序设计。

1)节点1:文档预处理(文本清洗)

功能:去除原始文档中的多余空格、换行,提取核心内容。

def process_document(state: SummaryState) -> SummaryState:

    """预处理原始文档:清洗文本格式"""

    raw_doc = state["raw_document"]

    # 清洗逻辑:去除多余空格、换行,统一格式

    processed = raw_doc.strip().replace("\n\n", "\n").replace("  ", " ")

    # 返回更新后的状态

    return {"processed_text": processed}

2)节点2:DeepSeek LLM摘要生成

功能:调用DeepSeek API,基于预处理文本生成摘要。

from dotenv import load_dotenv

import os

from langchain_openai import ChatOpenAI

from langchain_core.prompts import ChatPromptTemplate

from langchain_core.output_parsers import StrOutputParser

# 加载环境变量(API Key

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

)

# 定义摘要提示词模板(指令+上下文)

summary_prompt = ChatPromptTemplate.from_messages([

    ("system", "你是专业的文本摘要助手,需基于以下文本生成简洁、准确的摘要,不添加额外信息,长度控制在300字以内。"),

    ("human", "文本内容:{processed_text}")

])

# 构建摘要链(提示词+LLM+输出解析器)

summary_chain = summary_prompt | llm | StrOutputParser()

def generate_summary(state: SummaryState) -> SummaryState:

    """调用DeepSeek LLM生成文本摘要"""

    processed_text = state["processed_text"]

    # 执行摘要链

    summary = summary_chain.invoke({"processed_text": processed_text})

    # 返回更新后的状态

    return {"summary": summary}

3)节点3:摘要后处理(格式优化)

功能:对 LLM 生成的摘要进行格式优化(如分段、添加标题)。

def format_summary(state: SummaryState) -> SummaryState:

    """后处理摘要结果:优化格式"""

    summary = state["summary"]

    # 格式优化逻辑:添加标题、分段

    formatted_summary = f"### 文档摘要\n\n{summary.replace('. ', '.\n')}"

    # 返回最终状态

    return {"summary": formatted_summary}

3. 构建线性工作流图(StateGraph)

通过StateGraph串联节点,在线性流程中使用add_edge(from_node, to_node)定义顺序关系,最后通过compile()生成可执行的图实例。

from langgraph.graph import StateGraph, END

# 1. 初始化状态图(绑定状态结构)

graph_builder = StateGraph(SummaryState)

# 2. 添加节点(每个节点映射到对应的处理函数)

graph_builder.add_node("process_doc", process_document)  # 节点1:预处理

graph_builder.add_node("generate_summary", generate_summary)  # 节点2LLM摘要

graph_builder.add_node("format_summary", format_summary)  # 节点3:后处理

# 3. 定义线性边(顺序执行:预处理摘要生成格式优化结束)

graph_builder.add_edge("process_doc", "generate_summary")

graph_builder.add_edge("generate_summary", "format_summary")

graph_builder.add_edge("format_summary", END)  # 最后一个节点指向END(工作流结束)

# 4. 设置入口节点(工作流起始点)

graph_builder.set_entry_point("process_doc")

# 5. 编译生成工作流实例

summary_graph = graph_builder.compile()

4. 执行工作流与调试

通过graph.invoke(input_state)触发工作流执行,输入状态需包含入口节点依赖的初始数据(如raw_document)。LangGraph支持可视化工作流结构,便于调试。

1)执行工作流

# 测试用原始文档(可替换为实际文档)

raw_document = """

LangGraph LangChain 生态系统中的一个框架,专门用于构建状态ful、可循环的工作流。它基于状态机的思想,允许开发者定义节点和边,通过状态对象管理整个工作流的数据流转。与传统的线性脚本相比,LangGraph提供了更好的可扩展性和可观测性,特别适合 LLM 应用中的复杂流程编排,例如多轮对话、文档分析、工具调用链等场景。LangGraph 的核心组件包括 StateGraphStateNode Edge,这些组件共同构成了灵活且强大的工作流系统。

"""

# 执行工作流:输入初始状态(仅包含raw_document

result = summary_graph.invoke({

    "raw_document": raw_document

})

# 输出结果

print("最终摘要:")

print(result["summary"])

2)可视化工作(可选)

LangGraph支持通过graph.draw()生成工作流结构图(需安装pygraphviz依赖):

pip install pygraphviz

# 生成PNG格式的工作流图(保存到本地)

summary_graph.draw("summary_workflow.png")

print("工作流图已保存为 summary_workflow.png")

5. 流程执行解析
  • 初始状态:仅包含raw_document(原始文档)。
  • 节点1(process_doc):清洗原始文档,更新processed_text。
  • 节点2(generate_summary):读取processed_text,调用DeepSeek LLM生成摘要,更新summary。
  • 节点3(format_summary):优化summary格式,最终状态包含所有字段。
  • 工作流结束:返回最终状态,提取summary作为输出。

5.1.3  实战案例:文档摘要工作流的实现

LangGraph线性工作流的核心价值在于将“离散的LLM调用+数据处理”封装为结构化、可维护的流程,相比传统脚本来说具有以下优点:

  • 状态管理更清晰:通过统一的State对象避免全局变量。
  • 可扩展性更强:节点可独立修改、复用,支持后续扩展分支/循环。
  • 可观测性更好:支持日志追踪、流程可视化,便于问题定位。
【示例5.1】文档摘要工作流完整程序实现(LangGraph_linear_workflow.py)。

# 1. 导入核心依赖库(仅保留必要组件)

from typing import TypedDict, Optional

from dotenv import load_dotenv

import os

import requests

from langgraph.graph import StateGraph, END

# 2. 加载环境变量(DASHSCOPE_API_KEY

load_dotenv()

DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")

# 校验API Key是否存在

if not DASHSCOPE_API_KEY:

    raise ValueError(" 请在.env文件中配置DASHSCOPE_API_KEY")

# 3. 定义工作流状态结构(保持简洁)

class SummaryState(TypedDict):

    raw_document: str  # 输入:原始文档

    processed_text: Optional[str]  # 中间结果:预处理后的文本

    summary: Optional[str]  # 输出:LLM生成的摘要

# 4. 直接实现DASHSCOPE_API_KEY调用(无任何LangChain封装依赖)

def call_deepseek_api(user_content: str, temperature: float = 0.3, max_tokens: int = 500) -> str:

    """直接调用DeepSeek API,不依赖LangChain任何模块"""

    url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/ text-generation/generation"

    headers = {

        "Content-Type": "application/json",

        "Authorization": f"Bearer {DASHSCOPE_API_KEY}"

    }

    # 完整的API请求体(包含system指令和user内容)

    data = {

        "model": "deepseek-chat",

        "messages": [

            {

                "role": "system",

                "content": "你是专业的文本摘要助手,需基于以下文本生成简洁、准确的摘要,不添加额外信息,长度控制在300字以内。"

            },

            {

                "role": "user",

                "content": user_content

            }

        ],

        "temperature": temperature,

        "max_tokens": max_tokens,

        "stream": False,

        "stop": None

    }

   

    try:

        # 发送API请求

        response = requests.post(url, headers=headers, json=data, timeout=60)

        response.raise_for_status()  # 抛出HTTP错误(4xx/5xx

        result = response.json()

        # 提取LLM生成的内容

        return result["choices"][0]["message"]["content"].strip()

    except requests.exceptions.Timeout:

        return " LLM调用超时,请检查网络连接或重试。"

    except requests.exceptions.ConnectionError:

        return " 网络连接失败,请确保能访问DeepSeek API"

    except requests.exceptions.HTTPError as e:

        return f" API请求错误(状态码:{response.status_code}):{e.response.text[:200]}"

    except Exception as e:

        return f" LLM调用失败:{str(e)[:100]}"

# 5. 工作流节点逻辑(完全独立,无外部依赖)

def process_document(state: SummaryState) -> SummaryState:

    """节点1:文档预处理(清洗文本格式)"""

    raw_doc = state["raw_document"]

    # 增强清洗:处理换行、空格、制表符、全角空格

    processed = (raw_doc.strip()

                .replace("\n\n", "\n")

                .replace("  ", " ")

                .replace("\t", " ")

                .replace(" ", " ")  # 中文全角空格

                .replace("\r", ""))  # 回车符

    return {"processed_text": processed}

def generate_summary(state: SummaryState) -> SummaryState:

    """节点2:调用DeepSeek API生成摘要"""

    try:

        processed_text = state["processed_text"]

        if not processed_text or len(processed_text) < 10:

            raise ValueError("预处理后的文本为空或过短")

       

        # 直接调用API(无须LangChain提示词模板)

        summary = call_deepseek_api(user_content=processed_text)

        return {"summary": summary}

    except Exception as e:

        error_msg = f" 摘要生成失败:{str(e)}"

        print(error_msg)

        return {"summary": error_msg}

def format_summary(state: SummaryState) -> SummaryState:

    """节点3:摘要后处理(优化格式)"""

    summary = state["summary"]

    line_break = "\n"

    # 中英文句号分句,避免格式混乱

    split_summary = summary.replace(". ", f".{line_break}").replace(" ", f"{line_break}")

    # 最终格式化(添加标题,去除末尾多余换行)

    formatted_summary = f"### 文档摘要{line_break}{line_break}{split_summary}".rstrip(line_break)

    return {"summary": formatted_summary}

# 6. 构建LangGraph线性工作流(核心逻辑不变)

def build_summary_graph():

    """构建并返回工作流实例(仅依赖LangGraph核心)"""

    graph_builder = StateGraph(SummaryState)

   

    # 添加节点

    graph_builder.add_node("process_doc", process_document)  # 预处理

    graph_builder.add_node("generate_summary", generate_summary)  # 生成摘要

    graph_builder.add_node("format_summary", format_summary)  # 格式优化

   

    # 定义线性执行顺序:预处理生成摘要格式优化结束

    graph_builder.add_edge("process_doc", "generate_summary")

    graph_builder.add_edge("generate_summary", "format_summary")

    graph_builder.add_edge("format_summary", END)

   

    # 设置入口节点

    graph_builder.set_entry_point("process_doc")

   

    return graph_builder.compile()

# 7. 可视化工作流(可选,失败不影响核心功能)

def visualize_graph(graph, save_path: str = "summary_workflow.png"):

    """简化可视化逻辑,避免依赖报错"""

    try:

        graph.draw(save_path, format="png")

        print(f"\n 工作流可视化图已保存至:{os.path.abspath(save_path)}")

    except Exception:

        print("\n   可视化功能未启用(需安装pygraphvizGraphviz软件,不影响核心功能)")

# 8. 主函数(执行入口)

if __name__ == "__main__":

    # 测试用原始文档(可替换为任意文本)

    raw_document = """

    LangGraph LangChain 生态系统中的一个框架,专门用于构建有状态、可循环的工作流。它基于状态机的思想,允许开发者定义节点和边,并通过状态对象管理整个工作流的数据流转。与传统的线性脚本相比,LangGraph 提供了更好的可扩展性和可观测性, 特别适合 LLM 应用中的复杂流程编排,例如多轮对话、文档分析、工具调用链等场景。LangGraph 的核心组件包括 StateGraphStateNode Edge 这些组件共同构成了灵活且强大的工作流系统。此外,LangGraph 还支持与 LangChain 生态的其他工具无缝集成,如提示词模板、向量数据库、工具调用等,进一步降低了复杂 LLM 应用的开发门槛。

    """

   

    # 初始化工作流

    print(" 正在初始化LangGraph线性工作流...")

    summary_graph = build_summary_graph()

   

    # 可视化工作流(可选)

    visualize_graph(summary_graph)

   

    # 执行工作流

    print("\n 正在执行文档摘要工作流...")

    result = summary_graph.invoke({

        "raw_document": raw_document

    })

   

    # 输出最终结果

    print("\n" + "="*60)

    print(" 最终摘要结果:")

    print("="*60)

    print(result["summary"])

    print("="*60)

运行输出:

 正在初始化LangGraph线性工作流...

  可视化功能未启用(需安装pygraphvizGraphviz软件,不影响核心功能)

 正在执行文档摘要工作流...

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

 最终摘要结果:

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

### 文档摘要

LangGraph是LangChain生态系统中的框架,用于构建有状态、可循环的工作流。它基于状态机思想,通过定义节点和边,利用状态对象管理数据流转。相比传统线性脚本,LangGraph具有更好的可扩展性和可观测性,适用于LLM应用中的复杂流程编排,如多轮对话、文档分析和工具调用链。其核心组件包括StateGraph、State、Node和Edge,共同构成灵活的工作流系统。此外,LangGraph能与LangChain生态中的提示词模板、向量数据库等工具无缝集成,降低复杂LLM应用的开发门槛。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值