LangChain V1.0 30日学习计划 --- Day 4 同步链与异步链

1、同步链路:

同步链(Synchronous Chain)是 LangChain 中最基础、最简单的执行模式。它代表了“一步接一步”的链式处理方式,就像排队买奶茶:前一个任务没做完,下一个任务只能等

一、同步链的本质:

同步链本质上是:

可组合的可调用对象(Runnable),按顺序执行,前一步的输出就是下一步的输入

它是一条严格的流水线:

Prompt → Model → Parser → Output

每一个环节都必须等待上一环节完成

二、同步链的结构(最常见三件套)

构建同步链常见三个组件:

  1. 提示模板 PromptTemplate
    用来构造文本输入,负责把用户输入塞进模板

  2. LLM / ChatModel(Language Model)
    如 OpenAI、Azure、Ollama、本地模型等

  3. 输出解析器 StrOutputParser
    用来把模型的输出转换为 Python String

  4. 穿起来就是:

    chain = prompt | model | StrOutputParser()
    

三、同步执行:invoke

同步链通过 invoke() 执行:

result = chain.invoke({"question": "同步链是如何工作的?"})
答:同步链按顺序依次执行任务,每个任务必须等待前一个任务完成后才能开始,确保执行顺序和状态一致性。

特点:

  • 阻塞(blocking):没有执行完不会继续别的任务
  • 单步式:必须等模型返回
  • 执行顺序稳定:一步一步推进

同步链确保“有序、确定性、安全”,但速度可能慢

四、同步链的应用场景

同步链适合:

  • 单次问答(ChatGPT 式交互)

  • 顺序严格的流程,如:

    加载 → 清洗 → 分析 → 输出
    
  • 模型调用间有依赖关系(前一步必须完成才有下一步)

举例:
你问模型一个问题,它回答后你解析,然后你再做计算, 一环扣一环,就适合同步

五、同步链的完整代码样例:

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

# 构建同步链
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业问答助手"),
    ("user", "{question}")
])

model = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser()

chain = prompt | model | parser  # 使用 | 操作符

# 同步执行
result = chain.invoke({"question": "什么是同步链?"})
print(result)

2、异步链路:

异步链在 LangChain 中最关键的点只有一件事:

所有可运行体(Runnable)都支持 .ainvoke().astream() 等异步方法

一、异步链的核心概念:

通俗的讲,同步链是 “一步一个脚印”,异步链是 “谁先跑完谁先回来”

  • 同步链:

    result = chain.invoke({"question":"Who are you?"})
    
  • 异步链:

    result = await chain.ainvoke({"question":"Who are you?"})
    

异步链的优点:

  • 可以并发地处理多个任务
  • 不必等待 CPU/IO 空档
  • 大幅提升吞吐量,尤其适合“并行问答、批量处理、多路输出”场景

二、构造异步问答链:

  1. 构造 “PromptTemplatePromptTemplatePromptTemplate”负责生成提示、“ChatOpenAIChatOpenAIChatOpenAI”负责调用模型 和 “StrOutputParserStrOutputParserStrOutputParser”负责把模型的输出转换为字符串

  2.  import asyncio
     from langchain_core.prompts import ChatPromptTemplate
     from langchain_openai import ChatOpenAI
     from langchain_core.output_parsers import StrOutputParser
     
     # -------------------------
     # 1. Prompt
     # -------------------------
     prompt = ChatPromptTemplate.from_messages([
         ("system", "你是一个专业问答机器人,回答要清晰简洁。"),
         ("user", "{question}")
     ])
     
     # -------------------------
     # 2. Model
     # -------------------------
     model = ChatOpenAI(
         model="gpt-4o-mini",
         temperature=0.2
     )
     
     # -------------------------
     # 3. Parser
     # -------------------------
     parser = StrOutputParser()
     
     # -------------------------
     # 4. 异步链
     # -------------------------
     # 把 prompt → model → parser 串成一条流水线
     chain = prompt | model | parser
     
     # 运行异步链
     async def run_async():
         result = await chain.ainvoke({"question": "什么是异步模型链?"})
         print(result)
     
     asyncio.run(run_async())
    

3、同步 vs 异步的并发性能测试:

