LangChain 快速入门

·

7 min read

LangChain 是使用语言模型开发应用的框架。通常应用不仅通过 API 调用大语言模型,同时还需要:

  • 数据意识:将语言模型连接到其他数据源

  • 代理性:让语言模型可以和环境交互。 以上是 LangChain 的设计原则。

组件说明

以下是根据复杂度的顺序的组件列表:

  • Schema: 包括整个库中使用的接口和基类。

  • Models: 包括各种LLMs、聊天模型和嵌入模型的整合。

  • Prompts: 包括提示模板和与提示有关的功能,如输出解析器和实例选择器。

  • Indexes: 包括处理你自己的数据的模式和功能,使其准备好与语言模型(包括文档加载器、矢量存储、文本分割器和检索器)互动。

  • Memory: 内存是指在链/代理的调用之间保持状态。LangChain提供了一个内存的标准接口,一个内存实现的集合,以及使用内存的链/代理的例子。

  • Chains: 链超越了单一的LLM调用,是一系列的调用(无论是对LLM还是不同的工具)。LangChain为链提供了一个标准接口,与其他工具进行了大量的整合,并为常见的应用提供了端到端的链。

  • Agents: 代理使用LLM决定采取哪种行动,并观察结果,并重复该行动直到完成任务。LangChain提供了一个标准的代理接口,提供了一些可供选择的代理,以及端到端的代理实例。

快速入门

一、安装和设置

需要 Node.js (ESM and CommonJS) - 18.x, 19.x, 20.x

可以 clone starter 仓库 https://github.com/domeccleston/langchain-ts-starter

npm install -S langchain

二、构建一个语言模型的的应用

假设我们正在构建一个根据公司产品生成公司名的应用。

首先我们导入 OpenAI 的 LLM wrapper。

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

使用 OpenAI 我们需要配置 OPENAI_API_KEY,我们可以在 .env 文件中配置然后使用 dotenv 包来读取。

OPENAI_API_KEY="..."

或者通过直接在 shell 中导出环境变量。

export OPENAI_API_KEY=sk-....

也可以直接在代码中初始化。

const model = new OpenAI({ 
  openAIApiKey: "sk-...", // 如果用上面的方法配置了,这个参数可以省略
  temperature: 0.9 
});

然后我们就可以使用模型的预测能力:

const res = await model.call(  
"What would be a good company name a company that makes colorful socks?"  
);  
console.log(res);

输出:

Rainbow Toes

三、Prompt Templates:管理提示词

通常情况不会直接把用户的输入发送给LLM,而是使用用户的输入构建提示词,再发送给LLM。

比如说,上面的那个例子里,我们只需要用户输入公司的产品,然后用这个信息再构建提示词。

我们首先定义提示词模版:

import { PromptTemplate } from "langchain/prompts";  

const template = "What is a good name for a company that makes {product}?";
const prompt = new PromptTemplate({
  template: template,
  inputVariables: ["product"],
});

然后再传入product:

const res = await prompt.format({ product: "colorful socks" });  
console.log(res);

完整代码示例:

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

const model = new OpenAI({ temperature: 0.9, modelName: 'gpt-3.5-turbo' });

const template = "What is a good name for a company that makes {product}?";
const prompt = new PromptTemplate({
  template: template,
  inputVariables: ["product"],
});

async function main() {
  const promptFormated = await prompt.format({ product: "colorful socks" });
  console.log(promptFormated)
  const res = await model.call(promptFormated);
  console.log(res);
}

main()

输出:

What is a good name for a company that makes colorful socks?

四、Chains:在多步骤工作流中将 LLMs 和提示词结合起来

LangChain 的 Chain 是由 links 组成的,link 可以是基本的 LLMs 或者其他 chain。最核心的是由 PromptTemplate 和 LLM 组成的 LLMChain。

import { LLMChain } from "langchain/chains";  

const chain = new LLMChain({ llm: model, prompt: prompt });
const res = await chain.call({ product: "colorful socks" });  
console.log(res);

完整代码示例:

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

const model = new OpenAI({ temperature: 0.9, modelName: 'gpt-3.5-turbo' });

const template = "What is a good name for a company that makes {product}?";
const prompt = new PromptTemplate({
  template: template,
  inputVariables: ["product"],
});

const chain = new LLMChain({ llm: model, prompt: prompt });

async function main() {
  const res = await chain.call({ product: "colorful socks" });  
  console.log(res);
}

main()

五、Agents:根据用户的输入动态地选择 Chain 运行

Agent 可以使用 LLM 来决定按哪种顺序采取哪个 aciton,一个 action 可以使用一种 tool 并观察它的输出,或者返回给用户。

下面是需要理解的概念:

  • Tool:完成特定任务的函数,比如在网上搜索,在数据库里查询,执行代码,或者其他的 Chain。tool 的接口接受字符串作为输入,字符串作为输出。

  • LLM:Agent使用的大语言模型。

  • Agent:使用的代理,

