Langchain.js 实战一:快速入门

14 阅读8分钟

Langchain.js 教程一:快速入门

LangChain 让您轻松构建完全自定义的智能体和基于 LLM 的应用程序。只需不到 10 行代码,即可连接到千问、DeepSeek、Kimi、 OpenAI、Anthropic、Google 等平台 LangChain 提供预构建的智能体架构和模型集成,帮助您快速上手,并将 LLM 无缝集成到您的智能体和应用程序中。当前最新的版本是 V1.2.13

1. 安装与说明

LangChain 是使用 TypeScript 编写的,可以在以下环境中使用:

  • Node.js (ESM 和 CommonJS) - 18.x, 19.x, 20.x
  • 明确不保证支持: Node.js 16,如果仍旧希望在 16版本运行,也可以参考官方文档安装fetch

要开始使用 LangChain,请使用以下命令安装:

npm install langchain @langchain/core
# Requires Node.js 20+

安装完毕后使用以下命令查看安装版本:

npm list langchain

如果是LangChain的0.0.52之前的版本,需要更新导入以使用新的路径结构,如果你的是新版本则忽略。如果你参考的是之前老版本的教程,可能这些导包路径在之后的版本中更新了,新版导包参考如下:

import { OpenAI } from "langchain/llms/openai";

适用于下列6个模块的所有导入,这些模块已分割为每个集成的子模块。组合模块已被弃用,在 Node.js 之外不起作用,并将在将来的版本中删除。

  • 如果您使用的是 langchain/llms,请参见 LLMs 以获取更新后的导入路径。
  • 如果您使用的是 langchain/chat_models,请参见 Chat Models 以获取更新后的导入路径。
  • 如果您使用的是 langchain/embeddings,请参见 Embeddings 以获取更新后的导入路径。
  • 如果您使用的是 langchain/vectorstores,请参见 Vector Stores 以获取更新后的导入路径。
  • 如果您使用的是 langchain/document_loaders,请参见 Document Loaders 以获取更新后的导入路径。
  • 如果您使用的是 langchain/retrievers,请参见 Retrievers 以获取更新后的导入路径。

其他模块不受此更改影响,您可以继续从同一路径导入它们。

此外,还有一些模块也需要进行重大更改:

  • import { Calculator } from "langchain/tools"; 现已移至import { Calculator } from "langchain/tools/calculator";
  • import { loadLLM } from "langchain/llms"; 现已移至import { loadLLM } from "langchain/llms/load";
  • import { loadAgent } from "langchain/agents"; 现已移至import { loadAgent } from "langchain/agents/load";
  • import { loadPrompt } from "langchain/prompts"; 现已移至import { loadPrompt } from "langchain/prompts/load";
  • import { loadChain } from "langchain/chains"; 现已移至import { loadChain } from "langchain/chains/load";

LangChain 提供与数百个 LLM 和其他数千个集成的集成。这些集成以独立提供商软件包的形式存在。一般大模型厂商都兼容 openai 的接口规范。

# Installing the OpenAI integration
npm install @langchain/openai

目前为止,我们搞定了环境,接下来创建一个聊天模型快速上手!

2. 快速入门

本节介绍如何使用聊天模型入门。首先我们选择一个开放大模型,国内可以选择千问,DeepSeek 、智谱或者 Kimi的大模型 API 接口。通过访问相关模型官网获取到请求接口与API_KEY(密钥)。本文以千问大模型为例快速构建一个聊天模型。

语言学习模型(LLM)是功能强大的AI工具,能够像人类一样理解和生成文本。它们用途广泛,无需针对每项任务进行专门训练,即可撰写内容、翻译语言、撰写摘要和回答问题。下面主要会学习几点:

  • 非流式调用大模型
  • 流式调用大模型
  • 批量调用大模型
  • Zod 结构化输出
  • 获取模型回答置信度

2.1 创建项目

在一个空白项目目录下执行如下命令:

npm install @langchain/openai

在这个项目根目录下创建.env文件,并填入千问大模型的API_KEY(密钥)作为项目的环境变量,如下

QWEN_API_KEY=sk-e85d75869a433333333333333333

package.json中添加 typemodule

 {
  "type": "module",
  "dependencies": {
    "@langchain/core": "^1.1.32",
    "@langchain/openai": "^1.2.13",
    "dotenv": "^17.3.1",
    "langchain": "^1.2.32"
  }
}

2.2 创建大模型对象

导入如下依赖包:

import dotenv from "dotenv" // 加载环境变量中的模型 API 密钥
import { ChatOpenAI } from "@langchain/openai" // 使用 OpenAi 接口规范

创建大模型对象,下面注释掉的参数一般不需要配置:

dotenv.config() // 读取项目 .env 中的环境变量
const llm = new ChatOpenAI({
  model: "qwen-plus",
  apiKey: process.env.QWEN_API_KEY,
  temperature: 0.7,
  streamUsage: false, // 是否开启流式返回,默认 false
  // maxTokens: 1000, // 最大Tokens
  // maxRetries: 6 , // 最大重试次数,
  // timeout: undefined, // 超时时间
  configuration: {
    baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"
  }
})

2.3 非流式调用

