#!/usr/bin/env python3 """ Maritime QA Comparison Demo RAG vs Wiki side-by-side comparison With server-side conversation history persistence """ import json import os import sys import time import threading from datetime import datetime from flask import Flask, request, jsonify, render_template_string import yaml # Load paths from agent config _agent_cfg_path = os.environ.get('AGENT_CONFIG', '/app/agentexample/qaagent67/agent-config.yml') _wiki_cfg_path = os.environ.get('WIKI_CONFIG', '/app/agentexample/qaagent67wiki/agent-config.yml') _cfg = {} _wiki_cfg = {} if os.path.exists(_agent_cfg_path): with open(_agent_cfg_path) as f: _cfg = yaml.safe_load(f) if os.path.exists(_wiki_cfg_path): with open(_wiki_cfg_path) as f: _wiki_cfg = yaml.safe_load(f) # ═══ instance.yml overlay (domain-specific overrides) ═══ def _deep_merge(base, override): import copy result = copy.deepcopy(base) for k, v in (override or {}).items(): if k in result and isinstance(result[k], dict) and isinstance(v, dict): result[k] = _deep_merge(result[k], v) else: result[k] = copy.deepcopy(v) return result _instance_cfg_path = os.environ.get('INSTANCE_CONFIG', '') if not _instance_cfg_path: _base = (_cfg.get('knowledge', {}) or {}).get('baseDir', '') if _base: _cand = os.path.join(_base, 'instance.yml') if os.path.isfile(_cand): _instance_cfg_path = _cand if _instance_cfg_path and os.path.isfile(_instance_cfg_path): with open(_instance_cfg_path) as _f: _instance = yaml.safe_load(_f) or {} _cfg = _deep_merge(_cfg, _instance) print('[config] loaded instance overlay:', _instance_cfg_path) _knowledge = _cfg.get('knowledge', {}) KNOWLEDGE_BASE = _knowledge.get('baseDir', os.environ.get('KNOWLEDGE_DIR', '/data/knowledge/maritime')) sys.path.insert(0, KNOWLEDGE_BASE) try: from search_unified import search except ImportError: from search_engine import search from lambdagent.providers import create_provider app = Flask(__name__) HISTORY_FILE = os.environ.get('HISTORY_FILE', '/app/qademo/conversation_history.json') # WIKI_DIR — prefer instance.yml overlay (_cfg.wiki.dir), then standalone wiki config, then default WIKI_DIR = ( (_cfg.get('wiki', {}) or {}).get('dir', '') or (_wiki_cfg.get('wiki', {}) or {}).get('dir', '') or '/app/agentexample/qaagent67wiki/wiki' ) # ═══ Demo UI config (domain-specific, loaded from instance.yml) ═══ _demo = _cfg.get('demo', {}) or {} DEMO_TITLE = _demo.get('title', 'Knowledge QA Demo') DEMO_TAGLINE = _demo.get('tagline', 'RAG vs Wiki | Qwen2.5:32B local') DEMO_PLACEHOLDER = _demo.get('placeholder', 'Please enter a question...') DEMO_DOMAIN = _demo.get('domain', 'knowledge') DEMO_EXAMPLES = _demo.get('examples', []) # list of {q, label} DEMO_PORT = int(os.environ.get('PORT') or _demo.get('port', 8080)) DEMO_BIND = os.environ.get('BIND_HOST') or _demo.get('bind') or '0.0.0.0' # 左右面板默认选项: 海事域左边默认 LambdaRAG,其他域默认 Lite _is_maritime = 'maritime' in DEMO_DOMAIN.lower() or '海事' in DEMO_TITLE DEMO_LEFT_DEFAULT = _demo.get('leftDefault', 'rag' if _is_maritime else 'lite') DEMO_RIGHT_DEFAULT = _demo.get('rightDefault', 'wiki') def _html_attr_esc(s): """Escape for HTML attribute in double quotes.""" return (str(s).replace('&', '&').replace('<', '<').replace('>', '>') .replace('"', '"').replace("'", ''')) def _html_text_esc(s): return (str(s).replace('&', '&').replace('<', '<').replace('>', '>')) _OLD_TITLE_TITLE = '
RAG vs Wiki | 1326 docs | Qwen2.5:32B local
' _OLD_PLACEHOLDER = 'placeholder="Please enter a maritime question..."' _OLD_EXAMPLES = ( ' Ship entry/exit reporting system\n' ' Crew competency certificates\n' ' Qiongzhou Strait routing system\n' ' Maritime law comparison\n' ' Crew development trends' ) def _apply_demo_config(html): html = html.replace(_OLD_TITLE_TITLE, '' + _html_text_esc(DEMO_TAGLINE) + '
') html = html.replace(_OLD_PLACEHOLDER, 'placeholder="' + _html_attr_esc(DEMO_PLACEHOLDER) + '"') if DEMO_EXAMPLES: lines = [] for e in DEMO_EXAMPLES: q = str(e.get('q', '')) label = str(e.get('label', q)) q_esc = _html_attr_esc(q) label_esc = _html_text_esc(label) lines.append(' ' + label_esc + '') html = html.replace(_OLD_EXAMPLES, '\n'.join(lines)) # Inject default panel selections as JS init_script = ( '\n\n' ) html = html.replace('