AI代码开发宝库系列:LangChain 工具链:从LCEL到实际应用

深入理解LangChain工具链:从LCEL到实际应用

在人工智能和大语言模型(LLM)快速发展的今天,LangChain作为一个强大的框架,为我们提供了构建复杂AI应用的工具。其中,工具链(ToolChain)和LCEL(LangChain Expression Language)是LangChain的核心概念,它们让开发者能够轻松地组合和编排不同的组件,构建出功能强大的AI应用。

一、什么是LangChain工具链?

LangChain工具链是指将多个工具按特定顺序组合起来执行复杂任务的方式。它们可以:

  1. 按顺序执行多个相关工具:一个工具的输出可以作为下一个工具的输入

  2. 并行处理多个独立任务:提高执行效率

  3. 实现条件分支:根据不同的输入选择不同的处理路径

  4. 构建复杂的业务逻辑:通过组合简单的工具构建复杂的应用

二、LCEL(LangChain Expression Language)简介

LCEL是LangChain提供的声明式编程语言,专门用于快速构建和优化链的语法工具。它具有以下优势:

  1. 简洁性 :使用管道符|连接组件,代码更加简洁易读

  2. 灵活性:支持分支、条件判断、并行执行

  3. 统一接口:所有组件都遵循Runnable接口,具有统一的方法

  4. 原生支持异步、流式处理和批处理

三、核心组件详解

1. RunnableLambda

RunnableLambda是将Python函数(包括lambda函数和普通函数)封装成可运行组件的工具类。它允许开发者在链中插入自定义逻辑,如数据转换、过滤或格式化。

复制代码
from langchain_core.runnables import RunnableLambda
​
# 使用lambda函数
runnable = RunnableLambda(lambda x: x.upper())
​
# 使用普通函数
def add_prefix(text: str) -> str:
    return f"处理: {text}"
​
runnable = RunnableLambda(add_prefix)

2. RunnableSequence

RunnableSequence用于顺序执行一系列流程,前一个组件的输出作为下一个组件的输入。使用|操作符可以轻松创建RunnableSequence。

复制代码
chain = prompt | model | output_parser

3. RunnableParallel

RunnableParallel用于并行执行多个组件,为每个组件提供相同的输入。它通过字典形式定义,最终返回一个包含所有结果的字典。

复制代码
from langchain_core.runnables import RunnableParallel
​
parallel_chain = RunnableParallel({
    "summary": summarize_chain,
    "translation": translate_chain
})

四、实际应用案例

1. 文本分析工具链

让我们通过一个实际的例子来理解工具链的工作原理。以下是一个简单的文本分析工具链示例:

复制代码
# 自定义工具1:文本分析工具
class TextAnalysisTool:
    """文本分析工具,用于分析文本内容"""
    def __init__(self):
        self.name = "文本分析"
        self.description = "分析文本内容,提取字数、字符数和情感倾向"
    
    def run(self, text: str) -> str:
        word_count = len(text.split())
        char_count = len(text)
        # 简单的情感分析
        positive_words = ["好", "优秀", "喜欢", "快乐", "成功", "美好"]
        negative_words = ["差", "糟糕", "讨厌", "悲伤", "失败", "痛苦"]
        positive_count = sum(1 for word in positive_words if word in text)
        negative_count = sum(1 for word in negative_words if word in text)
        sentiment = "积极" if positive_count > negative_count else "消极" if negative_count > positive_count else "中性"
        return f"文本分析结果:\n- 字数: {word_count}\n- 字符数: {char_count}\n- 情感倾向: {sentiment}"
​
# 自定义工具2:数据转换工具
class DataConversionTool:
    """数据转换工具,用于在不同格式之间转换数据"""
    def __init__(self):
        self.name = "数据转换"
        self.description = "在不同数据格式之间转换,如JSON、CSV等"
    
    def run(self, input_data: str, input_format: str, output_format: str) -> str:
        # 实现数据格式转换逻辑
        pass