// aiMsg 是一个 AIMessage 实例,包含了模型的回复内容及其调用 Token 统计信息
const aiMsg = await llm.invoke([
  {
    role: "system",
    content: "你是一个智能助手,你会根据用户的问题回答用户的问题,直接回答不知道.",
  },
  {
    role: "user",
    content: "世界上最高的山峰是哪座?"
  },
])
console.log(aiMsg)
console.log(aiMsg.content)

上面是手动构造一个问答json喂给模型,其实可以使用 OpenAI 提供的接口,如下:

import { HumanMessage, AIMessage, SystemMessage } from "langchain";

const conversation = [
  new SystemMessage("你是一个智能助手,你会根据用户的问题回答用户的问题,直接回答不知道."),
  new HumanMessage("世界上最高的山峰是哪座?"),
  new AIMessage("xxxx"), // AI 上次响应的消息 
];

const aiMsg = await model.invoke(conversation);

大模型返回的aiMsg是一个 AIMessage 实例,包含了模型的回复内容及其调用 Token 统计信息,如下面的内容:

AIMessage {
  "id": "chatcmpl-ff7226a5-dc79-949f-8776-1dc8685bc1f6",
  // content 是重点,也就是大模型对问题的回答内容
  "content": "世界上最高的山峰是珠穆朗玛峰(Mount Everest),海拔高度为8848.86米(2020年中国和尼泊尔联合测量的最新官方数据)。它位于中国与尼泊尔边境的喜马拉雅山脉中段。",
  "additional_kwargs": {},
  "response_metadata": {
    "tokenUsage": { // token 使用量
      "promptTokens": 38,
      "completionTokens": 58,
      "totalTokens": 96
    },
    "finish_reason": "stop",
    "model_provider": "openai",
    "model_name": "qwen-plus"
  },
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": { // token 消耗信息
    "output_tokens": 58,
    "input_tokens": 38,
    "total_tokens": 96,
    "input_token_details": {
      "cache_read": 0
    },
    "output_token_details": {}
  }
}

如果对用户提供收费服务,一般用usage_metadata来计算用户大模型的调用次数(调用量)。

console.log(aiMsgForMetadata.usage_metadata);
// 输出:{ input_tokens: 28, output_tokens: 5, total_tokens: 33 }

2.3 流式调用

大多数模型都能在生成输出内容的同时进行流式传输。通过逐步显示输出,流式传输显著提升了用户体验,尤其是在处理较长的响应时。调用stream()返回一个迭代器它会在生成过程中实时输出数据块。您可以使用循环来实时处理每个数据块,只需要调用如下的命令:

const stream = await llm.stream("世界上最高的山峰是哪座?简单回答!");
for await (const chunk of stream) {
  console.log(chunk.text)
}
let full = null;
for await (const chunk of stream) {
  full = full ? full.concat(chunk) : chunk;
  console.log(full.text);
}
console.log(full.contentBlocks);

最后我们只需要实时将 full刷新到 UI 上就能看到流式输出结果。

2.4 批量调用

同样也可以对大模型批量调用,提高调用效率:

const responses = await llm.batch([
  "Why do parrots have colorful feathers?",
  "How do airplanes fly?",
  "What is quantum computing?",
  "Why do parrots have colorful feathers?",
  "How do airplanes fly?",
  "What is quantum computing?",
], {
  maxConcurrency: 5,  // 控制最大并行调用次数
});
for (const response of responses) {
  console.log(response);
}

2.5 结构化输出

模型可以按照给定的模式提供响应。这有助于确保输出易于解析,并可用于后续处理。LangChain 支持多种模式类型和方法来强制输出结构化数据。

zod schema是定义输出模式的首选方法。请注意,如果提供了 zod schema,模型输出还会使用 zod 的解析方法根据该 schema 进行验证。zod 也可以 json 嵌套。

import * as z from "zod";

const Movie = z.object({
  title: z.string().describe("The title of the movie"),
  year: z.number().describe("The year the movie was released"),
  director: z.string().describe("The director of the movie"),
  rating: z.number().describe("The movie's rating out of 10"),
});


const modelWithStructure = llm.withStructuredOutput(Movie);
const json = await modelWithStructure.invoke("提供关于泰坦尼克号电影的评价 JSON 信息!");
console.log(json);


// 输出:{ title: '泰坦尼克号', year: 1997, director: '詹姆斯·卡梅隆', rating: 9.2 }
// 如果需要包含原始数据:用于includeRaw: true同时获取解析后的格式化输出和原始数据。
const modelWithStructure = llm.withStructuredOutput(Movie, { includeRaw: true });
const raw_parsed_json = await modelWithStructure.invoke("提供关于泰坦尼克号电影的评价 JSON 信息!");
console.log(json);

// 输出
// {
//   raw: AIMessage { ... },
//   parsed: { title: "Inception", ... }
// }

2.6 模型回答置信度

logprobs某些模型可以通过在初始化模型时设置参数来配置,从而返回模型响应给定token可能性的标记级日志概率(只能标记生成 token 的概率,并不是对整句话生成的概率,没啥用,还不如每次输出时要模型给自己的回答打一个置信度分来的准确。):

const model = new ChatOpenAI({ 
    ...
    logprobs: true,
});
const responseMessage = await model.invoke("Why do parrots talk?");
responseMessage.response_metadata.logprobs.content.slice(0, 5);

下一章节我们详细来介绍:至关重要的 Messages 大模型消息。