analyze_results.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #!/usr/bin/env python3
  2. """
  3. Step 3: Analyze RAG vs Wiki comparison results
  4. """
  5. import json
  6. import sys
  7. from collections import Counter, defaultdict
  8. from pathlib import Path
  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. RESULTS_FILE = Path('/home/67/knowledge/maritime/batch_results_200.json')
  15. REPORT_FILE = Path('/home/67/knowledge/maritime/analysis_report.md')
  16. from lambdagent.providers import create_provider
  17. def main():
  18. with open(RESULTS_FILE) as f:
  19. data = json.load(f)
  20. results = data['results']
  21. meta = data.get('meta', {})
  22. total = len(results)
  23. print(f"Analyzing {total} Q&A pairs...")
  24. # ── Basic Stats ──
  25. rag_times = [r['rag']['elapsed'] for r in results if not r['rag'].get('error')]
  26. wiki_times = [r['wiki']['elapsed'] for r in results if not r['wiki'].get('error')]
  27. rag_lens = [len(r['rag']['answer']) for r in results if not r['rag'].get('error')]
  28. wiki_lens = [len(r['wiki']['answer']) for r in results if not r['wiki'].get('error')]
  29. rag_errors = sum(1 for r in results if r['rag'].get('error'))
  30. wiki_errors = sum(1 for r in results if r['wiki'].get('error'))
  31. # Wiki source types
  32. wiki_sources = Counter(r['wiki'].get('source_type', 'unknown') for r in results)
  33. # By question type
  34. by_type = defaultdict(lambda: {'rag_times': [], 'wiki_times': [], 'rag_lens': [], 'wiki_lens': [], 'count': 0})
  35. for r in results:
  36. t = r.get('type', 'unknown')
  37. by_type[t]['count'] += 1
  38. if not r['rag'].get('error'):
  39. by_type[t]['rag_times'].append(r['rag']['elapsed'])
  40. by_type[t]['rag_lens'].append(len(r['rag']['answer']))
  41. if not r['wiki'].get('error'):
  42. by_type[t]['wiki_times'].append(r['wiki']['elapsed'])
  43. by_type[t]['wiki_lens'].append(len(r['wiki']['answer']))
  44. # By difficulty
  45. by_diff = defaultdict(lambda: {'rag_times': [], 'wiki_times': [], 'count': 0})
  46. for r in results:
  47. d = r.get('difficulty', 'medium')
  48. by_diff[d]['count'] += 1
  49. if not r['rag'].get('error'):
  50. by_diff[d]['rag_times'].append(r['rag']['elapsed'])
  51. if not r['wiki'].get('error'):
  52. by_diff[d]['wiki_times'].append(r['wiki']['elapsed'])
  53. # ── Quality Analysis using LLM ──
  54. # Sample 20 questions for LLM-based quality evaluation
  55. print("Running LLM quality evaluation on 20 sampled answers...")
  56. provider = create_provider('ollama', model='qwen2.5:32b', timeout=600)
  57. import random
  58. sample = random.sample(results, min(20, total))
  59. quality_scores = {'rag': [], 'wiki': []}
  60. for i, r in enumerate(sample):
  61. eval_prompt = f"""请评估以下两个回答的质量。评分标准(1-5分):
  62. - 准确性: 信息是否正确
  63. - 完整性: 是否覆盖问题要点
  64. - 引用: 是否有来源标注
  65. - 条理性: 结构是否清晰
  66. 问题: {r['question']}
  67. 回答A (RAG模式):
  68. {r['rag']['answer'][:800]}
  69. 回答B (Wiki模式):
  70. {r['wiki']['answer'][:800]}
  71. 请严格按以下JSON格式输出评分,不要输出其他内容:
  72. {{"rag_score": <1-5>, "wiki_score": <1-5>, "rag_strength": "<一句话>", "wiki_strength": "<一句话>", "winner": "<rag或wiki或tie>"}}"""
  73. try:
  74. result = provider.chat([{'role': 'user', 'content': eval_prompt}])
  75. # Try to parse JSON from response
  76. import re
  77. json_match = re.search(r'\{[^{}]+\}', result)
  78. if json_match:
  79. scores = json.loads(json_match.group())
  80. quality_scores['rag'].append(scores.get('rag_score', 3))
  81. quality_scores['wiki'].append(scores.get('wiki_score', 3))
  82. print(f" [{i+1}/20] RAG={scores.get('rag_score')}, Wiki={scores.get('wiki_score')}, Winner={scores.get('winner')}")
  83. else:
  84. print(f" [{i+1}/20] Could not parse scores")
  85. except Exception as e:
  86. print(f" [{i+1}/20] Error: {e}")
  87. # ── Generate Report ──
  88. def avg(lst):
  89. return round(sum(lst) / len(lst), 1) if lst else 0
  90. def median(lst):
  91. if not lst: return 0
  92. s = sorted(lst)
  93. n = len(s)
  94. return s[n//2] if n % 2 else round((s[n//2-1] + s[n//2]) / 2, 1)
  95. rag_avg_score = avg(quality_scores['rag']) if quality_scores['rag'] else '-'
  96. wiki_avg_score = avg(quality_scores['wiki']) if quality_scores['wiki'] else '-'
  97. report = f"""# Maritime QA Comparison Report
  98. # RAG vs Wiki -- {total} Questions
  99. Generated: {meta.get('completed_at', 'N/A')}
  100. Total time: {meta.get('total_minutes', 'N/A')} minutes
  101. ---
  102. ## 1. Overview
  103. | Metric | RAG | Wiki |
  104. |--------|-----|------|
  105. | Questions answered | {total - rag_errors} | {total - wiki_errors} |
  106. | Errors | {rag_errors} | {wiki_errors} |
  107. | Avg response time | {avg(rag_times)}s | {avg(wiki_times)}s |
  108. | Median response time | {median(rag_times)}s | {median(wiki_times)}s |
  109. | Min/Max time | {min(rag_times) if rag_times else '-'}/{max(rag_times) if rag_times else '-'}s | {min(wiki_times) if wiki_times else '-'}/{max(wiki_times) if wiki_times else '-'}s |
  110. | Avg answer length | {avg(rag_lens)} chars | {avg(wiki_lens)} chars |
  111. | Total time | {sum(rag_times):.0f}s | {sum(wiki_times):.0f}s |
  112. ## 2. Quality Evaluation (LLM-judged, 20 sample)
  113. | Metric | RAG | Wiki |
  114. |--------|-----|------|
  115. | Average score (1-5) | {rag_avg_score} | {wiki_avg_score} |
  116. | Scores distribution | {Counter(quality_scores['rag'])} | {Counter(quality_scores['wiki'])} |
  117. ## 3. By Question Type
  118. | Type | Count | RAG avg time | Wiki avg time | RAG avg len | Wiki avg len |
  119. |------|-------|-------------|--------------|------------|-------------|
  120. """
  121. for t in ['fact', 'reasoning', 'synthesis']:
  122. if t in by_type:
  123. d = by_type[t]
  124. report += f"| {t} | {d['count']} | {avg(d['rag_times'])}s | {avg(d['wiki_times'])}s | {avg(d['rag_lens'])} | {avg(d['wiki_lens'])} |\n"
  125. report += f"""
  126. ## 4. By Difficulty
  127. | Difficulty | Count | RAG avg time | Wiki avg time |
  128. |-----------|-------|-------------|--------------|
  129. """
  130. for d_name in ['easy', 'medium', 'hard']:
  131. if d_name in by_diff:
  132. d = by_diff[d_name]
  133. report += f"| {d_name} | {d['count']} | {avg(d['rag_times'])}s | {avg(d['wiki_times'])}s |\n"
  134. report += f"""
  135. ## 5. Wiki Knowledge Accumulation
  136. | Source Type | Count | Percentage |
  137. |------------|-------|-----------|
  138. """
  139. for src, cnt in wiki_sources.most_common():
  140. report += f"| {src} | {cnt} | {cnt*100/total:.1f}% |\n"
  141. report += f"""
  142. ## 6. Key Findings
  143. ### RAG Mode Characteristics
  144. - Average response time: {avg(rag_times)}s
  145. - Direct retrieval from 62,011 text chunks
  146. - Each answer based on top-{results[0]['rag'].get('chunks_used', 5) if results else 5} retrieved chunks
  147. - Consistent response time regardless of question complexity
  148. ### Wiki Mode Characteristics
  149. - Average response time: {avg(wiki_times)}s
  150. - First query requires compilation (2 LLM calls), subsequent queries faster if wiki hit
  151. - Wiki pages accumulated: {wiki_sources.get('wiki', 0)} hits / {wiki_sources.get('compiled', 0)} compilations
  152. - Knowledge accumulates over time -- later questions benefit from earlier compilations
  153. ### Speed Comparison
  154. - RAG is {'faster' if avg(rag_times) < avg(wiki_times) else 'slower'} by {abs(avg(rag_times) - avg(wiki_times))}s on average
  155. - Wiki first-query overhead: ~{avg(wiki_times) - avg(rag_times):.0f}s extra for compilation
  156. ### Quality Comparison (LLM-judged)
  157. - RAG average score: {rag_avg_score}/5
  158. - Wiki average score: {wiki_avg_score}/5
  159. """
  160. with open(REPORT_FILE, 'w', encoding='utf-8') as f:
  161. f.write(report)
  162. print(f"\nReport saved to: {REPORT_FILE}")
  163. print("\n" + report)
  164. if __name__ == '__main__':
  165. main()