Gyovana Santos do Prado

Gyovana Santos do Prado

Data & AI Engineer — Human in the Loop

São Paulo, Brazil

Data & AI Engineer with 9 years in tech, focused on building scalable data pipelines and designing AI systems where human judgment stays in the loop. Specialist in dbt, BigQuery, and GCP, with hands-on experience building Claude-based solutions — custom skills, MCP servers, and API integrations — for enterprise clients. Comfortable with both technical and business stakeholders, and recognized for leading internal training and data democratization.

6 repos0 followers0 stars

Now

updated Jul 13, 2026

Feed

  1. Shipped

    Delivered the first milestone of an LLM-powered insights generation API built for an enterprise client. The system takes structured survey data, atomizes it into grounded facts, and produces layered briefings — with a guardrail that enforces every claim traces back to a source. Validated on real survey data: 29 insights generated with 100% grounding correctness and zero guardrail failures.

    Outcome: 29 insights generated on real survey data; 100% grounding correctness; 0 guardrail failures.

    Technical detail

    Built in FastAPI with Pydantic AI for parsing. Core pipeline: fact extraction → multi-pass atomization → document generation (H3 pattern). Grounding guardrail rejects any claim without direct record provenance. Multi-provider architecture (OpenAI and Anthropic validated interchangeably via model-as-config). DAG-aware build sequencing: parallel cards where dependencies allowed, sequential where schemas needed to stabilize first. Empirically closed one architectural question — on-demand fact calculation was scoped out after zero occurrences appeared in real data runs, avoiding premature abstraction. Six stacked PRs structured for clean merge sequence.

    fastapillm-pipelinepydantic-aigroundinginsights-generation
  2. Decision

    When asked to extract structured insights from thousands of open-ended survey responses about a consumer mobile app, the first question wasn't which model to use — it was which extraction *strategy* to use. Three approaches were benchmarked end-to-end: a two-stage atomic-claims pipeline, a single-pass document approach, and a hybrid with a pre-draft refinement step. The winning strategy wasn't the most sophisticated one.

    Outcome: H3 selected as default extraction strategy: lowest cost ($0.0216/doc), zero altitude violations, 100% quote grounding across all test documents.

    Technical detail

    Spike comparing three LLM extraction flows for qualitative open-ended survey data using Pydantic AI and Jinja-templated prompts (via ai-prompter):

    • H3 (two-stage: facts → atomic claims → synthesis): $0.0216/doc, 0 altitude violations, 100% quote grounding
    • H1+separate-doc: $0.0296/doc, 1 altitude violation, 71% fact-family coverage
    • H3+predraft (H3 + refinement draft): $0.0348/doc, 3 altitude violations, 60% more claims but noisier

    Key trade-off: H3+predraft generated 60% more claims than H3 but introduced 3 'altitude violations' (over-generalized statements unsupported by the data). H1 had better fact coverage (71% vs 57% of families) but higher cost and one violation. H3 won on every weighted criterion.

    Pipeline also includes a deterministic PII guard (emails, phones, CPF, social handles) running before any LLM call — 20% of real responses contained PII, making this non-optional. A quote_grounding validator confirms every generated claim traces back to a verbatim quote, making the pipeline fully auditable. Fact index covers 67 facts across 7 families (boolean, categorical, cooccurrence, cross_tab, matrix, multi_select, persona).

    llmpydantic-aiprompt-engineeringqualitative-analysisdata-pipeline
  3. Shipped

    Built a second LLM-powered parser for a survey analytics platform — this one converts a plain-text analyst description of audience personas into a validated structured spec. The interesting constraint: the downstream clustering factory checks for feature configuration using Python's `'key' in dict` (key presence), not `dict.get('key')` (value check). That distinction forced a non-obvious model design choice.

    Outcome: 16 tests passing (2 skipped pending API key), ruff clean — parser, models, route, and test suite shipped as one cohesive unit.

    Technical detail

    The parser uses Pydantic AI with output_type=PersonaSpec, and PersonaSpec contains PersonaVariable objects with optional order and scale fields. The design choice: both fields use Optional[list] = None rather than list[str] = []. Reason: the downstream factory calls model_dump(exclude_none=True) and then checks 'order' in var — an empty list [] would be falsy in a boolean check but truthy for in, silently breaking ordinal variable handling. With None defaults, exclude_none=True removes the keys entirely, and 'order' in var correctly returns False for categorical variables. The test suite uses Pydantic AI's TestModel(custom_output_args={...}) to override the LLM with deterministic structured output — no API calls needed in CI. One test (test_parse_persona_spec_model_dump_exclui_nulos) explicitly documents the key-absence invariant. Stack: Pydantic AI, FastAPI, Pydantic v2, pytest.

    pydantic-aillm-parserspythonfastapiapi-design
  4. Problem solved

    Wrote a reversible database schema migration for a new data model milestone. Along the way, discovered that sharing a SurrealDB namespace between two local projects caused silent migration tracker corruption — the migration history table accumulated ghost entries from a different project, making rollback fail. Isolated the root cause, cleaned the state manually, and documented the isolation pattern.

    Outcome: Migration applied and rolled back cleanly: ruff clean, 10/10 tests passing, all field types verified via INFO FOR TABLE.

    Technical detail

    SurrealDB v3, Python, uv. The migration adds new SCHEMAFULL tables and re-types several existing fields. Key constraint: re-typing a field on a pre-existing SCHEMAFULL table requires DEFINE FIELD OVERWRITE — a plain DEFINE FIELD silently no-ops or errors depending on SurrealDB version. The down migration reverses in strict dependency order and explicitly removes array sub-fields to avoid residual ghost sub-fields after table cleanup.

    Environment bug: two projects sharing the same SurrealDB namespace/database made their migration tracking tables collide. The other project's runner had inserted rows into the shared table; when rolling back, the runner couldn't find the corresponding migration files in the current project's directory and aborted. Immediate fix: manual deletion of the contaminating rows. Long-term fix: each project gets its own namespace/database pair in local dev.

    surrealdbschema-migrationdatabasedev-environment
  5. Decision

    Designing an API that turns survey data into business insights forced a core question: how do you make AI-generated findings trustworthy enough to act on? Instead of debating in the abstract, we measured first — then decided. The answer landed on two independent verification strategies that keep the AI accountable to the source data at every step, without adding embedding infrastructure or discarding the runs that fail.

    Technical detail

    Architecture session for an async FastAPI service (Python / Pydantic AI / SurrealDB) that processes survey data into structured, auditable insights — replacing a legacy claude -p shell pipeline.

    Measure before decide: Before committing to a retrieval/selection pipeline, wrote a scratch script querying the live SurrealDB instance to measure the actual persisted fact space. Result: 12–14 k tokens (6% of context window). No pipeline was justified until that symptom appeared in production — a concrete number closed a debate that would otherwise have been speculative.

    Hybrid deterministic/LLM regime: Split the insight core into two invariant regimes:

    • Closed questions (Likert, NPS, multiple-choice): pure Python, fully deterministic, golden-testable — LLM never touches this path.
    • Open-ended / qualitative: LLM identifies themes and frequency patterns; a deterministic quote-grounding check (verbatim substring match against the source CSV) validates every claim before persistence. Hallucination caught without embeddings — zero cost, fully auditable. The invariant: every qualitative fact must cite a traceable source fragment.

    Guardrail failure as eval signal: Pydantic AI output validator retries once on failure; persistent failures are stored with a guardrail_failed flag rather than discarded. Rationale: in early production runs, failure rate is the most useful quality signal — silently discarding failed outputs hides exactly what you need to learn.

    Fact space recomputed per run: Each run rebuilds its own fact space from source specs and persists it independently. Trade-off: redundant computation vs. full reproducibility — any run can be re-audited in isolation regardless of future schema or data changes.

    architecturepydantic-aillm-guardrailssurvey-analysisapi-design
  6. ShippedPart of Living Portfolio

    Shipped the engine behind this very site: a bilingual portfolio whose content is curated automatically from real work sessions. A session-end hook evaluates each Claude Code session against a relevance rubric; when something clears the bar, it is written up in two languages, sanitized, and committed as a draft that publishes with one command.

    Outcome: The site you are reading was produced by this pipeline.

    Technical detail

    Three components: an Astro static site (i18n EN/PT, feed/projects/CV rendered from JSON at build time), a Python MCP server exposing five tools (log_activity, review_and_publish, add_project, update_now, get_portfolio_state) that write through the GitHub Contents API, and a Stop hook that embeds the rubric verbatim so criteria changes require no code changes. Validation runs three layers deep: JSON Schema, bilingual completeness (a missing translation fails the build, by design), and a local denylist as a deterministic backstop behind the LLM's category-based sanitization.

    astromcppythonclaude-codei18n
  7. LearningPart of Living Portfolio

    First real-world use of CSS scroll-driven animations: this feed's timeline draws itself as you scroll, with zero JavaScript. The motion concept for the site — pulse, trace, breath — had to communicate 'alive' without a single animation library.

    Outcome: Feed timeline draws on scroll in ~20 lines of CSS; zero JS, zero libraries.

    Technical detail

    animation-timeline: view() maps an animation's progress to an element's position in the viewport — the timeline border scales from 0 to full height as each entry crosses it. Wrapped in @supports so unsupported browsers just see the static border, and disabled under prefers-reduced-motion. The rest of the motion system is a ~30-line inline IntersectionObserver (staggered reveal) gated behind an html.js class, so a JavaScript failure can never leave content hidden.

    cssscroll-driven-animationsprogressive-enhancement
  8. Problem solvedPart of Rotas-SP — transit routing that thinks past the obvious

    Needed AI-powered route re-ranking for a personal transit app but had no API key — only a Claude Code subscription. Built a zero-cost LLM integration by calling the Claude CLI in headless mode via subprocess. Hit a non-obvious bug: global session hooks were being inherited by the subprocess, injecting evaluation text into the JSON output and silently corrupting every response. Traced the corruption, found the root cause, and fixed it with a single flag.

    Outcome: M4 milestone complete: AI re-ranking working without an API key; 57 tests green, 6/6 ranking outputs valid.

    Technical detail
    • Stack: Python + subprocess, Claude Code CLI (claude -p --output-format json)
    • Added ROTAS_LLM_PROVIDER=claude-code provider alongside the existing API-based path, so both can coexist
    • Root cause: global Stop hooks (used for portfolio session evaluation) were inherited by the headless CLI child process, appending their evaluation output to the result field and breaking JSON parsing downstream
    • Fix: --settings '{"disableAllHooks": true}' passed to the subprocess invocation
    • JSON parser made tolerant of surrounding markdown prose as a defensive measure against future prompt drift
    • Trade-off accepted: ~95s per query vs 5-10s with a real API key — cost-zero outweighs latency for a personal prototype; UX shows a 'slow AI' warning
    • Acceptance criteria met: 6/6 itinerary IDs correct, justifications ≤280 chars, contextual re-ranking correctly inverted priority order and flagged a late-night street transfer as eliminatory
    llm-integrationsubprocesstransit-routingbug-fixzero-cost
  9. DecisionPart of Living Portfolio

    My portfolio needed to update itself without becoming a server to maintain. The decision: no CMS, no database, no backend — content lives as JSON in a git repository, every update is a commit, and the site is a static build triggered by pushes. A drafts branch acts as a mandatory buffer: automation can write, but nothing goes public without a human merge.

    Outcome: Zero infrastructure to operate; every content change is auditable in git history.

    Technical detail

    A competent peer might reasonably have chosen a headless CMS or a small API. Git-as-database won on three fronts: full history and rollback for free, zero infrastructure cost, and a deploy pipeline (push → rebuild) that static hosts provide out of the box. The drafts branch exists for confidentiality, not relevance — client work must never auto-publish without a human glance. Trade-off accepted: no real-time updates, which a portfolio doesn't need.

    architecturestatic-sitegitastro

