v1.0.111: llm_prompt.py reads active prompt from DB via Lucee API, fallback to hardcoded

This commit is contained in:
2026-06-23 07:03:35 +04:00
parent 69cfb3457b
commit 38f580e658
2 changed files with 62 additions and 23 deletions
+61 -22
View File
@@ -1,16 +1,14 @@
"""llm_prompt.py — Формирование промпта для LLM-анализа ДС."""
"""llm_prompt.py — Формирование промпта для LLM-анализа ДС.
Читает активный промпт из БД (через Lucee API). При ошибке — fallback на хардкод."""
import os
import httpx
def build_prompt(current_spec: list, doc_text: str) -> str:
is_first = len(current_spec) == 0
if is_first:
return _build_initial(doc_text)
else:
return _build_diff(current_spec, doc_text)
LUCEE_URL = os.environ.get("LUCEE_URL", "https://contractor.luceek8s.dev.nubes.ru")
# ── Fallback-промпты (если БД недоступна) ──────────────────────
def _build_initial(doc_text: str) -> str:
return f"""Ты — анализатор договоров облачного провайдера.
FALLBACK_EXTRACT = """Ты — анализатор договоров облачного провайдера.
Ниже текст спецификации услуг из ПЕРВОГО документа (базовый договор).
Извлеки ВСЕ строки спецификации в JSON-массив.
@@ -36,18 +34,7 @@ def _build_initial(doc_text: str) -> str:
{doc_text}
---"""
def _build_diff(current_spec: list, doc_text: str) -> str:
lines = []
for i, r in enumerate(current_spec):
lines.append(
f"[id: r{i+1}] {r.get('name', '?')} | "
f"цена={r.get('price', '')} | объём={r.get('qty', '')} | "
f"сумма={r.get('sum', '')} | начало={r.get('date_start', '')}"
)
spec_text = "\n".join(lines)
return f"""Ты — анализатор допсоглашений к договорам облачного провайдера.
FALLBACK_DIFF = """Ты — анализатор допсоглашений к договорам облачного провайдера.
У тебя есть текущая спецификация услуг и текст нового допсоглашения (ДС).
Твоя задача — определить, какие изменения вносит ДС в текущую спецификацию.
@@ -75,9 +62,61 @@ def _build_diff(current_spec: list, doc_text: str) -> str:
7. price, qty, sum — ЧИСЛА (не строки).
ТЕКУЩАЯ СПЕЦИФИКАЦИЯ:
{spec_text}
{spec_current}
ТЕКСТ ДОПСОГЛАШЕНИЯ:
---
{doc_text}
---"""
def _fetch_prompt(role: str) -> dict | None:
"""Получить активный промпт из БД. Возвращает {id, body} или None."""
try:
with httpx.Client(http2=True, timeout=10, verify=False) as client:
resp = client.get(
f"{LUCEE_URL}/prompt.cfm",
params={"action": "get_active", "role": role},
)
resp.raise_for_status()
data = resp.json()
if data.get("ok") and data.get("body"):
return {"id": data.get("id", ""), "body": data["body"]}
except Exception:
pass
return None
def _build_spec_text(current_spec: list) -> str:
"""Перечисление строк спецификации для подстановки в {spec_current}."""
lines = []
for i, r in enumerate(current_spec):
lines.append(
f"[id: r{i+1}] {r.get('name', '?')} | "
f"цена={r.get('price', '')} | объём={r.get('qty', '')} | "
f"сумма={r.get('sum', '')} | начало={r.get('date_start', '')}"
)
return "\n".join(lines)
def build_prompt(current_spec: list, doc_text: str) -> str:
"""
Формирует промпт для LLM.
1. Пробует получить активный промпт из БД (Lucee API).
2. При неудаче — fallback на хардкод.
Возвращает готовый текст промпта.
"""
is_first = len(current_spec) == 0
role = "extract" if is_first else "diff"
db = _fetch_prompt(role)
if db:
template = db["body"]
else:
template = FALLBACK_EXTRACT if is_first else FALLBACK_DIFF
# Подстановка плейсхолдеров
result = template.replace("{doc_text}", doc_text)
result = result.replace("{spec_current}", _build_spec_text(current_spec))
return result
+1 -1
View File
@@ -75,7 +75,7 @@
<body>
<div class="topbar">
<img src="/nubes-logo.svg" alt="Nubes">
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.110 — Lucee</span></span>
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.111 — Lucee</span></span>
</div>
<div class="content">