我们这个例子中使用 SerpAPI 来调用 Google Search API,在 .env 文件中配置环境变量:

SERPAPI_API_KEY="..."

安装 serpapi 包:

npm install -S serpapi

完整代码示例:

import { OpenAI } from "langchain/llms/openai";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { SerpAPI } from "langchain/tools";
import { Calculator } from "langchain/tools/calculator";

const model = new OpenAI({ temperature: 0 });
const tools = [
  new SerpAPI(process.env.SERPAPI_API_KEY, {
    location: "Austin,Texas,United States",
    hl: "en",
    gl: "us",
  }),
  new Calculator(),
];

async function main() {
  const executor = await initializeAgentExecutorWithOptions(tools, model, {
    agentType: "zero-shot-react-description",
  });
  console.log("Loaded agent.");

  const input =
    "Who is Olivia Wilde's boyfriend?" +
    " What is his current age raised to the 0.23 power?";
  console.log(`Executing with input "${input}"...`);

  const result = await executor.call({ input });

  console.log(`Got output ${result.output}`);
}

main()

六、Memory:给 Chain 和 Agent 添加状态

之前的 chain 和 agent 都是无状态的。你常常希望 chain 或者 agent 有它之前交互的记忆,最简单的就是设计聊天机器人的时候,希望它记住以前的对话,这样可以根据上下文更好的对话,这是一种“短期记忆”。你还会希望chain和agent随着时间能够记住关键信息,一种“长期记忆”。

LangChain 提供了几个专门的 Chain,下面将介绍 ConversationChain。

ConversationChain 默认有一个简单的内存类型,可以记住以前所有的输入/输出,并将它们添加到传递的上下文中国呢,让我们来看看如何使用这个链。

import { OpenAI } from "langchain/llms/openai";
import { BufferMemory } from "langchain/memory";
import { ConversationChain } from "langchain/chains";

const model = new OpenAI({});
const memory = new BufferMemory();
const chain = new ConversationChain({ llm: model, memory: memory });

async function main() {
  const res = await chain.call({ input: "Hi! I'm Jim." });
  console.log(res);
}

main()

七、Streaming

可以通过 streaming API 来逐个获取生成的单词。

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

const chat = new OpenAI({
  streaming: true,
  callbacks: [
    {
      handleLLMNewToken(token: string) {
        process.stdout.write(token);
      },
    },
  ],
});

async function main() {
  await chat.call("Write me a song about sparkling water.");
}

main()

八、Chat Models

Chat models 是语言模型的一个变种,虽然Chat Models也是用 LLM,但是暴露的接口有一些不一样,不是暴露 “text in, text out“ API,而是暴露一个 Chat Messages 的输入输出。

import { ChatOpenAI } from "langchain/chat_models/openai";
import { HumanChatMessage, SystemChatMessage } from "langchain/schema";

const chat = new ChatOpenAI({ temperature: 0 });

async function main() {
  const response = await chat.call([
    new HumanChatMessage(
      "Translate this sentence from English to French. I love programming."
    ),
  ]);

  console.log(response);
}

main()

输出:

AIChatMessage { text: "J'aime programmer.", name: undefined }

OpenAI基于聊天的模型(目前是gpt-3.5-turbo和gpt-4)支持多条消息作为输入。下面是一个向聊天模型发送系统和用户消息的例子:

import { ChatOpenAI } from "langchain/chat_models/openai";
import { HumanChatMessage, SystemChatMessage } from "langchain/schema";

const chat = new ChatOpenAI({ temperature: 0 });

async function main() {
  const responseB = await chat.call([
    new SystemChatMessage(
      "You are a helpful assistant that translates English to French."
    ),
    new HumanChatMessage("Translate: I love programming."),
  ]);

  console.log(response);
}

main()

你可以更进一步生成多组消息的补全。这将返回一个带有额外消息参数的LLMResult。

import { ChatOpenAI } from "langchain/chat_models/openai";
import { HumanChatMessage, SystemChatMessage } from "langchain/schema";

const chat = new ChatOpenAI({ temperature: 0 });

async function main() {
  const response = await chat.generate([
    [
      new SystemChatMessage(
        "You are a helpful assistant that translates English to French."
      ),
      new HumanChatMessage(
        "Translate this sentence from English to French. I love programming."
      ),
    ],
    [
      new SystemChatMessage(
        "You are a helpful assistant that translates English to French."
      ),
      new HumanChatMessage(
        "Translate this sentence from English to French. I love artificial intelligence."
      ),
    ],
  ]);

  console.log(JSON.stringify(response, null, 2));
}

main()

输出:

{
  "generations": [
    [
      {
        "text": "J'aime programmer.",
        "message": {
          "type": "ai",
          "data": {
            "content": "J'aime programmer."
          }
        }
      }
    ],
    [
      {
        "text": "J'aime l'intelligence artificielle.",
        "message": {
          "type": "ai",
          "data": {
            "content": "J'aime l'intelligence artificielle."
          }
        }
      }
    ]
  ],
  "llmOutput": {
    "tokenUsage": {
      "completionTokens": 16,
      "promptTokens": 73,
      "totalTokens": 89
    }
  }
}

