review_hitl.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import asyncio
  2. from langgraph.graph import StateGraph, START, END
  3. from langgraph.checkpoint.memory import MemorySaver
  4. from langgraph.types import Command # 恢复运行
  5. from backend.agents.bid_review.state import BidReviewState
  6. from backend.agents.bid_review.nodes import expert_review_node
  7. # 公共基础 state(expert_review_node 用到的所有字段)
  8. BASE_STATE = {
  9. "submission_id": "sub-test-001",
  10. "bidder_id": "stu-test-001",
  11. "pre_review_summary": {
  12. "by_question": [
  13. {"question_id": "q-001", "score": 7, "comment": "基本正确"},
  14. {"question_id": "q-002", "score": 5, "comment": "答案不完整"},
  15. ],
  16. "total_score": 12,
  17. },
  18. "weak_points": ["递归算法", "时间复杂度分析"],
  19. "weak_points_summary": "投标人对递归理解薄弱,建议补充练习",
  20. }
  21. def _build_review_graph():
  22. """构造只含 expert_review_node 的最小测试图"""
  23. builder = StateGraph(BidReviewState)
  24. builder.add_node("expert_review", expert_review_node)
  25. builder.add_edge(START, "expert_review")
  26. builder.add_edge("expert_review", END)
  27. return builder.compile(checkpointer=MemorySaver())
  28. # ── 用例①:评审专家直接批准 AI 结果 ─────────────────────────────
  29. async def atest_review_approve():
  30. print("\n" + "=" * 55)
  31. print("【expert_review_node】用例① approve")
  32. print("=" * 55)
  33. graph = _build_review_graph()
  34. config = {"configurable": {"thread_id": "test-approve-001"}}
  35. # 第一次 invoke:图在 interrupt() 处暂停,返回 display_data
  36. snapshot = await graph.ainvoke(BASE_STATE, config=config)
  37. print(f"图已暂停,展示给评审专家的数据: {snapshot}")
  38. # 评审专家选择"批准",不修改任何评分项
  39. decision = {
  40. "action": "approve",
  41. "modifications": [],
  42. "expert_id": "expert-001",
  43. }
  44. final = await graph.ainvoke(Command(resume=decision), config=config)
  45. print("*"*80)
  46. print(f'final: {final}')
  47. print("*" * 80)
  48. print(f"expert_decision: {final['expert_decision']}")
  49. print("✅ approve 用例通过")
  50. # ── 用例②:专家修改部分评分项分数 ─────────────────────────────
  51. async def atest_review_modify():
  52. print("\n" + "=" * 55)
  53. print("【expert_review_node】用例② modify(修改 q-002 分数)")
  54. print("=" * 55)
  55. graph = _build_review_graph()
  56. config = {"configurable": {"thread_id": "test-modify-001"}}
  57. await graph.ainvoke(BASE_STATE, config=config)
  58. # 专家修改 q-002 分数,同时追加评语;q-001 不动
  59. decision = {
  60. "action": "modify",
  61. "modifications": [
  62. {
  63. "question_id": "q-002",
  64. "new_score": 8,
  65. "comment": "重新审阅,核心逻辑正确,调高分数",
  66. }
  67. ],
  68. "expert_id": "expert-001",
  69. }
  70. final = await graph.ainvoke(Command(resume=decision), config=config)
  71. print(f"final: {final}")
  72. print("*" * 80)
  73. td = final["expert_decision"]
  74. print(f"expert_decision: {td}")
  75. print("✅ modify 用例通过")
  76. if __name__ == '__main__':
  77. import asyncio
  78. # asyncio.run(atest_review_approve())
  79. asyncio.run(atest_review_modify())