PromptTemplate 高级特性:partial、自定义格式器、安全转义
在上一章中,我们探讨了 PromptTemplate 如何从简单的字符串模板演进为完整的 实现。本章将深入探讨 PromptTemplate 的高级特性,包括 partial 模板、自定义格式器和安全转义机制,这些特性使得 PromptTemplate 更加灵活和安全。
Partial Templates(部分模板)
Partial Templates 允许我们预先填充模板中的一部分变量,创建一个新的、更具体的模板。这对于创建模板的变体非常有用。
基本实现
typescript
class PromptTemplate extends Runnable<any, string> {
private template: string;
private inputVariables: string[];
constructor(config: { template: string; inputVariables: string[] }) {
super();
this.template = config.template;
this.inputVariables = config.inputVariables;
}
// Partial 方法实现
partial(values: Record<string, any>): PromptTemplate {
let newTemplate = this.template;
const newInputVariables = [...this.inputVariables];
// 替换提供的变量值
for (const [key, value] of Object.entries(values)) {
// 在模板中替换变量
newTemplate = newTemplate.replace(
new RegExp(`\\{${key}\\}`, 'g'),
this.escapeValue(String(value))
);
// 从输入变量列表中移除已填充的变量
const index = newInputVariables.indexOf(key);
if (index > -1) {
newInputVariables.splice(index, 1);
}
}
return new PromptTemplate({
template: newTemplate,
inputVariables: newInputVariables
});
}
private escapeValue(value: string): string {
// 简单的转义实现
return value.replace(/\$/g, '$$$$'); // 转义 $ 字符
}
async invoke(input: Record<string, any>): Promise<string> {
return this.format(input);
}
format(input: Record<string, any>): string {
let result = this.template;
for (const variable of this.inputVariables) {
if (!(variable in input)) {
throw new Error(`缺少必需的变量: ${variable}`);
}
result = result.replace(
new RegExp(`\\{${variable}\\}`, 'g'),
this.escapeValue(String(input[variable]))
);
}
return result;
}
}实际应用示例
typescript
// 创建基础模板
const baseTranslationTemplate = new PromptTemplate({
template: "将 {sourceLanguage} 翻译为 {targetLanguage}: {text}",
inputVariables: ["sourceLanguage", "targetLanguage", "text"]
});
// 创建特定语言的模板
const enToZhTemplate = baseTranslationTemplate.partial({
sourceLanguage: "英语",
targetLanguage: "中文"
});
const frToEsTemplate = baseTranslationTemplate.partial({
sourceLanguage: "法语",
targetLanguage: "西班牙语"
});
// 使用部分模板
const result1 = await enToZhTemplate.invoke({
text: "Hello, world!"
});
// 输出: "将 英语 翻译为 中文: Hello, world!"
const result2 = await frToEsTemplate.invoke({
text: "Bonjour, le monde!"
});
// 输出: "将 法语 翻译为 西班牙语: Bonjour, le monde!"复杂的 Partial 应用
Partial Templates 还可以用于创建更复杂的模板变体:
typescript
// 创建一个复杂的模板
const complexTemplate = new PromptTemplate({
template: `角色: {role}
任务: {task}
上下文: {context}
输入: {input}
输出格式: {format}
约束: {constraints}`,
inputVariables: ["role", "task", "context", "input", "format", "constraints"]
});
// 创建特定角色的模板
const expertTemplate = complexTemplate.partial({
role: "领域专家",
constraints: "回答必须准确、专业,使用领域术语"
});
const teacherTemplate = complexTemplate.partial({
role: "教师",
constraints: "回答应该通俗易懂,适合初学者理解"
});
// 使用特定角色模板
const expertResponse = await expertTemplate.invoke({
task: "解释量子力学",
context: "用户是物理学博士",
input: "什么是量子纠缠?",
format: "技术术语 + 数学公式"
});
const teacherResponse = await teacherTemplate.invoke({
task: "解释量子力学",
context: "用户是高中生",
input: "什么是量子纠缠?",
format: "类比 + 简单解释"
});自定义格式器
自定义格式器允许开发者定义自己的模板格式化逻辑,提供更大的灵活性。
实现自定义格式器
typescript
interface TemplateFormatter {
format(template: string, input: Record<string, any>): string;
}
class PromptTemplate extends Runnable<any, string> {
private template: string;
private inputVariables: string[];
private formatter: TemplateFormatter;
constructor(config: {
template: string;
inputVariables: string[];
formatter?: TemplateFormatter;
}) {
super();
this.template = config.template;
this.inputVariables = config.inputVariables;
this.formatter = config.formatter || new DefaultFormatter();
}
async invoke(input: Record<string, any>): Promise<string> {
return this.formatter.format(this.template, input);
}
}
// 默认格式器
class DefaultFormatter implements TemplateFormatter {
format(template: string, input: Record<string, any>): string {
let result = template;
for (const [key, value] of Object.entries(input)) {
result = result.replace(
new RegExp(`\\{${key}\\}`, 'g'),
String(value)
);
}
return result;
}
}
// JSON 格式器
class JsonFormatter implements TemplateFormatter {
format(template: string, input: Record<string, any>): string {
// 将输入转换为 JSON 格式并插入到模板中
const jsonInput = JSON.stringify(input, null, 2);
return template.replace('{input}', jsonInput);
}
}
// Markdown 格式器
class MarkdownFormatter implements TemplateFormatter {
format(template: string, input: Record<string, any>): string {
let result = template;
for (const [key, value] of Object.entries(input)) {
// 将值格式化为 Markdown
const formattedValue = this.formatAsMarkdown(value);
result = result.replace(
new RegExp(`\\{${key}\\}`, 'g'),
formattedValue
);
}
return result;
}
private formatAsMarkdown(value: any): string {
if (Array.isArray(value)) {
return value.map((item, index) => `${index + 1}. ${item}`).join('\n');
}
if (typeof value === 'object' && value !== null) {
return Object.entries(value)
.map(([key, val]) => `**${key}**: ${val}`)
.join('\n');
}
return String(value);
}
}使用自定义格式器
typescript
// 使用 JSON 格式器
const jsonTemplate = new PromptTemplate({
template: `分析以下 JSON 数据:
{input}
请提供分析结果:`,
inputVariables: ["input"],
formatter: new JsonFormatter()
});
const jsonData = {
users: [
{ name: "张三", age: 25 },
{ name: "李四", age: 30 }
],
total: 2
};
const jsonResult = await jsonTemplate.invoke({ input: jsonData });
/* 输出:
分析以下 JSON 数据:
{
"users": [
{
"name": "张三",
"age": 25
},
{
"name": "李四",
"age": 30
}
],
"total": 2
}
请提供分析结果:
*/
// 使用 Markdown 格式器
const markdownTemplate = new PromptTemplate({
template: `用户信息:
{userInfo}
任务列表:
{tasks}`,
inputVariables: ["userInfo", "tasks"],
formatter: new MarkdownFormatter()
});
const markdownResult = await markdownTemplate.invoke({
userInfo: { 姓名: "王五", 职位: "工程师", 部门: "研发部" },
tasks: ["完成项目A", "审查代码", "参加会议"]
});
/* 输出:
用户信息:
**姓名**: 王五
**职位**: 工程师
**部门**: 研发部
任务列表:
1. 完成项目A
2. 审查代码
3. 参加会议
*/安全转义机制
安全转义机制防止恶意输入导致的提示注入攻击,是构建安全 LLM 应用的重要组成部分。
实现安全转义
typescript
class SecurePromptTemplate extends PromptTemplate {
private escapeHtml: boolean;
private escapeSpecialChars: boolean;
constructor(config: {
template: string;
inputVariables: string[];
escapeHtml?: boolean;
escapeSpecialChars?: boolean;
}) {
super(config);
this.escapeHtml = config.escapeHtml ?? true;
this.escapeSpecialChars = config.escapeSpecialChars ?? true;
}
async invoke(input: Record<string, any>): Promise<string> {
// 对输入进行安全转义
const escapedInput = this.escapeInput(input);
return super.format(escapedInput);
}
private escapeInput(input: Record<string, any>): Record<string, any> {
const escapedInput: Record<string, any> = {};
for (const [key, value] of Object.entries(input)) {
if (typeof value === 'string') {
escapedInput[key] = this.escapeString(value);
} else {
escapedInput[key] = value;
}
}
return escapedInput;
}
private escapeString(str: string): string {
let escaped = str;
// HTML 转义
if (this.escapeHtml) {
escaped = escaped
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// 特殊字符转义
if (this.escapeSpecialChars) {
escaped = escaped
.replace(/\{/g, '\\{')
.replace(/\}/g, '\\}')
.replace(/\$/g, '\\$')
.replace(/\\/g, '\\\\');
}
return escaped;
}
}安全转义的实际应用
typescript
// 创建安全的提示模板
const secureTemplate = new SecurePromptTemplate({
template: "用户输入: {userInput}\n请分析此输入:",
inputVariables: ["userInput"]
});
// 测试安全转义
const maliciousInput = "正常文本 {malicious_code} <script>alert('xss')</script>";
const secureResult = await secureTemplate.invoke({
userInput: maliciousInput
});
/* 输出:
用户输入: 正常文本 \{malicious_code\} <script>alert('xss')</script>
请分析此输入:
*/
// 对比不安全的处理
const unsafeTemplate = new PromptTemplate({
template: "用户输入: {userInput}\n请分析此输入:",
inputVariables: ["userInput"]
});
const unsafeResult = await unsafeTemplate.invoke({
userInput: maliciousInput
});
/* 输出(可能存在问题):
用户输入: 正常文本 {malicious_code} <script>alert('xss')</script>
请分析此输入:
*/综合应用示例
让我们看一个综合应用所有高级特性的示例:
typescript
// 创建一个功能完整的高级提示模板系统
class AdvancedPromptTemplate extends Runnable<Record<string, any>, string> {
private template: string;
private inputVariables: string[];
private formatter: TemplateFormatter;
private escapeHtml: boolean;
private escapeSpecialChars: boolean;
constructor(config: {
template: string;
inputVariables: string[];
formatter?: TemplateFormatter;
escapeHtml?: boolean;
escapeSpecialChars?: boolean;
}) {
super();
this.template = config.template;
this.inputVariables = config.inputVariables;
this.formatter = config.formatter || new DefaultFormatter();
this.escapeHtml = config.escapeHtml ?? true;
this.escapeSpecialChars = config.escapeSpecialChars ?? true;
}
// Partial 方法
partial(values: Record<string, any>): AdvancedPromptTemplate {
let newTemplate = this.template;
const newInputVariables = [...this.inputVariables];
for (const [key, value] of Object.entries(values)) {
const escapedValue = this.escapeString(String(value));
newTemplate = newTemplate.replace(
new RegExp(`\\{${key}\\}`, 'g'),
escapedValue
);
const index = newInputVariables.indexOf(key);
if (index > -1) {
newInputVariables.splice(index, 1);
}
}
return new AdvancedPromptTemplate({
template: newTemplate,
inputVariables: newInputVariables,
formatter: this.formatter,
escapeHtml: this.escapeHtml,
escapeSpecialChars: this.escapeSpecialChars
});
}
async invoke(input: Record<string, any>): Promise<string> {
// 安全转义
const escapedInput = this.escapeInput(input);
// 使用自定义格式器
return this.formatter.format(this.template, escapedInput);
}
private escapeInput(input: Record<string, any>): Record<string, any> {
const escapedInput: Record<string, any> = {};
for (const [key, value] of Object.entries(input)) {
if (typeof value === 'string') {
escapedInput[key] = this.escapeString(value);
} else {
escapedInput[key] = value;
}
}
return escapedInput;
}
private escapeString(str: string): string {
let escaped = str;
if (this.escapeHtml) {
escaped = escaped
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
if (this.escapeSpecialChars) {
escaped = escaped
.replace(/\{/g, '\\{')
.replace(/\}/g, '\\}')
.replace(/\$/g, '\\$')
.replace(/\\/g, '\\\\');
}
return escaped;
}
}
// 使用示例
const baseTemplate = new AdvancedPromptTemplate({
template: `任务: {task}
输入数据: {data}
输出要求: {requirements}`,
inputVariables: ["task", "data", "requirements"],
formatter: new MarkdownFormatter()
});
// 创建特定任务模板
const analysisTemplate = baseTemplate.partial({
task: "数据分析",
requirements: "提供详细的统计分析和可视化建议"
});
// 使用模板处理可能的恶意输入
const result = await analysisTemplate.invoke({
data: "用户数据包含 <script> 恶意代码和 {注入尝试}"
});
console.log(result);总结
PromptTemplate 的高级特性包括 partial templates、自定义格式器和安全转义机制,这些特性大大增强了其灵活性和安全性:
- Partial Templates 允许创建模板的特定变体,通过预先填充部分变量来简化使用
- 自定义格式器 提供了灵活的模板格式化机制,支持不同的输出格式需求
- 安全转义机制 防止提示注入攻击,确保应用的安全性
这些高级特性使得 PromptTemplate 成为一个功能强大且安全的组件,能够满足各种复杂的应用场景需求。
在下一章中,我们将探讨动态提示:如何根据输入选择不同模板,进一步提升 PromptTemplate 的灵活性。