第一节:最基础的LLM对话
让我们从最简单的对话开始,理解OpenAI API的基本用法。
消息的基本结构
每个消息都有两个核心字段:
javascript
interface Message {
role: 'user' | 'assistant'; // 先只关注这两个角色
content: string; // 消息内容
}
最简单的对话示例
javascript
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
// 最基础的对话
async function simpleChat() {
const messages = [
{
role: 'user' as const,
content: '你好,请介绍一下自己'
}
];
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: messages
});
console.log(response.choices[0].message.content);
}
多轮对话的实现
javascript
class SimpleChat {
private messages: Message[] = [];
private openai: OpenAI;
constructor(apiKey: string) {
this.openai = new OpenAI({ apiKey });
}
// 发送用户消息并获取回复
async sendMessage(userInput: string): Promise<string> {
// 1. 添加用户消息
this.messages.push({
role: 'user',
content: userInput
});
// 2. 调用API
const response = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: this.messages
});
const assistantReply = response.choices[0].message.content || '';
// 3. 保存AI回复
this.messages.push({
role: 'assistant',
content: assistantReply
});
return assistantReply;
}
// 查看对话历史
getHistory(): Message[] {
return [...this.messages];
}
}
使用示例
javascript
async function demo() {
const chat = new SimpleChat(process.env.OPENAI_API_KEY!);
// 第一轮对话
const reply1 = await chat.sendMessage('你好');
console.log('AI:', reply1);
// 第二轮对话(AI会记住之前的内容)
const reply2 = await chat.sendMessage('我刚才说了什么?');
console.log('AI:', reply2);
// 查看完整对话历史
console.log('对话历史:', chat.getHistory());
}
关键要点:
- 每次API调用都要传入完整的消息历史
- 用户消息用role: 'user',AI回复用role: 'assistant'
- 消息顺序很重要,按时间顺序排列
第二节:使用System消息设定AI行为
现在我们学习如何通过System消息来"调教"AI的行为。
System消息的作用
System消息就像给AI的"工作说明书",告诉它应该如何表现。
javascript
interface Message {
role: 'system' | 'user' | 'assistant'; // 新增system角色
content: string;
}
带System消息的聊天类
javascript
class SystemChat {
private messages: Message[] = [];
private openai: OpenAI;
constructor(apiKey: string, systemPrompt: string) {
this.openai = new OpenAI({ apiKey });
// System消息总是放在第一位
this.messages.push({
role: 'system',
content: systemPrompt
});
}
async sendMessage(userInput: string): Promise<string> {
this.messages.push({
role: 'user',
content: userInput
});
const response = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: this.messages
});
const assistantReply = response.choices[0].message.content || '';
this.messages.push({
role: 'assistant',
content: assistantReply
});
return assistantReply;
}
}
不同的System Prompt示例
javascript
// 1. 专业助手
const technicalAssistant = new SystemChat(
process.env.OPENAI_API_KEY!,
'你是一个专业的技术文档助手。请用简洁、准确的语言回答编程相关问题,并提供代码示例。'
);
// 2. 创意写手
const creativeWriter = new SystemChat(
process.env.OPENAI_API_KEY!,
'你是一个富有创意的作家。请用生动、有趣的语言回答问题,多使用比喻和故事。'
);
// 3. 严格的老师
const strictTeacher = new SystemChat(
process.env.OPENAI_API_KEY!,
'你是一个严格的编程老师。当学生问问题时,不要直接给答案,而是引导他们思考,问一些启发性问题。'
);
对比测试
javascript
async function compareSystemPrompts() {
const question = '什么是递归?';
console.log('=== 技术助手 ===');
const tech = await technicalAssistant.sendMessage(question);
console.log(tech);
console.log('\n=== 创意写手 ===');
const creative = await creativeWriter.sendMessage(question);
console.log(creative);
console.log('\n=== 严格老师 ===');
const teacher = await strictTeacher.sendMessage(question);
console.log(teacher);
}
System消息的最佳实践:
- 明确定义AI的角色和专业领域
- 指定回答的风格和格式
- 设定行为边界和限制
- 保持简洁但具体
第三节:工具调用 - 让AI具备行动能力
现在我们学习最强大的功能:让AI调用外部工具。
工具调用的消息结构
工具调用涉及两个新的消息角色:
javascript
interface Message {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string | null;
tool_calls?: ToolCall[]; // assistant消息可能包含工具调用
tool_call_id?: string; // tool消息必须有这个ID
}
interface ToolCall {
id: string;
type: 'function';
function: {
name: string;
arguments: string; // JSON字符串
};
}
定义工具
首先定义AI可以使用的工具:
javascript
const tools = [
{
type: 'function' as const,
function: {
name: 'get_weather',
description: '获取指定城市的天气信息',
parameters: {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称'
}
},
required: ['city']
}
}
},
{
type: 'function' as const,
function: {
name: 'calculate',
description: '执行数学计算',
parameters: {
type: 'object',
properties: {
expression: {
type: 'string',
description: '数学表达式,如 "2 + 3 * 4"'
}
},
required: ['expression']
}
}
}
];
实现工具函数
javascript
// 模拟天气API
async function getWeather(city: string): Promise<string> {
// 实际项目中这里会调用真实的天气API
const weatherData = {
'北京': '晴天,25°C',
'上海': '多云,22°C',
'广州': '雨天,28°C'
};
return weatherData[city as keyof typeof weatherData] || '未知城市';
}
// 计算器
function calculate(expression: string): string {
try {
// 注意:实际项目中需要安全的表达式求值
const result = eval(expression);
return `${expression} = ${result}`;
} catch (error) {
return '计算错误';
}
}
支持工具调用的聊天类
kotlin
class ToolChat {
private messages: Message[] = [];
private openai: OpenAI;
private tools: any[];
constructor(apiKey: string, systemPrompt: string, tools: any[]) {
this.openai = new OpenAI({ apiKey });
this.tools = tools;
this.messages.push({
role: 'system',
content: systemPrompt
});
}
async sendMessage(userInput: string): Promise<string> {
// 1. 添加用户消息
this.messages.push({
role: 'user',
content: userInput
});
// 2. 调用API(包含工具定义)
const response = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: this.messages,
tools: this.tools,
tool_choice: 'auto' // 让AI自动决定是否使用工具
});
const message = response.choices[0].message;
// 3. 处理AI的回复
if (message.tool_calls) {
// AI想要调用工具
return await this.handleToolCalls(message);
} else {
// 普通回复
this.messages.push({
role: 'assistant',
content: message.content || ''
});
return message.content || '';
}
}
private async handleToolCalls(message: any): Promise<string> {
// 1. 保存AI的工具调用消息
this.messages.push({
role: 'assistant',
content: message.content,
tool_calls: message.tool_calls
});
// 2. 执行每个工具调用
for (const toolCall of message.tool_calls) {
const { name, arguments: args } = toolCall.function;
const parsedArgs = JSON.parse(args);
let result: string;
// 根据工具名称执行对应函数
switch (name) {
case 'get_weather':
result = await getWeather(parsedArgs.city);
break;
case 'calculate':
result = calculate(parsedArgs.expression);
break;
default:
result = '未知工具';
}
// 3. 保存工具执行结果
this.messages.push({
role: 'tool',
content: result,
tool_call_id: toolCall.id
});
}
// 4. 让AI基于工具结果给出最终回复
const finalResponse = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: this.messages,
tools: this.tools
});
const finalMessage = finalResponse.choices[0].message.content || '';
this.messages.push({
role: 'assistant',
content: finalMessage
});
return finalMessage;
}
}
使用示例
javascript
async function toolDemo() {
const chat = new ToolChat(
process.env.OPENAI_API_KEY!,
'你是一个智能助手,可以查询天气和进行计算。',
tools
);
// 测试天气查询
console.log('用户: 北京今天天气怎么样?');
const weather = await chat.sendMessage('北京今天天气怎么样?');
console.log('AI:', weather);
// 测试计算
console.log('\n用户: 帮我算一下 15 * 8 + 32');
const calc = await chat.sendMessage('帮我算一下 15 * 8 + 32');
console.log('AI:', calc);
}
工具调用的关键要点:
- AI会自动判断是否需要使用工具
- 工具调用是异步的,需要等待执行结果
- 每个工具调用都有唯一的ID用于关联结果
- AI会基于工具结果给出最终的用户友好回复
第四节:流式响应 - 让AI回复更自然
当AI需要生成长篇回复时,用户不想等待完整答案,而是希望看到实时的"打字"效果。这就需要流式响应。
启用流式响应
javascript
class StreamingChat {
private openai: OpenAI;
private messages: Message[] = [];
constructor(apiKey: string, systemPrompt: string) {
this.openai = new OpenAI({ apiKey });
this.messages.push({
role: 'system',
content: systemPrompt
});
}
async sendMessageStream(userInput: string): Promise<void> {
// 添加用户消息
this.messages.push({
role: 'user',
content: userInput
});
// 启用流式响应
const stream = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: this.messages,
stream: true // 关键:启用流式响应
});
let fullResponse = '';
// 处理流式数据
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
if (content) {
fullResponse += content;
// 实时输出每个片段
process.stdout.write(content);
}
}
// 保存完整回复
this.messages.push({
role: 'assistant',
content: fullResponse
});
}
}
在Web应用中使用流式响应
javascript
// 后端API路由
app.post('/api/chat/stream', async (req, res) => {
const { message } = req.body;
// 设置SSE响应头
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*'
});
try {
const stream = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: message }],
stream: true
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
if (content) {
// 发送SSE数据
res.write(`data: ${JSON.stringify({ content })}\n\n`);
}
}
res.write('data: [DONE]\n\n');
res.end();
} catch (error) {
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
res.end();
}
});// 后端API路由app.post('/api/chat/stream', async(req, res)=>{ const{message}=req.body; // 设置SSE响应头 res.writeHead(200, { 'Content-Type':'text/event-stream', 'Cache-Control':'no-cache', 'Connection':'keep-alive', 'Access-Control-Allow-Origin':'*' }); try{ conststream=awaitopenai.chat. completions.create({ model:'gpt-3.5-turbo', messages:[{role:'user', content: message}], stream:true }); forawait(constchunkofstream){ constcontent=chunk.choices[0]?. delta?.content||''; if(content){ // 发送SSE数据 res.write(`data: ${JSON.stringify ({content})}\n\n`); } } res.write('data: [DONE]\n\n'); res.end(); }catch(error){ res.write(`data: ${JSON.stringify({ error:error.message})}\n\n`); res.end(); }});
前端接收流式数据
javascript
// 前端JavaScript
function sendStreamMessage(message: string) {
const eventSource = new EventSource('/api/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message })
});
const responseDiv = document.getElementById('response');
responseDiv.innerHTML = '';
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close();
return;
}
try {
const data = JSON.parse(event.data);
if (data.content) {
responseDiv.innerHTML += data.content;
}
} catch (error) {
console.error('解析数据失败:', error);
}
};
eventSource.onerror = (error) => {
console.error('流式连接错误:', error);
eventSource.close();
};
}
关键要点:
- 设置stream: true启用流式响应
- 使用for await循环处理数据流
- 前端用EventSource接收SSE数据
- 记得在完整回复后保存到消息历史
总结
通过本文的学习,我们从零开始掌握了在Node.js中调用OpenAI API的核心技能。OpenAI API为我们打开了AI应用开发的大门,但真正的价值在于将这些技术应用到实际场景中,解决真实的问题。现在你已经具备了基础技能,是时候开始构建属于你自己的AI应用了!
记住 :最好的学习方式就是实践。选择一个你感兴趣的场景,动手构建一个AI助手,在实践中不断完善和优化。