马一丁

Update Logic When JSON Cannot Be Parsed

@@ -31,6 +31,7 @@ from .nodes import ( @@ -31,6 +31,7 @@ from .nodes import (
31 ChapterGenerationNode, 31 ChapterGenerationNode,
32 ChapterJsonParseError, 32 ChapterJsonParseError,
33 ChapterContentError, 33 ChapterContentError,
  34 + ChapterValidationError,
34 DocumentLayoutNode, 35 DocumentLayoutNode,
35 WordBudgetNode, 36 WordBudgetNode,
36 ) 37 )
@@ -601,11 +602,16 @@ class ReportAgent: @@ -601,11 +602,16 @@ class ReportAgent:
601 stream_callback=chunk_callback 602 stream_callback=chunk_callback
602 ) 603 )
603 break 604 break
604 - except (ChapterJsonParseError, ChapterContentError) as structured_error:  
605 - error_kind = (  
606 - "content_sparse" if isinstance(structured_error, ChapterContentError) else "json_parse"  
607 - )  
608 - readable_label = "内容密度异常" if error_kind == "content_sparse" else "JSON解析失败" 605 + except (ChapterJsonParseError, ChapterContentError, ChapterValidationError) as structured_error:
  606 + if isinstance(structured_error, ChapterContentError):
  607 + error_kind = "content_sparse"
  608 + readable_label = "内容密度异常"
  609 + elif isinstance(structured_error, ChapterValidationError):
  610 + error_kind = "validation"
  611 + readable_label = "结构校验失败"
  612 + else:
  613 + error_kind = "json_parse"
  614 + readable_label = "JSON解析失败"
609 if isinstance(structured_error, ChapterContentError): 615 if isinstance(structured_error, ChapterContentError):
610 candidate = getattr(structured_error, "chapter_payload", None) 616 candidate = getattr(structured_error, "chapter_payload", None)
611 candidate_score = getattr(structured_error, "body_characters", 0) or 0 617 candidate_score = getattr(structured_error, "body_characters", 0) or 0
@@ -636,6 +642,10 @@ class ReportAgent: @@ -636,6 +642,10 @@ class ReportAgent:
636 'error': str(structured_error), 642 'error': str(structured_error),
637 'reason': error_kind, 643 'reason': error_kind,
638 } 644 }
  645 + if isinstance(structured_error, ChapterValidationError):
  646 + validation_errors = getattr(structured_error, "errors", None)
  647 + if validation_errors:
  648 + status_payload['errors'] = validation_errors
639 if will_fallback: 649 if will_fallback:
640 status_payload['warning'] = 'content_sparse_fallback_pending' 650 status_payload['warning'] = 'content_sparse_fallback_pending'
641 emit('chapter_status', status_payload) 651 emit('chapter_status', status_payload)
@@ -6,7 +6,12 @@ Report Engine节点处理模块。 @@ -6,7 +6,12 @@ Report Engine节点处理模块。
6 6
7 from .base_node import BaseNode, StateMutationNode 7 from .base_node import BaseNode, StateMutationNode
8 from .template_selection_node import TemplateSelectionNode 8 from .template_selection_node import TemplateSelectionNode
9 -from .chapter_generation_node import ChapterGenerationNode, ChapterJsonParseError, ChapterContentError 9 +from .chapter_generation_node import (
  10 + ChapterGenerationNode,
  11 + ChapterJsonParseError,
  12 + ChapterContentError,
  13 + ChapterValidationError,
  14 +)
10 from .document_layout_node import DocumentLayoutNode 15 from .document_layout_node import DocumentLayoutNode
11 from .word_budget_node import WordBudgetNode 16 from .word_budget_node import WordBudgetNode
12 17
@@ -17,6 +22,7 @@ __all__ = [ @@ -17,6 +22,7 @@ __all__ = [
17 "ChapterGenerationNode", 22 "ChapterGenerationNode",
18 "ChapterJsonParseError", 23 "ChapterJsonParseError",
19 "ChapterContentError", 24 "ChapterContentError",
  25 + "ChapterValidationError",
20 "DocumentLayoutNode", 26 "DocumentLayoutNode",
21 "WordBudgetNode", 27 "WordBudgetNode",
22 ] 28 ]
@@ -77,6 +77,18 @@ class ChapterContentError(ValueError): @@ -77,6 +77,18 @@ class ChapterContentError(ValueError):
77 self.non_heading_blocks: int = int(non_heading_blocks or 0) 77 self.non_heading_blocks: int = int(non_heading_blocks or 0)
78 78
79 79
  80 +class ChapterValidationError(ValueError):
  81 + """
  82 + 章节结构在本地和LLM修复后仍无法通过校验时抛出。
  83 +
  84 + 该异常用于在Agent层触发针对单章的重试,而无需重启整本报告。
  85 + """
  86 +
  87 + def __init__(self, message: str, errors: Optional[List[str]] | None = None):
  88 + super().__init__(message)
  89 + self.errors: List[str] = list(errors or [])
  90 +
  91 +
80 class ChapterGenerationNode(BaseNode): 92 class ChapterGenerationNode(BaseNode):
81 """ 93 """
82 负责按章节调用LLM并校验JSON结构。 94 负责按章节调用LLM并校验JSON结构。
@@ -268,8 +280,9 @@ class ChapterGenerationNode(BaseNode): @@ -268,8 +280,9 @@ class ChapterGenerationNode(BaseNode):
268 ) 280 )
269 281
270 if not valid: 282 if not valid:
271 - raise ValueError(  
272 - f"{section.title} 章节JSON校验失败: {'; '.join(errors[:5])}" 283 + raise ChapterValidationError(
  284 + f"{section.title} 章节JSON校验失败: {'; '.join(errors[:5])}",
  285 + errors=errors,
273 ) 286 )
274 if content_error: 287 if content_error:
275 raise content_error 288 raise content_error
@@ -1555,4 +1568,9 @@ class ChapterGenerationNode(BaseNode): @@ -1555,4 +1568,9 @@ class ChapterGenerationNode(BaseNode):
1555 raise last_exc 1568 raise last_exc
1556 1569
1557 1570
1558 -__all__ = ["ChapterGenerationNode", "ChapterJsonParseError"] 1571 +__all__ = [
  1572 + "ChapterGenerationNode",
  1573 + "ChapterJsonParseError",
  1574 + "ChapterContentError",
  1575 + "ChapterValidationError",
  1576 +]