We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
书接上回,我们已经实现了一个 langchain.js 接入火山引擎的 ChatModel。
langchain.js
ChatModel
本文我们实现将这个大模型接入到聊天 CLI 实现和大模型进行交互式问答
我们希望这个简易的聊天 CLI 能够拥有以下功能
由于实现基本的 CLI 输入输出不是本文重点。这里我们直接通过以下代码实现一个简单的 node.js 交互式程序,实现了
/clear
import readline from 'node:readline' import process, { stdin, stdout } from 'node:process' import { EventEmitter } from 'node:events' class ChatCli extends EventEmitter { constructor() { super() this.input = stdin this.output = stdout this.input.setEncoding('utf-8') this.output.setEncoding('utf-8') } async runInputLoop() { const prompt = await this.prompt('请输入 prompt\n > ') console.log('prompt', prompt) return new Promise((resolve) => { const rl = readline.createInterface(this.input, this.output) rl.setPrompt('> ') rl.prompt() rl.on('line', async (line) => { if (line === '\\clear') { this.write('清空上下文\n') } else { this.write(`xxx ${line}`) this.write('\n') } rl.prompt() }) rl.on('close', resolve) rl.on('SIGINT', () => { rl.close() process.emit('SIGINT', 'SIGINT') }) }) } write(data) { this.output.write(data) } prompt(query = '> ') { return new Promise((resolve) => { const rl = readline.createInterface(this.input, this.output) rl.question(query, (answer) => { resolve(answer) rl.close() }) }) } } const cli = new ChatCli() cli.runInputLoop()
由于聊天 CLI 已经实现,我们只需要在对应的代码点进行模型的交互。相关接入火山引擎细节见LLM 应用开发入门 - 实现 langchain.js ChatModel 接入火山引擎大模型和实现一个 CLI 聊天机器人(上)
构造函数中初始化火山大模型
import { ChatVolcengine } from 'langchain-bytedance-volcengine' class ChatCli extends EventEmitter { constructor() { super() // .... // 初始化火山大模型 this.chatModel = new ChatVolcengine({ volcengineApiHost: process.env.VOLCENGINE_HOST, volcengineApiKey: process.env.VOLCENGINE_API_KEY, model: process.env.VOLCENGINE_MODEL, }) // .... } }
将接受的 prompt 作为SystemMessage和将用户输入作为 HumanMessage传入stream方法。这里的SystemMessage和HumanMessage也是 langchain 提供的工具类用于构造消息
SystemMessage
HumanMessage
stream
解析大模型返回的流式数据输出到终端
import { HumanMessage, SystemMessage } from '@langchain/core/messages' async runInputLoop() { const prompt = await this.prompt('请输入 prompt\n > ') return new Promise((resolve) => { //.... rl.on('line', async (line) => { if (line === '\\clear') { this.write('清空上下文\n') } else { const stream = await this.chatModel.stream([new SystemMessage(prompt), new HumanMessage(line)]) for await (const chunk of stream) { this.write(chunk.content) } this.write('\n') } //.... }) //.... }) }
运行效果
虽然我们已经和 CLI 打通了和大模型的交互聊天,但是此时聊天 CLI 是没有聊天上下文功能的。
我们需要为这个聊天 CLI 增加上下文功能。对于直接调用大模型 OPEN API 来说,这通常需要我们将上下文手动处理传入大模型的 API。
但是上面提过,作为一个强大的 LLM 应用开发框架,langchain 提供了开箱即用的能力帮助我们实现。
langchain 只所以称为 chain,它是可以以自定义chain的形式将多个工具串联起来使用。每个串联起来的工具必须是一个实现了 Runnable 接口的实例,目前 langchain 中实现了Runnable 接口的组件有 Prompt ChatModel LLM OutputParser Retriever Tool
langchain
chain
Runnable
Prompt
LLM
OutputParser
Retriever
Tool
这里我们使用 langchain 提供的RunnableWithMessageHistory进行聊天上下文的记录和调用;使用InMemoryChatMessageHistory来实现内存的聊天上下文的存储
RunnableWithMessageHistory
InMemoryChatMessageHistory
修改代码实现如下
通过 ChatPromptTemplate.fromMessages 来初始化传给模型的完整 prompt。其中第一项为我们输入的SystemMessage,第二项为占位传递的历史上下文,第三项是本次我们的输入
ChatPromptTemplate.fromMessages
通过自定义链将这个prompt和我们的火山chatModel串联起来
prompt
chatModel
将自定义链传递给RunnableWithMessageHistory构造出 withMessageHistory 对象,并实现聊天历史的上下文对象
withMessageHistory
通过 withMessageHistory.stream 进行模型的调用,并同时传递本次的上下文config对象
withMessageHistory.stream
config
import { ChatPromptTemplate, MessagesPlaceholder, } from '@langchain/core/prompts' import { InMemoryChatMessageHistory } from '@langchain/core/chat_history' import { RunnableWithMessageHistory } from '@langchain/core/runnables' async runInputLoop() { const _prompt = await this.prompt('请输入 prompt\n > ') // 通过 `ChatPromptTemplate.fromMessages` 来初始化传给模型的完整 prompt。其中第一项为我们输入的`SystemMessage`,第二项为占位传递的历史上下文,第三项是本次我们的输入 const prompt = ChatPromptTemplate.fromMessages([ ['system', _prompt], new MessagesPlaceholder('chat_history'), ['human', '{input}'], ]) // 通过自定义链将这个`prompt`和我们的火山`chatModel`串联起来 const chain = prompt.pipe(this.chatModel) const messageHistories = {} // 将自定义链传递给`RunnableWithMessageHistory`构造出 `withMessageHistory` 对象,并实现聊天历史的上下文对象 const withMessageHistory = new RunnableWithMessageHistory({ runnable: chain, getMessageHistory: async (sessionId) => { if (messageHistories[sessionId] === undefined) { messageHistories[sessionId] = new InMemoryChatMessageHistory() } return messageHistories[sessionId] }, inputMessagesKey: 'input', historyMessagesKey: 'chat_history', }) return new Promise((resolve) => { const config = { configurable: { sessionId: `${Date.now()}`, }, } rl.on('line', async (line) => { if (line === '\\clear') { // 接收重置上下文是更新 config config.configurable.sessionId = `${Date.now()}` } else { // 通过 `withMessageHistory.stream` 进行模型的调用,并同时传递本次的上下文`config`对象 const stream = await withMessageHistory.stream({ input: line, }, config) for await (const chunk of stream) { this.write(chunk.content) } this.write('\n') } rl.prompt() }) //.... }) }
再次运行代码测试,表现符合预期
代码详见
import readline from 'node:readline' import process, { stdin, stdout } from 'node:process' import { EventEmitter } from 'node:events' import { ChatVolcengine } from 'langchain-bytedance-volcengine' import 'dotenv/config' import { HumanMessage, SystemMessage } from '@langchain/core/messages' import { ChatPromptTemplate, MessagesPlaceholder, } from '@langchain/core/prompts' import { InMemoryChatMessageHistory } from '@langchain/core/chat_history' import { RunnableWithMessageHistory } from '@langchain/core/runnables' class ChatCli extends EventEmitter { constructor() { super() this.input = stdin this.output = stdout this.input.setEncoding('utf-8') this.output.setEncoding('utf-8') this.chatModel = new ChatVolcengine({ volcengineApiHost: process.env.VOLCENGINE_HOST, volcengineApiKey: process.env.VOLCENGINE_API_KEY, model: process.env.VOLCENGINE_MODEL, }) } async runInputLoop() { const _prompt = await this.prompt('请输入 prompt\n > ') const prompt = ChatPromptTemplate.fromMessages([ ['system', _prompt], new MessagesPlaceholder('chat_history'), ['human', '{input}'], ]) const chain = prompt.pipe(this.chatModel) const messageHistories = {} const withMessageHistory = new RunnableWithMessageHistory({ runnable: chain, getMessageHistory: async (sessionId) => { if (messageHistories[sessionId] === undefined) { messageHistories[sessionId] = new InMemoryChatMessageHistory() } return messageHistories[sessionId] }, inputMessagesKey: 'input', historyMessagesKey: 'chat_history', }) return new Promise((resolve) => { const rl = readline.createInterface(this.input, this.output) rl.setPrompt('> ') rl.prompt() const config = { configurable: { sessionId: `${Date.now()}`, }, } rl.on('line', async (line) => { if (line === '\\clear') { config.configurable.sessionId = `${Date.now()}` } else { const stream = await withMessageHistory.stream({ input: line, }, config) for await (const chunk of stream) { this.write(chunk.content) } this.write('\n') } rl.prompt() }) rl.on('close', resolve) rl.on('SIGINT', () => { rl.close() process.emit('SIGINT', 'SIGINT') }) }) } write(data) { this.output.write(data) } prompt(query = '> ') { return new Promise((resolve) => { const rl = readline.createInterface(this.input, this.output) rl.question(query, (answer) => { resolve(answer) rl.close() }) }) } } const cli = new ChatCli() cli.runInputLoop()
通过本文我们实现了一个简易的聊天 CLI,并成功接入了火山引擎大模型,实现了流式输出和上下文管理功能。通过 langchain.js 提供的工具类和自定义链,我们不仅简化了与大模型的交互,还实现了连续聊天的能力
The text was updated successfully, but these errors were encountered:
No branches or pull requests
书接上回,我们已经实现了一个
langchain.js
接入火山引擎的ChatModel
。本文我们实现将这个大模型接入到聊天 CLI 实现和大模型进行交互式问答
需求
我们希望这个简易的聊天 CLI 能够拥有以下功能
聊天 CLI 基础能力实现
由于实现基本的 CLI 输入输出不是本文重点。这里我们直接通过以下代码实现一个简单的 node.js 交互式程序,实现了
/clear
指令打印清空大模型接入
由于聊天 CLI 已经实现,我们只需要在对应的代码点进行模型的交互。相关接入火山引擎细节见LLM 应用开发入门 - 实现 langchain.js ChatModel 接入火山引擎大模型和实现一个 CLI 聊天机器人(上)
初始化 langchain 火山大模型
构造函数中初始化火山大模型
prompt 接收和大模型聊天交互
将接受的 prompt 作为
SystemMessage
和将用户输入作为HumanMessage
传入stream
方法。这里的SystemMessage
和HumanMessage
也是 langchain 提供的工具类用于构造消息解析大模型返回的流式数据输出到终端
运行效果
连续聊天能力实现
虽然我们已经和 CLI 打通了和大模型的交互聊天,但是此时聊天 CLI 是没有聊天上下文功能的。
我们需要为这个聊天 CLI 增加上下文功能。对于直接调用大模型 OPEN API 来说,这通常需要我们将上下文手动处理传入大模型的 API。
但是上面提过,作为一个强大的 LLM 应用开发框架,langchain 提供了开箱即用的能力帮助我们实现。
langchain
只所以称为chain
,它是可以以自定义chain
的形式将多个工具串联起来使用。每个串联起来的工具必须是一个实现了Runnable
接口的实例,目前 langchain 中实现了Runnable
接口的组件有Prompt
ChatModel
LLM
OutputParser
Retriever
Tool
这里我们使用 langchain 提供的
RunnableWithMessageHistory
进行聊天上下文的记录和调用;使用InMemoryChatMessageHistory
来实现内存的聊天上下文的存储修改代码实现如下
通过
ChatPromptTemplate.fromMessages
来初始化传给模型的完整 prompt。其中第一项为我们输入的SystemMessage
,第二项为占位传递的历史上下文,第三项是本次我们的输入通过自定义链将这个
prompt
和我们的火山chatModel
串联起来将自定义链传递给
RunnableWithMessageHistory
构造出withMessageHistory
对象,并实现聊天历史的上下文对象通过
withMessageHistory.stream
进行模型的调用,并同时传递本次的上下文config
对象再次运行代码测试,表现符合预期
完整实现
代码详见
总结
通过本文我们实现了一个简易的聊天 CLI,并成功接入了火山引擎大模型,实现了流式输出和上下文管理功能。通过 langchain.js 提供的工具类和自定义链,我们不仅简化了与大模型的交互,还实现了连续聊天的能力
The text was updated successfully, but these errors were encountered: