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 allChars = charString['3500'].split(''); console.log(`共有 ${allChars.length} 个汉字需要处理`); // 检查是否有已存在的进度文件 const outputPath = path.join(__dirname, '../data/char/char_introduce.json'); let existingResults = []; const generatedSet = new Set(); if (fs.existsSync(outputPath)) { try { existingResults = JSON.parse(fs.readFileSync(outputPath, 'utf-8')); existingResults.forEach((item) => generatedSet.add(item.char)); console.log(`发现已有 ${existingResults.length} 个汉字介绍`); } catch (e) { console.log('已有文件解析失败,从头开始'); } } // 过滤出未生成的汉字 const charsToGenerate = allChars.filter((char) => !generatedSet.has(char)); console.log(`其中 ${generatedSet.size} 个已生成,${charsToGenerate.length} 个待生成`); if (charsToGenerate.length === 0) { console.log('所有汉字介绍已生成完毕,无需处理'); return; } // 批量处理配置 const BATCH_SIZE = 20; // 每次 API 调用处理 20 个汉字 const DELAY_BETWEEN_BATCHES = 1000; // 批次间延迟1秒 for (let i = 0; i < charsToGenerate.length; i += BATCH_SIZE) { const batch = charsToGenerate.slice(i, Math.min(i + BATCH_SIZE, charsToGenerate.length)); console.log( `\n处理第 ${i + 1} - ${Math.min(i + BATCH_SIZE, charsToGenerate.length)} 个汉字(共 ${batch.length} 个)...` ); console.log(`汉字列表: ${batch.join('')}`); // 单次 API 调用处理整批汉字 const batchResults = await generateBatchIntroduce(batch); // 打印结果 for (const item of batchResults) { console.log(` ${item.char}: ${item.introduce}`); } // 合并到已有结果 existingResults.push(...batchResults); batchResults.forEach((item) => generatedSet.add(item.char)); // 每批处理完后保存进度 fs.writeFileSync(outputPath, JSON.stringify(existingResults, null, 2), 'utf-8'); console.log(`已保存进度:${existingResults.length}/${allChars.length}`); // 批次间延迟,避免API限流 if (i + BATCH_SIZE < charsToGenerate.length) { await delay(DELAY_BETWEEN_BATCHES); } } console.log(`\n完成!共生成 ${existingResults.length} 个汉字介绍`); console.log(`结果已保存到: ${outputPath}`); } main().catch(console.error);