使用 Chat Prompt Templates:

import { ChatOpenAI } from "langchain/chat_models/openai";
import { HumanChatMessage, SystemChatMessage } from "langchain/schema";
import {
  SystemMessagePromptTemplate,
  HumanMessagePromptTemplate,
  ChatPromptTemplate,
} from "langchain/prompts";

const chat = new ChatOpenAI({ temperature: 0 });

const translationPrompt = ChatPromptTemplate.fromPromptMessages([
  SystemMessagePromptTemplate.fromTemplate(
    "You are a helpful assistant that translates {input_language} to {output_language}."
  ),
  HumanMessagePromptTemplate.fromTemplate("{text}"),
]);

async function main() {
  const response = await chat.generatePrompt([
    await translationPrompt.formatPromptValue({
      input_language: "English",
      output_language: "French",
      text: "I love programming.",
    }),
  ]);

  console.log(JSON.stringify(response, null, 2));
}

main()

输出:

{
  "generations": [
    [
      {
        "text": "J'adore la programmation.",
        "message": {
          "type": "ai",
          "data": {
            "content": "J'adore la programmation."
          }
        }
      }
    ]
  ],
  "llmOutput": {
    "tokenUsage": {
      "completionTokens": 8,
      "promptTokens": 28,
      "totalTokens": 36
    }
  }
}

Model + Prompt = LLMChain:

import { ChatOpenAI } from "langchain/chat_models/openai";
import { LLMChain } from "langchain/chains";
import {
  SystemMessagePromptTemplate,
  HumanMessagePromptTemplate,
  ChatPromptTemplate,
} from "langchain/prompts";

const chat = new ChatOpenAI({ temperature: 0 });

const translationPrompt = ChatPromptTemplate.fromPromptMessages([
  SystemMessagePromptTemplate.fromTemplate(
    "You are a helpful assistant that translates {input_language} to {output_language}."
  ),
  HumanMessagePromptTemplate.fromTemplate("{text}"),
]);

const chain = new LLMChain({
  prompt: translationPrompt,
  llm: chat,
});

async function main() {
  const response = await chain.call({
    input_language: "English",
    output_language: "French",
    text: "I love programming.",
  });

  console.log(JSON.stringify(response, null, 2));
}

main()

输出:

{
  "text": "J'adore la programmation."
}

使用 Agents:

import { ChatOpenAI } from "langchain/chat_models/openai";
import { SerpAPI } from "langchain/tools";
import { ChatAgent, AgentExecutor } from "langchain/agents"

const chat = new ChatOpenAI({ temperature: 0 });

// Define the list of tools the agent can use  
const tools = [
  new SerpAPI(process.env.SERPAPI_API_KEY, {
    location: "Austin,Texas,United States",
    hl: "en",
    gl: "us",
  }),
];
// Create the agent from the chat model and the tools  
const agent = ChatAgent.fromLLMAndTools(chat, tools);
// Create an executor, which calls to the agent until an answer is found  
const executor = AgentExecutor.fromAgentAndTools({ agent, tools });


async function main() {
  const response = await executor.run(
    "How many people live in canada as of 2023?"
  );
  console.log(response);
}

main()

输出:

The estimated population of Canada as of January 1, 2023 is 39,566,248.

使用 Memory 给 chain 和 agent 添加状态:

import { ChatOpenAI } from "langchain/chat_models/openai";
import { ConversationChain } from "langchain/chains";
import { BufferMemory } from "langchain/memory";
import {
  SystemMessagePromptTemplate,
  HumanMessagePromptTemplate,
  ChatPromptTemplate,
  MessagesPlaceholder
} from "langchain/prompts";

const chat = new ChatOpenAI({ temperature: 0 });

const chatPrompt = ChatPromptTemplate.fromPromptMessages([
  SystemMessagePromptTemplate.fromTemplate(
    "The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know."
  ),
  new MessagesPlaceholder("history"),
  HumanMessagePromptTemplate.fromTemplate("{input}"),
]);

const chain = new ConversationChain({
  memory: new BufferMemory({ returnMessages: true, memoryKey: "history" }),
  prompt: chatPrompt,
  llm: chat,
});


async function main() {
  const responseH = await chain.call({
    input: "hi from London, how are you doing today",
  });
  console.log(responseH);

  const responseI = await chain.call({
    input: "Do you know where I am?",
  });
  console.log(responseI);
}

main()

输出:

{
  response: "Hello! As an AI language model, I don't have feelings, but I'm functioning properly and ready to assist you with any questions or tasks you may have. How can I help you today?"
}
{
  response: "Yes, you mentioned that you are from London. However, as an AI language model, I don't have access to your current location unless you provide me with that information."
}