Projects

Living Portfolio

Jul 2026 – present

A portfolio that updates itself: a static Astro site whose content is curated automatically from real work sessions through an MCP publishing pipeline.

Case study

The problem: personal sites go stale because keeping them updated is a chore. The idea: a session-end hook evaluates each Claude Code session against a relevance rubric; when the work is portfolio-worthy, an LLM writes it up in two layers (narrative + technical) and two languages (EN/PT), sanitizes client-confidential details by category, and commits it to a drafts branch. The owner publishes with one command. The site is a static build — 'aliveness' comes from the content pipeline, not a running server. Git is the database: every change is a commit, giving full history and zero infra cost.

astrotypescriptpythonmcpclaude-codegithub-actions

Rotas-SP — transit routing that thinks past the obvious

Jul 2026 – present

A public-transit route planner for São Paulo that generates structurally different alternatives (walk to a terminal, take the farther station), classifies how safe each transfer is, and uses an LLM to rank and explain them by the priority the user picks at query time.

Case study

The hard part isn't drawing a route — off-the-shelf APIs do that. It's surfacing the good alternative a normal app never shows. The engine runs a self-hosted OpenTripPlanner over São Paulo's GTFS/OSM data and does a parameter sweep: several routing queries with different cost functions (anti-walking, pro-walking-fewer-transfers, rail-only) whose union produces genuinely diverse candidates. A precomputed index classifies each transfer as terminal / station / street, and a deterministic profile-based scorer ranks them before an LLM re-ranks the top options and writes plain-language justifications — with a strict rule to only cite numbers present in the data. Spec-first: the whole system was documented before a line of code, so the implementation surfaced only small, well-isolated divergences (a regex bug, a latency budget that didn't survive the real graph — each measured and fixed). Phase 1 is a CLI with 74 tests; phase 2 is an API + PWA reusing the same engine.

