极客时间已完结课程限时免费阅读

25|工具与框架

25|工具与框架-徐昊 · AI时代的软件工程-极客时间
下载APP

25|工具与框架

讲述:徐昊

时长05:30大小12.57M

你好,我是徐昊,今天我们来继续学习 AI 时代的软件工程。
上节课我们讲解了如何使用大语言模型(Large Language Model,LLM)稳定地构造基于语义的自动化脚本,并介绍了三种今日常用的技巧。
今天我们要讨论一个更为前沿(cutting edge)的问题,在大语言模型时代(Large Language Model,LLM),框架与工具将何去何从?

知识复用还是载体复用?

你的第一个疑问可能是,为什么大语言模型会影响框架与工具?要知道,在大语言模型出现之前,我们复用知识的主要形式就是框架和工具。无论你是使用 ReactJS,还是 Spring Boot 编写前后端应用,实际上你复用了凝聚在这些框架中的知识。
所以使用框架和工具的过程,也是一个知识过程。你还会发现,当你能够熟练应用框架时,你基本上处于庞杂认知行为模式(Complicated),即按照框架提供的思路解决具体问题。而使用工具,或者工具性质的库,则更接近于清晰认知行为模式(Clear),即按照问题选取对应工具。
那么我们不禁要问一个问题,如果我掌握了凝聚在框架和工具中的知识,我是否还需要这些框架和工具?在 LLM 出现之前,答案是肯定的,因为我们无法直接复用知识,我们只能通过知识的载体——软件——对这些知识进行复用。
然而今天,LLM 带来了更高效的知识复用方式,它使得我们可以直接复用知识。
让我们看一个简单的例子,假设我们现在使用 JavaScript 在开发前端应用,我们会经常用到一些小的工具方法,当然有很多库都为我们提供了封装好的工具方法,我们之前可以通过这些库来复用这些功能,但是现在我们有了另一个选择,就是让 LLM 为我们构造这些工具方法。
比如我们想要 escape 字符串中的特殊字符,我们当然可以使用诸如 underscore.js 之类的库,通过 _.escape 实现这个功能。
现在让我们对比一下,直接复用知识和复用载体的两种做法。直接复用知识,我们可以使用下面的提示词:
处理输入的文字,escape掉字符串中的所有的特殊字符,&, <, >, ", `, 和 '。用js编写
function escapeSpecialChars(str) {
return str.replace(/[&<>"'`]/g, function(match) {
switch(match) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
case '\'':
return '&#x27;'; // &apos; is not recommended
case '`':
return '&#x60;'; // &#96; is not recommended
}
});
}
// 示例用法
var originalString = 'This is a <b>bold</b> statement & a "quote"';
var escapedString = escapeSpecialChars(originalString);
console.log(escapedString);
复用载体,我们可以使用这样的提示词:
处理输入的文字,escape掉字符串中的所有的特殊字符,&, <, >, ", `, 和 ', 用js编写,并使用underscore.js
从实现上来看二者没有什么差别,一个自己写了实现,另一个调用了 underscore.js 中对应的函数。但如果比较提示词的话,我们会发现,复用载体的方法对我们有更大的认知负载。它需要我们知道存在一个 underscore.js 库,并且知道在这个库中存在一个可以 escape 特殊字符的函数。而从结果上来看,知不知道这个额外的知识,并不构成显著的差异。换句话说,我们付出了额外的认知负载,但并没有得到额外的收益。
那么我们能在多大程度上直接复用知识呢?让我们看一下我们这个课程中可以用到的例子,我们前面讲过,对于提示词的模板化(prompt templating)是非常重要的技巧,但是 OpenAI 默认并不支持提示词模板。不过几乎所有使用 LLM 的框架,都支持提示词模板。
其实,我们也不需要任何的框架,只要我们明白我们所需要做的事情,很容易就能通过 LLM 实现对应的功能。
编写一个调用openai chat api的js方法。这个方法接受prompt做为输入。prompt支持参数化。
async function callOpenAIChatAPI(prompt) {
const apiKey = 'YOUR_API_KEY'; // 替换为你的 OpenAI API 密钥
const response = await fetch('https://api.openai.com/v1/engines/text-davinci-003/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
prompt: prompt,
max_tokens: 150,
temperature: 0.7,
}),
});
const data = await response.json();
if (data && data.choices && data.choices.length > 0) {
return data.choices[0].text.trim();
} else {
throw new Error('Failed to generate response from OpenAI Chat API.');
}
}
// Example usage:
const prompt = "你好,我的名字是{{ name }}。我今年{{ age }}岁。";
const parameters = { name: "小明", age: 25 };
const formattedPrompt = prompt.replace(/{{\s*(\w+)\s*}}/g, (match, p1) => parameters[p1]);
callOpenAIChatAPI(formattedPrompt)
.then(response => {
console.log("Response:", response);
}).catch(error => {
console.error("Error:", error);
});
可以看到,这虽然算不上一个结构良好的框架,但是基本功能都在,我们甚至可以在这个基础上继续扩展功能。比如对于更复杂的 prompt 应用,我们需要构成 chain 或者 flow。那么我们可以这么写:
现在我有一组prompt希望组成一个chain。每一个prompt执行之后,给一个指定function处理openai的返回结果。然后作为参数,传递给下一个prompt
async function processPromptsInChain(prompts, handleResponseFunction) {
if (prompts.length === 0) {
return; // 如果没有剩余的 prompt,结束递归
}
const currentPrompt = prompts[0];
const formattedPrompt = formatPrompt(currentPrompt); // 格式化 prompt,如果有必要的话
try {
const response = await callOpenAIChatAPI(formattedPrompt); // 调用 OpenAI API
// 调用指定的处理函数处理返回的结果
const processedResponse = handleResponseFunction(response);
// 继续处理剩余的 prompt
await processPromptsInChain(prompts.slice(1), handleResponseFunction);
} catch (error) {
console.error("Error processing prompt:", error);
// 可能需要添加适当的错误处理逻辑
}
}
// 格式化 prompt,替换参数
function formatPrompt(prompt, parameters) {
if (parameters) {
return prompt.replace(/{{\s*(\w+)\s*}}/g, (match, p1) => parameters[p1]);
} else {
return prompt;
}
}
// 调用 OpenAI API
async function callOpenAIChatAPI(prompt) {
const apiKey = 'YOUR_API_KEY'; // 替换为你的 OpenAI API 密钥
const response = await fetch('https://api.openai.com/v1/engines/text-davinci-003/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
prompt: prompt,
max_tokens: 150,
temperature: 0.7,
}),
});
const data = await response.json();
if (data && data.choices && data.choices.length > 0) {
return data.choices[0].text.trim();
} else {
throw new Error('Failed to generate response from OpenAI Chat API.');
}
}
// 处理返回结果的示例函数
function handleResponse(response) {
// 在这里可以处理 OpenAI 返回的结果,例如日志记录、分析等
console.log("Received response from OpenAI:", response);
// 这里可以返回处理后的结果,如果不需要,则返回原始结果
return response;
}
// 示例用法:
const prompts = [
"你好,我的名字是{{ name }}。我今年{{ age }}岁。",
"我来自{{ city }}。",
"我的职业是{{ occupation }}。",
];
const parameters = { name: "小明", age: 25, city: "北京", occupation: "工程师" };
processPromptsInChain(prompts, handleResponse)
.then(() => {
console.log("All prompts processed successfully.");
}).catch(error => {
console.error("Error processing prompts:", error);
});

