zhizhi/dingtalk-bot/broadcast-generator.js

187 lines
5.2 KiB
JavaScript
Raw Normal View History

/**
* 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. 完成状态用emojicompleted用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 };