LangChain快速筑基(带代码)P2-链式调用Chains

98 阅读4分钟

继续上篇内容。了解在解析中出现的讲多个步骤串联起来的链Chains。

概念补充

  • StrOutputParser StrOutputParser() 是 LangChain 里的一个输出解析器,其主要功能是把语言模型(LLM)返回的输出转换为字符串类型。需要纯文本内容时使用。
  • LCEL LCEL 是 LangChain 引入的一种新的链式调用语法,它借助 |(管道运算符)来构建链式操作。这种语法让开发者能够以更直观、简洁的方式组合不同的组件,例如提示模板、语言模型、输出解析器等。 比如不使用LCEL时创建链实例将llm与prompt串联起来为
title_chain = LLMChain(llm=llm_creative, prompt=title_prompt)

使用LCEL为

title_chain = title_prompt | llm_creative | StrOutputParser() # LCEL
  • RunnablePassthrough RunnablePassthrough 可以用来传递原始输入或修改字典。RunnablePassthrough.assign() 允许你将前一个步骤的输出(或者原始输入)分配给一个新的键,或者合并到现有的字典中,从而为下一个步骤准备好输入。

多步骤串联:步骤链条Chains

使用LangChain可以将多个步骤串联起来,形成一个处理流程,也就是Chains。 这个“步骤”可以是LLM调用或其他组件串联。 最简单的链就是“提示模板 + LLM”。复杂的链可以包含多个LLM调用,或者LLM调用与工具使用等。

SimpleSequentialChain:简单顺序链

SimpleSequentialChain可以将多个子链串联起来作为简单顺序链,前一个链的输出直接作为下一个链的输入。每个子链都只有一个输入和一个输出。

场景: 步骤1 (LLM调用): 用户提供一个主题,LLM写一个关于该主题的简短故事的标题。 步骤2 (LLM调用): 将上一步生成的标题作为输入,LLM基于这个标题写一个简短的故事情节摘要。

import os

# 基础对话所需
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import SystemMessage, HumanMessage

# 提示模板
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
# 输出解析器
from langchain_core.output_parsers import  StrOutputParser

# 链条
from langchain.chains import LLMChain, SimpleSequentialChain, SequentialChain

# 用于传递原始输入或修改字典
from langchain_core.runnables import RunnablePassthrough


# 环境key
os.environ["DEEPSEEK_API_KEY"] = 'sk-xxx'

def get_deepseek_key():
    key = os.getenv('DEEPSEEK_API_KEY')
    if key is None:
        raise ValueError("DEEPSEEK_API_KEY not found in environment variables.")
    return key

# --- LLM 初始化 ---
def create_deepseek_llm():
    api_key = get_deepseek_key()
    if not api_key:
        raise ValueError("没有ds key")
    return ChatDeepSeek(
        model = "deepseek-chat",
        temperature=1, # 低温度,更具备确定性
        max_tokens=1024,
        timeout=None,
        max_retries=2,
        api_key=api_key
    )

def test_simple_sequential_chain():
    print("测试简单序列链条")
    # step 1:创建生成故事标题的链实例1  
    deepseek = create_deepseek_llm()
    title_prompt = ChatPromptTemplate.from_template(
        "你是一位富有想象力的作家。为一个关于“{topic}”的短篇故事写一个引人入胜的标题。"
    )
    title_chain = title_prompt | deepseek | StrOutputParser() # 使用LCEL
    
    # step 2:创建基于标题写故事情节的链实例2
    synopsis_prompt = ChatPromptTemplate.from_template(
        "你是一位编剧。根据以下标题写一个简短的故事情节(大约100字):\n标题:{story_title}"
    )
    """
    SimpleSequentialChain 期望每个链只有一个输入(在第一个链中)和一个输出
    它会自动将第一个链的输出传递给第二个链的输入(变量名需要匹配或自动推断)
    如果前一个链的输出是字符串,且后一个链的提示模板只有一个输入变量,它会自动映射
    """
    synopsis_chain = synopsis_prompt | deepseek | StrOutputParser() 
    
    """
    setp 3:在链之间传递值 使用RunnablePassthrough.assign()
    
    """
    full_chain = RunnablePassthrough.assign(
            story_title_from_llm  = title_chain
        ) | {"story_title":lambda x: x["story_title_from_llm"],} | synopsis_chain 
    
    # step 4:运行链
    topic_input = "一个能与动物对话的女孩"
    try:
        print(f"\n为主题“{topic_input}”生成故事:")
        synopsis = full_chain.invoke({"topic":"一个能与动物对话的女孩"})
        print(f"生成的故事概要: {synopsis}")
    except Exception as e:
        import traceback
        print(f"\n生成故事失败: {e}")
        traceback.print_exc()

if __name__ == '__main__':
    test_simple_sequential_chain()
  • 输出测试 ![[【LangChain】LangChain2-基础概念P2-链式调用Chains.png]]

SequentialChain

SequentialChain 更加灵活,它允许链中的每个子链有多个输入和输出,并且你可以明确指定如何将前一个链的输出映射到下一个链的输入。你还可以访问所有中间步骤的输出。 (多个输入/输出,可以访问中间步骤结果)