Node.js调用OpenAI API指南:从入门到工具调用

第一节:最基础的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助手,在实践中不断完善和优化。

相关推荐
SuperherRo1 小时前
Web攻防-大模型应用&LLM安全&提示词注入&不安全输出&代码注入&直接间接&数据投毒
大模型·llm·提示词注入·不安全输出·直接·间接
堆栈future4 小时前
LangGraph实践-构建AI工作流:创建一本大模型应用开发书籍
langchain·llm·aigc
大志说编程5 小时前
LangChain框架入门15:深度解析Retrievers检索器组件
python·langchain·llm
kngines6 小时前
【Node.js从 0 到 1:入门实战与项目驱动】1.3 Node.js 的应用场景(附案例与代码实现)
node.js
AI大模型6 小时前
基于 Ollama 本地 LLM 大语言模型实现 ChatGPT AI 聊天系统
程序员·llm·ollama
AI大模型6 小时前
AI大模型选择指南:从ChatGPT到国产新秀,一文看懂如何选对你的AI助手
gpt·程序员·llm
努力还债的学术吗喽8 小时前
2020 GPT3 原文 Language Models are Few-Shot Learners 精选注解
gpt·大模型·llm·gpt-3·大语言模型·few-shot·zero-shot
龍小南10 小时前
RAG第2章:向量数据库(理论和常见数据库)
llm
AI大模型1 天前
深度解析AI大模型【架构→训练→推理】核心技术全景图
程序员·llm·agent
AI大模型1 天前
一文读懂:大模型应用开发平台选型指南(附教程)
程序员·llm·agent