Langchain入门到精通0x02:ICEL

chain

langchain框架的基础组织单元 ,解决"如何把AI模型、工具、逻辑按顺序组合成一个可执行任务"的问题。本质上是一个调用语言模型或其他工具、并按照特定顺序处理输入输出的对象。它封装了"接收输入 -> 执行一系列操作 -> 返回输出"的完整流程。

LCEL

LCEL (LangChain Expression Language) :这是框架的声明式组合语法 ,解决"如何更优雅、更健壮地构建和调用上述链"的问题。是langchain在2023年引入的革命性特性 。它提供了一种使用管道操作符 |声明式组合链的语法,极大地提升了代码的可读性、可维护性和内置了高级功能(如流式传输、异步、并行、重试等)。

  • 核心逻辑 :LCEL让链的构建从"面向对象式的配置"转变为"函数式的流水线组装"。每一个组件(如提示词模板、模型、输出解析器)都是一个可调用的对象,通过 |连接,表示"将前一个的输出作为后一个的输入"。

基本方法

  • stream: 流式返回响应的块
  • invoke: 接受输入返回输出
  • batch: 接受批量输入返回输出列表,核心是并发优化

batch效率

invoke我们之前已经用过,stream也简单是一个流式输出。batch的核心是并发优化,那么我们就可以简单写一个demo来印证下batch相比invoke的提效到底有多少。

Coding
构造chain
ini 复制代码
# 1. 创建LCEL链
chain = (
    ChatPromptTemplate.from_template("用一句话介绍{topic}")
    | ChatTongyi()
    | StrOutputParser()
)

# 2. 准备批量输入
topics = ["人工智能", "区块链", "量子计算", "基因编辑"]
inputs = [{"topic": topic} for topic in topics] 
invoke
ini 复制代码
# 3. 单次调用计时  串行执行,一个个主题执行
start = time.time()
single_results = [chain.invoke({"topic": topic}) for topic in topics]
single_time = time.time() - start
batch
ini 复制代码
start = time.time()
# 注意事项
# API供应商可能有批量请求限制(如每分钟请求数)
# 输入列表中的所有字典必须有相同的键结构
# 批量处理不适合有状态的操作(如带记忆的对话链)

batch_results = chain.batch(inputs)  # 关键批量调用方法
batch_time = time.time() - start
结果
python 复制代码
# 5. 结果对比
print(f"\n=== 单次调用耗时: {single_time:.2f}s ===")
for i, res in enumerate(single_results):
    print(f"{topics[i]}: {res}")

print(f"\n=== 批量调用耗时: {batch_time:.2f}s (加速 {single_time/batch_time:.1f}x) ===")
for i, res in enumerate(batch_results):
    print(f"{topics[i]}: {res}")

运行结果,正印证了batch的并行优化特性,他不仅仅是简单的批处理。

stream和batch

那stream又有什么特性呢?他和普通的invoke有何区别?

stream()方法返回一个生成器(Generator),可以逐块(chunk)地产生输出。这对于大语言模型生成文本、构建聊天机器人等需要实时反馈的场景至关重要。它能将首字符响应时间(Time to First Token, TTFT)优化到最佳,极大提升用户体验。

那么stream和batch到底该怎么选呢?我们结合invoke综合对比一下。

