Skip to content

LCEL

LangChain Expression Language,是 LangChain 提供的一种声明式构建 链式 调用流程的方式。它允许开发者用 .pipe() 操作符将不同的模块(如提示模板、模型、解析器等)连接起来,形成一个完整的“链(Chain)”。

LCEL 的设计理念:

下面是一个快速上手示例:

import { ChatOllama } from '@langchain/ollama'
import { PromptTemplate } from '@langchain/core/prompts'
import { StringOutputParser } from '@langchain/core/output_parsers'
// 创建模块:提示词模块、模型模块、解析器模块
// 1. 创建提示词
const pt = PromptTemplate.fromTemplate('请严格使用中文解释:{question}')
// 2. 创建模型
const model = new ChatOllama({
model: 'llama3',
temperature: 0.7,
})
// 3. 创建一个解析器
const parser = new StringOutputParser()
// 4. 将上面的 3 个模块连接起来:pipe
// 相当于创建了一个链条
const chain = pt.pipe(model).pipe(parser)
// 5. 使用 chain 这个链条
const res = await chain.invoke({
question: '什么是闭包',
})
console.log(res)

可以把 .pipe() 理解成模块之间的数据通道

image-20250811104531989

从输入到输出,整个流程结构清晰、逻辑统一,而且每个环节都可以随时替换或插拔,非常适合真实项目开发。在 LCEL 中,所有组件都抽象为一种“可执行单元”,都实现了统一的 Runnable 接口,从而都都变成可以链式组合的模块。

核心方法#

1. invoke#

这是最常用的同步执行方式,该方法会执行整条链,返回完整结果。

import { ChatOllama } from '@langchain/ollama'
import { PromptTemplate } from '@langchain/core/prompts'
import { StringOutputParser } from '@langchain/core/output_parsers'
// 创建模块
const prompt = PromptTemplate.fromTemplate('请使用中文解释:{topic}')
const llm = new ChatOllama({ model: 'llama3', temperature: 0.7 })
const parser = new StringOutputParser()
// 构建链式流程
const chain = prompt.pipe(llm).pipe(parser)
// 执行链式调用
const result = await chain.invoke({ topic: '闭包(closure)是什么?' })
console.log('LLM 输出:', result)

2. pipe#

这是用于 组合模块 的方法,会将输出“接入”下一个模块,主要用于拼接模块构建 chain,比如:

Prompt -> 模型 -> 解析器

例如我们上面例子中的:

const chain = prompt.pipe(llm).pipe(parser)
// 你可以继续 pipe 更多环节,比如日志记录、后处理

本质上 .pipe() 会返回一个新的 Runnable,你可以链式无限拼接。换句话说,只要模块实现了 Runnable 接口,就可以进行相互拼接。

3. stream#

该方法用于获取流式结果,逐块返回 LLM 输出,适合实现 ChatGPT 风格对话输出。

import { ChatOllama } from '@langchain/ollama'
import { PromptTemplate } from '@langchain/core/prompts'
import { StringOutputParser } from '@langchain/core/output_parsers'
// 创建模块:提示词模块、模型模块、解析器模块
// 1. 创建提示词
const pt = PromptTemplate.fromTemplate('请严格使用中文解释:{question}')
// 2. 创建模型
const model = new ChatOllama({
model: 'llama3',
temperature: 0.7,
})
// 3. 创建一个解析器
const parser = new StringOutputParser()
// 4. 将上面的 3 个模块连接起来:pipe
// 相当于创建了一个链条
const chain = pt.pipe(model).pipe(parser)
// 5. 使用 chain 这个链条
const res = await chain.stream({
question: '什么是闭包',
})
for await (const chunk of res) {
process.stdout.write(chunk)
}

4. batch#

该方法用于同时处理多个输入,适合批量问答、摘要等场景,返回一个结果数组

import { ChatOllama } from '@langchain/ollama'
import { PromptTemplate } from '@langchain/core/prompts'
import { StringOutputParser } from '@langchain/core/output_parsers'
// 创建模块:提示词模块、模型模块、解析器模块
// 1. 创建提示词
const pt = PromptTemplate.fromTemplate('请严格使用中文解释:{question}')
// 2. 创建模型
const model = new ChatOllama({
model: 'llama3',
temperature: 0.7,
})
// 3. 创建一个解析器
const parser = new StringOutputParser()
// 4. 将上面的 3 个模块连接起来:pipe
// 相当于创建了一个链条
const chain = pt.pipe(model).pipe(parser)
// 5. 多条输入
const inputs = [
{ question: '闭包' },
{ question: '原型链' },
{ question: '事件循环' },
]
const res = await chain.batch(inputs)
// console.log(res);
res.forEach((r, idx) => {
console.log(`${idx + 1}条结果:`, r)
})

🤔 这个时候,有的同学可能会想:能不能一边批量处理多个输入(batch),一边使用流式输出(stream)逐条显示结果?

很遗憾这是不行的,因为:

chain.batch()chain.stream() 不是同一套返回范式,不能在一次调用里既 batch 又拿到 token 级别的流。

那有没有办法实现“批量 + 流式”呢?当然也可以,我们可以自己来控制并发。下节课介绍。