← Back to projects

Pillar project / 01

BOC Allocation Review Agent

Offline-first accounting review assistant combining deterministic rules, human review workflows, local RAG, and an ADK-inspired runtime.

Pillar project7 min read
BOC Allocation Review Agent Streamlit dashboard
Streamlit dashboard

At a glance

Project overview

Case study content

The problem: accounting review doesn’t scale well on spreadsheets

Accounting review often starts with a workbook. On the surface, ledger rows look repetitive — but each transaction can carry review risk across multiple dimensions: location of expenditure, eligibility for tax credit, allocation category, province, vendor identity, description clarity, and whether supporting documentation exists.

Manual review of this kind is slow, hard to audit, and easy to apply inconsistently when the same business rules have to be interpreted fresh across hundreds of rows. The real problem isn’t that reviewers make mistakes — it’s that the process gives them no structured scaffolding to work with. No automatic pre-classification, no separation of clear cases from exceptions, no queue of what actually needs a human decision.

The question this project tried to answer was: could a data workflow handle the structured, rule-based part of that process — and route only the genuinely uncertain cases to a human reviewer?

Role: sole builder, from data model to deployment docs

This was a solo project submitted to the Kaggle 5-Day AI Agents Intensive Vibe Coding Capstone with Google, under the Agents for Business track. Every part of the system was designed and built independently:

  • Designed the overall data workflow and system architecture
  • Defined and implemented the deterministic business rule engine
  • Built the ingestion, validation, cleaning, and transformation pipeline
  • Designed the Human-in-the-Loop review queue and reviewer decision recording
  • Built the Streamlit dashboard and export functionality
  • Implemented the local TF-IDF RAG layer for document and workflow questions
  • Designed the ADK-inspired runtime (Planner, Executor, Tool Registry, RuntimeTrace)
  • Wrote the test suite (307 automated tests) and documentation
  • Wrote the migration blueprint for eventual native ADK adoption
  • Prepared Docker packaging and Cloud Run readiness documentation

The process: a data workflow first, an agent second

The approach that shaped everything else was a deliberate sequencing decision: treat this as a data workflow problem before treating it as an AI problem.

That meant the first thing designed was not the agent or the LLM layer — it was the business rules. What makes a transaction eligible? What triggers an allocation flag? What combination of location code and province puts a transaction in the Quebec review queue? Those rules were encoded as deterministic logic in allocation_tool.py and kept frozen throughout the project. No documentation update, no portfolio polish, no architectural change was ever allowed to silently modify the allocation logic. Every phase explicitly re-verified that the business rules remained unchanged — because presentation improvements should never accidentally alter the thing the system actually does.

Once the rule engine was stable, the rest of the workflow assembled around it: ingestion and schema validation, cleaning and transformation, classification and eligibility evaluation, dashboard metrics, Human-in-the-Loop queue, export, and finally the conversational assistant layer powered by local TF-IDF RAG.

The ADK-inspired runtime was built to organize this workflow without using native Google ADK. Agent-like components (Planner, Executor, Tool Registry), RuntimeTrace records for internal observability, and conversational orchestration were all implemented locally. A complete migration blueprint was later written explaining how this architecture could eventually move to native Google ADK — carefully written to explain the path forward without claiming the integration already existed.

What the system does

  • Loads a synthetic GL workbook with 201 transactions
  • Validates the input schema before processing anything downstream
  • Cleans and transforms transaction rows into review-ready records
  • Classifies each transaction through deterministic rules
  • Evaluates eligibility and review risk
  • Suggests allocation outcomes where the rules are unambiguous
  • Routes uncertain or risky cases to a Human-in-the-Loop queue
  • Lets a reviewer record decisions for queued cases
  • Produces a reviewed workbook and exportable review queue
  • Calculates dashboard metrics that separate clear cases from exceptions
  • Provides a read-only conversational assistant for project documentation and workflow questions
  • Includes Docker packaging and Cloud Run readiness documentation
  • Includes a native ADK migration blueprint

Results on the demo dataset

On the synthetic demo workbook submitted for capstone verification:

  • 201 transactions processed
  • 113 auto-approved
  • 88 sent to human review
  • 18 ineligible costs identified
  • 70 eligibility review cases flagged
  • 10 out-of-Canada cases flagged
  • 33 Quebec review cases flagged
  • 307 automated tests passing

These numbers describe the demo dataset and test suite — not a universal accounting benchmark or a production performance claim.

Engineering reflections: what actually took the most time

The hardest parts of this project were not the Python logic or the data pipeline. They were documentation quality and repository integrity — and the engineering problems those turned out to contain.

On keeping AI out of accounting decisions. The most deliberate architectural choice was to never let an LLM decide accounting allocation or tax eligibility outcomes. Those decisions need to be explainable and reproducible: a reviewer has to be able to see exactly why a transaction was classified a certain way, trace it back to a specific rule, and trust that running the same input again produces the same output. LLM outputs don’t reliably provide that. So the rule engine stayed deterministic, and the AI layer was limited to organizing the workflow, generating explanations, and handling document retrieval — not making the calls that matter.

On building an ADK-inspired runtime without native ADK. Designing agent-like components locally — Planner, Executor, Tool Registry, RuntimeTrace — was technically interesting, but the harder part was documenting it honestly. The architecture takes clear inspiration from ADK patterns without implementing the actual Google ADK runtime. That distinction had to be written carefully everywhere: in the README, in the architecture section, in the migration blueprint. Getting that language precise without either underselling the design or overclaiming the implementation took more revision than expected.

On false claim detection as a text-processing problem. The automated test suite eventually included validators that checked documentation for misleading claims — false deployment statements, incorrect ADK or Vertex AI implementation claims, stale test counts, broken links. The first versions of these detectors were too coarse: a sentence like “Cloud Run deployment readiness is documented, but the app is deployed to Cloud Run” would pass because safe words elsewhere in the sentence suppressed the unsafe claim. The detectors had to be redesigned to work at sentence level and match level — normalize text, split into sentences, detect unsafe phrases, then verify that each match is directly covered by a qualifying safe phrase rather than allowing unrelated safe wording to cancel a real problem. What started as a documentation hygiene task became a genuine text-processing engineering problem.

On separation of concerns under pressure. Every late-phase review cycle meant resisting the temptation to “just fix one small thing” in the business logic while also updating documentation. Keeping allocation_tool.py explicitly frozen and re-verifying it after every phase reinforced something worth carrying forward: in any system where the rules are the source of truth, the discipline of not touching them is as important as writing them correctly in the first place.

By the end of the project, the clearest lesson was that building a trustworthy data system — one a reviewer can actually rely on — requires as much engineering effort in testing, traceability, documentation, and honest communication as it does in the logic itself.

Limitations

  • Uses synthetic data only
  • Does not provide official tax, legal, or compliance advice
  • Does not connect to live government databases
  • Does not generate official CAVCO Form 6 filings
  • Does not implement native Google ADK, Vertex AI, or Gemini runtime integration
  • Cloud Run-ready by documentation and packaging; not claimed as a live production deployment
  • Supports review workflows and human decision-making; does not replace accountants or auditors

If I did it again

  • Start the test suite and documentation validators earlier in the process, not as a final-phase activity
  • Explore lightweight sentence-level classification for the false-claim detectors instead of hand-coded phrase matching
  • Prototype the Human-in-the-Loop queue UX with a real reviewer before finalizing the interface
  • Build the ADK migration blueprint in parallel with the local runtime rather than retrospectively

Start a conversation

Have a question worth exploring?

I’m open to data roles, thoughtful collaborations, and conversations about the work behind this case study.

Get in touch