"""提示词配置服务 — 从 SQLite 读写系统提示词和模板正文 配置项(config_key): - system_prompt: 系统提示词(覆盖默认) - template_simple: 通俗解释模板正文 - template_professional: 专业分析模板正文 - template_compare: 对比条文模板正文 """ import sqlite3 import time import logging from typing import Optional, Dict from pathlib import Path from app.config import SQLITE_PATH logger = logging.getLogger(__name__) # 默认值(与 rag.py 中 PROMPT_TEMPLATES 保持一致) DEFAULTS = { "system_prompt": "你是法律助手,根据提供的法规条文回答问题,必须引用法规名和条号。", "template_simple": ( "你是一位耐心的法律科普助手。请根据以下法规条文,用通俗易懂的语言回答用户问题。\n" "要求:\n" "1. 避免法律术语,用日常语言解释\n" '2. 必须引用依据的法规名和条号,格式如"根据《XX法》第X条"\n' '3. 如果条文不足以回答,明确说明"根据现有法规无法完全回答"\n' "4. 不要编造法规\n\n" "【相关法规条文】\n{context}\n\n【用户问题】\n{question}" ), "template_professional": ( "你是一位专业的法律分析助手。请根据以下法规条文,对用户问题进行专业分析。\n" "要求:\n" "1. 使用规范法律术语\n" '2. 必须引用依据的法规名和条号,格式如"依据《XX法》第X条"\n' "3. 分析条文的适用条件、法律后果\n" "4. 如有多条相关,对比分析\n" "5. 指出条文的适用边界和可能的争议点\n" "6. 不要编造法规\n\n" "【相关法规条文】\n{context}\n\n【用户问题】\n{question}" ), "template_compare": ( "你是一位法规研究助手。请根据以下法规条文,对比分析不同法规对同一问题的规定。\n" "要求:\n" "1. 列出每条相关法规的具体规定\n" "2. 对比规定的异同\n" "3. 标注法规名、条号、发布日期\n" "4. 指出适用范围差异(如全国性 vs 地方性)\n" "5. 不要编造法规\n\n" "【相关法规条文】\n{context}\n\n【用户问题】\n{question}" ), # LLM 参数默认值 "llm_thinking_enabled": "true", # thinking 开关 "llm_max_tokens": "4096", # 最大生成 token 数 "llm_temperature": "0.3", # 温度 "llm_thinking_budget": "2048", # thinking 预算(thinking 最大 token 数) } # 模板 key 与 rag.py PROMPT_TEMPLATES 的映射 TEMPLATE_KEY_MAP = { "simple": "template_simple", "professional": "template_professional", "compare": "template_compare", } def _get_conn() -> sqlite3.Connection: """获取 SQLite 连接(自动建表)""" SQLITE_PATH.parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(str(SQLITE_PATH), check_same_thread=False) # 自动建表(若数据库不存在或表不存在) conn.executescript(""" CREATE TABLE IF NOT EXISTS prompt_configs ( id INTEGER PRIMARY KEY AUTOINCREMENT, config_key TEXT NOT NULL UNIQUE, config_value TEXT, updated_at TEXT NOT NULL ); """) return conn def get_all_configs() -> Dict[str, str]: """读取所有提示词配置,未配置的用默认值填充 Returns: {config_key: config_value} 所有配置项(含默认值) """ configs = dict(DEFAULTS) # 先用默认值 try: conn = _get_conn() rows = conn.execute( "SELECT config_key, config_value FROM prompt_configs" ).fetchall() for row in rows: key, val = row[0], row[1] if val: # 空值视为未配置,用默认 configs[key] = val conn.close() except Exception as e: logger.warning(f"读取提示词配置失败,使用默认值: {e}") return configs def get_config(key: str) -> str: """读取单个配置,未配置返回默认值""" try: conn = _get_conn() row = conn.execute( "SELECT config_value FROM prompt_configs WHERE config_key = ?", (key,) ).fetchone() conn.close() if row and row[0]: return row[0] except Exception as e: logger.warning(f"读取配置 {key} 失败: {e}") return DEFAULTS.get(key, "") def set_config(key: str, value: str) -> bool: """写入单个配置(空值删除记录,回退到默认) Returns: True 成功 """ try: conn = _get_conn() ts = time.strftime("%Y-%m-%d %H:%M:%S") if value: # 有值则 upsert conn.execute( """ INSERT INTO prompt_configs (config_key, config_value, updated_at) VALUES (?, ?, ?) ON CONFLICT(config_key) DO UPDATE SET config_value = excluded.config_value, updated_at = excluded.updated_at """, (key, value, ts), ) else: # 空值则删除(回退默认) conn.execute( "DELETE FROM prompt_configs WHERE config_key = ?", (key,) ) conn.commit() conn.close() return True except Exception as e: logger.error(f"写入配置 {key} 失败: {e}") return False def get_template_text(template: str) -> str: """获取指定模板的正文 Args: template: 模板名(simple/professional/compare) Returns: 模板正文(含 {context} 和 {question} 占位符) """ key = TEMPLATE_KEY_MAP.get(template, "template_simple") return get_config(key) def get_llm_params() -> dict: """获取 LLM 参数配置 Returns: {"thinking_enabled": bool, "max_tokens": int, "temperature": float, "thinking_budget": int} """ configs = get_all_configs() try: return { "thinking_enabled": configs.get("llm_thinking_enabled", "true").lower() == "true", "max_tokens": int(configs.get("llm_max_tokens", "4096")), "temperature": float(configs.get("llm_temperature", "0.3")), "thinking_budget": int(configs.get("llm_thinking_budget", "2048")), } except (ValueError, TypeError): return { "thinking_enabled": True, "max_tokens": 4096, "temperature": 0.3, "thinking_budget": 2048, } def get_system_prompt() -> str: """获取系统提示词""" return get_config("system_prompt")