一、基础方式:同步全量输出(Non-Stream)
这是最开始提到的「一次性输出」,也是大模型 API 的默认方式------ 发送 Prompt 后,等待模型生成完整结果,再一次性返回全量响应。
1. 核心逻辑
- 客户端发送请求 → 服务器接收后,让模型完整生成所有内容 → 生成完毕后,一次性返回全量数据(无增量推送)。
- 本质是「请求 - 响应」模式(类似普通 HTTP 接口),无需处理流式拼接,逻辑最简单。
2. 核心价值
- 开发成本极低:无需处理流数据、增量拼接,直接解析完整响应即可;
- 适合短文本场景:生成内容长度短(如 Commit 信息、单句代码优化、简短问答),等待时间可接受(通常 1-3 秒);
- 数据完整性高:直接拿到完整结果,无需担心流中断导致的内容缺失。
3.具体信息
ts
const express = require("express");
const path = require("path");
const OpenAI = require("openai");
const app = express();
const PORT = 3000;
// 解析JSON请求体(必须在路由之前)
app.use(express.json());
// 添加请求日志中间件(用于调试)
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
next();
});
// 初始化 OpenAI 客户端
// 优先从环境变量读取 API Key,如果没有则使用硬编码的 Key
const API_KEY =
process.env.MOONSHOT_API_KEY ||
"sk-V3bOPqDn6gfsQUiQzxL6n7SgbJI1O0YlV6QFoLv2xplXUyuI";
if (!API_KEY || API_KEY === "") {
console.warn(
"警告: 未设置 API Key,请设置环境变量 MOONSHOT_API_KEY 或在代码中配置"
);
}
const client = new OpenAI({
apiKey: API_KEY,
baseURL: "https://api.moonshot.cn/v1",
});
// 非流式端点:调用大模型并一次性返回完整结果
app.post("/api/chat/non-stream", async (req, res) => {
console.log("收到非流式请求:", req.body);
try {
const { message, systemPrompt } = req.body;
if (!message) {
console.log("错误: 消息内容为空");
return res.status(400).json({ error: "消息内容不能为空" });
}
// 构建消息数组
const messages = [];
if (systemPrompt) {
messages.push({
role: "system",
content: systemPrompt,
});
} else {
messages.push({
role: "system",
content:
"你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。",
});
}
messages.push({
role: "user",
content: message,
});
console.log("调用大模型API(非流式),模型: kimi-k2-turbo-preview");
// 调用大模型非流式API
const completion = await client.chat.completions.create({
model: "kimi-k2-turbo-preview",
messages: messages,
temperature: 0.6,
stream: false, // 非流式
});
console.log("API调用成功,返回完整结果");
// 返回完整结果
res.json({
success: true,
content: completion.choices[0].message.content,
usage: completion.usage,
});
} catch (error) {
console.error("非流式API错误:", error);
console.error("错误详情:", {
message: error.message,
status: error.status,
code: error.code,
type: error.type,
});
res.status(error.status || 500).json({
success: false,
error: error.message || "未知错误",
status: error.status,
});
}
});
// 根路由(必须在静态文件服务之前)
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
// 静态文件服务(放在最后,避免拦截API路由)
app.use(express.static(path.join(__dirname, "public")));
// 404处理(必须在所有路由之后)
app.use((req, res) => {
console.log(`404错误: ${req.method} ${req.path} 未找到`);
// 如果是API请求,返回JSON
if (req.path.startsWith("/api/")) {
res
.status(404)
.json({ error: "API路由未找到", path: req.path, method: req.method });
} else {
// 其他请求返回HTML
res.status(404).send("页面未找到");
}
});
// 启动服务器
app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
console.log(`访问 http://localhost:${PORT} 查看应用`);
});
比较简单,直接返回响应结果
二、SSE(Server-Sent Events):流式输出(解决 "等待全量结果" 问题)
1. 场景核心
- 基础用法:发送 Prompt 后,等待模型生成完整结果再一次性返回(比如生成 1000 字文案要等 5 秒);
- SSE 用法:模型生成每一个 token(词 / 字) 就实时推送给客户端,像 ChatGPT 网页版那样 "打字机式输出",无需等待全量结果。
2. 核心价值
- 提升交互体验:用户能实时看到输出进度,减少 "等待焦虑";
- 支持长文本:生成几千字的文档 / 代码时,不会因超时导致请求失败;
- 节省带宽:无需缓存全量结果,逐段接收即可。
3. 举例
ts
// SSE端点:调用大模型并流式输出
app.post("/api/chat/stream", async (req, res) => {
try {
const { message, systemPrompt } = req.body;
if (!message) {
return res.status(400).json({ error: "消息内容不能为空" });
}
// 设置SSE响应头
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
// 构建消息数组
const messages = [];
if (systemPrompt) {
messages.push({
role: "system",
content: systemPrompt,
});
} else {
messages.push({
role: "system",
content:
"你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。",
});
}
messages.push({
role: "user",
content: message,
});
// 发送开始消息
res.write(
`data: ${JSON.stringify({
type: "start",
message: "开始生成回答...",
})}\n\n`
);
// 调用大模型流式API
console.log("调用大模型API,模型: kimi-k2-turbo-preview");
const stream = await client.chat.completions.create({
model: "kimi-k2-turbo-preview",
messages: messages,
temperature: 0.6,
stream: true,
});
console.log("API调用成功,开始流式返回");
// 流式处理响应
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.content) {
// 将每个内容块通过SSE发送给前端
res.write(
`data: ${JSON.stringify({
type: "content",
content: delta.content,
})}\n\n`
);
}
}
// 发送结束消息
res.write(
`data: ${JSON.stringify({ type: "done", message: "回答生成完成" })}\n\n`
);
// 关闭连接
res.end();
} catch (error) {
console.error("SSE错误:", error);
console.error("错误详情:", {
message: error.message,
status: error.status,
code: error.code,
type: error.type,
});
// 处理401错误(认证失败)
if (error.status === 401) {
res.write(
`data: ${JSON.stringify({
type: "error",
message: "API Key 认证失败,请检查您的 API Key 是否正确或是否已过期",
details: "401 Unauthorized",
})}\n\n`
);
} else {
res.write(
`data: ${JSON.stringify({
type: "error",
message: error.message || "未知错误",
status: error.status,
})}\n\n`
);
}
res.end();
}
});
关键代码
ts
// 设置响应请求头
res.setHeader("Content-Type", "text/event-stream");
const client = new OpenAI({
apiKey: API_KEY,
baseURL: "https://api.moonshot.cn/v1",
});
// 调用大模型流式 PI,stream: true,
const stream = await client.chat.completions.create({
model: "kimi-k2-turbo-preview",
messages: messages,
temperature: 0.6,
stream: true,
});
// 流式处理响应
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.content) {
// 将每个内容块通过SSE发送给前端
res.write(
`data: ${JSON.stringify({
type: "content",
content: delta.content,
})}\n\n`
);
}
}
// 发送结束消息
res.write(
`data: ${JSON.stringify({ type: "done", message: "回答生成完成" })}\n\n`
);
如果希望返回多个供用户选择, 入参的时候可以加上 n这个参数
ts
let data = {
"model": "kimi-k2-turbo-preview",
"messages": [ // 具体的 messages ],
"temperature": 0.6,
"stream": true,
"n": 2 // <-- 注意这里,我们要求 Kimi 大模型输出 2 个回复
};
也可以直接使用 HTTP 请求, 不适用 Openai SDK
ts
// SSE端点:使用原生 HTTP 请求实现流式输出
app.post("/api/chat/stream", async (req, res) => {
try {
const { message, systemPrompt } = req.body;
if (!message) {
return res.status(400).json({ error: "消息内容不能为空" });
}
// 设置SSE响应头
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
// 构建消息数组
const messages = [];
if (systemPrompt) {
messages.push({
role: "system",
content: systemPrompt,
});
} else {
messages.push({
role: "system",
content:
"你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。",
});
}
messages.push({
role: "user",
content: message,
});
// 发送开始消息
res.write(
`data: ${JSON.stringify({
type: "start",
message: "开始生成回答...",
})}\n\n`
);
// 使用原生 fetch 调用流式 API
console.log("调用大模型API,模型: kimi-k2-turbo-preview");
const response = await fetch("https://api.moonshot.cn/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${API_KEY}`,
},
body: JSON.stringify({
model: "kimi-k2-turbo-preview",
messages: messages,
temperature: 0.6,
stream: true,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error?.message || `HTTP错误! 状态码: ${response.status}`);
}
console.log("API调用成功,开始流式返回");
// 读取流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
// 解码数据块
buffer += decoder.decode(value, { stream: true });
// 按行分割(SSE 格式:data: {...}\n\n)
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (line.startsWith("data: ")) {
const dataStr = line.slice(6);
// 跳过 [DONE] 标记
if (dataStr.trim() === "[DONE]") {
continue;
}
try {
const chunk = JSON.parse(dataStr);
const delta = chunk.choices[0]?.delta;
if (delta?.content) {
// 将每个内容块通过SSE发送给前端
res.write(
`data: ${JSON.stringify({
type: "content",
content: delta.content,
})}\n\n`
);
}
} catch (e) {
console.error("解析流式数据错误:", e, "原始数据:", dataStr);
}
}
}
}
// 发送结束消息
res.write(
`data: ${JSON.stringify({ type: "done", message: "回答生成完成" })}\n\n`
);
// 关闭连接
res.end();
} catch (error) {
console.error("SSE错误:", error);
// 处理错误
if (error.status === 401) {
res.write(
`data: ${JSON.stringify({
type: "error",
message: "API Key 认证失败,请检查您的 API Key 是否正确或是否已过期",
details: "401 Unauthorized",
})}\n\n`
);
} else {
res.write(
`data: ${JSON.stringify({
type: "error",
message: error.message || "未知错误",
})}\n\n`
);
}
res.end();
}
});