Files
platform-pipi/packages/voice-production/scripts/generate_char_introduce.js
T
2026-02-02 10:38:18 +08:00

263 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs');
const path = require('path');
const https = require('https');
const crypto = require('crypto');
require('dotenv').config({ path: path.join(__dirname, '../.env') });
// 腾讯云混元大模型配置
// 请在 .env 文件中配置 TENCENT_SECRET_ID 和 TENCENT_SECRET_KEY
const SECRET_ID = process.env.TENCENT_SECRET_ID;
const SECRET_KEY = process.env.TENCENT_SECRET_KEY;
// API配置
const HOST = 'hunyuan.tencentcloudapi.com';
const SERVICE = 'hunyuan';
const REGION = 'ap-guangzhou';
const ACTION = 'ChatCompletions';
const VERSION = '2023-09-01';
const MODEL = 'hunyuan-lite'; // 使用混元lite模型,可根据需要更换
// 签名相关函数
function sha256(message, secret = '', encoding = 'hex') {
const hmac = secret ? crypto.createHmac('sha256', secret) : crypto.createHash('sha256');
return hmac.update(message).digest(encoding);
}
function getDate(timestamp) {
const date = new Date(timestamp * 1000);
const year = date.getUTCFullYear();
const month = ('0' + (date.getUTCMonth() + 1)).slice(-2);
const day = ('0' + date.getUTCDate()).slice(-2);
return `${year}-${month}-${day}`;
}
function generateSignature(secretId, secretKey, host, payload, timestamp) {
const date = getDate(timestamp);
// 步骤1:拼接规范请求串
const httpRequestMethod = 'POST';
const canonicalUri = '/';
const canonicalQueryString = '';
const contentType = 'application/json';
const canonicalHeaders = `content-type:${contentType}\nhost:${host}\nx-tc-action:${ACTION.toLowerCase()}\n`;
const signedHeaders = 'content-type;host;x-tc-action';
const hashedRequestPayload = sha256(payload);
const canonicalRequest = `${httpRequestMethod}\n${canonicalUri}\n${canonicalQueryString}\n${canonicalHeaders}\n${signedHeaders}\n${hashedRequestPayload}`;
// 步骤2:拼接待签名字符串
const algorithm = 'TC3-HMAC-SHA256';
const credentialScope = `${date}/${SERVICE}/tc3_request`;
const hashedCanonicalRequest = sha256(canonicalRequest);
const stringToSign = `${algorithm}\n${timestamp}\n${credentialScope}\n${hashedCanonicalRequest}`;
// 步骤3:计算签名
const secretDate = sha256(date, 'TC3' + secretKey, 'buffer');
const secretService = sha256(SERVICE, secretDate, 'buffer');
const secretSigning = sha256('tc3_request', secretService, 'buffer');
const signature = sha256(stringToSign, secretSigning);
// 步骤4:拼接Authorization
const authorization = `${algorithm} Credential=${secretId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
return authorization;
}
// 调用混元API
async function callHunyuan(prompt) {
return new Promise((resolve, reject) => {
const timestamp = Math.floor(Date.now() / 1000);
const payload = JSON.stringify({
Model: MODEL,
Messages: [
{
Role: 'user',
Content: prompt,
},
],
Stream: false,
});
const authorization = generateSignature(SECRET_ID, SECRET_KEY, HOST, payload, timestamp);
const options = {
hostname: HOST,
port: 443,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
Host: HOST,
'X-TC-Action': ACTION,
'X-TC-Version': VERSION,
'X-TC-Timestamp': timestamp.toString(),
'X-TC-Region': REGION,
Authorization: authorization,
},
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
try {
const result = JSON.parse(data);
if (result.Response && result.Response.Choices) {
resolve(result.Response.Choices[0].Message.Content.trim());
} else if (result.Response && result.Response.Error) {
reject(new Error(result.Response.Error.Message));
} else {
reject(new Error('Unknown API response: ' + data));
}
} catch (e) {
reject(e);
}
});
});
req.on('error', reject);
req.write(payload);
req.end();
});
}
// 批量为多个汉字生成介绍(单次 API 调用处理多个汉字)
async function generateBatchIntroduce(chars) {
const charList = chars.join('、');
const prompt = `请为以下汉字分别生成简短的词组介绍。
汉字列表:${charList}
要求:
1. 每个汉字的介绍格式为:"字,词组的字",其中词组是一个包含该汉字的常见词语
2. 请严格按照 JSON 数组格式输出,不要有其他内容
示例输出格式:
[{"char":"天","introduce":"天,天空的天"},{"char":"地","introduce":"地,大地的地"},{"char":"人","introduce":"人,人民的人"}]
请直接输出 JSON 数组,不要包含任何解释或 markdown 代码块标记。`;
try {
const response = await callHunyuan(prompt);
// 尝试解析 JSON 响应
let jsonStr = response.trim();
// 移除可能的 markdown 代码块标记
jsonStr = jsonStr.replace(/^```json?\s*/i, '').replace(/\s*```$/i, '');
// 尝试提取 JSON 数组
const jsonMatch = jsonStr.match(/\[[\s\S]*\]/);
if (jsonMatch) {
jsonStr = jsonMatch[0];
}
const parsed = JSON.parse(jsonStr);
// 验证并修正结果
const results = [];
for (const char of chars) {
const found = parsed.find((item) => item.char === char);
if (found && found.introduce) {
// 确保格式正确
let introduce = found.introduce.replace(/["""]/g, '').trim();
if (!introduce.startsWith(char + '')) {
introduce = `${char}${introduce}`;
}
results.push({ char, introduce });
} else {
// 如果没找到,使用默认格式
results.push({ char, introduce: `${char}${char}字的${char}` });
}
}
return results;
} catch (error) {
console.error(`批量生成介绍失败:`, error.message);
// 失败时返回默认格式
return chars.map((char) => ({
char,
introduce: `${char}${char}字的${char}`,
}));
}
}
// 延时函数
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 主函数
async function main() {
// 检查环境变量
if (!SECRET_ID || !SECRET_KEY) {
console.error('请在 .env 文件中配置 TENCENT_SECRET_ID 和 TENCENT_SECRET_KEY');
console.error('');
console.error('1. 复制 .env.example 为 .env');
console.error(' cp .env.example .env');
console.error('');
console.error('2. 编辑 .env 文件,填入你的腾讯云密钥:');
console.error(' TENCENT_SECRET_ID=your_secret_id');
console.error(' TENCENT_SECRET_KEY=your_secret_key');
process.exit(1);
}
// 读取汉字数据
const charStringPath = path.join(__dirname, '../data/char/char_string.json');
const charString = JSON.parse(fs.readFileSync(charStringPath, 'utf-8'));
// 获取所有汉字
const chars = charString['3500'].split('');
console.log(`共有 ${chars.length} 个汉字需要处理`);
// 检查是否有已存在的进度文件
const outputPath = path.join(__dirname, '../data/char/char_introduce.json');
let results = [];
let startIndex = 0;
if (fs.existsSync(outputPath)) {
try {
results = JSON.parse(fs.readFileSync(outputPath, 'utf-8'));
startIndex = results.length;
console.log(`发现已有进度,从第 ${startIndex + 1} 个汉字继续`);
} catch (e) {
console.log('已有文件解析失败,从头开始');
}
}
// 批量处理配置
const BATCH_SIZE = 20; // 每次 API 调用处理 20 个汉字
const DELAY_BETWEEN_BATCHES = 1000; // 批次间延迟1秒
for (let i = startIndex; i < chars.length; i += BATCH_SIZE) {
const batch = chars.slice(i, Math.min(i + BATCH_SIZE, chars.length));
console.log(
`\n处理第 ${i + 1} - ${Math.min(i + BATCH_SIZE, chars.length)} 个汉字(共 ${batch.length} 个)...`
);
console.log(`汉字列表: ${batch.join('')}`);
// 单次 API 调用处理整批汉字
const batchResults = await generateBatchIntroduce(batch);
// 打印结果
for (const item of batchResults) {
console.log(` ${item.char}: ${item.introduce}`);
}
results.push(...batchResults);
// 每批处理完后保存进度
fs.writeFileSync(outputPath, JSON.stringify(results, null, 2), 'utf-8');
console.log(`已保存进度:${results.length}/${chars.length}`);
// 批次间延迟,避免API限流
if (i + BATCH_SIZE < chars.length) {
await delay(DELAY_BETWEEN_BATCHES);
}
}
console.log(`\n完成!共生成 ${results.length} 个汉字介绍`);
console.log(`结果已保存到: ${outputPath}`);
}
main().catch(console.error);