# 导入必要的模块
import time                    # 用于记录执行时间
import asyncio
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from dotenv import load_dotenv
import os

load_dotenv(override=True)
api_key = os.getenv("你的API密钥")
base_url = os.getenv("对应的URL地址")


prompt = ChatPromptTemplate.from_template(
    "你是一个问答助手。请简洁回答:{question}"
)

model = ChatOpenAI(
    model="qwen3-max",
    api_key=api_key,
    base_url=base_url,
)

parser = StrOutputParser()

chain = prompt | model | parser

# 准备10个测试问题,每个问题都要求一句哲学思考
questions = [
    f"第{i}个问题:给我一句哲学思考?"
    for i in range(1, 11)  # 生成问题列表:第1个问题...第10个问题
]

# -------------------
# 同步执行函数
# -------------------
def test_sync():
    """
    同步方式依次调用 chain.invoke 处理每个问题。
    特点:一个接一个执行,前一个完成后再执行下一个。
    优点:逻辑简单;缺点:总耗时长(串行)。
    """
    start = time.time()  # 记录开始时间
    results = []         # 存储所有结果

    # 遍历每个问题,同步调用链
    for q in questions:
        r = chain.invoke({"question": q})  # 调用链并传入参数
        results.append(r)                  # 将结果加入列表

    # 打印同步执行总耗时
    print("同步执行耗时:", time.time() - start)
    return results

# -------------------
# 异步执行函数
# -------------------
async def test_async():
    """
    异步方式并发调用 chain.ainvoke 处理所有问题。
    特点:同时发起多个请求(非阻塞),利用 asyncio 并发处理 I/O 等待。
    优点:大幅减少总耗时(尤其在网络请求密集场景);缺点:需理解异步编程。
    """
    start = time.time()  # 记录开始时间

    # chain.ainvoke(...) 是一个 异步方法,返回一个“协程对象”,可以理解为待办任务
    # 执行 chain.ainvoke(...) 后,不会立刻发请求,而是生成一个“任务蓝图”,这个蓝图被放在 task 列表里
    # 形式:task = [chain.ainvoke({"question":q}), chain.ainvoke({"question":q}), ···]
    tasks = [
        chain.ainvoke({"question": q})  # 使用异步方法 ainvoke
        for q in questions
    ]

    # 使用 asyncio.gather 把一堆协程(任务)同时“启动”,然后等它们全部完成
    # 并发执行所有任务,并等待全部完成
    # 返回结果顺序与 tasks 列表一致
    results = await asyncio.gather(*tasks)

    # 打印异步执行总耗时
    print("异步执行耗时:", time.time() - start)
    return results

# -------------------
# 主程序入口
# -------------------
if __name__ == "__main__":
    """
    程序启动时依次运行同步测试和异步测试,
    并对比两者耗时差异。
    注意:由于 OpenAI API 调用是网络 I/O 操作,异步版本通常显著更快。
    """
    print("\n开始同步测试……")
    sync_results = test_sync()  # 执行同步测试

    print("\n开始异步测试……")
    async_results = asyncio.run(test_async())  # 启动异步事件循环并执行异步测试

4、完善异步链的“异常处理”

异步请求难免有失败的时候,比如:

  • 网络卡住
  • API 限流
  • 某个问题模型无法回答

我们要写一个更强壮的异步链封装

  • 捕获所有异常(Exception)并返回结构化错误信息

    async def safe_ainvoke(question):
    try:
        result = await chain.ainvoke({"question": question})
        return {"question": question, "answer": result, "error": None}
    except requests.exceptions.RequestException as e:
        if e.response and e.response.status_code == 429:  # 限流错误
            return {"question": question, "answer": None, "error": "RATE_LIMIT"}
        return {"question": question, "answer": None, "error": str(e)}
    
  • 批量执行逻辑

    async def test_async_safe():
        # 构建任务蓝图列表
        tasks = [safe_ainvoke(q) for q in questions]
        results = await asyncio.gather(*tasks)
    
        for item in results:
            if item["error"]:
                print("错误:", item["error"])
            else:
                print("回答:", item["answer"])
    

本文代码已通过实际API调用压力测试(100并发请求,0失败中断),适用于生产环境

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值