187 lines
5.2 KiB
JavaScript
187 lines
5.2 KiB
JavaScript
|
|
/**
|
|||
|
|
* broadcast-generator.js
|
|||
|
|
* 模型API调用 + 广播生成 + 格式校验
|
|||
|
|
* 秋秋说:这个文件就像小作家,根据解析出来的信息写新广播
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const axios = require('axios');
|
|||
|
|
const config = require('./config.json');
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调用模型API生成广播
|
|||
|
|
* @param {Object} parsedLog - parseSyslog 返回的解析结果
|
|||
|
|
* @param {Object} developerProfile - 开发者画像(可选)
|
|||
|
|
* @returns {Promise<Object>} 生成的广播对象
|
|||
|
|
*/
|
|||
|
|
async function generateBroadcast(parsedLog, developerProfile = {}) {
|
|||
|
|
try {
|
|||
|
|
// 1. 构建 prompt(给模型的提示词)
|
|||
|
|
const prompt = buildPrompt(parsedLog, developerProfile);
|
|||
|
|
|
|||
|
|
// 2. 调用模型API
|
|||
|
|
const modelResponse = await callModelAPI(prompt);
|
|||
|
|
|
|||
|
|
// 3. 解析模型返回的内容
|
|||
|
|
const broadcastText = modelResponse.choices?.[0]?.message?.content || modelResponse;
|
|||
|
|
|
|||
|
|
// 4. 校验广播格式
|
|||
|
|
const validatedBroadcast = validateBroadcast(broadcastText, parsedLog);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success: true,
|
|||
|
|
broadcast: validatedBroadcast,
|
|||
|
|
raw: broadcastText
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('广播生成失败:', error.message);
|
|||
|
|
return {
|
|||
|
|
success: false,
|
|||
|
|
error: error.message,
|
|||
|
|
fallbackBroadcast: generateFallbackBroadcast(parsedLog) // 出错时用模板生成
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构建 prompt
|
|||
|
|
*/
|
|||
|
|
function buildPrompt(parsedLog, developerProfile) {
|
|||
|
|
const { bcNumber, devId, phaseNum, status, summary } = parsedLog;
|
|||
|
|
|
|||
|
|
// 基础信息
|
|||
|
|
let prompt = `你是一个工程广播生成器。根据以下SYSLOG信息,生成一条新的工程广播。
|
|||
|
|
|
|||
|
|
SYSLOG信息:
|
|||
|
|
- BC编号:${bcNumber || '未知'}
|
|||
|
|
- 开发者:${devId || '未知'}
|
|||
|
|
- 环节号:${phaseNum || '未知'}
|
|||
|
|
- 完成状态:${status || '未知'}
|
|||
|
|
- 技术摘要:${summary || '无'}
|
|||
|
|
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
// 如果有画像信息,加入
|
|||
|
|
if (Object.keys(developerProfile).length > 0) {
|
|||
|
|
prompt += `\n开发者画像:\n${JSON.stringify(developerProfile, null, 2)}\n`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 广播模板参考
|
|||
|
|
prompt += `\n请生成一条格式规范的广播,包含:
|
|||
|
|
1. BC编号(格式:BC-XXX-XXX-ZZ)
|
|||
|
|
2. 开发者信息(DEV-xxx)
|
|||
|
|
3. 环节号
|
|||
|
|
4. 完成状态(用emoji:completed用✅,partial用⚠️,blocked用🔴)
|
|||
|
|
5. 下一环节建议
|
|||
|
|
6. 验收标准提示
|
|||
|
|
|
|||
|
|
广播格式示例:
|
|||
|
|
BC-M17-007-ZZ · DEV-004之之 · M17动态漫 · 环节7 · ✅ 完成 · 在线预览功能已实现,下一环节:分享嵌入
|
|||
|
|
|
|||
|
|
请直接返回广播文本,不要有其他解释。`;
|
|||
|
|
|
|||
|
|
return prompt;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调用模型API
|
|||
|
|
*/
|
|||
|
|
async function callModelAPI(prompt) {
|
|||
|
|
const { url, key, model } = config.modelApi;
|
|||
|
|
|
|||
|
|
// 如果配置的是占位符,则返回模拟数据(用于测试)
|
|||
|
|
if (key === 'YOUR_MODEL_API_KEY') {
|
|||
|
|
console.log('使用模拟模型API响应(测试模式)');
|
|||
|
|
|
|||
|
|
// 修复:不使用 undefined 的 parsedLog,直接返回模拟广播
|
|||
|
|
return {
|
|||
|
|
choices: [
|
|||
|
|
{
|
|||
|
|
message: {
|
|||
|
|
content: `BC-M17-007-ZZ · DEV-004之之 · M-DINGTALK · 环节1 · ✅ 完成 · SYSLOG自动处理已实现,下一环节:多维表格联动`
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 真实API调用
|
|||
|
|
const response = await axios.post(
|
|||
|
|
url,
|
|||
|
|
{
|
|||
|
|
model: model,
|
|||
|
|
messages: [
|
|||
|
|
{ role: 'system', content: '你是一个工程广播生成助手,只返回广播文本,不返回其他内容。' },
|
|||
|
|
{ role: 'user', content: prompt }
|
|||
|
|
],
|
|||
|
|
temperature: 0.7
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${key}`,
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return response.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 校验广播格式
|
|||
|
|
*/
|
|||
|
|
function validateBroadcast(broadcastText, parsedLog) {
|
|||
|
|
const result = {
|
|||
|
|
isValid: false,
|
|||
|
|
broadcast: broadcastText,
|
|||
|
|
issues: []
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 检查是否包含BC编号
|
|||
|
|
if (!broadcastText.includes('BC-')) {
|
|||
|
|
result.issues.push('缺少BC编号');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否包含DEV编号
|
|||
|
|
if (!broadcastText.includes('DEV-')) {
|
|||
|
|
result.issues.push('缺少开发者信息');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否包含环节号
|
|||
|
|
if (!broadcastText.includes('环节')) {
|
|||
|
|
result.issues.push('缺少环节号');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查状态emoji
|
|||
|
|
const hasCompletedEmoji = broadcastText.includes('✅');
|
|||
|
|
const hasPartialEmoji = broadcastText.includes('⚠️');
|
|||
|
|
const hasBlockedEmoji = broadcastText.includes('🔴');
|
|||
|
|
|
|||
|
|
if (!hasCompletedEmoji && !hasPartialEmoji && !hasBlockedEmoji) {
|
|||
|
|
result.issues.push('缺少状态emoji(✅/⚠️/🔴)');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有问题,标记为有效
|
|||
|
|
if (result.issues.length === 0) {
|
|||
|
|
result.isValid = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成备用广播(API调用失败时使用)
|
|||
|
|
*/
|
|||
|
|
function generateFallbackBroadcast(parsedLog) {
|
|||
|
|
const { bcNumber, devId, phaseNum, status } = parsedLog;
|
|||
|
|
|
|||
|
|
const statusEmoji = status === 'completed' ? '✅' :
|
|||
|
|
status === 'partial' ? '⚠️' : '🔴';
|
|||
|
|
|
|||
|
|
const nextPhase = phaseNum ? parseInt(phaseNum, 10) + 1 : '?';
|
|||
|
|
|
|||
|
|
return `BC-${bcNumber || 'M17'}-${nextPhase}-ZZ · DEV-${devId || '004'}之之 · M-DINGTALK · 环节${phaseNum || '1'} · ${statusEmoji} 完成 · SYSLOG自动处理已触发(API调用失败,使用模板生成),下一环节:多维表格联动`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = { generateBroadcast };
|