AI · 2026
ReguLens
- Year
- 2026 – now
- Scope
- AI · Hackathon
- Role
- Full-stack Engineer
- Team
- Team of 2
- Stack
- Next.js, React, TypeScript, Tailwind CSS
- Status
- Ongoing
- Links
Autonomous regulatory monitoring for small food and beverage exporters.
Instead of a regulation chatbot, ReguLens builds a compliance twin of a product and continuously reconciles it against incoming regulatory clauses. When a rule changes it flips the product's status on its own, names the regulation responsible, and quotes the clause.
A small exporter has to satisfy several jurisdictions at once for a single product, and the binding requirements are scattered across government PDFs, industry association announcements, screenshots, and forwarded chat messages; all of which change without notice. The real question is never what a document says. It is which requirements bind this product in this destination country right now, and whether anything has just changed or contradicts what the exporter already believes.
Read moreShow less
Getting that wrong is expensive: a rejected shipment, a destroyed batch, or blocked customs clearance. The divergence is concrete and checkable: the EU caps benzoates at 150 mg/kg for food category 14.1.4, while Indonesia's BPOM allows 400 to 900 mg/kg depending on category. A product formulated between the two is compliant at home and non-compliant at its destination, and nothing in an exporter's workflow says so.
ReguLens answers that without being asked. The production stack found Commission Regulation (EU) 2023/2108 on its own in the EU's CELLAR catalogue, read it into 88 verbatim limits, and failed a curing sausage on a nitrite line of 30 mg/kg that took effect in October 2025. Nobody uploaded it.
The system runs four agents, and the document is deliberate about which are genuinely agentic. Extraction is a pipeline with one LLM step. Reconciliation lets code decide while the agent only advises on genuinely ambiguous pairs. Query is the one that truly chooses its own retrieval tools. Impact makes no model calls at all: it is pure arithmetic turning limits and measured quantities into pass or fail.
Built for the All Things Agentic Hackathon on the Collaborative Partner track, which required Google ADK, Gemini 3.5 or newer, and operation outside the standard chat loop.
A new rule triggers a new product verdict automatically.
Impact
618
Automated tests
Green from a clean checkout with no GCP account and no cost
25.5s
Upload to alert, single pasted rule
Measured on the deployed stack
174.3s
Upload to alert, 55-clause EU annex extract
Extraction accounts for roughly 72% of it, bound by output tokens
6/6
Country discovery accuracy
Regulator names and domain roots both correct six out of six; every model-written path was wrong, which is why paths are read from fetched pages
88
Self-discovered regulation
Verbatim limits read out of Commission Regulation (EU) 2023/2108, which the system found in CELLAR unprompted
Brief
Problem
Requirements binding a single exported product live across several jurisdictions and several kinds of source, all changing without notice. Small exporters have no compliance officer to track them, and the cost of missing a change is a rejected shipment or a destroyed batch.
Solution
Maintain a structured model of the product and reconcile it continuously against incoming clauses, so a change in a rule flips the product's status on its own, names the regulation that caused it, and quotes the source clause, with no user query involved.
My role
- 01
Worked across both sides of the stack (the FastAPI backend and the Next.js operations console) as one of two contributors, authoring 39 of 83 commits
- 02
Built and corrected the impact engine so a verdict names the rule it actually rests on and a requirement never outlives the clause it came from
- 03
Made alerts name and link the regulation that moved a verdict
- 04
Extended the guardrail to read food category, letting the review queue answer more cases without a human
- 05
Fixed query retrieval to target the market a question names rather than only its wording, and to stop treating a country as a jurisdiction
- 06
Reworked extraction to flush each PDF page instead of holding an entire document in memory
- 07
Laid out the web app as an operations console rather than a set of pages
Features
- 01
Compliance twin: a product with ingredients and quantities, targeted at one to three destination markets
- 02
Ingestion by PDF upload, pasted text, or a daily scheduled re-read of every watched address, no third path
- 03
Verbatim clause extraction with a composite confidence score, never a summary
- 04
Deterministic guardrail deciding whether two clauses may be compared at all
- 05
Reconciliation verdicts of created, superseded, conflict opened, or flagged for review, exactly one event per clause
- 06
Impact engine as pure arithmetic, with no agent and no model call
- 07
Immutable audit event written in the same batch as every state change
- 08
Alerts naming the regulation that moved a verdict and whether anyone uploaded it
- 09
Four kinds of watched source (document, feed, listing, and SPARQL catalogue) because a change means four different things
- 10
Country discovery that produces an index address to watch, then stops
- 11
Query agent that picks its own retrieval tools and refuses to answer without citing a stored clause
- 12
Readiness reported as a count of issues rather than a percentage
Architecture
One container image across four Cloud Run services, with everything slow placed behind Pub/Sub: the API hashes, stores, publishes, and returns 202. Three topics carry the pipeline: document uploaded triggers extraction, clause extracted triggers reconciliation, graph changed triggers impact, and any topic dead-letters after maximum retries. Deterministic code owns every mutation; a model response never reaches clauses, requirements, or conflicts without passing a Pydantic validator and the guardrail. The core package imports neither FastAPI nor ADK, so every tool body is an ordinary function testable without a framework.
- Front end
- Next.js, React, TypeScript, Tailwind CSS
- Back end
- Python, FastAPI, Google ADK, Pydantic
- Data
- Firestore, Cloud Storage
- Infrastructure
- Google Cloud Run, Pub/Sub, Cloud Scheduler, Cloud Build, Artifact Registry, Secret Manager
- Tools
- Docker, Make
- Integrations
- Gemini 3.5 Flash, Gemma, Vertex AI, EUR-Lex CELLAR, BPOM
Challenges
- 01
Problem
A system that confidently declares a false conflict destroys the trust the whole product rests on, and an LLM judge asked to compare two clauses will always produce an answer.
Solution
A deterministic guardrail decides whether two clauses may be compared at all (substance family, product type, unit, jurisdiction) before any model is consulted. Different jurisdictions with different limits resolve to a conflict in code. The LLM judge runs only on same-jurisdiction pairs whose effective dates leave the supersede question open.
- 02
Problem
Country discovery needed regulator URLs, but the model's URL paths were wrong every single time; measured at six regulator names correct out of six and six domain roots correct out of six, while every path it wrote was wrong.
Solution
Asked the model only what it can actually do (name the regulator and its domain root) then read paths from pages actually fetched. The model picks an index from a supplied link inventory, and any choice outside that list is discarded.
- 03
Problem
Content hashing on raw bytes never matched, because EUR-Lex stamps a fresh session id into every response; the same document looked new on every fetch.
Solution
Hashed the extracted text rather than the bytes, so the cache short-circuits genuinely identical documents that have already reached a terminal state.
- 04
Problem
The hackathon required Gemini 3.5 or newer, but the asia-southeast1 region only offered gemini-2.5-flash, using the nearest region would have broken a hard entry rule.
Solution
Split the topology: infrastructure stays in asia-southeast1 while Gemini is reached through the global endpoint, with multilingual embeddings alongside it.
- 05
Problem
Pub/Sub delivers at least once and will redeliver, so a naive pipeline produces duplicate clauses and duplicate alerts for a single document.
Solution
Made every handler idempotent through a state check, with redelivery tested explicitly rather than assumed, and Firestore transactions around each clause mutation so parallel reconciliation cannot race on a shared clause.
- 06
Problem
A requirement generated from a clause kept applying after that clause had been superseded, so a product could fail against a rule that no longer existed.
Solution
Retired requirements whose source clause was superseded at every materialization, and made each alert name the rule its verdict actually rests on rather than the one that started the run.
Lessons
- 01
An honest needs_review beats a confident wrong answer. Low confidence or low authority queues a rule for a human and never quietly shifts a limit.
- 02
Readiness is shown as a count of issues rather than a percentage, because a percentage needs a denominator nobody actually has.
- 03
Being precise about which parts are genuinely agentic mattered more than the number of agents; one of the four makes no model calls at all.
- 04
A flag that runs the entire pipeline with no model calls kept the local stack and most of the test suite free to run.



