赞
踩
大家好,我是feng,欢迎关注公众号和我一起探索。如果文章对你有所启发,请为我点赞、转发!
上文《AI探索实践13 - Typescript开发AI应用5:抓取网页、文档分割、向量存储与检索链语义检索的使用 【推荐】》我们对抓取网页、文档加载、分割文档、嵌入、从向量数据库检索等关键的概念进行了一一了解。
在用户和大模型的对话过程中,为了更好的帮助大模型提供准确的回答,除了加载第三方文档数据之外,还有一个重要的内容:用户和大模型的对话历史记录。因为有些问题必须结合之前的问答记录结合RAG,才能得到更准确的回答。
本文介绍如何将对话的历史记录,加入到整个链中,以更好地辅助大模型应答。
在LangChain中,有专门的类代表消息对象。在@langchain/core/messages中,有3个主要的消息类型:
AIMessage: 代表大模型消息
HumanMessage:代表用户的消息
SystemMessage:代表系统消息
有了这3种类型的消息,就能够让我们灵活定义链中的逻辑。
注:本文代码基于上一篇代码基础上进行编写。
- const chatHistory = [
- new HumanMessage('你好,我是张山'),
- new AIMessage('你好,张山。有什么可以帮到你?'),
- new HumanMessage('What is LCEL ?'),
- new AIMessage('LCEL stands fro Langchain Expression Language'),
- new HumanMessage('What does LCEL stand for?'),
- new AIMessage('Langchain Expression Language'),
- ];
我们可以轻松构建一个消息对象,只需要将内容作为构造参数即可。上面的代码,构建了一个包含6条件消息记录的数组。
在提示语模板中,增加历史记录变量:
- const prompt = ChatPromptTemplate.fromTemplate(`
- 回答用户的问题。
- Context:{context}
- 历史记录: {chat_history}
- 问题:{input}。
- `);
虽然在模板中,我们定义了一个 chat_history 的变量,但是我们在调用链时,并不能直接将上面定义的消息数组: chatHistory 传入链中。这是因为 chatHistory 是一个数组,而提示语模板中 chat_history 变量,需要的是一个字符串类型。我们需要进行一个转换的处理。LangChain中,@langchain/core/prompts中的MessagesPlaceholder类负载处理这种逻辑。
要使用额外的逻辑处理,那么原有基于字符串类型的模板就不适用了,需要修改提示语模板为消息模板:
- // MessagesPlaceholder 的作用就是接收一个消息对象数组,并将它转化为字符串
- const prompt = ChatPromptTemplate.fromMessages([
- ['system', '根据接下来的上下文: {context} 回答用户提出的问题。'],
- new MessagesPlaceholder('chat_history'),
- ['user', '{input}'],
- ]);
- const response = await chain.invoke({
- //input: 'what is it?',
- input: '我的名字叫什么?',
- chat_history: chatHistory,
- //input: '密码是多少',
- //context: docs,
- });
-
- console.log(response);
-
最后,我们传入变量,并将写好的历史记录数组传入链中即可。
运行链,观察输出结果:
可以看到,对话历史记录数组连同抓取的网页内容,一起打印出来。在调用链时,我们提出的问题是:我的名字叫什么?这个问题和抓取网页的内容完全不相干,但是由于有对话的历史记录的存在,大模型得以准确地回答。
在实际的应用开发中,我们往往会将历史记录存入数据库中。当用户开启历史会话时,从数据库中读取历史记录后,显示在页面上。每当用户提出对话时,将历史记录数组转为模板中的变量作为上下文,一起提交给大模型。
由于大模型每次对话对于token长度的处理能力有所限制,我们应该在页面上引导用户在不同的问题,使用新的会话。这样不至于由于历史记录过长,超出token长度限制,或上下文内容重复导致大模型错误或者幻觉类的回答。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。