Files
law-kb/app/services/prompt_config.py
T
freedak 641e33b834 feat: QYLAW 法律法规知识库
- 语义检索(FAISS + embedding)+ 精确查找(法规名+条号)
- RAG 问答(SSE 流式,支持 thinking 折叠显示)
- 法规浏览(原文阅读)
- 历史记录(检索+对话持久化到 SQLite)
- 设置页(系统提示词/模板/LLM 参数可配置)
- 检索质量评估脚本

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-07 14:55:25 +08:00

191 lines
6.5 KiB
Python

"""提示词配置服务 — 从 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")