特性 invoke batch stream
🧠技术原理 同步阻塞的单次请求-响应,最简单、最直接的RPC模式。 并发/并行的批量作业,利用I/O多路复用或后端批量推理接口,实现并发处理 基于生成器(Generator)的增量处理。建立一种长连接或持续的数据通道,允许服务器在生成结果的过程中就逐步返回数据片段(如token)
🔑技术关键 错误处理与重试。重点是保证单次调用的健壮性,如网络超时、模型降级等策略。 1. 后端批量推理支持 :这是效率提升的前提。 2. 批次大小(Batch Size)优化 :寻找内存、延迟和吞吐量的最佳平衡点。 3. 动态批处理:高级系统能动态合并不同时间的请求 1. 前后端协议 :需使用SSE、WebSocket等支持流式的协议。 2. 增量解析 :下游组件必须能处理不完整的数据块。 3. 中断与取消:需要支持用户中途取消生成。
🎯核心目标 简单性、确定性与可靠性 最大化吞吐量(Throughput) 最小化延迟(Latency ),尤其是首字延迟(TTFT
📊数据处理 完整输入,完整输出 批量输入,批量输出 增量输入/输出
⚙️资源利用 通常最低。请求间存在大量空闲的I/O等待时间(网络、磁盘),CPU/GPU经常处于闲置状态。 最高。通过将计算任务密集打包,让GPU等昂贵硬件满负荷运转,摊薄了单次请求的固定开销。 中等但更公平。计算资源随用随取,适合高并发在线服务,能更公平、及时地响应多个并发的流式请求。
😀用户体验 "等待-结果"模式。可能存在等待焦虑。 无实时交互的离线体验。适用于后台任务。 "实时交互"模式。持续反馈,体验流畅、自然,符合人类对话习惯。
🚀应用场景 1. 简单的单轮问答。 2. 执行一次性的、独立的工具调用。 3. 原型开发与功能测试。 1. 离线数据处理 :批量生成文档摘要、嵌入向量。 2. 报表生成 :夜间批量分析日志,生成日报。 3. 模型评估:在测试集上批量运行模型以评估指标。 1. 对话式AI :如ChatGPT,逐字生成回复。 2. 实时翻译/字幕 :语音或文字流实时转换。 3. 代码补全 :IDE中随着输入实时推荐代码。 4. 长文生成:逐步输出文章段落,让用户提前阅读。

func Vs chain

缘起

我们之前传统的编程中,一个集中的功能块被称为函数。当然ICEL中内置并自动连接了许多函数,那么当我们需要将一个自定义的函数运用到ICEL中该怎么办?

  • 函数 :是执行特定任务的原子操作。它接收输入,返回输出,但其内部逻辑对调用者是不透明的,通常缺乏执行过程的可观测性,也难以与其他AI组件(如LLM调用、工具调用)无缝连接。

  • LCEL Chain :是使用LCEL语法(主要通过 |操作符)将多个可运行体 ​ 连接起来的工作流。其核心是 Runnable协议 。任何实现了此协议的对象(如LLM模型、工具、甚至另一个Chain)都可以被连接。Chain的优势在于提供了标准的接口内置的流式处理日志与追踪 ,以及异步、批处理等能力

Runnable协议

可见,其转换核心便是 Runnable 协议。其本质:为所有能在LCEL管道(通过 |连接)中工作的对象,规定了一套必须实现的"通用语言"和"标准操作"

简单说,它是LCEL这个"生态系统"里的宪法,确保了千差万别的组件(从LLM调用、工具函数到简单的数据转换)能够用统一的方式进行交互、组合和执行。

协议核心的方法主要就是上面讲到的几个LCEL基本方法:

  1. invoke(input, config?) -> output (同步调用)
    • 功能:最基础的调用方式,输入一个值,返回一个值。
    • 工程意义:定义了组件最直接的、阻塞式的单次执行逻辑。
  2. ainvoke(input, config?) -> output (异步调用)
    • 功能:invoke的异步版本。
    • 工程意义:支撑高并发应用的关键。当链中有网络请求(如调用LLM API)时,使用此方法可避免阻塞,极大提升吞吐量。
  3. batch(inputs, config?) -> outputs (同步批处理)
    • 功能:接受一个输入列表,返回一个输出列表。注意:它不一定是简单的for循环,框架或具体实现可能会进行并行优化。
    • 工程意义:为批量处理任务提供标准化接口,是高效数据处理的基础。
  4. abatch(inputs, config?) -> outputs (异步批处理)
    • 功能:batch的异步版本,是处理大批量任务的推荐方式。
    • 工程意义:结合了批量与并发的优势,是实现高性能AI应用流水线的核心技术。
  5. stream(input, config?) -> Iterator[output] (流式输出)
    • 功能:返回一个生成器,逐步产出结果。这对于LLM生成长文本、实时返回中间结果至关重要。
    • 工程意义:实现了端到端的流式用户体验,是构建响应式应用的核心。

至于,将函数转换到LCEL中使用的方法。后面会着重讲解。

RunnableSequence

顺序处理 ,处理链的"传送带"。是LCEL中最基础、最常用的组件,用于顺序执行多个任务。它将前一个"可运行对象"(Runnable)的输出,作为后一个的输入。

  • 本质:定义了一个函数调用链 f(g(h(x)))。每个环节可以是LLM调用、提示词模板、输出解析器或任意函数。LCEL负责处理类型校验、异步支持、流式传输等底层复杂性。或者更简单地说就是我们以往用的|连接符。
  • case:
ini 复制代码
# 顺序执行每一个节点
chain = RunnableSequence(prompt,model,out)
# 等同于 | 连接符,如下
# chain = prompt | model | out

RunnableParallel

并行处理 ,任务分发的"多叉路口"。接收一个输入,同时分发给多个Runnable,各分支独立运行,最后将结果按指定键名汇总。

  • RunnableSequence组合 :这是构建复杂Agent的核心模式。通常结构为:Parallel(并行收集信息)-> Sequence(串联分析决策)。
  • case:
python 复制代码
# 1. 模拟两个"信息源"------通常它们是外部API、数据库查询或工具调用
def query_knowledge_base(product_name: str) -> str:
    """模拟查询产品知识库(可能访问数据库或内部文档)"""
    time.sleep(0.2)  # 模拟网络延迟
    knowledge = {
        "智能音箱X3": "这是我们的旗舰款智能音箱。核心特色:1. 搭载8核AI芯片,唤醒率99.9%;2. 支持全屋智能联动;3. 内置Hi-Fi级音响,获得金耳朵认证;4. 待机时间长达72小时。"
    }
    return knowledge.get(product_name, "未找到该产品的官方资料。")

def query_recent_feedback(product_name: str) -> str:
    """模拟查询近期用户评论(可能调用舆情分析API)"""
    time.sleep(0.3)  # 模拟另一个服务的延迟
    feedback_db = {
        "智能音箱X3": "最近30天用户评价精华:1. 音质受到普遍好评,低音表现突出;2. 与'智慧家居'App偶尔出现连接不稳定(占比约5%的反馈);3. 新出的'儿童模式'很受家庭用户欢迎。"
    }
    return feedback_db.get(product_name, "暂无该产品的近期用户反馈。")


# 2. 核心:构建 RunnableParallel 来并行收集
# 它接收一个字典,定义多个并行的执行分支
parallel_info_gathering = RunnableParallel({
    "official_info": RunnableLambda(lambda x: query_knowledge_base(x["product_name"])),
    "user_feedback": RunnableLambda(lambda x: query_recent_feedback(x["product_name"])),
    # RunnablePassthrough() 用于将原始输入(如product_name)也传递下去
    "product_name": RunnablePassthrough()
})

# 4. 构建 RunnableSequence 来串联分析与决策
# 这是一个提示词模板,它将接收parallel_info_gathering输出的所有信息
analysis_prompt = ChatPromptTemplate.from_template("""
你是一名专业的客服助理。请根据以下关于产品"{product_name}"的信息,生成一份给用户的回复要点。

【官方产品信息】
{official_info}

【近期用户反馈摘要】
{user_feedback}

---
生成要求:
1. 首先概括产品的核心优势(来自官方信息)。
2. 然后,提及用户反馈中注意到的亮点。
3. 最后,如果用户反馈中提到了任何潜在问题或顾虑,请用委婉、专业的方式在回复中有所提及或安抚。
4. 回复语言口语化,亲切,面向最终消费者。
请直接输出回复内容:
""")

# 5. 将并行和串行组合成完整的链
# 使用管道操作符 `|` 连接,形成:输入 -> Parallel -> Sequence -> 输出
agent_chain = (
        {"product_name": RunnablePassthrough()}  # 将用户输入转化为字典格式,键为"product_name"
        | parallel_info_gathering
        | analysis_prompt
        | ChatTongyi()
        | StrOutputParser()
)

RunnablePassthrough

LCEL中的 "透明管道"与"数据中继",是一个什么也不做的"透明人"。他好比一个快递员,他只负责把文件原封不动地从一个部门送到下一个部门,不查看、不修改。

数据传递

就是啥也不做,原样传递。前面在RunnableParallel的示例代码中已经见过,这里的作用就是将原始输入(如product_name)原封不动地传递下去

css 复制代码
parallel_info_gathering = RunnableParallel({
    "official_info": RunnableLambda(lambda x: query_knowledge_base(x["product_name"])),
    "user_feedback": RunnableLambda(lambda x: query_recent_feedback(x["product_name"])),
    # RunnablePassthrough() 用于将原始输入(如product_name)也传递下去
    "product_name": RunnablePassthrough()
})

数据增强-assign特性

利用assign特性将数据增强后继续传递。如下,执行后:

  • 原数据:{'k1': 'hello world'}
  • 新数据:{'k1': 'hello world', 'modified': 'hello world!!!'}
ini 复制代码
# 数据增强,增强后进行继续传递
chain = RunnableParallel(
    passed = RunnablePassthrough().assign(modified= lambda x: x["k1"]+"!!!"),
)
# 增强调用,需要使用字典格式
print(chain.invoke({"k1": "hello world"}))

RunnableLambda

函数包装器 ,接入旧世界的"万能适配器"。我们前面讲到的自定义函数在LCEL中使用就是需要使用这个。它包装一个普通函数,使其符合Runnable接口,从而能够与其他LCEL组件无缝衔接。

示例函数

python 复制代码
# 创建一个Pydantic模型, 用于估算费用
class TripDetails(BaseModel):
    destination: str = Field(description="旅行目的地城市")
    duration: int = Field(description="旅行天数")
    estimated_cost_per_person: float = Field(description="估算人均成本", default=None)
    summary: str = Field(description="给用户的旅行建议摘要")

def calculate_total_cost(trip_details: TripDetails) -> TripDetails:
    """根据旅行天数和目的地,估算一个非常粗略的人均成本"""
    daily_cost = {"北京": 600, "东京": 1200}.get(trip_details.destination, 500)
    trip_details.estimated_cost_per_person = daily_cost * trip_details.duration
    return trip_details

方法1-RunnableLambda

ini 复制代码
# 将函数封装为Runnable
chain = prompt | model | out | RunnableLambda(calculate_total_cost)

方法2-@chain装饰器

python 复制代码
# 使用@chain装饰器
@chain
def chain_calculate_total_cost(trip_info: dict) -> dict:
    return calculate_total_cost(trip_info)

# 直接使用装饰后的方法名,无需RunnableLambda
chain = prompt | model | out | chain_calculate_total_cost

其他代码

ini 复制代码
model = ChatTongyi()
out = PydanticOutputParser(pydantic_object=TripDetails)
format_instructions = out.get_format_instructions()
prompt = ChatPromptTemplate.from_template("""
你是一名专业的旅游助手。请根据以下关于旅游信息"{tripInfo}",生成一份给用户的回复要点\n{format_instructions}。

【旅游信息】
{tripInfo}

""")
prompt = prompt.partial(format_instructions=format_instructions)

# 方法1
chain = prompt | model | out | RunnableLambda(calculate_total_cost)
# 方法2
# chain = prompt | model | out | chain_calculate_total_cost
result = chain.invoke({"tripInfo": "北京-东京 3天"})
print(result.model_dump()) # 查看完整的结构化结果
print("-" * 50)
print(result.summary) # 访问文本回复
print("-" * 50)
print(f"估算成本:{result.estimated_cost_per_person}") # 访问计算后的成本

RunnableMap

数据转换器 ,规整数据的"操作台"。常用于在链中顺序执行输入数据的转换,是"精加工,重新包装"的过程,核心目标在于为下一步做准备。常用语数据分析师整理和清洗已收集的数据。

  • RunnableMap与RunnableLambda的关键区别在于意图 。Map明确表示"我将输入映射为一个新的字典",这通常用于数据整形。而Lambda可以返回任何类型。在复杂链中,用Map来保证数据接口一致性,能使流程更清晰。

一个简单的🌰:电商商品信息处理流水线 - RunnableMap。场景:处理用户上传的商品信息,生成标准化商品详情页

示例数据

makefile 复制代码
# 示例输入:用户上传的商品原始数据
raw_product_data = {
    "title": "Apple iPhone 15 Pro Max 256GB 原色钛金属 行货正品",
    "price": "8999.00",
    "seller": "Apple官方旗舰店",
    "specs": "6.7英寸, A17 Pro芯片, 4800万像素, 5倍长焦",
    "tags": "手机|苹果|iPhone|旗舰|5G",
    "description": "全新iPhone 15 Pro Max,采用航空级钛金属设计,史上最轻的Pro机型。",
    "timestamp": "2024-03-15T14:30:00Z"
}

多个处理函数

📢:数据处理流水线,这里每个步骤都依赖上一步的输出。

  • 提取基础信息
python 复制代码
def extract_basic_info(data: Dict) -> Dict:
    """提取基础信息 - 假设我们需要品牌和型号"""
    title = data.get("title", "")
    # 简单解析品牌和型号
    if "iPhone" in title:
        brand = "Apple"
        model = "iPhone 15 Pro Max"
    elif "小米" in title or "Xiaomi" in title:
        brand = "Xiaomi"
        model = "未知型号"
    else:
        brand = "其他"
        model = "未知"

    return {
        "brand": brand,
        "model": model,
        "title_cleaned": title.replace("行货正品", "").strip()
    }
  • 解析价格信息
python 复制代码
def parse_price_info(data: Dict) -> Dict:
    """解析价格信息 - 依赖基础信息中的品牌"""
    price_str = data.get("price", "0")
    try:
        price = float(price_str)
    except:
        price = 0.0

    # 根据品牌确定货币单位
    brand = data.get("brand", "未知")
    currency = "CNY" if brand in ["Apple", "Xiaomi", "华为"] else "USD"

    return {
        "price_numeric": price,
        "currency": currency,
        "price_formatted": f"{currency} {price:,.2f}"
    }
  • 分类商品
python 复制代码
def categorize_product(data: Dict) -> Dict:
    """分类商品 - 依赖清理后的标题和价格"""
    title = data.get("title_cleaned", "")
    price = data.get("price_numeric", 0)

    category = "电子产品"
    subcategory = "手机"

    if price > 8000:
        price_segment = "高端"
    elif price > 3000:
        price_segment = "中端"
    else:
        price_segment = "入门"

    return {
        "category": category,
        "subcategory": subcategory,
        "price_segment": price_segment
    }

RunnableMap

scss 复制代码
# 1.2 使用RunnableMap构建数据处理流水线
# 注意:这里每个步骤都依赖上一步的输出
data_processing_pipeline = RunnableMap({
    "basic_info": RunnableLambda(extract_basic_info),
    "price_info": RunnableLambda(lambda x: parse_price_info({**x, **extract_basic_info(x)})),
    "category_info": RunnableLambda(lambda x: categorize_product({
        **x,
        **extract_basic_info(x),
        **parse_price_info({**x, **extract_basic_info(x)})
    })),
    "original_data": RunnablePassthrough()
})

print("执行RunnableMap数据清洗...")
start_time = time.time()
processed_data = data_processing_pipeline.invoke(raw_product_data)
map_time = time.time() - start_time

print(f"\n✅ RunnableMap处理结果(耗时: {map_time:.3f}秒):")
print("清洗后的数据:")
for key, value in processed_data.items():
    if key != "original_data":
        print(f"  {key}: {value}")

🤔和RunnableParallel混用思考???

RunnableMap处理有依赖的数据,如果这个【电商项目】继续展开还能做啥?

  1. 使用RunnableParallel并行获取:

    • 竞品信息
    • 市场分析
    • 库存和物流
    • SEO关键词
  2. 获取到后再使用RunnableMap,依赖前面的生成一个【电商项目xxx报告】

  3. 总结RunnableMap + RunnableParallel的混合使用场景:

    • 先用Map清洗/准备数据(清洗、验证、标准化)
    • 然后用Parallel并发独立操作(并发获取外部数据)
    • 最后用另一个Map整合结果(整合、丰富、格式化)

RunnableBranch

LCEL中实现条件逻辑动态路由 的核心组件,是流程"决策树" 。它是实现if-else逻辑的核心,其根据条件动态选择下一步执行哪个分支。它接收一个(条件, 分支)的列表和一个默认分支。例如,根据用户问题意图判断是走"问答分支"、"数据查询分支"还是"闲聊分支"。

在大模型应用中,简单地说就好比你问一个问题,RunnableBranch先判断这个问题属于哪个分治,然后调用哪个分治链来解决。废话少说上🌰:一个产品信息系统,包括价格查询、技术咨询、退款流程等。

条件判断函数

python 复制代码
# 1. 定义条件判断函数
def classify_query(query_data: dict) -> str:
    """根据查询内容分类"""
    query = query_data.get("query", "").lower()

    if "价格" in query or "多少钱" in query or "cost" in query:
        return "price_inquiry"
    elif "故障" in query or "问题" in query or "error" in query:
        return "technical_issue"
    elif "退货" in query or "退款" in query or "return" in query:
        return "refund_request"
    elif "客服" in query or "人工" in query or "support" in query:
        return "human_support"
    else:
        return "general_inquiry"

分支链

价格咨询分支
python 复制代码
# 价格查询分支
price_chain = (
        ChatPromptTemplate.from_template("""
    你是一个专业的销售顾问。用户询问价格信息。

    用户查询:{query}

    请以专业、友好的方式回答价格相关问题。
    如果知道具体价格,请明确告知。
    如果不知道,请提供获取价格的途径。
    """)
        | llm
        | StrOutputParser()
)
技术咨询分支
python 复制代码
# 技术问题分支
tech_chain = (
        ChatPromptTemplate.from_template("""
    你是一个技术专家。用户遇到技术问题。

    用户查询:{query}

    请提供详细的技术解决方案。
    分步骤说明,确保用户能理解。
    如果问题复杂,建议联系技术支持。
    """)
        | llm
        | StrOutputParser()
)
退款分支
python 复制代码
# 退款请求分支
refund_chain = (
        ChatPromptTemplate.from_template("""
    你是一个客服专员。用户希望退款或退货。

    用户查询:{query}

    请友好地解释退款政策。
    询问订单详细信息以便协助。
    提供明确的后续步骤。
    """)
        | llm
        | StrOutputParser()
)
默认查询分治

这里就像我们传统编程的switch,为了程序健壮性,总是有一个默认default。

python 复制代码
# 通用查询分支
general_chain = (
        ChatPromptTemplate.from_template("""
    你是一个有用的助手。回答用户的通用查询。

    用户查询:{query}

    请提供有帮助的回答。
    """)
        | llm
        | StrOutputParser()
)

RunnableBranch

python 复制代码
# 3. 创建RunnableBranch
# 格式:RunnableBranch( (条件1, 分支1), (条件2, 分支2), ..., 默认分支 )
query_router = RunnableBranch(
    # 条件是一个函数,接收输入数据,返回True/False
    (lambda x: classify_query(x) == "price_inquiry", price_chain),
    (lambda x: classify_query(x) == "technical_issue", tech_chain),
    (lambda x: classify_query(x) == "refund_request", refund_chain),
    # 默认分支(当所有条件都不满足时执行)
    general_chain
)

完整链

ini 复制代码
# 4. 构建完整链:接收查询 -> 路由 -> 处理
full_chain = RunnableLambda(
    lambda x: {"query": x}  # 将字符串包装为字典
) | query_router

测试代码

python 复制代码
# 5. 测试不同查询
test_queries = [
    "iPhone 15的价格是多少?",
    "我的手机无法开机,怎么办?",
    "我想退货,流程是什么?",
    "转人工客服",
    "你们公司的营业时间是什么?",
    "推荐一款适合拍照的手机"
]

print("测试不同查询的路由结果:\n")
for i, query in enumerate(test_queries, 1):
    print(f"{i}. 查询: {query}")
    print("-" * 40)

    start_time = time.time()
    try:
        response = full_chain.invoke(query)
        elapsed = time.time() - start_time

        # 显示分类结果
        category = classify_query({"query": query})
        print(f"分类: {category}")
        print(f"响应时间: {elapsed:.2f}秒")
        print(f"回答: {response}")
    except Exception as e:
        print(f"错误: {e}")

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

测试结果

RunnableWithMessageHistory

对话的"记忆体",这是构建对话式Agent的基石。今天不做扩展,后续我们在记忆模块再做专题学习。

综合对比

特性 技术原理 核心功能 应用场景 注意事项 性能特点 组合模式
RunnableSequence 函数管道模式,将多个Runnable按顺序组合,前一个输出作为后一个输入 顺序执行多个任务,形成处理流水线 1. 多步骤处理流程 2. LLM调用链 3. 数据预处理+模型调用+后处理 1. 链条过长时调试困难 2. 错误会沿链传递 3. 需注意步骤间的数据类型匹配 总耗时=各步骤耗时之和,无并行优化 基础组合单元,可与所有其他组件组合
RunnableParallel 并行执行模式,将同一输入同时分发给多个独立分支,结果合并为字典 并行执行多个独立任务,提升吞吐量 1. 同时调用多个外部API 2. 并行查询多个数据源 3. 独立的数据处理任务 1. 分支间不能有数据依赖 2. 一个分支失败可能影响整个并行块 3. 需注意资源竞争 总耗时≈最慢分支耗时,显著提升IO密集型任务性能 常与Sequence组合:Parallel收集 → Sequence处理
RunnableLambda 适配器模式,将任意Python函数包装为Runnable接口 集成自定义业务逻辑到LCEL链 1. 调用外部API/数据库 2. 复杂业务规则计算 3. 数据验证与清洗 4. 格式转换 1. 过度使用破坏声明式风格 2. 函数应尽量保持纯函数特性 3. 需自行处理错误 取决于函数逻辑,可能成为性能瓶颈 可插入任何需要自定义逻辑的位置
RunnableMap 数据转换模式,对输入进行结构化转换,输出单个字典 数据清洗、格式标准化、字段提取与映射 1. 数据预处理 2. API响应格式化 3. 特征工程 4. 输入输出格式适配 1. 常与RunnableLambda混淆 2. 输出必须是字典 3. 是顺序执行,非并行 顺序执行,总耗时=各转换步骤之和 常用于链的开头(数据准备)或结尾(结果格式化)
RunnableBranch 条件路由模式,根据条件函数动态选择执行分支 实现if-else逻辑,动态工作流路由 1. 意图识别与路由 2. 多级决策树 3. 异常处理与降级 4. A/B测试路由 1. 条件顺序重要(短路评估) 2. 条件函数应轻量 3. 必须提供默认分支 4. 条件函数应是纯函数 条件判断开销+选中分支开销,条件函数应高效 可构建复杂决策树,常与Parallel组合实现动态工作流
RunnablePassthrough 恒等函数模式,原样传递输入,不做任何处理 数据传递、上下文保留、占位符 1. 在Parallel中保留原始输入 2. 链中传递上下文 3. 调试时检查中间状态 4. 配合.assign()动态添加字段 1. 常被误解为"无用"组件 2. 不提供并行能力 3. 可能意外传递可变对象引用 近乎零开销,是最轻量的Runnable 常与Parallel组合保留原始数据,或用于链中数据传递

源码

github

相关推荐
Tzarevich1 小时前
从零手写一个“迷你版 Cursor”:让 AI 真正帮你写代码
langchain·node.js·agent
吴佳浩2 小时前
什么是算力?
人工智能·pytorch·llm
charlex2 小时前
【陈同学】走进 AI Agent:从“对话框”到“自主智能体”
人工智能·agent
百度Geek说3 小时前
打造高效易用的Agent Skill
人工智能
豆芽包3 小时前
实战部署OpenClaw
人工智能
大厂码农老A3 小时前
3天实现"睡后收入"—— Cursor & Skills打造"全自动出海"Agent
人工智能·aigc·ai编程
攻城羊Weslie4 小时前
🐑 从手动到自动:Yi-Shepherd 如何驯服 150+ 个 AI 模型
人工智能·程序员·开源
肥晨4 小时前
OpenClaw 卸载不完全?手把手教你“连根拔起”
人工智能
前端小趴蔡4 小时前
web2api 开源了(稳定的claude2api方案)
人工智能