feat(fusion_accounting_reports): commentary_prompt for LLM-generated narratives
Made-with: Cursor
This commit is contained in:
@@ -5,4 +5,5 @@ from . import currency_conversion
|
||||
from . import line_resolver
|
||||
from . import drill_down_resolver
|
||||
from . import anomaly_detection
|
||||
from . import commentary_prompt
|
||||
from . import commentary_generator
|
||||
|
||||
67
fusion_accounting_reports/services/commentary_prompt.py
Normal file
67
fusion_accounting_reports/services/commentary_prompt.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""LLM prompt for AI report commentary.
|
||||
|
||||
Provider-agnostic system + user prompt builder. Output contract:
|
||||
JSON with keys summary, highlights, concerns, next_actions."""
|
||||
|
||||
|
||||
SYSTEM_PROMPT = """You are an experienced CFO providing executive-level commentary
|
||||
on a financial report. Your output MUST be valid JSON of this exact shape:
|
||||
|
||||
{
|
||||
"summary": "<2-3 sentence executive summary of the report period>",
|
||||
"highlights": ["<observation 1>", "<observation 2>", ...],
|
||||
"concerns": ["<thing to investigate 1>", ...],
|
||||
"next_actions": ["<suggested action 1>", ...]
|
||||
}
|
||||
|
||||
Rules:
|
||||
- Use the data provided. Do not invent numbers.
|
||||
- Tone: professional, concise, factual.
|
||||
- Currency formatting: always include the $ symbol and 2 decimal places.
|
||||
- For anomalies: explicitly mention the variance percentage AND the dollar amount.
|
||||
- Do NOT include markdown code fences. Do NOT include any prose outside the JSON.
|
||||
"""
|
||||
|
||||
|
||||
def build_prompt(report_result: dict, anomalies: list) -> tuple[str, str]:
|
||||
"""Build (system_prompt, user_prompt) tuple."""
|
||||
parts = []
|
||||
|
||||
# Report context
|
||||
parts.append(f"REPORT: {report_result.get('report_name', 'Untitled')}")
|
||||
period = report_result.get('period', {})
|
||||
parts.append(f"PERIOD: {period.get('label', '')} "
|
||||
f"({period.get('date_from', '')} to {period.get('date_to', '')})")
|
||||
comp_period = report_result.get('comparison_period')
|
||||
if comp_period:
|
||||
parts.append(f"COMPARED TO: {comp_period.get('label', '')} "
|
||||
f"({comp_period.get('date_from', '')} to {comp_period.get('date_to', '')})")
|
||||
parts.append("")
|
||||
|
||||
# Rows (the actual numbers)
|
||||
parts.append("REPORT LINES:")
|
||||
for row in report_result.get('rows', []):
|
||||
line = f" - {row.get('label', '?')}: ${row.get('amount', 0):,.2f}"
|
||||
if row.get('amount_comparison') is not None:
|
||||
line += f" (comparison: ${row['amount_comparison']:,.2f}"
|
||||
if row.get('variance_pct') is not None:
|
||||
line += f", {row['variance_pct']:+.1f}%"
|
||||
line += ")"
|
||||
if row.get('is_subtotal'):
|
||||
line += " [SUBTOTAL]"
|
||||
parts.append(line)
|
||||
parts.append("")
|
||||
|
||||
# Anomalies
|
||||
if anomalies:
|
||||
parts.append("ANOMALIES (variances exceeding threshold):")
|
||||
for a in anomalies[:10]:
|
||||
parts.append(
|
||||
f" - {a['label']}: {a['direction']}d {a['variance_pct']:.1f}% "
|
||||
f"(${a['variance_amount']:+,.2f}, severity: {a['severity']})"
|
||||
)
|
||||
parts.append("")
|
||||
|
||||
parts.append("Generate the JSON commentary per the system prompt.")
|
||||
|
||||
return (SYSTEM_PROMPT, "\n".join(parts))
|
||||
Reference in New Issue
Block a user