validation_nodes.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. from __future__ import annotations
  2. """方案校验节点:调用PlanValidationService对行程做确定性规则校验,验证航班/酒店ID真实性与预算合理性。"""
  3. from typing import Any, Literal
  4. from app.graph.state import TravelState
  5. from app.services.plan_validation_service import (
  6. PlanValidationService,
  7. )
  8. def route_after_planning(
  9. state: TravelState,
  10. ) -> Literal[
  11. "validate_plan",
  12. "end",
  13. ]:
  14. """Planner成功后进入确定性校验。"""
  15. if state.get("errors"):
  16. return "end"
  17. if not state.get("itinerary_plan"):
  18. return "end"
  19. return "validate_plan"
  20. def make_plan_validation_node(
  21. service: PlanValidationService
  22. | None = None,
  23. ):
  24. """创建确定性行程校验节点。"""
  25. validation_service = (
  26. service
  27. or PlanValidationService()
  28. )
  29. async def plan_validation_node(
  30. state: TravelState,
  31. ) -> dict[str, Any]:
  32. """方案校验节点:用确定性规则校验行程方案,将校验结果和预算估算写入state。"""
  33. try:
  34. result = validation_service.validate(
  35. request=state["travel_request"],
  36. plan=state["itinerary_plan"],
  37. selection=state[
  38. "candidate_selection_result"
  39. ],
  40. map_result=state[
  41. "map_research_result"
  42. ],
  43. route_result=state[
  44. "route_evaluation_result"
  45. ],
  46. )
  47. warnings = [
  48. issue.message
  49. for issue in result.issues
  50. if issue.severity == "warning"
  51. ]
  52. if result.is_valid:
  53. message = (
  54. "确定性校验通过:"
  55. f"发现{result.warning_count}项"
  56. "非阻塞警告;"
  57. "等待Trip Reviewer审查。"
  58. )
  59. else:
  60. message = (
  61. "确定性校验未通过:"
  62. f"发现{result.error_count}项错误、"
  63. f"{result.warning_count}项警告;"
  64. "需要Planner修改方案。"
  65. )
  66. # 注意:
  67. # 业务校验失败不能写入全局errors,
  68. # 否则LangGraph会立即终止,
  69. # 后续无法进入重新规划循环。
  70. return {
  71. "plan_validation_result": result,
  72. "validation_passed": (
  73. result.is_valid
  74. ),
  75. "validation_warnings": warnings,
  76. "revision_feedback": (
  77. result.revision_feedback
  78. ),
  79. "final_answer": message,
  80. }
  81. except Exception as exc:
  82. message = (
  83. "确定性校验节点执行失败:"
  84. f"{type(exc).__name__}: {exc}"
  85. )
  86. existing_errors = list(
  87. state.get("errors", [])
  88. )
  89. existing_errors.append(message)
  90. return {
  91. "errors": existing_errors,
  92. "validation_passed": False,
  93. "final_answer": message,
  94. }
  95. return plan_validation_node