| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- #!/usr/bin/env python3
- """
- Batch comparison: RAG vs Wiki using vLLM on NPU (fast!)
- Uses OpenAI-compatible API served by vLLM on Ascend NPU
- """
- import json
- import sys
- import time
- import urllib.request
- from datetime import datetime
- from pathlib import Path
- sys.path.insert(0, '/home/67/knowledge/maritime')
- from search_engine import search
- QUESTIONS_FILE = Path('/home/67/knowledge/maritime/test_200_questions.json')
- RESULTS_FILE = Path('/home/67/knowledge/maritime/batch_results_npu.json')
- WIKI_DIR = '/home/67/lambdagentpaas/agentexample/qaagent67wiki/wiki'
- # vLLM API on NPU
- VLLM_URL = "http://127.0.0.1:8000/v1/chat/completions"
- def llm_chat(messages, max_tokens=4096, temperature=0.3):
- """Call vLLM OpenAI-compatible API"""
- body = json.dumps({
- "model": "qwen2.5-32b",
- "messages": messages,
- "temperature": temperature,
- "max_tokens": max_tokens,
- }, ensure_ascii=False).encode('utf-8')
- req = urllib.request.Request(
- VLLM_URL,
- data=body,
- headers={"Content-Type": "application/json"},
- method="POST"
- )
- with urllib.request.urlopen(req, timeout=300) as resp:
- data = json.loads(resp.read())
- return data["choices"][0]["message"]["content"].strip()
- def rag_answer(question, top_k=5):
- t0 = time.time()
- results = search(question, top_k=top_k)
- context = '\n\n'.join([f'[doc{i+1}: {r["source"]}]\n{r["text"]}' for i, r in enumerate(results)])
- prompt = f"""你是一个海事领域问答助手。请严格基于以下参考文档回答问题。
- 如果文档中没有相关信息,请说明"文档中未找到相关信息"。
- 回答要具体,引用文档中的原文,标注来源 [来源: 文件名]。
- ## 参考文档
- {context}
- ## 问题
- {question}
- ## 回答"""
- answer = llm_chat([{'role': 'user', 'content': prompt}])
- elapsed = time.time() - t0
- return {
- 'answer': answer,
- 'sources': [r['source'] for r in results],
- 'elapsed': round(elapsed, 1),
- 'chunks_used': len(results),
- }
- def wiki_answer(question, top_k=5):
- t0 = time.time()
- wiki_path = Path(WIKI_DIR)
- wiki_pages = []
- for subdir in ['sources', 'entities', 'topics', 'analyses']:
- d = wiki_path / subdir
- if d.exists():
- for f in d.glob('*.md'):
- try:
- content = f.read_text(encoding='utf-8')
- q_chars = set(question)
- match_score = sum(1 for c in q_chars if c in content)
- if match_score > len(question) * 0.3:
- wiki_pages.append({'name': f.stem, 'content': content[:1500], 'score': match_score})
- except:
- pass
- wiki_pages.sort(key=lambda x: x['score'], reverse=True)
- wiki_pages = wiki_pages[:3]
- if wiki_pages:
- wiki_context = '\n\n'.join([f'[Wiki: {p["name"]}]\n{p["content"]}' for p in wiki_pages])
- source_type = 'wiki'
- else:
- results = search(question, top_k=top_k)
- compile_context = '\n\n'.join([f'[doc: {r["source"]}]\n{r["text"]}' for r in results])
- compile_prompt = f"""请阅读以下文档片段,提炼出与问题相关的核心知识点。
- 用结构化的方式组织,标注来源。
- 文档:
- {compile_context}
- 问题: {question}
- 请输出结构化的知识摘要:"""
- compiled = llm_chat([{'role': 'user', 'content': compile_prompt}])
- wiki_context = compiled
- source_type = 'compiled'
- safe_name = question[:30].replace('/', '_').replace(' ', '_')
- analysis_path = wiki_path / 'analyses' / f'{safe_name}.md'
- analysis_path.parent.mkdir(parents=True, exist_ok=True)
- with open(analysis_path, 'w', encoding='utf-8') as f:
- f.write(f'# {question}\n\n{compiled}\n')
- answer_prompt = f"""你是一个海事领域 wiki 知识库问答助手。
- 基于以下已编译的 wiki 知识回答问题。回答要具体,标注来源。
- ## Wiki 知识
- {wiki_context}
- ## 问题
- {question}
- ## 回答"""
- answer = llm_chat([{'role': 'user', 'content': answer_prompt}])
- elapsed = time.time() - t0
- return {
- 'answer': answer,
- 'source_type': source_type,
- 'wiki_pages_used': len(wiki_pages),
- 'elapsed': round(elapsed, 1),
- }
- def save_results(results, meta):
- with open(RESULTS_FILE, 'w', encoding='utf-8') as f:
- json.dump({'meta': meta, 'results': results}, f, ensure_ascii=False, indent=2)
- def main():
- # Test vLLM connection
- print("Testing vLLM NPU connection...")
- try:
- test = llm_chat([{'role': 'user', 'content': 'Hello, respond with OK'}], max_tokens=10)
- print(f"vLLM OK: {test}")
- except Exception as e:
- print(f"vLLM connection failed: {e}")
- print("Make sure vLLM is running: bash /home/67/start_vllm_npu.sh")
- sys.exit(1)
- with open(QUESTIONS_FILE) as f:
- data = json.load(f)
- questions = data['questions']
- # Resume support
- existing = []
- done_ids = set()
- if RESULTS_FILE.exists():
- with open(RESULTS_FILE) as f:
- prev = json.load(f)
- existing = prev.get('results', [])
- done_ids = {r['question_id'] for r in existing}
- print(f"Resuming: {len(done_ids)} already done")
- results = list(existing)
- total = len(questions)
- remaining = [q for q in questions if q['id'] not in done_ids]
- print(f"Total: {total}, Remaining: {len(remaining)}")
- print(f"NPU inference: estimated ~3-5s per answer, total ~{len(remaining) * 8 / 60:.0f} min")
- print("=" * 60)
- start_time = time.time()
- for idx, q in enumerate(remaining):
- qnum = len(done_ids) + idx + 1
- print(f"\n[{qnum}/{total}] [{q['type']}/{q.get('difficulty','?')}] {q['question'][:60]}...")
- record = {
- 'question_id': q['id'],
- 'question': q['question'],
- 'type': q['type'],
- 'difficulty': q.get('difficulty', 'medium'),
- 'category': q.get('category', ''),
- }
- # RAG
- try:
- print(f" RAG...", end=' ', flush=True)
- rag = rag_answer(q['question'])
- record['rag'] = rag
- print(f"{rag['elapsed']}s, {len(rag['answer'])} chars")
- except Exception as e:
- record['rag'] = {'answer': f'ERROR: {e}', 'elapsed': 0, 'error': True}
- print(f"ERROR: {e}")
- # Wiki
- try:
- print(f" Wiki...", end=' ', flush=True)
- wiki = wiki_answer(q['question'])
- record['wiki'] = wiki
- print(f"{wiki['elapsed']}s, {len(wiki['answer'])} chars, src={wiki.get('source_type','?')}")
- except Exception as e:
- record['wiki'] = {'answer': f'ERROR: {e}', 'elapsed': 0, 'error': True}
- print(f"ERROR: {e}")
- results.append(record)
- # Save every 10 questions
- if (idx + 1) % 10 == 0 or idx == len(remaining) - 1:
- elapsed_total = time.time() - start_time
- avg_per_q = elapsed_total / (idx + 1)
- eta = avg_per_q * (len(remaining) - idx - 1)
- meta = {
- 'engine': 'vLLM on Ascend 910B4 NPU',
- 'model': 'Qwen2.5-32B-Instruct',
- 'completed': len(results),
- 'total': total,
- 'elapsed_minutes': round(elapsed_total / 60, 1),
- 'eta_minutes': round(eta / 60, 1),
- }
- save_results(results, meta)
- print(f" [Saved] {len(results)}/{total}, ETA: {meta['eta_minutes']}min")
- total_time = time.time() - start_time
- meta = {
- 'engine': 'vLLM on Ascend 910B4 NPU',
- 'model': 'Qwen2.5-32B-Instruct',
- 'completed_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
- 'completed': len(results),
- 'total': total,
- 'total_minutes': round(total_time / 60, 1),
- }
- save_results(results, meta)
- print(f"\n{'=' * 60}")
- print(f"DONE! {len(results)} questions in {meta['total_minutes']} min")
- print(f"Results: {RESULTS_FILE}")
- if __name__ == '__main__':
- main()
|