pythonopentripplannergtfsosmdockertyperpydanticesperantoclaude-code

Critical Pipeline Optimization (88% Faster)

Jun 2023 – Sep 2023

A materialized table was timing out. Modularization and modeling best practices cut its execution time by up to 88%.

Case study

A critical materialized table had grown to the point of timing out. I broke the monolithic transformation into modular steps and applied dbt best practices, reducing execution time by up to 88% and removing the timeouts. I also used AI-assisted techniques (prompt engineering and in-context learning) to speed up the investigation and validate candidate refactors.

dbtbigquerysqlprompt-engineering

Multi-Outcome Journey Modeling in dbt

Jan 2023 – Jun 2023

Redesigned a data model whose original shape couldn't represent multiple outcomes per user journey, using data-warehouse modeling concepts to make it concise and scalable.

Case study

The existing model assumed a single outcome per journey, which broke down as the business needed to track several. I identified the structural limitation, then proposed and implemented a restructured dbt model aligned with the business rules — trading a rigid, hard-to-extend shape for one that stays concise as new outcomes are added.

dbtbigquerysql

Experience

Data & AI Engineer (Human in the Loop) · Supernova Labs

Feb 2026 – present

Remote

Design and operate data and AI pipelines on GCP, building Claude-based tooling that lets models operate with each client's knowledge, rules, and context while keeping human judgment in the loop.

  • Design and operate data/AI pipelines on GCP (Cloud Functions, BigQuery), integrating external APIs to feed structured context to LLMs and process outputs before final consumption.
  • Build reusable Claude skills and custom MCP (Model Context Protocol) servers for Claude Code, Claude API, and Claude Enterprise.
  • Delivered an AI-powered analytics pipeline for a large enterprise fitness client that reads survey responses at scale, with human validation and refinement kept in the loop.
  • Curate prompts and context to ensure consistent, production-grade LLM outputs, and translate business needs into architectures where AI accelerates delivery without replacing human judgment.

