Langchain.js | 基础-LCEL(一)👀👀👀

609 阅读4分钟

LCEL

LangChain Expression Language(LCEL)有着独特的特性与优势。

LCEL 介绍 | 🦜️🔗 Langchain

在特点方面,它属于一种声明式方法,这是其新特性所在。例如,我们构建一个自然语言处理流程时,运用 LCEL 能够清晰地声明各步骤,像下面这样:

import { RunnableSequence } from "langchain/schema/runnable";
const textFormatter = (input) => input.toUpperCase();
const languageModelCaller = async (input) => {
    // 这里假设调用语言模型的简单逻辑,实际会更复杂
    return `Processed by model: ${input}`;
};
const lcelFlow = RunnableSequence.from([textFormatter, languageModelCaller]);
const result = await lcelFlow.invoke("hello world");
console.log(result);

它还是一种便于将链或组件组合在一起的机制,借助管道操作符,构建出的任何链都会自动具备完整的同步、异步以及流式支持。

从优势来讲,其底层设计支持从原型到生产无需修改任何代码。好比你写好了一个简单的文本处理原型代码,比如只是做文本简单拼接、格式转换操作,当应用到生产环境,即便有并行处理或者流式处理需求,像大量文本文件并行读取、实时流式返回处理结果等情况,原代码基本不用改动就能支持这些生产级别的特性。

管道(Pipeline)

管道可形象地被看作一条流水生产线。以处理一篇文章文本为例,若要做文本分析任务,代码如下:

import { RunnableSequence } from "langchain/schema/runnable";
const textReader = (filePath) => {
    // 这里简单模拟读取文件文本内容,实际会用相关文件读取模块
    return `Text from file: ${filePath}`;
};
const textCleaner = (text) => text.replace(/\s+/g, " ").trim();
const featureExtractor = (text) => {
    // 简单提取一些特征,比如单词数量等
    return { wordCount: text.split(" ").length };
};
const pipeline = RunnableSequence.from([textReader, textCleaner, featureExtractor]);
const result = await pipeline.invoke("path/to/article.txt");
console.log(result);

先是通过 textReader 把文章文本内容读取进来(如同流水线上准备好基础零件),接着 textCleaner 对文本进行清理,去除多余空格等(类似流水线上的初步加工),最后 featureExtractor 提取特征(相当于后续的进一步加工工序),最终得到期望的分析结果。

Runnable 接口

它定义了一种可执行的对象规范,是一个协议或者模板,规定了那些实现该接口的类或者对象需要具备执行某种操作的能力。

其用处之一是多个实现了 Runnable 接口的对象能够串联在一个管道中。例如我们构建一个文本处理及生成的流程,有文本读取器负责读取本地文本文件内容,示例代码如下:

import { RunnableSequence } from "langchain/schema/runnable";
const localTextReader = (filePath) => {
    // 模拟读取本地文件内容
    return fs.readFileSync(filePath, "utf8");
};
const textFormatter = (text) => text.toUpperCase();
const languageModelCaller = async (input) => {
    // 假设调用语言模型生成相关内容
    return `Generated from model: ${input}`;
};
const textProcessingPipeline = RunnableSequence.from([localTextReader, textFormatter, languageModelCaller]);
const result = await textProcessingPipeline.invoke("path/to/local/file.txt");
console.log(result);

这里 localTextReader(文本读取器)、textFormatter(文本格式化对象)、languageModelCaller(调用语言模型的 Runnable 对象)都实现了 Runnable 接口,从而构成一整套文本处理及生成的流程。

同时它能适配不同数据源和任务。像本地文件、数据库、网络接口数据等不同数据源,只要相关的数据获取或处理对象实现 Runnable 接口,就能以相似方式整合到 LCEL 的体系里。对于不同的自然语言处理任务,比如文本分类、问答、文本生成等,实现 Runnable 接口后就可被灵活调用和组合。

其常用标准接口包含多个。invoke 是基础的调用并传入参数,像前面很多示例中用 invoke 方法执行流程并传入初始数据。batch 可进行批量调用,输入一组参数,例如批量处理多个文本文件内容时就能用这个接口,示例如下:

import { RunnableSequence } from "langchain/schema/runnable";
const textReader = (filePath) => {
    return fs.readFileSync(filePath, "utf8");
};
const textProcessors = [textReader, (text) => text.toUpperCase()];
const batchPipeline = RunnableSequence.from(textProcessors).batch([
    "path/to/file1.txt",
    "path/to/file2.txt"
]);
const results = await batchPipeline.invoke();
console.log(results);

stream 用于调用并以 stream 流的方式返回数据,适合处理大量数据实时返回部分结果的场景。streamLog 除了像 stream 流一样返回数据,还会返回中间的运行结果,方便调试和查看流程中各环节的产出情况。具体代码实战可以参考 start.ipynb 文件。

