使用JavaScript的api直接在前端问答速度虽然快但是有token直接暴露的风险。
现在使用nodejs也可以快速进行流式输出并且可以隐藏用户敏感信息。
const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;
//启动服务node index.js
app.get('/test', async (req, res) => {
try {
// 设置流式响应头(保持不变)
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userQuestion = req.query.questions || '默认问题:你好!';
const response = await axios({
method: 'POST',
url: 'https://api.siliconflow.cn/v1/chat/completions',
headers: {
Authorization: `Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,
'Content-Type': 'application/json'
},
data: {
model: "Qwen/Qwen2.5-72B-Instruct",
messages: [{ role: "user", content: userQuestion }],
stream: true,
max_tokens: 2048,
stop: ["END"]
},
responseType: 'stream'
});
let buffer = ''; // 新增:内容缓冲区
let shouldStop = false; // 新增:停止标志
response.data.on('data', (chunk) => {
if (shouldStop) return;
const lines = chunk.toString('utf8').split('\n').filter(line => line.trim() !== '');
lines.forEach(line => {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
try {
const jsonData = JSON.parse(line.replace(/^data: /, ''));
if (jsonData.choices?.[0]?.delta?.content) {
const content = jsonData.choices[0].delta.content;
// 修正2:客户端检测停止词
buffer += content;
if (buffer.includes('END')) {
shouldStop = true;
// 发送END前的有效内容
const validContent = buffer.split('END')[0];
// 发送流式输出结果
res.write(validContent);
res.end();
return;
}
res.write(content);
}
} catch (error) {
console.error('处理错误:', error);
}
}
});
});
response.data.on('end', () => {
if (!shouldStop) res.end();
});
} catch (error) {
console.error('请求出错:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
app.listen(port, () => {
console.log(`Example app listening on http://localhost:${port}`);
});
如果不需要流式输出的话按下面的方式即可
app.post('/test', async (req, res) => {
try {
console.log(req.body);
const userQuestion = req.body.questions || '';
// 设置流式响应头(保持不变)
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 发送请求获取配置信息
const response = await axios.get('http://www.config.cn', {
headers: {
Accept: 'application/json',
Authorization: `token信息`,
},
});
// 假设API返回的数据在response.data中
// 根据实际API返回的数据结构,你可能需要调整这里的属性名
const configData = response.data;
let model_name_yt = configData.data[0].model_name_yt;//获取的模型名称
let model_key_yt = configData.data[0].model_key_yt;//获取的模型apiKey
let model_url_yt = configData.data[0].model_url_yt;//访问模型地址的url
let model_prompt_check = configData.data[0].model_prompt_check;//设置的提示词信息
Authorization_key = 'Bearer '+model_key_yt
templateStr = model_prompt_check.replace("${questions}", userQuestion); // 执行替换
console.log(templateStr)
const response_json = await axios({
method: 'POST',
url: model_url_yt,
headers: {
Authorization: Authorization_key,
'Content-Type': 'application/json'
},
data: {
model: model_name_yt,
messages: [{ role: "user", content: templateStr }],
max_tokens: 2048,
},
});
const messageContent = response_json.data;
let contentInfo = messageContent.choices[0].message.content;
res.json(contentInfo);//返回模型输出的内容
} catch (error) {
console.error('请求出错:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
在流式输出中使用tools工具
const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;
// 使用中间件解析JSON格式的请求体
app.use(express.json());
//启动服务node index.js
app.get('/test', async (req, res) => {
try {
// 设置流式响应头(保持不变)
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userQuestion = req.query.questions || '默认问题:你好!';
const response = await axios({
method: 'POST',
url: 'https://api.siliconflow.cn/v1/chat/completions',
headers: {
Authorization: `Bearer sk-*******************************************`,
'Content-Type': 'application/json'
},
data: {
model: "Qwen/Qwen2.5-72B-Instruct",
messages: [{ role: "user", content: userQuestion }],
stream: true,
max_tokens: 2048,
tools: [
{
type: "function",
function: {
name: "multiplication",
description: "Calculate the multiplication of two numbers",
parameters: {
number1:"数字1",
number2:"数字2"
}
},
strict: false
},
{
type: "function",
function: {
name: "weather",
description: "获取天气预报数据",
},
strict: false
}
],
stop: ["END"],
},
responseType: 'stream'
});
let buffer = ''; // 新增:内容缓冲区
let shouldStop = false; // 新增:停止标志
response.data.on('data', (chunk) => {
if (shouldStop) return;
const lines = chunk.toString('utf8').split('\n').filter(line => line.trim() !== '');
lines.forEach(line => {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
try {
const jsonData = JSON.parse(line.replace(/^data: /, ''));
//console.log(jsonData);
if (jsonData.choices?.[0]?.delta?.content) {
const content = jsonData.choices[0].delta.content;
console.log(content);
// 修正2:客户端检测停止词
buffer += content;
if (buffer.includes('END')) {
shouldStop = true;
// 发送END前的有效内容
const validContent = buffer.split('END')[0];
// 发送流式输出结果
res.write(validContent);
res.end();
return;
}
if (content == '<tool_call>') {
res.write("");
}else{
res.write(content);
}
}
//*********************************************************** */
// 检查是否有 tool_calls 字段
console.log(line);
if (jsonData.choices?.[0]?.delta?.tool_calls) {
const function_name = jsonData.choices[0].delta.tool_calls[0].function.name;
//console.log(function_name);
if (function_name == 'weather'){
return_obj = weather()
console.log('天气查询:'+return_obj);
res.write(return_obj);
}
if (function_name == 'multiplication'){
// arguments = jsonData.choices[0].delta.tool_calls[0].function.arguments;
// let jsonObj = JSON.parse(arguments);
// console.log(jsonObj);
// let num1 = jsonObj.number1
// let num2 = jsonObj.number2
//return_obj = multiplication(num1,num2)
}
}
} catch (error) {
console.error('处理错误:', error);
}
}
});
});
response.data.on('end', () => {
if (!shouldStop) res.end();
});
} catch (error) {
console.error('请求出错:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 乘法函数
function multiplication(num1, num2) {
return num1 * num2;
}
// 天气函数示例
function weather() {
console.log('天气查询');
return '今天天气很好万里无云。';
}
app.listen(port, () => {
console.log(`Example app listening on http://localhost:${port}`);
});
流式输出时多工具调用的实现方式
const express = require('express');
const axios = require('axios');
const app = express();
const port = 3456;
// 使用中间件解析JSON格式的请求体
app.use(express.json());
// 启动服务 node index.js
app.get('/test', async (req, res) => {
try {
// 设置流式响应头
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userQuestion = req.query.questions || '默认问题:你好!';
const response = await axios({
method: 'POST',
url: 'https://api.siliconflow.cn/v1/chat/completions',
headers: {
Authorization: `Bearer sk-*****************************`,
'Content-Type': 'application/json'
},
data: {
model: "Qwen/Qwen2.5-72B-Instruct",
messages: [{ role: "system", content: "你是荆州市的问答助手,回复荆州市相关的问题,当判断需要使用工具时请直接调用工具不要做其他回复" },{ role: "user", content: userQuestion }],
stream: true,
max_tokens: 2048,
tools: [
{
type: "function",
function: {
name: "multiplication",
description: "Calculate the multiplication of two numbers",
parameters: {
number1:"数字1",
number2:"数字2"
}
},
strict: false
},
{
type: "function",
function: {
name: "weather",
description: "获取天气预报数据",
parameters: {
city:"城市"
}
},
strict: false
}
],
stop: ["END"],
},
responseType: 'stream'
});
// 存储所有工具调用的信息
const toolCalls = {};
let currentToolCallId = null;
let isToolCallInProgress = false;
response.data.on('data', async (chunk) => {
const lines = chunk.toString('utf8').split('\n').filter(line => line.trim() !== '');
for (let line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
try {
const jsonData = JSON.parse(line.replace(/^data: /, ''));
// 处理常规内容输出
if (jsonData.choices?.[0]?.delta?.content) {
res.write(line + "\n");
}
// 处理工具调用
if (jsonData.choices?.[0]?.delta?.tool_calls) {
isToolCallInProgress = true;
// 处理每个工具调用
for (const toolCall of jsonData.choices[0].delta.tool_calls) {
const { index, id, type, function: fn } = toolCall;
// 新的工具调用开始
if (id && !toolCalls[id]) {
currentToolCallId = id;
toolCalls[id] = {
index,
type,
name: fn?.name || '',
arguments: '',
complete: false
};
}
// 更新当前工具调用的信息
if (currentToolCallId && toolCalls[currentToolCallId]) {
// 更新工具名称(如果有)
if (fn?.name) {
toolCalls[currentToolCallId].name = fn.name;
}
// 收集参数片段
if (fn?.arguments) {
toolCalls[currentToolCallId].arguments += fn.arguments;
}
}
}
res.write(line + "\n"); // 转发工具调用信息
}
} catch (error) {
console.error('处理错误:', error);
}
} else if (line === 'data: [DONE]') {
// 处理所有工具调用结果
if (isToolCallInProgress) {
console.log(toolCalls);
try {
// 收集所有需要处理的工具调用
const validToolCalls = Object.entries(toolCalls)
.filter(([id, call]) => id && call.name && call.arguments && !call.complete);
// 按原始顺序处理工具调用
const sortedToolCalls = validToolCalls.sort((a, b) => a[1].index - b[1].index);
for (const [id, call] of sortedToolCalls) {
try {
const args = JSON.parse(call.arguments);
let toolResult;
// 根据工具名称调用不同的函数
switch (call.name) {
case 'weather':
toolResult = await getWeather(args.city);
break;
case 'multiplication':
toolResult = await multiplyNumbers(args.number1, args.number2);
break;
default:
toolResult = { error: `未知工具: ${call.name}` };
}
// 发送工具调用结果
const toolResponse = {
id: `tool_${id}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: "Qwen/Qwen2.5-72B-Instruct",
choices: [{
index: call.index,
delta: {
role: "assistant",
content: JSON.stringify(toolResult)
},
finish_reason: "stop"
}]
};
res.write(`data: ${JSON.stringify(toolResponse)}\n`);
} catch (parseError) {
console.error(`解析工具${call.name}参数错误:`, parseError);
}
}
} catch (error) {
console.error('处理工具调用错误:', error);
}
}
res.write('data: [DONE]\n');
res.end();
}
}
});
response.data.on('error', (error) => {
console.error('流式响应错误:', error);
res.end();
});
} catch (error) {
console.error('请求出错:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 天气函数示例
async function getWeather(city) {
console.log(`天气查询 - ${city}`);
// 天气查询接口获取的数据
const res = { "city": city, "msg": `${city}今天的天气很好` };
const resString = JSON.stringify(res);
const responseToolsJson = await axios({
method: 'POST',
url: 'https://api.siliconflow.cn/v1/chat/completions',
headers: {
Authorization: 'Bearer sk-********************************',
'Content-Type': 'application/json'
},
data: {
model: 'Qwen/Qwen2-7B-Instruct',
messages: [{ role: "system", content: '##要求\n提取json中的参数拼接成用户可以理解的语句,直接输出结果即可不需要添加自己的描述。' }, { role: "user", content: resString }],
max_tokens: 2048,
},
});
const messageContent = responseToolsJson.data.choices[0].message.content;
console.log(responseToolsJson.data);
return { city: city, msg: messageContent };
}
// 乘法函数
async function multiplyNumbers(num1, num2) {
const result = num1 * num2;
return {
operation: "multiplication",
number1: num1,
number2: num2,
result: result,
message: `${num1}乘以${num2}等于${result}`
};
}
app.listen(port, () => {
console.log(`Example app listening on http://localhost:${port}`);
});