设计模式与 LLM 的结合:以模版方法模式为例
模版方法模式概述
模版方法模式(Template Method Pattern)是一种行为型设计模式。它定义了一个算法的骨架,而将一些步骤延迟到子类中实现。这样,子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
想象一下泡一杯茶的场景:
- 煮水温杯
- 注水浸茶
- 茶水入杯
- 添加配料
无论泡什么茶,前三个步骤通常是不可省略的。我们可以把这些重复的步骤包装起来成为一个'骨架',需要时直接使用。当然,你也可以根据需求加枸杞或柠檬,即重新定义特定步骤。
在面向对象编程中,这对应于父类定义流程,子类实现具体细节。这种模式遵循'好莱坞原则'(Don't call us, we'll call you),由框架控制流程,开发者填充细节。
JavaScript 代码实现
在 JavaScript 中,我们可以通过构造函数和原型链来实现模版方法模式。
首先定义一个基类 Tea,用于创建泡茶的对象:
// 定义 Tea 构造函数
function Tea(type, add) {
this.add = add;
this.type = type;
console.log('你准备泡一杯' + this.type);
}
Tea 对象作为原型对象,负责构建对象实例的属性。每个 JavaScript 对象都有一个与之关联的原型(prototype)。当试图访问对象的某个属性时,如果对象本身没有这个属性,JavaScript 引擎会自动查找对象的原型链,直到找到该属性或者到达原型链的末端。
Tea 通过 prototype 属性添加方法,以它为原型的对象可以共享这些方法:
// 在 Tea 对象中使用 prototype 添加方法
Tea.prototype.boilWater = function () {
console.log("把水煮沸");
}
Tea.prototype.steepBag = function () {
console.log("用沸水浸泡茶叶");
}
Tea.prototype.pourInCap = function () {
console.log("把茶水倒进杯子");
}
Tea.prototype.additive = function () {
if (this.add != null) {
console.log("加" + this.add);
} else {
console.log("未添加");
}
}
// 定义模板方法 init,它定义了泡茶的整个流程
Tea.prototype.init = function () {
this.boilWater();
this.steepBag();
this.pourInCap();
this.additive();
}
// 实例化
var greenTea = new Tea('绿茶', '蜂蜜');
var wlTea = new Tea('乌龙茶');
// 调用模板方法 init 来执行泡茶的流程
greenTea.init();
优点分析
- 封装不变部分,扩展可变部分:模板方法模式将不变的行为搬移到父类中,去除子类中的重复代码,使得子类可以专注于实现自己的特定步骤。
- 提供一个很好的代码复用平台:通过模板方法,子类可以复用父类中的代码,同时也可以添加新的行为。
结合大语言模型(LLM)
模版模式毫无疑问是很有挖掘价值的,但完成这样一个任务,我们似乎也可以借助大模型的能力。大模型自身具有很强的自然语言处理能力,可以将非结构化的文本指令转化为结构化的步骤。
基础集成示例
我们需要引入 OpenAI SDK 并配置环境变量。以下是一个简单的 Node.js 示例,展示如何调用大模型来处理泡茶指令:
const OpenAI = require('openai');
require('dotenv').config();
// 初始化客户端
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: 'https://api.openai.com/v1' // 建议使用官方或合规的代理地址
});
const getChatResponse = async function (model, prompt) {
try {
const response = await client.chat.completions.create({
model: model,
messages: [
{
role: 'user',
content: prompt
}
]
});
return response.choices[0].message.content;
} catch (error) {
console.error('API Call Failed:', error.message);
throw error;
}
};
const main = async () => {
const text = `
泡一杯茶很容易,首先需要把水烧开。
在等待的过程中,把茶包放入一个杯子。
一旦水烧开了,就把它倒在茶包上,
等待一会,让茶包浸泡,几分钟后,取出茶包,
如果你愿意,加一些蔗糖或牛奶调味,
就这样,你可以享受一杯美味的茶了
`;
const prompt = `
你将获得由三个引号括起来的文本。
如果它包含了一系列的指令,则需要按照以下格式重新编写这些指令:
第一步 - ...
第二步 - ...
...
如果文本不包含一系列指令,则直接写'未提供步骤'。
"""${text}"""
`;
const chatCompletion = await getChatResponse('gpt-3.5-turbo', prompt);
console.log(chatCompletion);
};
main();
进阶应用:动态生成流程
我们可以进一步结合类结构与 LLM,实现更灵活的自助教程雏形。例如,根据用户输入的茶种,动态生成制作步骤并交由 LLM 格式化。
function Tea(type, add) {
this.type = type;
this.add = add || '无';
this.process = `我准备泡一杯${this.type}`;
}
Tea.prototype.init = function () {
this.process += `,需要添加:${this.add}`;
return this.process;
}
var greenTea = new Tea('卡布奇诺', "甜甜的你");
const main = async () => {
const prompt = `
你将获得由三个引号括起来的文本。
如果它包含一个制作饮料的想法,请你提供步骤如:
第一步 - ...
第二步 - ...
...
如果文本不包含一个想法,则直接写'没意思'。
"""${greenTea.init()}"""
`;
const chatCompletion = await getChatResponse('gpt-3.5-turbo', prompt);
console.log(chatCompletion);
};
main();
最佳实践与注意事项
在实际项目中结合设计模式与大模型时,需要注意以下几点:
- 提示词工程(Prompt Engineering):确保 Prompt 清晰明确,指定输出格式(如 JSON、Markdown 列表),以减少模型幻觉带来的解析错误。
- 异步处理:LLM 调用通常耗时较长,务必使用
async/await避免阻塞主线程,特别是在 Web 服务中。 - 错误处理:网络波动或 API 限流可能导致请求失败,应增加重试机制(Retry Policy)和超时设置。
- 成本控制:频繁调用大模型可能产生较高费用,建议对结果进行缓存(Cache),对于相同输入避免重复请求。
- 安全性:不要在前端代码中硬编码 API Key,应使用环境变量管理敏感信息,防止泄露。
总结
模式与语言模型(LLM)的结合是一种创新的跨领域应用。它将软件工程的最佳实践与 AI 的强大自然语言处理能力相结合,为软件开发带来了新的可能性和效率提升。通过将模版方法模式的骨架固定下来,利用 LLM 填充具体的业务逻辑描述,我们可以构建更加灵活、智能的系统架构。未来,随着多模态大模型的发展,这种结合将在自动化测试、代码生成、智能运维等领域发挥更大的作用。