​
# 自定义工具3:文本处理工具
class TextProcessingTool:
    """文本处理工具,用于处理文本内容"""
    def __init__(self):
        self.name = "文本处理"
        self.description = "处理文本内容,如查找、替换、统计等"
    
    def run(self, operation: str, content: str, **kwargs) -> str:
        # 实现文本处理逻辑
        pass

2. 使用LCEL构建工具链

复制代码
# 工具实例
text_analysis = TextAnalysisTool()
data_conversion = DataConversionTool()
text_processing = TextProcessingTool()
​
# 工具链(LCEL风格)
tools = {
    "文本分析": RunnableLambda(lambda x: text_analysis.run(x["text"])),
    "数据转换": RunnableLambda(lambda x: data_conversion.run(x["input_data"], x["input_format"], x["output_format"])),
    "文本处理": RunnableLambda(lambda x: text_processing.run(x["operation"], x["content"], **x.get("kwargs", {}))),
}
​
# 示例:LCEL任务链
def lcel_task_chain(task_type, params):
    """
    LCEL风格的任务链调度
    参数:
        task_type: 工具名称
        params: 参数字典
    返回:
        工具执行结果
    """
    if task_type not in tools:
        return "不支持的工具类型"
    return tools[task_type].invoke(params)

3. 金融领域应用

在金融领域,LangChain工具链可以用于构建智能分析助手:

复制代码
# 金融数据分析工具链示例
financial_analysis_chain = (
    {"stock_data": stock_data_retriever, 
     "news_sentiment": news_sentiment_analyzer,
     "market_trends": market_trend_analyzer}
    | financial_risk_assessor
    | investment_recommendation_generator
)

4. 客服系统应用

在智能客服系统中,工具链可以处理用户查询并提供个性化服务:

复制代码
# 智能客服工具链示例
customer_service_chain = (
    user_query_classifier 
    | {
        "faq": faq_retriever,
        "complaint": complaint_handler,
        "sales": product_recommender
    }
    | response_generator
)

五、工具链的优势

  1. 模块化设计:每个工具都是独立的组件,可以单独测试和维护

  2. 可复用性:工具可以在不同的链中重复使用

  3. 可扩展性:可以轻松添加新的工具或修改现有工具

  4. 灵活性:支持顺序执行、并行执行和条件分支

  5. 易于调试:每个工具的输入输出都清晰可见,便于问题定位

六、最佳实践

  1. 合理设计工具接口:确保工具的输入输出格式清晰一致

  2. 错误处理:为每个工具添加适当的错误处理机制

  3. 性能优化:对于耗时操作,考虑使用异步处理或缓存机制

  4. 文档完善:为每个工具提供详细的文档说明

  5. 测试覆盖:为每个工具编写单元测试,确保功能正确性

七、总结

LangChain工具链和LCEL为我们提供了一种强大而灵活的方式来构建AI应用。通过将复杂的任务分解为简单的工具,并使用LCEL进行组合,我们可以快速构建出功能丰富、性能优良的AI应用。无论是在金融分析、智能客服还是其他领域,工具链都能发挥重要作用,帮助我们提高开发效率和应用质量。

随着AI技术的不断发展,LangChain工具链将会在更多场景中得到应用,成为构建智能应用的重要工具。掌握工具链的使用方法,对于AI开发者来说具有重要意义。


作者简介:本文作者是AI技术爱好者,专注于大语言模型应用开发,欢迎关注我的博客获取更多AI技术分享。

版权声明:本文为博主原创文章,转载请注明出处。

代码

复制代码
# 2-simple_toolchain.py
# 使用 LCEL(LangChain Expression Language)方式构建多工具任务链

from langchain_community.llms import Tongyi
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
from langchain_core.runnables import RunnableLambda, RunnableMap, RunnablePassthrough
from langchain.agents import Tool
import json
import os
import dashscope

