PocketAccountant: custom ledger UI + deterministic agent (engine, ledger, retrieval, classifier)
c55ab5e verified | """Drive the full agent loop end-to-end and save a shareable trace. | |
| No weights needed: a ScriptedClient stands in for the fine-tuned model, replaying the | |
| tool calls it would make. This proves the loop — plan → call tools → observe → | |
| answer — and writes the trace to data/sample/trace_example.json for the Hub (📡). | |
| Run from the project root: python -m scripts.demo_agent | |
| """ | |
| import sys | |
| from pathlib import Path | |
| try: | |
| sys.stdout.reconfigure(encoding="utf-8") | |
| except (AttributeError, ValueError): | |
| pass | |
| from src.agent import Agent, AssistantTurn, ScriptedClient, ToolCall, ToolContext, build_tools | |
| from src.ledger import Ledger | |
| def seed_may(): | |
| lg = Ledger(":memory:") | |
| lg.record_income("2024-05-03", "Branding — Café Luna", 18000, iva_rate="0.16") | |
| lg.record_expense("2024-05-05", "Adobe Creative Cloud", 1300, iva_rate="0.16") | |
| lg.record_expense("2024-05-12", "Renta de oficina (parte)", 4000, iva_rate="0.16") | |
| lg.record_expense("2024-05-22", "Comida no deducible", 600, iva_rate="0.16", | |
| deductible=False) | |
| return lg | |
| def main(): | |
| ctx = ToolContext(seed_may()) | |
| tools = build_tools(ctx) | |
| user = ("Registra un ingreso de 27000 por un sitio web el 17 de mayo de 2024, " | |
| "con retención de ISR de 2700 e IVA de 2880. Luego dime qué régimen me " | |
| "conviene y cuánto IVA debo en mayo.") | |
| # What the fine-tuned model is expected to do, replayed deterministically: | |
| client = ScriptedClient([ | |
| AssistantTurn(tool_calls=[ToolCall("record_income", { | |
| "date": "2024-05-17", "description": "Sitio web — Dental MX", | |
| "amount": 27000, "iva_rate": 0.16, | |
| "isr_retenido": 2700, "iva_retenido": 2880})]), | |
| AssistantTurn(tool_calls=[ | |
| ToolCall("compare_regimes", {"year": 2024, "month": 5}), | |
| ToolCall("compute_iva", {"year": 2024, "month": 5}), | |
| ]), | |
| AssistantTurn(text=( | |
| "Listo, registré el ingreso de $27,000.\n" | |
| "• Régimen: te conviene RESICO — pagarías $495.00 de ISR vs $6,994.74 en " | |
| "el régimen general (fuente: tarifas SAT 2024).\n" | |
| "• IVA de mayo: $3,472.00 a cargo (IVA cobrado $7,200 − acreditable $848 − " | |
| "retenido por clientes $2,880; LIVA Art. 5-D).\n" | |
| "Recuerda: la elección de régimen es anual y tiene requisitos — confírmalo " | |
| "con tu contador.")), | |
| ]) | |
| agent = Agent(client, tools) | |
| trace = agent.run(user) | |
| print("USER:\n ", user, "\n") | |
| print("TOOL CALLS:") | |
| for s in trace.steps: | |
| flag = "ok" if s.ok else "ERR" | |
| head = s.result.get("amount") or s.result.get("recommended") or s.result.get("status") or "" | |
| print(f" [{flag}] {s.tool}({s.arguments}) -> {head}") | |
| print("\nFINAL ANSWER:\n", trace.final_answer) | |
| out = Path("data/sample/trace_example.json") | |
| out.write_text(trace.to_json(), encoding="utf-8") | |
| print(f"\nTrace saved → {out} ({len(trace.steps)} tool calls)") | |
| ctx.ledger.close() | |
| if __name__ == "__main__": | |
| main() | |