run_comparison.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #!/usr/bin/env python3
  2. """
  3. 海事问答智能体对比实验
  4. RAG 模式 vs Wiki 模式
  5. """
  6. import json
  7. import time
  8. import sys
  9. sys.path.insert(0, '/home/67/knowledge/maritime')
  10. sys.path.insert(0, '/home/67/lambdagentpaas')
  11. # lambdagent migrated to src-layout — add the src dir so 'from lambdagent.X'
  12. # still resolves when running outside an editable-install venv.
  13. sys.path.insert(0, '/home/67/lambdagentpaas/lambdagent/src')
  14. from search_engine import search
  15. from lambdagent.providers import create_provider
  16. # 初始化 LLM
  17. provider = create_provider('ollama', model='qwen2.5:32b', timeout=600)
  18. SEP = '=' * 70
  19. LINE = '-' * 70
  20. # RAG 模式
  21. def rag_answer(question, top_k=5):
  22. t0 = time.time()
  23. results = search(question, top_k=top_k)
  24. context_parts = []
  25. for i, r in enumerate(results):
  26. context_parts.append(f'[doc{i+1}: {r["source"]}]\n{r["text"]}')
  27. context = '\n\n'.join(context_parts)
  28. prompt = f"""你是一个海事领域问答助手。请严格基于以下参考文档回答问题。
  29. 如果文档中没有相关信息,请说明"文档中未找到相关信息"。
  30. 回答要具体,引用文档中的原文,标注来源 [来源: 文件名]。
  31. ## 参考文档
  32. {context}
  33. ## 问题
  34. {question}
  35. ## 回答"""
  36. answer = provider.chat([{'role': 'user', 'content': prompt}])
  37. elapsed = time.time() - t0
  38. return {
  39. 'answer': answer,
  40. 'sources': [r['source'] for r in results],
  41. 'elapsed': round(elapsed, 1),
  42. 'chunks_used': len(results),
  43. }
  44. # Wiki 模式
  45. WIKI_DIR = '/home/67/lambdagentpaas/agentexample/qaagent67wiki/wiki'
  46. def wiki_answer(question, top_k=5):
  47. t0 = time.time()
  48. from pathlib import Path
  49. wiki_path = Path(WIKI_DIR)
  50. # Step 1: 检查 wiki 中是否有相关页面
  51. wiki_pages = []
  52. for subdir in ['sources', 'entities', 'topics', 'analyses']:
  53. d = wiki_path / subdir
  54. if d.exists():
  55. for f in d.glob('*.md'):
  56. content = f.read_text(encoding='utf-8')
  57. q_chars = set(question)
  58. match_score = sum(1 for c in q_chars if c in content)
  59. if match_score > len(question) * 0.3:
  60. wiki_pages.append({
  61. 'path': str(f),
  62. 'name': f.stem,
  63. 'content': content[:1500],
  64. 'score': match_score,
  65. })
  66. wiki_pages.sort(key=lambda x: x['score'], reverse=True)
  67. wiki_pages = wiki_pages[:3]
  68. if wiki_pages:
  69. wiki_context = '\n\n'.join([
  70. f'[Wiki: {p["name"]}]\n{p["content"]}'
  71. for p in wiki_pages
  72. ])
  73. source_type = 'wiki'
  74. else:
  75. results = search(question, top_k=top_k)
  76. compile_context = '\n\n'.join([
  77. f'[doc: {r["source"]}]\n{r["text"]}'
  78. for r in results
  79. ])
  80. compile_prompt = f"""请阅读以下文档片段,提炼出与问题相关的核心知识点。
  81. 用结构化的方式组织,标注来源。这将作为 wiki 知识页面保存。
  82. 文档:
  83. {compile_context}
  84. 问题: {question}
  85. 请输出结构化的知识摘要:"""
  86. compiled = provider.chat([{'role': 'user', 'content': compile_prompt}])
  87. wiki_context = compiled
  88. source_type = 'compiled'
  89. # 保存到 wiki analyses 目录
  90. safe_name = question[:30].replace('/', '_').replace(' ', '_')
  91. analysis_path = wiki_path / 'analyses' / f'{safe_name}.md'
  92. analysis_path.parent.mkdir(parents=True, exist_ok=True)
  93. with open(analysis_path, 'w', encoding='utf-8') as f:
  94. f.write(f'# {question}\n\n{compiled}\n')
  95. answer_prompt = f"""你是一个海事领域 wiki 知识库问答助手。
  96. 基于以下已编译的 wiki 知识回答问题。回答要具体,标注来源。
  97. ## Wiki 知识
  98. {wiki_context}
  99. ## 问题
  100. {question}
  101. ## 回答"""
  102. answer = provider.chat([{'role': 'user', 'content': answer_prompt}])
  103. elapsed = time.time() - t0
  104. return {
  105. 'answer': answer,
  106. 'source_type': source_type,
  107. 'wiki_pages_used': len(wiki_pages),
  108. 'elapsed': round(elapsed, 1),
  109. }
  110. # 运行对比
  111. def run_comparison():
  112. with open('/home/67/knowledge/maritime/test_questions.json') as f:
  113. data = json.load(f)
  114. questions = data['questions']
  115. results = []
  116. print(SEP)
  117. print(' Maritime QA Comparison: RAG vs Wiki')
  118. print(SEP)
  119. for q in questions:
  120. print(f'\n{LINE}')
  121. print(f'Q{q["id"]} [{q["difficulty"]}] {q["question"]}')
  122. print(LINE)
  123. # RAG
  124. print('\n[RAG] answering...')
  125. try:
  126. rag = rag_answer(q['question'])
  127. print(f' time: {rag["elapsed"]}s | chunks: {rag["chunks_used"]}')
  128. print(f' sources: {", ".join(rag["sources"][:3])}')
  129. ans_preview = rag["answer"][:300]
  130. print(f' answer: {ans_preview}{"..." if len(rag["answer"]) > 300 else ""}')
  131. except Exception as e:
  132. rag = {'answer': f'ERROR: {e}', 'elapsed': 0}
  133. print(f' ERROR: {e}')
  134. # Wiki
  135. print('\n[Wiki] answering...')
  136. try:
  137. wiki = wiki_answer(q['question'])
  138. print(f' time: {wiki["elapsed"]}s | source: {wiki.get("source_type", "wiki")}')
  139. ans_preview = wiki["answer"][:300]
  140. print(f' answer: {ans_preview}{"..." if len(wiki["answer"]) > 300 else ""}')
  141. except Exception as e:
  142. wiki = {'answer': f'ERROR: {e}', 'elapsed': 0}
  143. print(f' ERROR: {e}')
  144. results.append({
  145. 'question': q,
  146. 'rag': rag,
  147. 'wiki': wiki,
  148. })
  149. out_path = '/home/67/knowledge/maritime/comparison_results.json'
  150. with open(out_path, 'w', encoding='utf-8') as f:
  151. json.dump(results, f, ensure_ascii=False, indent=2)
  152. print(f'\n{SEP}')
  153. print(f'Done! Results saved to: {out_path}')
  154. print(f'\nSummary:')
  155. print(f' {"Question":<40} {"RAG":>8} {"Wiki":>8}')
  156. for r in results:
  157. q_text = r['question']['question'][:36]
  158. print(f' {q_text:<40} {r["rag"]["elapsed"]:>6.1f}s {r["wiki"]["elapsed"]:>6.1f}s')
  159. if __name__ == '__main__':
  160. run_comparison()