# 从环境变量获取 dashscope 的 API Key
api_key = os.environ.get('DASHSCOPE_API_KEY')
dashscope.api_key = api_key

# 自定义工具1:文本分析工具
class TextAnalysisTool:
    """文本分析工具,用于分析文本内容"""
    def __init__(self):
        self.name = "文本分析"
        self.description = "分析文本内容,提取字数、字符数和情感倾向"
    def run(self, text: str) -> str:
        word_count = len(text.split())
        char_count = len(text)
        positive_words = ["好", "优秀", "喜欢", "快乐", "成功", "美好"]
        negative_words = ["差", "糟糕", "讨厌", "悲伤", "失败", "痛苦"]
        positive_count = sum(1 for word in positive_words if word in text)
        negative_count = sum(1 for word in negative_words if word in text)
        sentiment = "积极" if positive_count > negative_count else "消极" if negative_count > positive_count else "中性"
        return f"文本分析结果:\n- 字数: {word_count}\n- 字符数: {char_count}\n- 情感倾向: {sentiment}"

# 自定义工具2:数据转换工具
class DataConversionTool:
    """数据转换工具,用于在不同格式之间转换数据"""
    def __init__(self):
        self.name = "数据转换"
        self.description = "在不同数据格式之间转换,如JSON、CSV等"
    def run(self, input_data: str, input_format: str, output_format: str) -> str:
        try:
            if input_format.lower() == "json" and output_format.lower() == "csv":
                data = json.loads(input_data)
                if isinstance(data, list):
                    if not data:
                        return "空数据"
                    headers = set()
                    for item in data:
                        headers.update(item.keys())
                    headers = list(headers)
                    csv = ",".join(headers) + "\n"
                    for item in data:
                        row = [str(item.get(header, "")) for header in headers]
                        csv += ",".join(row) + "\n"
                    return csv
                else:
                    return "输入数据必须是JSON数组"
            elif input_format.lower() == "csv" and output_format.lower() == "json":
                lines = input_data.strip().split("\n")
                if len(lines) < 2:
                    return "CSV数据至少需要标题行和数据行"
                headers = lines[0].split(",")
                result = []
                for line in lines[1:]:
                    values = line.split(",")
                    if len(values) != len(headers):
                        continue
                    item = {}
                    for i, header in enumerate(headers):
                        item[header] = values[i]
                    result.append(item)
                return json.dumps(result, ensure_ascii=False, indent=2)
            else:
                return f"不支持的转换: {input_format} -> {output_format}"
        except Exception as e:
            return f"转换失败: {str(e)}"

# 自定义工具3:文本处理工具
class TextProcessingTool:
    """文本处理工具,用于处理文本内容"""
    def __init__(self):
        self.name = "文本处理"
        self.description = "处理文本内容,如查找、替换、统计等"
    def run(self, operation: str, content: str, **kwargs) -> str:
        if operation == "count_lines":
            return f"文本共有 {len(content.splitlines())} 行"
        elif operation == "find_text":
            search_text = kwargs.get("search_text", "")
            if not search_text:
                return "请提供要查找的文本"
            lines = content.splitlines()
            matches = []
            for i, line in enumerate(lines):
                if search_text in line:
                    matches.append(f"第 {i+1} 行: {line}")
            if matches:
                return f"找到 {len(matches)} 处匹配:\n" + "\n".join(matches)
            else:
                return f"未找到文本 '{search_text}'"
        elif operation == "replace_text":
            old_text = kwargs.get("old_text", "")
            new_text = kwargs.get("new_text", "")
            if not old_text:
                return "请提供要替换的文本"
            new_content = content.replace(old_text, new_text)
            count = content.count(old_text)
            return f"替换完成,共替换 {count} 处。\n新内容:\n{new_content}"
        else:
            return f"不支持的操作: {operation}"

# 工具实例
text_analysis = TextAnalysisTool()
data_conversion = DataConversionTool()
text_processing = TextProcessingTool()