可工作的知识(Working Knowledge)

上面的例子几乎就是一个 LangChain 最初版的极简版本。我在提出需求的时候,脑子里的确也是在以 LangChain 作为对照参考的。我们只需要大概了解某个框架的核心模式,那么借助 LLM 就不难构造一个简易版本。
在过去的三十年里,我们非常强调可工作的软件(Working Software),敏捷方法将可工作的软件作为度量进度的唯一标准。这是因为我们需要一个可执行的载体,来验证知识传递的效果。而今天,在大模型的帮助下,提炼出的知识几乎可以直接工作。我们是否还需要强调可工作的软件?还是应该直接关注可工作的知识?
这并不是一个容易回答的问题。简单来说就是太早了,目前仍难有定论
支持这种做法的人主要是从 LLM 使用效率出发,来考虑这个问题的。
想让 LLM 使用任何框架或工具,要么就是将该框架的文档资料汇入大模型的基础语料当中(作为预训练模型,或者 fine tuning 模型),要么就是通过上下文、少样本学习(few shots examples)和思维链(Chain of Thought,CoT)让大模型掌握应用框架或工具的诀窍。
如果 LLM 基础语料中关于模型的知识已经落伍(Out of date),那么 LLM 就无法生成有效的代码。比如,ChatGPT 采用了某框架的 1.0 版本,但是目前已经是 2.0,并存在破坏性改变(Breaking change)。那么大模型生成代码的效率就大大降低,还会大大增加使用 LLM 的认知成本。与其这样还不如让大模型从头写,减少对于框架的依赖。
对于自己提炼、构造的框架也存在同样的问题。提炼框架需要花时间花成本,训练大模型或是接入大模型还需要额外的时间与成本。从 LLM 使用效率的角度上说,还需要自己提取框架吗?把核心概念和模式说清楚是不是就够了?
从 LLM 使用效率的角度出发,可工作的知识的确比可工作的软件具有更高的效率。
而反对这种做法的,主要是基于现实情况的考量。除了核心模式之外,框架和工具中还包含了 1000 个细节。就以 LangChain 为例,构造基本的 Prompt Chain 并不困难,但是错误处理、超时、异步等等为了让系统更健壮、更稳定或更有效率的处理,想完全靠 LLM 重现,认知负载并不低。这样还不如直接使用框架或工具。
另外还有一个关于智力资产保护的问题,今天的知识版权保护法是建立在对于知识的特定呈现(representation)上的,知识产权保护法并不保护一个想法。这一前提的假设就是,孤立的、提炼的知识是没用的。现在这个情况改变了,孤立的、提炼的知识可以是有用的
那么试想一下,只要去听了一个产品的发布会,他们讲解了解决问题的核心思路,我们就不难通过 LLM 复制这个产品的主体框架,而不用担心任何对于智力资产的侵犯。那么可工作的知识还会为我们带来一个繁荣和开放的社区吗?

