Просмотр исходного кода

fix(reviser): tiered issue format (Critical/High/Medium) and length escalation

- Issues passed to reviser in auto mode are now grouped into three tiers:
  Critical (必须解决), High (应当改善), Medium (参考建议)
  replacing the flat [severity] list.

- Length out of softRange now marked as critical severity.

- Fixed epsilon comparison: >= instead of > for net improvement check.

All 516 tests pass.
Ma 7 месяцев назад
Родитель
Сommit
ba7523de30

+ 44 - 4
packages/core/src/agents/reviser.ts

@@ -35,6 +35,45 @@ export interface ReviseOutput {
   };
 }
 
+function buildTieredIssueList(
+  issues: ReadonlyArray<AuditIssue>,
+  isEnglish: boolean,
+): string {
+  const critical: string[] = [];
+  const high: string[] = [];
+  const medium: string[] = [];
+
+  for (const issue of issues) {
+    const line = `- ${issue.category}: ${issue.description}`;
+    if (issue.severity === "critical") {
+      critical.push(line);
+    } else if (issue.severity === "warning") {
+      high.push(line);
+    } else {
+      medium.push(line);
+    }
+  }
+
+  const parts: string[] = [];
+  if (critical.length > 0) {
+    parts.push(isEnglish
+      ? `## Critical — Must Fix\n${critical.join("\n")}`
+      : `## Critical(必须解决)\n${critical.join("\n")}`);
+  }
+  if (high.length > 0) {
+    parts.push(isEnglish
+      ? `## High — Should Improve\n${high.join("\n")}`
+      : `## High(应当改善)\n${high.join("\n")}`);
+  }
+  if (medium.length > 0) {
+    parts.push(isEnglish
+      ? `## Medium — Reference\n${medium.join("\n")}`
+      : `## Medium(参考建议)\n${medium.join("\n")}`);
+  }
+
+  return parts.join("\n\n");
+}
+
 const MODE_DESCRIPTIONS: Record<ReviseMode, string> = {
   auto: "", // auto mode uses buildAutoSystemPrompt instead
   polish: "润色:只改表达、节奏、段落呼吸,不改事实与剧情结论。禁止:增删段落、改变人名/地名/物品名、增加新情节或新对话、改变因果关系。只允许:替换用词、调整句序、修改标点节奏",
@@ -103,11 +142,12 @@ export class ReviserAgent extends BaseAgent {
 
     const isEnglish = (bookLanguage ?? gp.language) === "en";
     const resolvedLanguage = isEnglish ? "en" : "zh";
-    const suggestionLabel = isEnglish ? "Suggestion" : "建议";
 
-    const issueList = issues
-      .map((i) => `- [${i.severity}] ${i.category}: ${i.description}\n  ${suggestionLabel}: ${i.suggestion}`)
-      .join("\n");
+    const issueList = mode === "auto"
+      ? buildTieredIssueList(issues, isEnglish)
+      : issues
+          .map((i) => `- [${i.severity}] ${i.category}: ${i.description}\n  ${isEnglish ? "Suggestion" : "建议"}: ${i.suggestion}`)
+          .join("\n");
 
     const numericalRule = gp.numericalSystem
       ? (isEnglish

+ 7 - 7
packages/core/src/pipeline/chapter-review-cycle.ts

@@ -155,12 +155,12 @@ export async function runChapterReviewCycle(params: {
     if (!lengthInRange) {
       const direction = wordCount < params.lengthSpec.softMin ? "short" : "long";
       allIssues.push({
-        severity: "warning",
+        severity: "critical",
         category: "length",
-        description: `Chapter word count ${wordCount} is outside target range ${params.lengthSpec.softMin}-${params.lengthSpec.softMax} (too ${direction}).`,
+        description: `Chapter word count ${wordCount} is outside target range ${params.lengthSpec.softMin}-${params.lengthSpec.softMax} (too ${direction}). Must be brought within range.`,
         suggestion: direction === "long"
-          ? "Trim redundant explanations and weak-information sentences."
-          : "Expand key scenes with more sensory detail or dialogue.",
+          ? "Compress the chapter: trim redundant explanations, repeated actions, and weak-information sentences."
+          : "Expand key scenes with more sensory detail or dialogue to reach the target range.",
       });
     }
 
@@ -252,7 +252,7 @@ export async function runChapterReviewCycle(params: {
       }
 
       // Check net improvement
-      if (nextAssessment.score > currentAudit.score + NET_IMPROVEMENT_EPSILON) {
+      if (nextAssessment.score >= currentAudit.score + NET_IMPROVEMENT_EPSILON) {
         finalContent = reviseOutput.revisedContent;
         finalWordCount = revisedWordCount;
         postReviseCount = revisedWordCount;
@@ -272,12 +272,12 @@ export async function runChapterReviewCycle(params: {
   // Pick the best scoring snapshot for final output
   // ---------------------------------------------------------------------------
   const bestSnapshot = snapshots.reduce((best, snap) =>
-    snap.score > best.score + NET_IMPROVEMENT_EPSILON ? snap : best,
+    snap.score >= best.score + NET_IMPROVEMENT_EPSILON ? snap : best,
   );
 
   // If best snapshot differs from current content (repair made things worse
   // but an earlier version was better), roll back to the best version.
-  if (bestSnapshot.content !== finalContent && bestSnapshot.score > currentAudit.score + NET_IMPROVEMENT_EPSILON) {
+  if (bestSnapshot.content !== finalContent && bestSnapshot.score >= currentAudit.score + NET_IMPROVEMENT_EPSILON) {
     params.logWarn({
       zh: `回退到最高分版本(${bestSnapshot.score} 分 vs 当前 ${currentAudit.score} 分)`,
       en: `rolling back to highest-scoring version (${bestSnapshot.score} vs current ${currentAudit.score})`,