generate_template.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. """
  2. Phase 3: 公司信息收集模板生成
  3. 基于招标文件 + 历史投标文件,生成投标公司需要补充的信息清单。
  4. 输出为简化版 JSON:仅有"名称"、"内容/文件地址"、"来源参考"三项。
  5. 用法:
  6. python -m phase3_collection.generate_template
  7. 或:
  8. from phase3_collection.generate_template import generate_template
  9. template = generate_template(tender_text, bid_toc)
  10. """
  11. import sys
  12. import os
  13. import json
  14. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  15. from llm.llm_utils import LLM
  16. # ── 预定义信息项(基于招标文件已知要求的兜底清单) ──
  17. FALLBACK_ITEMS = [
  18. {"名称": "公司全称", "内容/文件地址": "", "来源参考": "投标函格式、历史投标文件封面"},
  19. {"名称": "统一社会信用代码", "内容/文件地址": "", "来源参考": "营业执照"},
  20. {"名称": "注册地址", "内容/文件地址": "", "来源参考": "投标函格式"},
  21. {"名称": "联系电话", "内容/文件地址": "", "来源参考": "投标函格式"},
  22. {"名称": "传真", "内容/文件地址": "", "来源参考": "投标函格式"},
  23. {"名称": "邮政编码", "内容/文件地址": "", "来源参考": "投标函格式"},
  24. {"名称": "开户银行", "内容/文件地址": "", "来源参考": "投标函格式"},
  25. {"名称": "银行账号", "内容/文件地址": "", "来源参考": "投标函格式"},
  26. {"名称": "营业执照扫描件", "内容/文件地址": "", "来源参考": "历史投标文件第十一项(第41页)"},
  27. {"名称": "法定代表人姓名", "内容/文件地址": "", "来源参考": "法定代表人授权书格式"},
  28. {"名称": "法定代表人职务", "内容/文件地址": "", "来源参考": "法定代表人授权书格式"},
  29. {"名称": "法定代表人身份证(正面)", "内容/文件地址": "", "来源参考": "历史投标文件第五章第(一)项(第19页)"},
  30. {"名称": "法定代表人身份证(反面)", "内容/文件地址": "", "来源参考": "历史投标文件第五章第(一)项"},
  31. {"名称": "授权委托人姓名", "内容/文件地址": "", "来源参考": "法定代表人授权书格式"},
  32. {"名称": "授权委托人职务", "内容/文件地址": "", "来源参考": "法定代表人授权书格式"},
  33. {"名称": "授权委托人电话", "内容/文件地址": "", "来源参考": "法定代表人授权书格式"},
  34. {"名称": "授权委托人身份证(正面)", "内容/文件地址": "", "来源参考": "历史投标文件第五章第(二)项"},
  35. {"名称": "授权委托人身份证(反面)", "内容/文件地址": "", "来源参考": "历史投标文件第五章第(二)项"},
  36. {"名称": "授权委托书(签字+公章扫描件)", "内容/文件地址": "", "来源参考": "格式参照招标文件第六章"},
  37. {"名称": "ISO 9001 质量管理体系认证证书", "内容/文件地址": "", "来源参考": "历史投标文件第十九项"},
  38. {"名称": "ISO 14001 环境管理体系认证证书", "内容/文件地址": "", "来源参考": "历史投标文件第十九项"},
  39. {"名称": "ISO 45001 职业健康安全管理体系认证证书", "内容/文件地址": "", "来源参考": "历史投标文件第十九项"},
  40. {"名称": "其他资质证书(保安备案/保洁能力/供应商证明)", "内容/文件地址": "", "来源参考": "历史投标文件第十二至十五项(第42-43页)"},
  41. {"名称": "上年度(2024年)审计报告或财务报表", "内容/文件地址": "", "来源参考": "历史投标文件第七项(第21-28页)"},
  42. {"名称": "近期依法缴纳税收的证明(完税凭证)", "内容/文件地址": "", "来源参考": "历史投标文件第八项第一(第29页)"},
  43. {"名称": "近期依法缴纳社保费的证明", "内容/文件地址": "", "来源参考": "历史投标文件第八项第二(第30页)"},
  44. {"名称": "信用中国无失信截图", "内容/文件地址": "", "来源参考": "历史投标文件第十项(第32页)"},
  45. {"名称": "政府采购网无严重违法失信截图", "内容/文件地址": "", "来源参考": "历史投标文件第九项(第31页)"},
  46. {"名称": "从业人员数量", "内容/文件地址": "", "来源参考": "中小企业声明函格式"},
  47. {"名称": "上年度营业收入(万元)", "内容/文件地址": "", "来源参考": "中小企业声明函格式"},
  48. {"名称": "上年度资产总额(万元)", "内容/文件地址": "", "来源参考": "中小企业声明函格式"},
  49. {"名称": "企业规模类型(中型/小型/微型)", "内容/文件地址": "", "来源参考": "中小企业声明函格式"},
  50. {"名称": "拟派项目经理姓名", "内容/文件地址": "", "来源参考": "项目经理情况表格式(招标文件第39页)"},
  51. {"名称": "项目经理出生年月", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  52. {"名称": "项目经理文化程度", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  53. {"名称": "项目经理毕业院校及专业", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  54. {"名称": "项目经理从事物业服务工作年限", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  55. {"名称": "项目经理职业资格", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  56. {"名称": "项目经理技术职称", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  57. {"名称": "项目经理主要工作经历和业绩", "内容/文件地址": "", "来源参考": "项目经理情况表格式"},
  58. {"名称": "项目经理近半年社保缴纳证明", "内容/文件地址": "", "来源参考": "招标文件评分标准—项目经理(3分)"},
  59. {"名称": "项目管理团队主要人员信息", "内容/文件地址": "", "来源参考": "招标文件第40页—管理人员汇总表"},
  60. {"名称": "各专业技术人员执业/职业资格证书", "内容/文件地址": "", "来源参考": "历史投标文件—各主管持证情况"},
  61. {"名称": "近三年办公楼物业业绩合同扫描件", "内容/文件地址": "", "来源参考": "招标文件评分标准—业绩(5分)"},
  62. {"名称": "类似业绩业主评价(满意度证明)", "内容/文件地址": "", "来源参考": "历史投标文件第十八项(第245页)"},
  63. {"名称": "企业获奖证明", "内容/文件地址": "", "来源参考": "历史投标文件第二十项(第261页)"},
  64. {"名称": "雇主责任险和公众责任险购买承诺", "内容/文件地址": "", "来源参考": "招标文件评分标准—责任保险(4分)"},
  65. {"名称": "拟投入本项目的设备一览表", "内容/文件地址": "", "来源参考": "招标文件第42页—设备一览表格式"},
  66. {"名称": "公司简介", "内容/文件地址": "", "来源参考": "历史投标文件公司概况介绍"},
  67. {"名称": "公司现有管理制度目录", "内容/文件地址": "", "来源参考": "招标文件第37页—管理制度一览表格式"},
  68. ]
  69. def generate_template(
  70. tender_text: str = None,
  71. bid_toc: list = None,
  72. output_path: str = None,
  73. use_llm: bool = False,
  74. ) -> dict:
  75. """
  76. 生成投标公司信息收集模板。
  77. Args:
  78. tender_text: 招标文件全文(可选,LLM 模式下使用)
  79. bid_toc: 历史投标文件目录(可选,LLM 模式下使用)
  80. output_path: 保存路径
  81. use_llm: 是否使用 LLM 动态生成(默认 False,使用预定义清单)
  82. Returns:
  83. 模板 JSON 字典
  84. """
  85. if use_llm and tender_text and bid_toc:
  86. items = _llm_generate(tender_text, bid_toc)
  87. else:
  88. items = FALLBACK_ITEMS
  89. template = {
  90. "投标公司信息收集表": {
  91. "说明": "投标公司根据此表提供信息,填充'内容/文件地址'列即可。",
  92. "数据来源": {
  93. "招标文件": "data/松江区机关事务管理局物业管理服务招标文件.pdf",
  94. "历史投标文件参考": "data/松江区机关事务管理局物业管理服务项目投标文件.pdf",
  95. },
  96. "信息项": items,
  97. }
  98. }
  99. if output_path:
  100. os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
  101. with open(output_path, "w", encoding="utf-8") as f:
  102. json.dump(template, f, ensure_ascii=False, indent=2)
  103. print(f"[Phase3] 模板已保存: {output_path} ({len(items)} 项)")
  104. return template
  105. def _llm_generate(tender_text: str, bid_toc: list) -> list:
  106. """使用 LLM 动态生成信息项(备用路径)"""
  107. llm = LLM()
  108. # 构建历史投标目录文本
  109. toc_lines = []
  110. for item in bid_toc[:60]:
  111. indent = " " * (item.get("level", 1) - 1)
  112. toc_lines.append(f"{indent}{item['title']} (p.{item.get('page','')})")
  113. bid_toc_text = "\n".join(toc_lines)
  114. system = (
  115. "你是一个投标文件分析助手。根据招标文件要求和历史投标文件内容,"
  116. "列出投标公司需要提供的所有信息和材料清单。\n\n"
  117. "输出 JSON 数组,每个元素包含:\n"
  118. '- "名称": 信息项名称\n'
  119. '- "内容/文件地址": 留空字符串\n'
  120. '- "来源参考": 招标文件第几章或历史投标文件第几页\n\n'
  121. "覆盖以下类别:\n"
  122. "- 公司基本信息(名称、地址、联系人等)\n"
  123. "- 法定代表人/授权委托人信息(姓名、身份证照片等)\n"
  124. "- 资质证书(营业执照、体系认证、专业资质等)\n"
  125. "- 财务状况(审计报告、纳税和社保凭证等)\n"
  126. "- 企业声明函(中小企业声明、无失信声明等)\n"
  127. "- 团队人员(项目经理、主要管理人员信息)\n"
  128. "- 历史业绩(合同扫描件、业主评价等)\n"
  129. "- 报价信息(报价明细)\n"
  130. "- 设备资源(设备一览表)\n\n"
  131. "只输出 JSON 数组,不要额外文字。"
  132. )
  133. user = (
  134. "请列出投标公司需要提供的全部信息材料。\n\n"
  135. f"===== 招标文件关键章节 =====\n"
  136. f"{tender_text[:15000]}\n\n"
  137. f"===== 历史投标目录 =====\n"
  138. f"{bid_toc_text[:10000]}"
  139. )
  140. result = llm._call(system, user, max_tokens=8192,
  141. temperature=0.2,
  142. response_format={"type": "json_object"})
  143. parsed = json.loads(result)
  144. # 兼容数组或 {items: [...]} 两种返回格式
  145. if isinstance(parsed, list):
  146. return parsed
  147. for key in parsed:
  148. if isinstance(parsed[key], list):
  149. return parsed[key]
  150. return FALLBACK_ITEMS
  151. # ── CLI ──
  152. if __name__ == "__main__":
  153. import sys
  154. sys.stdout.reconfigure(encoding="utf-8")
  155. generate_template(output_path="output/phase3_company_template.json")
  156. with open("output/phase3_company_template.json", "r", encoding="utf-8") as f:
  157. data = json.load(f)
  158. items = data["投标公司信息收集表"]["信息项"]
  159. print(f"\n共 {len(items)} 项信息待补充:")
  160. for item in items:
  161. print(f" □ {item['名称']}")