Add per-service system prompts with AI Settings tab view
Each feature service owns its system prompt in its config JSON on the shared volume. The AI Settings page now has General and System Prompts tabs — admins can view and edit any service's prompts at runtime with changes taking effect within 30 s (config cache TTL). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,8 @@ categories many-to-many via category_assignments
|
||||
|
||||
### AI extraction (via ai-service)
|
||||
|
||||
System prompt and user prompt template are loaded at runtime from `doc_service_config.json` (`system_prompts` key). Defaults are built into the service and used as fallback if the config key is absent. Changes made via the AI Settings UI take effect within 30 seconds (config cache TTL).
|
||||
|
||||
Prompt sends the first 50 000 chars of extracted text. Expected JSON response includes:
|
||||
- `title` — suggested human-readable title
|
||||
- `document_type` — invoice / bill / receipt / order / expense / revenue / unknown
|
||||
|
||||
@@ -4,7 +4,11 @@ import json
|
||||
import httpx
|
||||
|
||||
from app.core.config import settings
|
||||
from app.services.prompts import SYSTEM_PROMPT, USER_PROMPT_TEMPLATE
|
||||
from app.services.config_reader import (
|
||||
_DEFAULT_SYSTEM_PROMPT,
|
||||
_DEFAULT_USER_TEMPLATE,
|
||||
load_doc_config,
|
||||
)
|
||||
|
||||
_client = httpx.AsyncClient(timeout=120.0)
|
||||
|
||||
@@ -19,9 +23,14 @@ async def classify_document(text: str) -> dict:
|
||||
Returns the parsed JSON result dict.
|
||||
Raises AIServiceError on HTTP errors or unexpected response shapes.
|
||||
"""
|
||||
config = await load_doc_config()
|
||||
prompts = config.get("system_prompts", {})
|
||||
system_prompt = prompts.get("system") or _DEFAULT_SYSTEM_PROMPT
|
||||
user_template = prompts.get("user_template") or _DEFAULT_USER_TEMPLATE
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": USER_PROMPT_TEMPLATE.format(text=text[:50_000])},
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_template.format(text=text[:50_000])},
|
||||
]
|
||||
|
||||
try:
|
||||
|
||||
@@ -14,8 +14,39 @@ from pathlib import Path
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
_DEFAULT_SYSTEM_PROMPT = (
|
||||
"You are a financial document analysis assistant. "
|
||||
"Given the text extracted from a PDF document, return ONLY a JSON object "
|
||||
"with no markdown, no code fences, and no explanation."
|
||||
)
|
||||
|
||||
_DEFAULT_USER_TEMPLATE = (
|
||||
'Analyze the following document text and return a JSON object with exactly these keys:\n'
|
||||
'title (a short, descriptive human-readable title for this document, e.g. "ACME Corp Invoice April 2026", "Office Supplies Receipt", "Q1 Flower Delivery Order"),\n'
|
||||
'document_type (one of: invoice, bill, receipt, order, expense, revenue, unknown),\n'
|
||||
'total_amount (string or null),\n'
|
||||
'currency (string or null),\n'
|
||||
'vendor_name (string or null),\n'
|
||||
'customer_name (string or null),\n'
|
||||
'billing_address (string or null),\n'
|
||||
'customer_address (string or null),\n'
|
||||
'invoice_number (string or null),\n'
|
||||
'invoice_date (string or null),\n'
|
||||
'due_date (string or null),\n'
|
||||
'tags (array of short keyword strings describing the document),\n'
|
||||
'line_items (array of objects, each with keys: description, amount),\n'
|
||||
'suggested_categories (array of 2 to 5 short category name strings a user might want to file this document under, e.g. "Utilities", "Travel", "Software Subscriptions", "Client Invoices").\n'
|
||||
'\n'
|
||||
'Document text:\n'
|
||||
'{text}'
|
||||
)
|
||||
|
||||
_DEFAULT_CONFIG: dict = {
|
||||
"documents": {"max_pdf_bytes": 20 * 1024 * 1024},
|
||||
"system_prompts": {
|
||||
"system": _DEFAULT_SYSTEM_PROMPT,
|
||||
"user_template": _DEFAULT_USER_TEMPLATE,
|
||||
},
|
||||
}
|
||||
|
||||
_cache: dict | None = None
|
||||
|
||||
Reference in New Issue
Block a user