作为前端开发者,我们不需要深入理解大模型的底层原理(如 Transformer 架构、注意力机制等),但需要了解如何有效地使用它。
从前端视角来看,大模型可以被理解为:
一个智能的文本处理系统
一个 REST API 服务
一个需要优化成本的资源
这种理解方式让我们能够专注于如何在前端应用中更好地集成和使用大模型,而不是纠结于其内部实现细节。
Chat 模式是一种交互式对话方式,支持上下文理解。每次对话都包含完整的对话历史,使模型能够理解上下文并提供连贯的回答。
在 Chat 模式中,每条消息都需要指定角色(role),主要包括:
其中 system、user 和 assistant 是最基础和常用的角色。function 和 tool 角色主要用于函数调用和工具集成场景,让模型能够调用外部功能并获取结果。
角色系统让模型能够理解对话中各方的身份,有助于生成更合适的回答。
模型之所以能"记住"之前的对话,是因为每次请求时我们都会发送完整的对话历史:
const messages = [
{ role: "system", content: "你是一个友善的助手" },
{ role: "user", content: "你好" },
{ role: "assistant", content: "你好!有什么我可以帮你的吗?" },
{ role: "user", content: "请介绍一下自己" }
]
主要特点:
示例API调用:
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "你好,请介绍一下自己"}
]
}'
Completion 模式是早期的API形式,主要用于文本补全任务。
主要特点:
completions 的主要参数:
在 API 路径 “chat/completions” 中:
示例:
Token 是模型处理文本的基本单位:
Token 限制:
为了更高效地使用 token 配额,可以:
使用简洁的表达方式
// 完整写法(更多token)
请帮我写一个 JavaScript 函数,实现数组去重,需要考虑各种边界情况,并添加详细的注释说明
// 简洁写法(更少token)
JS: arr.unique() impl
使用约定的格式符号
str + reverse -> array
精简上下文
使用技术简写
这些优化可以在保持语义清晰的同时显著减少 token 使用量。
计算 token:
// 使用 tiktoken 等库可以预估 token 数量
import { encoding_for_model } from "tiktoken";
const enc = encoding_for_model("gpt-3.5-turbo");
const tokenCount = enc.encode("你好世界").length;
示例API调用:
curl https://api.openai.com/v1/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "text-davinci-003",
"prompt": "写一个故事,主题是:",
"max_tokens": 100
}'
在使用模型生成文本时,有几个重要的参数会影响 token 的补全效果:
Temperature(温度)
例如,对于提示词"写一个故事开头":
temperature = 0:
"从前有一个小女孩,她住在一个小村庄里。"(最常见/安全的开头)
temperature = 1:
"在霓虹闪烁的赛博城市里,一个机器人正在学习做梦。"(有创意但仍合理)
temperature = 2:
"紫色的风吹过钢琴键,奏响了时间的咖啡香。"(非常创意/可能不够连贯)
Top_p(核采样)
例如,对于提示词"今天天气":
top_p = 0.1:
"今天天气很好/不错/晴朗"(只从最高概率的选项中选择)
top_p = 0.9:
"今天天气有点阴沉沉的,好像要下雨了"(考虑更多可能性)
Frequency_penalty(频率惩罚)
例如,对于提示词"列举水果":
frequency_penalty = 0:
"苹果、香蕉、苹果、梨子、苹果"(可能重复)
frequency_penalty = 1:
"苹果、香蕉、梨子、橙子、葡萄"(避免重复)
Presence_penalty(存在惩罚)
例如,对于提示词"介绍一下猫":
presence_penalty = 0:
"猫是一种可爱的宠物,猫喜欢吃鱼,猫会抓老鼠..."(持续谈论猫的特点)
presence_penalty = 1:
"猫是一种宠物,它们在古埃及被视为神明,现代社会中宠物市场..."(会引入相关但不同的主题)
例如,对于提示词"我喜欢吃",模型可能预测下一个 token 的概率分布:
这些参数的调整会直接影响模型如何从这些候选 token 中进行选择。
以下是一个前端代码补全的综合示例,展示不同参数如何影响补全效果:
提示词:
function unique(arr) {
return arr.fil
// temperature = 0:最常见的补全方式
// 因为 filter + indexOf 是最常用的数组去重实现
function unique(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
// temperature = 1:会尝试其他可能的补全
// 使用 lastIndexOf 是另一种常见的去重思路
function unique(arr) {
return arr.filter((value, index, self) => self.lastIndexOf(value) === index);
}
// top_p = 0.1:只补全最高概率的实现
// filter + indexOf 的组合在代码库中出现频率最高
function unique(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
// top_p = 0.9:会考虑其他常见的补全方式
// includes 虽然不是最常用,但在实际开发中也很常见
function unique(arr) {
return arr.filter((item, i, array) => !array.slice(0, i).includes(item));
}
// frequency_penalty = 0:可能重复使用相同的方法
// 不惩罚重复,所以多次使用了 arr 和 index
function unique(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
// frequency_penalty = 1:倾向于使用不同的方法
// 通过引入 seen 变量避免重复使用数组方法
function unique(arr) {
return arr.filter((item, i) => {
const seen = arr.slice(0, i);
return !seen.includes(item);
});
}
// presence_penalty = 0:保持在基本的过滤逻辑
// 只关注基本的去重功能
function unique(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
// presence_penalty = 1:引入新的处理逻辑
// 添加了首项处理和 some 方法的新思路
function unique(arr) {
return arr.filter((item, i) => {
if (i === 0) return true;
return !arr.slice(0, i).some(prev => prev === item);
});
}
通过这些对比,我们可以看到代码补全时:
在实际开发中,我们可以根据需求调整这些参数:
从前端开发者的角度,我们可以这样理解和使用大模型:
作为智能 API 服务
作为开发助手
最佳实践
注意事项
通过这种理解方式,我们可以更好地在前端项目中集成和使用大模型,让它成为提升开发效率的得力助手。