# 工具链(LCEL风格)
tools = {
    "文本分析": RunnableLambda(lambda x: text_analysis.run(x["text"])),
    "数据转换": RunnableLambda(lambda x: data_conversion.run(x["input_data"], x["input_format"], x["output_format"])),
    "文本处理": RunnableLambda(lambda x: text_processing.run(x["operation"], x["content"], **x.get("kwargs", {}))),
}

# 示例:LCEL任务链
def lcel_task_chain(task_type, params):
    """
    LCEL风格的任务链调度
    参数:
        task_type: 工具名称
        params: 参数字典
    返回:
        工具执行结果
    """
    if task_type not in tools:
        return "不支持的工具类型"
    return tools[task_type].invoke(params)

# 示例用法
if __name__ == "__main__":
    # 示例1:文本分析
    result1 = lcel_task_chain("文本分析", {"text": "这个产品非常好用,我很喜欢它的设计,使用体验非常棒!"})
    print("示例1结果:", result1)
    print("-" * 30)

    # 示例2:数据格式转换
    csv_data = "name,age,comment\n张三,25,这个产品很好\n李四,30,服务态度差\n王五,28,性价比高"
    result2 = lcel_task_chain("数据转换", {"input_data": csv_data, "input_format": "csv", "output_format": "json"})
    print("示例2结果:", result2)
    print("-" * 30)

    # 示例3:文本处理
    text = "第一行内容\n第二行内容\n第三行内容"
    result3 = lcel_task_chain("文本处理", {"operation": "count_lines", "content": text})
    print("示例3结果:", result3)
    print("-" * 30)

    # 示例4:多步串联(先文本分析,再统计行数)
    # 先分析文本情感,再统计文本行数,展示LCEL链式组合
    text4 = "这个产品非常好用,我很喜欢它的设计,使用体验非常棒!\n价格也很合理,推荐大家购买。\n客服态度也很好,解答问题很及时。"
    # 第一步:文本分析
    analysis_result = lcel_task_chain("文本分析", {"text": text4})
    # 第二步:统计行数
    line_count_result = lcel_task_chain("文本处理", {"operation": "count_lines", "content": text4})
    print("示例4结果(多步串联):\n文本分析->", analysis_result, "\n行数统计->", line_count_result)
    print("-" * 30)

    # 示例5:条件分支(根据文本长度选择不同处理方式)
    # 如果文本长度大于20,做文本分析,否则只统计行数
    text5 = "短文本示例"
    if len(text5) > 20:
        result5 = lcel_task_chain("文本分析", {"text": text5})
        print("示例5结果(条件分支):文本较长,执行文本分析->", result5)
    else:
        result5 = lcel_task_chain("文本处理", {"operation": "count_lines", "content": text5})
        print("示例5结果(条件分支):文本较短,只统计行数->", result5)
    print("-" * 30)
相关推荐
岛屿旅人6 小时前
英国国防部推进本土化开放架构建设
网络·人工智能·安全·web安全·架构
TwoAnts&DingJoy6 小时前
数据分析-数据沙箱
人工智能·python·安全·数据分析·数据沙箱
FreeCode6 小时前
Agent开发:LangChain1.0快速入门(一)
人工智能·llm·agent
wu_jing_sheng06 小时前
销售数据分析
开发语言·python
风向玩家6 小时前
不放回抽样_生成不重样菜单
python
程序员小远7 小时前
Postman接口测试: Postman环境变量&全局变量设置,多接口顺序执行详解
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
CV实验室7 小时前
CV论文速递: 覆盖医学影像分析、视频理解与生成、3D场景理解与定位等方向! (10.27-10.31)
人工智能·计算机视觉·3d·音视频
程序员三藏7 小时前
Postman定义公共函数
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
飞哥数智坊7 小时前
MiniMax 是谁?为什么 M2 一出,大家又沸腾了?
人工智能