Data Engineer (Mid-Level) · UOL EdTech

Oct 2022 – Jan 2026

São Paulo, Brazil

Built scalable pipelines with dbt Cloud, BigQuery, and Looker, and led data-modeling and performance work alongside data democratization initiatives.

  • Restructured data models to support multiple outcomes per user journey.
  • Optimized a critical table, reducing execution time by 88%.
  • Contributed to RFCs, solution reviews, and stakeholder interviews.
  • Onboarded junior and mid-level hires and created an internal knowledge-sharing channel for data democratization.

Data Engineer (Mid-Level) · Tenbu

Dec 2021 – Jul 2022

São Paulo, Brazil

Developed ETL pipelines and analytical solutions, supporting squads from requirements gathering to delivery.

  • Developed ETL pipelines using IBM DataStage and designed Control-M workflows.
  • Built dashboards in Power BI and supported squads with requirements gathering and delivery.

Associate Application Developer & BTP Intern · IBM Brasil

Aug 2019 – May 2021

São Paulo, Brazil

Developed and maintained ETL jobs and supported BI reporting in an enterprise environment.

  • Developed and maintained ETL jobs with IBM DataStage.
  • Produced technical documentation, managed OLAP cubes, and supported reporting with IBM Cognos.

Skills

Languages & Frameworks
SQLPythonJinja
Data & Cloud
dbt CloudBigQueryGCPCloud FunctionsGitHub Actions
AI & LLM Engineering
Claude APIClaude CodeClaude EnterpriseMCPClaude SkillsPrompt Engineering
BI & Visualization
LookerPower BIIBM Cognos
ETL & Orchestration
dbtIBM DataStageControl-MPrefect

Education

Postgraduate in Data Science and Artificial Intelligence · PUC-RSNov 2023 – present
Associate Degree in Systems Analysis and Development · UNASPApr 2018 – Jun 2020
High School with Technical Degree in IT · ETEC Euro Albino de SouzaFeb 2015 – Dec 2018

Certifications

dbt Learning Path · dbt LabsMay 2025
dbt Fundamentals · dbt LabsMay 2025
Refactoring SQL to Modularity · dbt LabsMay 2025
Big Data Foundations · IBMNov 2019
Python for Data Science · IBMOct 2019

Languages

PortugueseNative
EnglishAdvanced

Awards

1st place — ABStartups Hackathon (university challenge focused on innovative, market-driven solutions) · ABStartups2019