小结

过去我们关注于可工作软件,因为除此之外我们难以直接应用知识。但现在对比一下直接复用知识和复用载体的两种做法,就会发现有些情况下,大模型帮我们提炼出的知识几乎可以直接工作。
可工作的知识(Working Knowledge)是一种非常有吸引力的知识载体,但是目前仍然还是太早了,我们无法作出有效的判断。
目前我们能知道的是,在未来一段时间,我们评估任何框架、工具和产品,都需要考虑 LLM 友好性(LLM Friendly)。

思考题

请将一个你自己构造的框架,变得对 LLM 更友好。
欢迎在留言区分享你的想法,我会让编辑置顶一些优质回答供大家学习讨论。

1. 大语言模型(LLM)的出现改变了知识复用的方式,使得直接复用知识成为可能,对传统的框架与工具复用提出了挑战。 2. 直接复用知识与复用载体的比较表明,复用载体的方法对认知负载更大,但并没有额外的收益,因此提出了直接复用知识的可行性。 3. 大语言模型的应用范围广泛,可以实现对应功能,如调用openai chat api的js方法,支持参数化的prompt等,甚至可以在此基础上继续扩展功能,如构成chain或者flow。 4. 提示词模板化是非常重要的技巧,而LLM使得这一技巧得以应用,大多数使用LLM的框架都支持提示词模板。 5. 直接复用知识的可行性高,通过LLM可以很容易地实现对应的功能,甚至不需要任何框架的支持。 6. 示例应用展示了直接复用知识的实际应用,如调用openai chat api的js方法、处理返回结果的示例函数以及处理prompt组成chain的示例代码。 7. LLM的出现改变了知识复用的方式,使得直接复用知识成为可能,对传统的框架与工具复用提出了挑战。 8. 在评估任何框架、工具和产品时,需要考虑其对LLM的友好性(LLM Friendly)。 9. 对于自己构造的框架,需要考虑如何使其对LLM更友好,以提高知识的复用效率。 10. 可工作的知识(Working Knowledge)是一种有吸引力的知识载体,但目前仍需要进一步评估其有效性和适用性。

