verify_qualification_e2e.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #!/usr/bin/env python3
  2. """
  3. 供应商资质审查 Agent —— 端到端本地验证脚本(免命令行版)
  4. 直接运行即可,无需输入任何参数。
  5. """
  6. import sys
  7. import time
  8. import httpx
  9. # ============ 在这里直接配置所有参数 ============
  10. FILE_PATH = "/Users/ligang/Desktop/LiveTenderAgent/samples/qualification_sample.pdf" # 改成你的 PDF 绝对路径或相对路径
  11. BASE_URL = "http://localhost:8000" # 后端地址
  12. USERNAME = "bidder01" # 登录用户名
  13. PASSWORD = "Bidder@123456" # 登录密码
  14. POLL_INTERVAL = 3.0 # 轮询间隔(秒)
  15. POLL_TIMEOUT = 180.0 # 最长等待(秒)
  16. # =============================================
  17. def login(client: httpx.Client, username: str, password: str) -> str:
  18. """登录,返回 access_token。"""
  19. resp = client.post("/api/v1/auth/login", json={"username": username, "password": password})
  20. if resp.status_code != 200:
  21. sys.exit(f"❌ 登录失败({resp.status_code}):{resp.text}")
  22. print(f'response: {resp.json()}')
  23. token = resp.json().get("access_token")
  24. if not token:
  25. sys.exit(f"❌ 登录响应里没有 access_token:{resp.text}")
  26. print(f"✅ 登录成功(用户:{username})")
  27. return token
  28. def upload(client: httpx.Client, headers: dict, file_path: str) -> str:
  29. """上传 PDF,返回 review_id。"""
  30. try:
  31. f = open(file_path, "rb")
  32. except OSError as e:
  33. sys.exit(f"❌ 打不开文件 {file_path}:{e}")
  34. with f:
  35. resp = client.post("/api/v1/qualification/upload", headers=headers,
  36. files={"file": (file_path.split("/")[-1], f, "application/pdf")})
  37. if resp.status_code != 202:
  38. sys.exit(f"❌ 上传失败({resp.status_code}):{resp.text}")
  39. data = resp.json()
  40. review_id = data["review_id"]
  41. print(f"✅ 上传成功 → review_id = {review_id}")
  42. print(f" 状态:{data['status']}(审查在后台进行,开始轮询…)\n")
  43. return review_id
  44. def poll(client: httpx.Client, headers: dict, review_id: str,
  45. interval: float, timeout: float) -> dict:
  46. """轮询查询,直到 done / failed / 超时。"""
  47. deadline = time.time() + timeout
  48. attempt = 0
  49. while time.time() < deadline:
  50. attempt += 1
  51. resp = client.get(f"/api/v1/qualification/reviews/{review_id}", headers=headers)
  52. if resp.status_code != 200:
  53. sys.exit(f"❌ 查询失败({resp.status_code}):{resp.text}")
  54. data = resp.json()
  55. status = data["status"]
  56. print(f" 第 {attempt} 次轮询 → status: {status}")
  57. if status in ("done", "failed"):
  58. return data
  59. time.sleep(interval)
  60. sys.exit("⏰ 超时:仍未完成。请检查后端日志(搜 qualification.background_task_failed / 是否配了可用的 DEEPSEEK_API_KEY)。")
  61. def print_report(data: dict) -> None:
  62. """漂亮地打印审查报告。"""
  63. if data["status"] == "failed":
  64. print("\n❌ 审查失败:", data.get("error_msg", "(无错误信息)"))
  65. return
  66. print("\n" + "=" * 48)
  67. print(" 资质审查报告")
  68. print("=" * 48)
  69. print(f"综合得分:{data.get('weighted_score')} / 100\n")
  70. print("【六维度评分】")
  71. for d in data.get("dimension_scores", []):
  72. weight = d.get("weight", 0)
  73. print(f" · {d.get('dimension')}:{d.get('score')} 分(权重 {int(weight * 100)}%)")
  74. for issue in d.get("issues", []):
  75. print(f" - 问题:{issue}")
  76. for sug in d.get("suggestions", []):
  77. print(f" - 建议:{sug}")
  78. print("\n【问题诊断】")
  79. issues = data.get("issues", [])
  80. if not issues:
  81. print(" (无)")
  82. for it in issues:
  83. loc = it.get("location", "")
  84. print(f" [{it.get('priority', '').upper()}] {it.get('description', '')}"
  85. + (f"({loc})" if loc else ""))
  86. if it.get("suggestion"):
  87. print(f" → {it['suggestion']}")
  88. summary = data.get("summary") or {}
  89. print("\n【整体评价】")
  90. print(" 亮点:", ";".join(summary.get("highlights", [])) or "(无)")
  91. print(" 核心改进:", ";".join(summary.get("core_improvements", [])) or "(无)")
  92. print(" 综合评语:", summary.get("overall_comment", "(无)"))
  93. print(" 岗位匹配:", summary.get("fit_assessment", "(无)"))
  94. print("=" * 48)
  95. def main() -> None:
  96. # 直接使用上面定义的全局常量,不再需要 argparse
  97. with httpx.Client(base_url=BASE_URL, timeout=30.0) as client:
  98. token = login(client, USERNAME, PASSWORD)
  99. print(f'token = {token}')
  100. headers = {"Authorization": f"Bearer {token}"}
  101. review_id = upload(client, headers, FILE_PATH)
  102. data = poll(client, headers, review_id, POLL_INTERVAL, POLL_TIMEOUT)
  103. print_report(data)
  104. if __name__ == "__main__":
  105. main()