在 React 项目里应用 LCEL 也很便捷,比如有个 React 组件,要展示对用户输入文本的处理结果,这个处理借助 LCEL 来完成,代码如下:

import React, { useState } from "react";
import { RunnableSequence } from "langchain/schema/runnable";

const textFormatter = (input) => input.toUpperCase();
const languageModelCaller = async (input) => {
    // 简单模拟语言模型返回结果
    return `Processed by model: ${input}`;
};
const lcelFlow = RunnableSequence.from([textFormatter, languageModelCaller]);

const TextProcessingComponent = () => {
    const [inputText, setInputText] = useState("");
    const [result, setResult] = useState("");

    const handleProcess = async () => {
        const processedResult = await lcelFlow.invoke(inputText);
        setResult(processedResult);
    };

    return (
        <div>
            <input type="text" value={inputText} onChange={(e) => setInputText(e.target.value)} />
            <button onClick={handleProcess}>Process Text</button>
            <div>{result}</div>
        </div>
    );
};

export default TextProcessingComponent;

在这个 React 组件中,用户输入文本后,点击按钮就能触发基于 LCEL 的文本处理流程,最后展示处理后的结果,很好地结合了 LCEL 的强大功能与 React 的交互性,让前端页面也能方便地利用 LCEL 处理自然语言相关任务。

LCEL 在自然语言处理以及各类涉及数据处理、流程构建的场景中都有着重要作用,熟练掌握其概念、管道、Runnable 接口等相关知识,有助于我们开发出更高效、灵活的应用程序。

实验

用LCEL框架写一段简单的对话机器人代码

以下是一个使用 LCEL(LangChain Expression Language)框架结合 OpenAI 的语言模型来实现简单对话机器人的示例代码。这里假设你已经正确安装了 langchain 库以及配置好了相应的 OpenAI API 密钥(如果使用 OpenAI 服务的话)。

import { Configuration, OpenAIApi } from "openai";
import { RunnableSequence, ChatOpenAI } from "langchain/schema/runnable";

// 配置OpenAI相关信息
const configuration = new Configuration({
    apiKey: "YOUR_OPENAI_API_KEY",
});
const openai = new OpenAIApi(configuration);

// 创建一个ChatOpenAI实例,用于调用语言模型来生成回复
const chatModel = new ChatOpenAI({
    openAIApi: openai,
    modelName: "gpt-3.5-turbo",  // 可根据实际情况选择合适的模型
});

// 定义一个简单的输入格式化函数,将用户输入包装成适合语言模型的格式
const formatInput = (userInput) => {
    return [
        {
            role: "user",
            content: userInput
        }
    ];
};

// 创建一个RunnableSequence,将输入格式化和调用语言模型串联起来
const conversationFlow = RunnableSequence.from([
    formatInput,
    chatModel
]);

// 模拟与对话机器人交互的函数
const startConversation = async () => {
    while (true) {
        const userInput = prompt("请输入你的消息(输入'exit'退出):");
        if (userInput === "exit") {
            break;
        }
        const response = await conversationFlow.invoke(userInput);
        console.log("机器人回复:", response.choices[0].message.content);
    }
};

startConversation();

在上述代码中:

  1. 首先导入了必要的模块,包括 Configuration 和 OpenAIApi 用于配置和调用 OpenAI 服务,以及 RunnableSequence 和 ChatOpenAI 等来自 langchain 库的模块,用于构建可运行的流程和与语言模型交互。

  2. 接着配置了 OpenAI 的 API 密钥以及创建了 ChatOpenAI 实例,指定了要使用的语言模型(这里示例用 gpt-3.5-turbo)。

  3. 定义了 formatInput 函数,它的作用是把用户输入的文本按照语言模型期望的对话格式进行包装,即将其变成包含 role(这里是 user 角色)和 content(用户输入的具体内容)的消息数组形式。

  4. 通过 RunnableSequence.from 方法将 formatInput 函数和 chatModel 实例串联起来,形成一个完整的对话流程,也就是先对输入进行格式化,再传递给语言模型去生成回复。

  5. 最后定义了 startConversation 函数,在一个循环中不断获取用户输入,当输入为 exit 时退出循环,否则调用 conversationFlow.invoke 方法将用户输入传递给整个流程去获取语言模型生成的回复,并将回复打印展示出来,实现简单的对话交互。

请注意:

  • 你需要将 "YOUR_OPENAI_API_KEY" 替换为你真实有效的 OpenAI API 密钥。

  • 实际应用中可以根据需求进一步扩展功能,比如添加更多的预处理、后处理步骤,或者整合更多的数据源等,让对话机器人更加智能和实用。

如果不想使用 OpenAI 的服务,也可以替换成其他支持的语言模型,相应地调整配置和调用的部分代码即可。