分享给需要的人,Ta购买本课程,你将得29
生成海报并分享
2024-05-03

赞 1

提建议

上一篇
24|构造基于语义的自动化脚本
下一篇
直播专场(三)|如何理解测试策略与测试工序
unpreview
 写留言

全部留言(3)

  • 最新
  • 精选
  • 术子米德
    2024-05-03 来自浙江
    🤔☕️🤔☕️🤔 【R】用框架 ~ 庞杂(Complicated),按照框架提供的思路去解决问题;用工具 ~ 清晰(Clear),明确问题后选择对应工具。 复用知识 vs 复用载体:结果一样,后者需要额外的认知负载(即:知道载体本身,如库的接口和用法等)。 可工作的软件(Working Software)vs 可工作的知识(Working Knowledge),是个可探讨的、暂无定论的问题,可工作的知识、相比可工作的软件,具有更高的效率,这已经显然。 【.I.】用框架,是填空题,用工具,是选择题。那么,做框架和做工具,就都是出题目的人,差别在于,框架题型关注思路的给出、过程的发挥、结果的创新,工具题型关注问题的明确、结果的预期。 【.I.】我想要做什么,即问题的定义,我想要怎么做,即问题的解法,这些是要LLM辅助我的前置条件。只有我对问题和解法清晰Clear,无论是复用知识的手段、还是复用载体的的手段,都变成LLM是否具有相应的知识、 以及是否能提示到LLM这部分的知识。当我有这些前置条件后,复用知识会更游刃有余,复用载体虽然有额外的认知负担,可也不会大到哪里去的负担,反而可以是知识应用的参考,说不定还能领略到框架的知识应用之美。 【Q】学习框架和工具,再造框架和工具,是否是学习知识、应用知识、验证知识掌握程度的好法子? 【Q】当代码农,脱口就是“show me the code”,这样的思维和习惯,是否会影响到,转型为读Markdown和Comment里,半结构化可工作知识的开发模式?是否可以猜测,从可工作的软件、到可工作的知识的转型,是否需要一代人的时间? — by 术子米德@2024年5月3日
    展开

    作者回复: 三年一代

  • D调的暖冬
    2024-05-09 来自浙江
    可工作的软件还是非常重要的,这是因为这个软件包含了很多隐式知识他把使用知识方式用简单的几个api暴露,降低了认知的复杂度,而没有这方面经验的人想要表达这个知识是非常困难的,即使大模型知道这个知识,因为ai生成的东西质量充满不确定性,那么人就很难评估是否合适,哪怕就是测试代码你可能也很难进行把控。 反过来看如果我的框架希望被LLM学习并通过LLM这个媒介让更多人使用,那我就应该将框架的知识模型化表达(正如前面所示通过领域模型对话也能够帮助我们了解业务),让框架知识更多通过测试用例代码去表达(文档是给人看的,但是文档表达的内容测试用例都能覆盖到,使得模型能够更好生成使用代码),
    展开
  • 6点无痛早起学习的和...
    2024-05-05 来自北京
    读下来有一个感受就是,以前是人了解知识、框架和工具(载体),但现在 LLM 知道这些框架和工具,那就是给 LLM 知识,LLM 自己去找合适的框架和工具,不知道理解是否正确。