live_check.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. """Explicit live smoke command. Saves outputs only under ignored output/.
  2. No credentials, company fields or full request bodies are printed to logs.
  3. """
  4. import argparse
  5. from dataclasses import replace
  6. import json
  7. from pathlib import Path
  8. from .graph import build_graph
  9. from .intent import deepseek_analyzer
  10. from .qcc import QccClient, load_config
  11. from .recommendation import JsonModel, Recommender
  12. from .retrieval import Retriever
  13. from .report import export
  14. from langgraph.types import Command
  15. def main():
  16. parser = argparse.ArgumentParser()
  17. parser.add_argument('--mode', choices=('personal', 'enterprise'), required=True)
  18. args = parser.parse_args()
  19. root = Path(__file__).resolve().parents[2]
  20. env = root / '.env'
  21. client = QccClient(replace(load_config(env), honor_max_pages=1))
  22. retriever = Retriever(env, root / 'knowledge/prepared/ingestion/manifest.json')
  23. retriever.warmup()
  24. graph = build_graph(client, deepseek_analyzer(env), retriever=retriever, recommender=Recommender(JsonModel(env)))
  25. # Public vendor fixture; never identifies this entity as the user's company.
  26. company_name = '企查查科技股份有限公司'
  27. question = ('我在青浦失业了,想参加职业技能培训,能得到什么帮助?' if args.mode == 'personal' else
  28. company_name + '想在青浦设立分公司,招聘研发人员,有哪些人才或研发支持?')
  29. config = {'configurable': {'thread_id': 'live-' + args.mode}, 'recursion_limit': 100}
  30. result = graph.invoke({'question': question}, config)
  31. clarified = False
  32. if result.get('__interrupt__') and args.mode == 'enterprise' and result['__interrupt__'][0].value['kind'] == 'company_need':
  33. clarified = True
  34. result = graph.invoke(Command(resume={'needs_company': True, 'company_hint': company_name}), config)
  35. if result.get('__interrupt__'):
  36. value = result['__interrupt__'][0].value
  37. if args.mode == 'enterprise' and value['kind'] == 'company_selection':
  38. selected = [r for r in value['result']['candidates'] if r['Name'] == company_name]
  39. if len(selected) != 1:
  40. result = graph.invoke(Command(resume={'action': 'refine', 'keyword': company_name}), config)
  41. value = result['__interrupt__'][0].value
  42. selected = [r for r in value['result']['candidates'] if r['Name'] == company_name]
  43. if len(selected) != 1:
  44. raise ValueError('fixture_not_unique')
  45. result = graph.invoke(Command(resume={'action': 'select', 'key_no': selected[0]['KeyNo']}), config)
  46. else:
  47. raise ValueError('unexpected_intent_interrupt')
  48. answer = result['recommendation']
  49. html, output_json = export(answer, root / 'output/consultations')
  50. print(json.dumps({'mode': args.mode, 'complete': answer['complete'],
  51. 'retrieval_analysis_complete': answer['retrieval_analysis_complete'],
  52. 'recommendations': len(answer['items']), 'sources': len(answer['sources']),
  53. 'analysis_errors': answer['analysis_errors'],
  54. 'failed_databases': answer['retrieval']['failed_databases'],
  55. 'content_failures': answer['retrieval']['content_failures'],
  56. 'qcc_profile_verified': result.get('profile') is not None,
  57. 'intent_clarified': clarified,
  58. 'html': str(html), 'json': str(output_json)}, ensure_ascii=False), flush=True)
  59. if __name__ == '__main__':
  60. try:
  61. main()
  62. except Exception as exc:
  63. safe_code = str(exc) if str(exc) in ('fixture_not_unique', 'unexpected_intent_interrupt') else 'live_smoke_failed'
  64. print(json.dumps({'error_type': type(exc).__name__, 'status': safe_code}), flush=True)
  65. raise SystemExit(1) from None