fix: address code review feedback - extract constants, fix Claude model matching, workflow conditions

- llm-router.js: fix isClaudeModel() to properly match against CLAUDE_MODELS array
- deputy-message-board.js: extract magic numbers to named constants (RETRY_BASE_DELAY_MS, MAX_ERRORS_KEPT, etc.)
- subscription-server-v3.js: extract rate limit to MAX_SEND_CODE_PER_HOUR constant
- deputy-message-board.yml: add github.event_name != 'schedule' condition to event mode job

Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/a513d091-ce5c-40d4-bb3c-0adeff70b5b0

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-04-07 17:27:51 +00:00 committed by GitHub
parent 656febf8d6
commit 8d408d1ce2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 18 additions and 10 deletions

View File

@ -25,12 +25,13 @@ jobs:
name: "🎖️ 副将回复留言"
runs-on: ubuntu-latest
if: >
(github.event_name == 'issues' &&
github.event_name != 'schedule' &&
((github.event_name == 'issues' &&
github.event.action == 'opened' &&
contains(join(github.event.issue.labels.*.name, ','), 'deputy-message-board')) ||
(github.event_name == 'issue_comment' &&
contains(join(github.event.issue.labels.*.name, ','), 'deputy-message-board') &&
github.event.comment.user.login != 'github-actions[bot]')
github.event.comment.user.login != 'github-actions[bot]'))
steps:
- uses: actions/checkout@v4

View File

@ -42,6 +42,12 @@ const {
const REPO = process.env.GITHUB_REPOSITORY || 'qinfendebingshuo/guanghulab';
const STATUS_FILE = path.join(__dirname, '..', 'data', 'deputy-status.json');
// ── 可调常量 ──
const RETRY_BASE_DELAY_MS = 2000; // LLM重试基础延迟
const MAX_ERRORS_KEPT = 20; // 保留最近N条错误记录
const MAX_ESCALATIONS_KEPT = 10; // 保留最近N条升级记录
const MAX_LLM_FAILURES_BEFORE_ESCALATION = 3; // 连续N次全模型失败后升级
// ═══════════════════════════════════════════════
// LLM多模型自动降级路由器 (内嵌版)
// ═══════════════════════════════════════════════
@ -143,7 +149,7 @@ async function callLLMWithFallback(systemPrompt, userMessage) {
const errMsg = `${model}(attempt ${attempt}): ${err.message}`;
console.log(`[副将] ⚠️ ${errMsg}`);
errors.push(errMsg);
if (attempt < 2) await new Promise(r => setTimeout(r, 2000 * attempt));
if (attempt < 2) await new Promise(r => setTimeout(r, RETRY_BASE_DELAY_MS * attempt));
}
}
}
@ -384,7 +390,7 @@ async function processMessage(issueNumber, question, author, ctx, systemSummary,
// ═══════════════════════════════════════════════
async function checkAndEscalate(status) {
if (status.consecutive_llm_failures >= 3) {
if (status.consecutive_llm_failures >= MAX_LLM_FAILURES_BEFORE_ESCALATION) {
const title = `🚨 [副将升级] LLM连续${status.consecutive_llm_failures}次全模型调用失败`;
const body = `## <20><> 铸渊副将升级通报\n\n` +
`**时间**: ${new Date().toISOString()}\n` +
@ -402,7 +408,7 @@ async function checkAndEscalate(status) {
try {
await createEscalationIssue(title, body);
status.escalations.push({ time: new Date().toISOString(), type: 'llm_failure', detail: `连续${status.consecutive_llm_failures}次失败` });
if (status.escalations.length > 10) status.escalations = status.escalations.slice(-10);
if (status.escalations.length > MAX_ESCALATIONS_KEPT) status.escalations = status.escalations.slice(-MAX_ESCALATIONS_KEPT);
console.log(`[副将] 🚨 升级Issue已创建`);
} catch (err) { console.error(`[副将] ⚠️ 创建升级Issue失败: ${err.message}`); }
}
@ -466,7 +472,7 @@ async function patrolMode() {
status.errors.push(`${new Date().toISOString()} · patrol: ${err.message}`);
}
if (status.errors.length > 20) status.errors = status.errors.slice(-20);
if (status.errors.length > MAX_ERRORS_KEPT) status.errors = status.errors.slice(-MAX_ERRORS_KEPT);
await checkAndEscalate(status);
status.last_success = repliedCount > 0 || processedCount === 0 ? new Date().toISOString() : status.last_success;
saveStatus(status);
@ -506,7 +512,7 @@ async function eventMode() {
status.errors.push(`${new Date().toISOString()} · event #${ISSUE_NUMBER}: ${err.message}`);
}
if (status.errors.length > 20) status.errors = status.errors.slice(-20);
if (status.errors.length > MAX_ERRORS_KEPT) status.errors = status.errors.slice(-MAX_ERRORS_KEPT);
await checkAndEscalate(status);
saveStatus(status);
}
@ -527,7 +533,7 @@ main().catch(err => {
try {
const status = readStatus();
status.errors.push(`${new Date().toISOString()} · fatal: ${err.message}`);
if (status.errors.length > 20) status.errors = status.errors.slice(-20);
if (status.errors.length > MAX_ERRORS_KEPT) status.errors = status.errors.slice(-MAX_ERRORS_KEPT);
saveStatus(status);
} catch { /* ignore */ }
process.exit(1);

View File

@ -46,7 +46,7 @@ const CLAUDE_RELAY_PORT = parseInt(process.env.ZY_CLAUDE_RELAY_PORT || '18443',
const CLAUDE_RELAY_ENABLED = process.env.ZY_CLAUDE_RELAY_ENABLED === 'true';
function isClaudeModel(model) {
return CLAUDE_MODELS.some(cm => model.includes('claude'));
return CLAUDE_MODELS.some(cm => model.includes(cm)) || model.includes('claude');
}
// ── 路由器状态 ──────────────────────────────

View File

@ -39,6 +39,7 @@ const userManager = require('./user-manager');
// ── 操作快照文件 ──────────────────────────────
const AUTH_SNAPSHOT_FILE = path.join(DATA_DIR, 'bandwidth-auth-snapshots.json');
const MAX_SNAPSHOTS = 500; // 最多保留500条快照记录
const MAX_SEND_CODE_PER_HOUR = 3; // 每个IP每小时最多发送验证码次数
/**
* 保存用户操作快照
@ -1751,7 +1752,7 @@ async function submitCode(e) {
const hourPart = parseInt(k.split(':').pop(), 10);
if (hourPart < currentHourPrefix - 1) delete rl[k];
}
if (rl[hourKey] > 3) {
if (rl[hourKey] > MAX_SEND_CODE_PER_HOUR) {
res.writeHead(429, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ success: false, message: '发送频率过高请1小时后重试' }));
return;