Skip to content

结构化提示词

在跟各种 聊天模型 交互的时候,在构建聊天信息时,不仅仅包含了像上文中的文本内容,也需要与每条消息关联的角色信息。

例如这条信息是由 人类、AI、还是给 chatbot 指定的 system 信息,这种结构化的消息输入有助于模型更好地理解对话的上下文和流程,从而生成更准确、更自然的回应。

为了方便地构建和处理这种结构化的聊天消息,LangChain.js 提供了专门的结构化提示模板系统,来帮助我们方便地构造这类消息:

类名用途
ChatPromptTemplate构造整个多轮提示词结构
SystemMessagePromptTemplate设置系统规则 / 行为模式
HumanMessagePromptTemplate模拟用户输入
AIMessagePromptTemplate模拟模型输出(上下文中使用)

角色的概念对 LLM 理解和构建整个对话流程非常重要,相同的内容由不同的 role 发送出来的意义是不同的。

快速上手#

import {
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
} from '@langchain/core/prompts'
// 构建系统提示
const systemPrompt = SystemMessagePromptTemplate.fromTemplate(
'你是一位专业导游,负责用中文向游客介绍北京特产。'
)
// 构建用户提示
const humanPrompt = HumanMessagePromptTemplate.fromTemplate('{question}')
// 组合为 ChatPromptTemplate
const chatPrompt = ChatPromptTemplate.fromMessages([systemPrompt, humanPrompt])
// 填充变量,生成最终结构
const messages = await chatPrompt.formatMessages({
question: '北京有哪些值得推荐的特产?',
})
console.log('生成的消息结构:', messages)

执行结果如下:

[
SystemMessage {
"content": "你是一位中国的专业导游,请使用中文向游客介绍中国的某些地区的特产",
"additional_kwargs": {},
"response_metadata": {}
},
HumanMessage {
"content": "我想问:北京的特产有哪些",
"additional_kwargs": {},
"response_metadata": {}
}
]

这是 LangChain.js 中 标准的聊天消息对象结构,每条消息都表示一次对话中的一轮发言(具有角色和内容),它们会传递给聊天模型进行推理。各个字段的意思如下:

字段名类型含义与作用
SystemMessage / HumanMessage类实例表示消息的角色类型
contentstring消息正文,即这条消息说了什么
additional_kwargsobject额外的模型参数(可选),如工具调用、函数定义、引用等
response_metadataobject模型生成后自动附加的元信息(如 token 用量、日志信息等)

一个完整的示例:

  1. 生成结构化提示词
  2. 将结构化提示词喂给大模型
import {
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
ChatPromptTemplate,
} from '@langchain/core/prompts'
import { ChatOllama } from '@langchain/ollama'
function buildPrompt(sysTemplate, userTemplate) {
const sysPt = SystemMessagePromptTemplate.fromTemplate(sysTemplate)
const hPt = HumanMessagePromptTemplate.fromTemplate(userTemplate)
return ChatPromptTemplate.fromMessages([sysPt, hPt])
}
// 1. 系统提示词
const sysTemplate =
'你是一位中国的专业导游,请一定使用中文向游客介绍{location}的{topic}。记住:一定要使用中文来介绍'
// 2. 用户输入提示词
const userTemplate = '当前用户的问题:{question}'
// 3. 拿到最终组合的提示词模板
const pt = buildPrompt(sysTemplate, userTemplate)
// 4. 做一个填充
const result = await pt.formatMessages({
location: '北京',
topic: '特产',
question: '北京有哪些值得推荐的特产',
})
// 5. 创建模型实例
const model = new ChatOllama({
model: 'llama3',
stream: true,
temperature: 0.7,
})
const stream = await model.stream(result)
for await (const chunk of stream) {
process.stdout.write(chunk.content)
}

组合多个提示词#

在实际工程中,我们常常需要根据多个变量、多个上下文来源,动态拼接出一个结构复杂、逻辑清晰的 Prompt。此时,如果把所有逻辑都写在一个大模板里,会让 Prompt 难以维护、难以复用。

这就是 PipelinePromptTemplate 的用武之地,它允许我们将多个小的 Prompt 模板,按顺序组合成一个“流水线式”的大模板,既能 模块化构建,又能 复用逻辑片段

PipelinePromptTemplate 中,有两个重要组成部分:

可以想象每个 pipelinePrompt 是一个工序,负责“加工”一段文本,finalPrompt 则是总装线,拼出最终成品。

import { PromptTemplate, PipelinePromptTemplate } from '@langchain/core/prompts'
// 获取当前日期字符串
const getDate = () => new Date().toLocaleDateString()
// 1. 创建一个主模板
const mainPt = PromptTemplate.fromTemplate(
`你是一个智能助理,今天是 {date},主人的信息是 {userInfo},
请根据上下文完成以下任务:
{todo}`
)
// 2. 创建子模板
const timePl = PromptTemplate.fromTemplate('{date},现在是 {time}')
const filledTimePl = await timePl.partial({
date: getDate,
})
const userTpl = PromptTemplate.fromTemplate('姓名:{name},性别:{gender}')
const taskTpl = PromptTemplate.fromTemplate(`
我想吃 {time} 的 {dish}。
请再次确认我的信息:{userInfo}
`)
// 3. 可以将子模板填充到主模板里面
const finalPt = new PipelinePromptTemplate({
pipelinePrompts: [
{
name: 'date',
prompt: filledTimePl,
},
{
name: 'userInfo',
prompt: userTpl,
},
{
name: 'todo',
prompt: taskTpl,
},
],
finalPrompt: mainPt,
})
const result = await finalPt.format({
time: '12:01',
name: '张三',
gender: '',
dish: '煎蛋',
})
console.log(result)

有了 pipelinePrompts,我们可以极大程度的复用和管理 prompt template,从而让 llm app 的开发更加工程化。