main.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. """Info Extractor - 使用示例.
  2. 演示如何使用 InfoExtractor 从文章中提取结构化信息。
  3. 用法:
  4. # 设置 API 密钥(支持 OpenAI 格式的任何模型)
  5. set OPENAI_API_KEY=sk-xxx
  6. # 或使用第三方服务
  7. set OPENAI_API_KEY=your-key
  8. set OPENAI_BASE_URL=https://api.deepseek.com/v1
  9. # 运行
  10. python main.py # 默认: single_phase 模式
  11. python main.py two_phase # 两阶段模式
  12. python main.py --debug # 开启 debug 日志 (打印每次 LLM 交互的 prompt)
  13. python main.py two_phase --debug # 组合使用
  14. set DEBUG=true && python main.py # 通过环境变量开启 debug
  15. """
  16. from __future__ import annotations
  17. import json
  18. import logging
  19. import os
  20. import sys
  21. from src.extractor import InfoExtractor
  22. logging.basicConfig(
  23. level=logging.INFO,
  24. format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
  25. )
  26. logger = logging.getLogger(__name__)
  27. # ── 示例数据 ──────────────────────────────────────────────────────────────────
  28. SAMPLE_ARTICLE = (
  29. "2026年6月1日晚8点,好友们决定为王若飞(男)组织一场生日派对。"
  30. "这次小范围聚会共有5人参加,刘阳(女)作为总策划,"
  31. "除寿星和总策划外,还有李四(女)、王五(男)和赵六(男)参加。"
  32. "活动总预算为3000元,且已完成备案,"
  33. "现场安排了掷骰子、扔飞镖和打桌球等破冰游戏,确保大家玩得尽兴。"
  34. )
  35. SAMPLE_DEFINITION = {
  36. "metadata": {
  37. "node_label": "PDJH",
  38. "node_name": "派对计划",
  39. "node_type": "concept",
  40. },
  41. "subnodes": {
  42. "JBXX": {
  43. "metadata": {
  44. "node_label": "JBXX",
  45. "node_name": "基本信息",
  46. "node_type": "concept",
  47. },
  48. "subnodes": {
  49. "PDMC": {
  50. "metadata": {
  51. "node_label": "PDMC",
  52. "node_name": "派对名称",
  53. "node_type": "property",
  54. "value_type": "string",
  55. "is_list": False,
  56. }
  57. },
  58. "PDKSSJ": {
  59. "metadata": {
  60. "node_label": "PDKSSJ",
  61. "node_name": "派对开始时间",
  62. "node_type": "property",
  63. "value_type": "date_time",
  64. "is_list": False,
  65. }
  66. },
  67. "PDZRS": {
  68. "metadata": {
  69. "node_label": "PDZRS",
  70. "node_name": "派对总人数",
  71. "node_type": "property",
  72. "value_type": "integer",
  73. "is_list": False,
  74. }
  75. },
  76. "PDZYS": {
  77. "metadata": {
  78. "node_label": "PDZYS",
  79. "node_name": "派对总预算",
  80. "node_type": "property",
  81. "value_type": "float",
  82. "is_list": False,
  83. }
  84. },
  85. "SFBA": {
  86. "metadata": {
  87. "node_label": "SFBA",
  88. "node_name": "是否备案",
  89. "node_type": "property",
  90. "value_type": "boolean",
  91. "is_list": False,
  92. }
  93. },
  94. },
  95. },
  96. "RYXX": {
  97. "metadata": {
  98. "node_label": "RYXX",
  99. "node_name": "人员信息",
  100. "node_type": "concept",
  101. },
  102. "subnodes": {
  103. "ZCH": {
  104. "metadata": {
  105. "node_label": "ZCH",
  106. "node_name": "总策划",
  107. "node_type": "property",
  108. "value_type": "class",
  109. "is_list": False,
  110. },
  111. "subnodes": {
  112. "XM": {
  113. "metadata": {
  114. "node_label": "XM",
  115. "node_name": "姓名",
  116. "node_type": "property",
  117. "value_type": "string",
  118. "is_list": False,
  119. }
  120. },
  121. "XB": {
  122. "metadata": {
  123. "node_label": "XB",
  124. "node_name": "性别",
  125. "node_type": "property",
  126. "value_type": "string",
  127. "is_list": False,
  128. }
  129. },
  130. },
  131. },
  132. "RYQD": {
  133. "metadata": {
  134. "node_label": "RYQD",
  135. "node_name": "人员清单",
  136. "node_type": "property",
  137. "value_type": "class",
  138. "is_list": True,
  139. },
  140. "subnodes": {
  141. "XM": {
  142. "metadata": {
  143. "node_label": "XM",
  144. "node_name": "姓名",
  145. "node_type": "property",
  146. "value_type": "string",
  147. "is_list": False,
  148. }
  149. },
  150. "XB": {
  151. "metadata": {
  152. "node_label": "XB",
  153. "node_name": "性别",
  154. "node_type": "property",
  155. "value_type": "string",
  156. "is_list": False,
  157. }
  158. },
  159. },
  160. },
  161. },
  162. },
  163. "HDXX": {
  164. "metadata": {
  165. "node_label": "HDXX",
  166. "node_name": "活动信息",
  167. "node_type": "concept",
  168. },
  169. "subnodes": {
  170. "PBYXQD": {
  171. "metadata": {
  172. "node_label": "PBYXQD",
  173. "node_name": "破冰游戏清单",
  174. "node_type": "property",
  175. "value_type": "string",
  176. "is_list": True,
  177. }
  178. }
  179. },
  180. },
  181. },
  182. }
  183. def main() -> None:
  184. # 初始化提取器
  185. # 方式 1: 使用环境变量(OPENAI_API_KEY, OPENAI_BASE_URL)
  186. # 方式 2: 直接传入参数
  187. extractor = InfoExtractor(
  188. model_name=os.getenv("MODEL_NAME", "gpt-4o"),
  189. api_key=os.getenv("OPENAI_API_KEY"),
  190. base_url=os.getenv("OPENAI_BASE_URL"),
  191. temperature=0,
  192. max_retries=3,
  193. )
  194. # 解析命令行参数
  195. args = sys.argv[1:]
  196. mode = "single_phase"
  197. debug = os.getenv("DEBUG", "").lower() in ("1", "true", "yes")
  198. for arg in args:
  199. if arg == "--debug":
  200. debug = True
  201. elif arg in ("single_phase", "two_phase"):
  202. mode = arg
  203. logger.info(f"使用 {mode} 模式提取信息 (debug={debug})")
  204. # 执行提取(含标注)
  205. result = extractor.extract(
  206. article=SAMPLE_ARTICLE,
  207. definition=SAMPLE_DEFINITION,
  208. mode=mode,
  209. annotate=True,
  210. debug=debug,
  211. )
  212. # 输出提取结果
  213. print("\n" + "=" * 60)
  214. print("提取结果:")
  215. print("=" * 60)
  216. print(json.dumps(result["instance"], ensure_ascii=False, indent=2))
  217. # 输出标注后的文章
  218. print("\n" + "=" * 60)
  219. print("标注后的文章:")
  220. print("=" * 60)
  221. print(result["annotated_text"])
  222. if __name__ == "__main__":
  223. main()