#!/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()