大家好,我是feng,感谢你阅读我的博文,如果你也关注AI应用开发,欢迎关注公众号和我一起探索。如果文章对你有所启发,请为我点赞!
在博文《AI探索实践9 - 不用Python!用前端也能开发一个本地运行的"ChatGPT"!》,实现了仅依靠前端程序,就可以和本地大模型通信,实现了类似ChatGPT聊天的效果。但是在运行程序后,我发现本地模型的响应是一次性显示的,并没有像ChatGPT那样实现打字机的效果。本文就来研究如何实现流式输出的效果。
在博文《AI探索实践10 - 前端实现本地模型流式响应输出》,了解到前端程序如何将模型的响应以流式方式呈现在页面上。
本篇文章将介绍一个比较重要的概念 Prompt Template - 提示语模板,以及如何在前端实现。
一、重点回顾
在前端程序中,使用LangChain框架来实现大模型应用的开发。基本逻辑是:
- 声明一个 ChatOllama 对象,配置本地大模型参数:ip、模型名称、temperature等。
- 使用模型的invoke、batch或stream等方法,实现向模型发送请求和收到响应。
- 根据请求的方法不同,处理不同的响应数据对象,同步更新到模板。
二、问题
上面的代码逻辑,实现了类似ChatGPT聊天的效果。但是在使用过程中发现,我们在连续提出问题时,如果发的提示语比较简单、没有把相关的上下文也一起发送,那么模型回答会显得莫名其妙:
可以看出,模型的回答仅仅针对的是你这一次的提问。现有的代码逻辑,并没有将你这次提问的上下文一起发送给模型,所以在模型看来有些问题可能就会莫名奇妙,答案自然也很离谱。
人与人之间的交流过程中,如果你想得到期望的回答,你就需要说出让对方 听得懂的话,这很重要。话中的歧义越少,对方理解偏差就越小,给你的响应回答也就越符合你的期望。和大模型的通信也是一样的道理。提示语,就是我们发给模型的内容、说的话。好的提示语能够让模型更好的理解你的问题,从而给你更好的答案。
因此,提示语 Prompt很重要。
三、实现提示语模版功能
实际上,说提示语 prompt很重要,但这是针对现阶段大模型的处理能力来说的。理想情况下,大模型应该有能力充分的理解你想要表达的意思,我相信这在未来是会实现的。现实是大模型还不具备这样的能力,所以我们在实现大模型应用时,要充分的考虑到这一点。应该通过技术来提供一种机制,来优化和补充用户发出的提示语,帮助大模型更好的理解用户意图。
我们可以通过模型对象的方法(invoke/batch/stream),来获取模型的响应。我们也可以通过使用提示语模版对象(Prompt Template)来获得响应。提示语模版,常用于将用户的输入内容,转换为对于大语言模型更易于理解的内容。
创建一个提示语模板 Prompt Template,可以有2种不同的方法创建:
3.1 从字符串提示语模板中创建
第一种方法,是从字符串模版中创建。它需要以单个字符串作为输入,该字符串代表了系统消息,指示模型以某种方式运行。
新建一个 prompt-template.js 文件:
TypeScript
import { ChatOllama } from '@langchain/community/chat_models/ollama';
import { ChatPromptTemplate } from '@langchain/core/prompts';
// 创建一个模型
const chatModel = new ChatOllama({
baseUrl: 'http://localhost:11434', // Default value
model: 'qwen:4b',
temperature: 0.7,
});
const prompt = ChatPromptTemplate.fromTemplate(
'你是一个喜剧演员,使用接下来的词创作一个笑话: {input}',
);
ChatPromptTemplate 类,可以帮助我们创建一个提示语模版对象。fromTemplate 函数中 {input} 代表用户输入的变量,使用大括号 {} 作为占位符。
在LangChain中,可以以某种方式组合模型和提示语,这被称之为:chain。接下来,我们创建一个chain,通过pipe方法将模型和提示语链接起来。当然也可以链接其他对象:
TypeScript
const chain = prompt.pipe(chatModel);
接下来,我们不再是调用模型的方法来获取响应,而是调用 chain对象来做同样的事情:
TypeScript
const response = await chain.invoke({
input: '小狗',
});
// 在控制台中
console.log(response);
我们执行一下这个js,来看看控制台输出的内容:
可以看到,通过chain将模型与提示语模版对象链接起来,并调用chain的方法同样实现了对模型的请求与响应。我们再来梳理和说明一下逻辑:
模板字符串内容是:
你是一个喜剧演员,使用接下来的词创作一个笑话: {input}
这个提示语起到3个作用:
- 告诉了模型的定位:喜剧演员。模型将重点使用这个定位的知识来做响应。
- 创作笑话:任务指令,告诉模型要做什么
- {input}:告诉模型,要根据用户的数据,并且输入的变量名为:input
当我们调用链时:
input: '小狗',
这个小狗,实际上代表的是用户在页面文本框中输入的内容。也是告诉大模型,变量名为input的值是:小狗
3.2 从消息数组中创建
另一种创建提示语模板的方法是,从消息数组中创建。这个可以更好的控制模型的输出。在这个消息数组中,你可以指定角色以及角色要执行的任务。数组中,每一个元素是一个键值数组:
TypeScript
const prompt2 = ChatPromptTemplate.fromMessages([
['system', '根据用户提供的词语,来创作一首四言绝句'],
['human', '{input}'],
]);
在代码中,定义了2个角色:system和human。system代表了系统角色,human代表了用户。
TypeScript
const chain2 = prompt2.pipe(chatModel);
const response2 = await chain2.invoke({
input: '大海',
});
console.log(response2);
执行代码,看看返回的结果:
四、总结
通过提示语模板功能,我们可以为用户发送的提示语,补充更多的上下文信息,从而得到更好的答案。
我们可以在提示语中间指定模型的角色、明确模型要做的任务,以及说明需要识别用户的输入占位符等。
很明显,质量更高的提示语内容能够帮助大模型给我们回答更准确的结果。如何编写高质量的提示语不在本文的讨论范围,网络也有很多资料可以学习。