Pular para o conteúdo

A camada de contexto de dados para agents — Yoni Michael e Brandon Callender, Typedef

Ver no idioma original →

Autoria: AI Engineer · Assistir no YouTube

Vídeo de autoria do canal indicado, não da IANIA; todos os direitos pertencem ao criador. Transcrição, resumo e tradução gerados automaticamente; o conteúdo no idioma original é preservado na versão em idioma original.

Idioma original: inglês

Vídeo original: The Data Context Layer for Agents — Yoni Michael & Brandon Callender, Typedef · Canal: AI Engineer · 1:45:49 · Assistir no YouTube

Tópicos: Agentes de IA, Avaliação e benchmarks de IA, Dados, bancos de dados e embeddings, Engenharia de prompts e contexto

Yoni Michael apresenta a camada de contexto de dados da Typedef: uma representação em grafo que reúne estrutura, dependências, granularidade e semântica para ajudar agents a operar sistemas de dados com segurança. Brandon Callender conduz um workshop que aplica princípios semelhantes a um codebase Rust, extraindo símbolos e relações para criar ferramentas de busca e análise de impacto. A conversa destaca que contexto determinístico, evals rigorosos e atualização incremental são essenciais para tornar os agents mais confiáveis. Também mostra que criar boas ferramentas não basta: é preciso orientar e medir se o agent realmente as utiliza.

  • O principal limite dos agents em tarefas de dados pode ser a falta de contexto sobre o significado e as dependências dos dados, não a capacidade do modelo de escrever SQL.
  • A camada de contexto representa dados e relações em um grafo consultável, atualizado conforme o sistema muda.
  • Granularidade, segurança de joins, significado de métricas e dependências são fatos essenciais para avaliar mudanças em sistemas de dados.
  • Sempre que possível, extraia fatos de forma determinística; use LLMs para resolver ambiguidades, classificar e resumir.
  • A análise de impacto, as recomendações de joins e a detecção de mudanças semânticas podem ocorrer antes de uma alteração chegar à produção.
  • No workshop de código, a progressão vai de busca textual a símbolos resolvidos e, depois, a um grafo de relações navegável.
  • Ferramentas especializadas podem ser ignoradas se o agent preferir hábitos aprendidos, como pesquisar arquivos com grep; nomes e formatos familiares podem ajudar na adoção.
  • Evals devem priorizar critérios determinísticos, tarefas representativas e execuções repetidas, além de medir o uso das ferramentas.
  • A indexação incremental reduz o custo de manter grafos atualizados em codebases e arquiteturas de dados grandes.
  • Uma camada de contexto pode reunir informações de catálogos, sistemas de origem, pipelines e BI, apoiando tanto equipes técnicas quanto usuários de negócio.
Início Capítulo Resumo
00:13 A proposta da camada de contexto de dados Yoni Michael apresenta a Typedef e explica por que agents precisam de contexto especializado para operar sistemas de dados.
08:55 Os fatos que faltam ao agent A apresentação identifica granularidade, segurança de joins, significado de métricas e relações entre ativos como contexto essencial.
17:39 Análise de impacto antes da mudança O grafo permite rastrear dependências downstream e estimar o impacto de uma alteração antes que ela seja aplicada.
25:20 Do contexto à operação confiável Yoni resume os princípios da camada de contexto e passa a palavra a Brandon para o workshop prático.
32:29 Níveis de contexto para agents de código Brandon compara busca textual, recuperação de código e grafos navegáveis como formas de dar contexto a um coding agent.
39:30 Da resolução de símbolos ao grafo O workshop mostra como distinguir símbolos homônimos e relacioná-los com suas definições e referências.
46:03 Consultas ao grafo e significado semântico Brandon demonstra consultas em Cypher e apresenta o uso de LLMs para resumir símbolos de forma auditável.
52:59 Limitações e benchmarks em sistemas reais A conversa aborda linguagens dinâmicas, custos de atualização e avaliações de tarefas de engenharia de dados.
57:44 Por que o agent ignora ferramentas melhores Brandon explica que o agent pode preferir grep e busca de arquivos mesmo quando dispõe de ferramentas de grafo.
62:31 Como avaliar consistência e uso de ferramentas O workshop descreve agentes de comparação, perguntas com respostas verificáveis e métricas de consistência.
69:40 Orientação do agent e qualidade das evals Ajustar prompts e inspecionar traces ajuda a aumentar o uso das ferramentas e revelar falhas ocultas nas avaliações.
76:12 Catálogos, benchmarks e tarefas de dados A discussão explica como catálogos podem complementar grafos de contexto e como estruturar cenários de avaliação realistas.
82:13 Adoção, traces e rigor nas avaliações Brandon ressalta que avaliações devem medir a adoção das ferramentas, examinar traces e evitar conclusões baseadas apenas em placares.
88:01 Semântica auditável e camadas de análise A apresentação detalha como combinar estrutura determinística com perguntas semânticas pequenas e focadas.
95:05 Escala, indexação incremental e golden data A conversa trata de grafos maiores, atualização incremental e conjuntos de avaliação estáveis vinculados a snapshots.
102:05 Uma visão unificada da arquitetura de dados Os participantes discutem como conectar fontes, pipelines, BI e responsáveis para ajudar a diagnosticar e encaminhar problemas.

O modelo não é o gargalo. — 01:47

Armazene evidências, não conclusões. — 23:36

Demos ferramentas incríveis a ele, e mesmo assim ele executou grep. — 58:47

Uma ferramenta elegante que é usada um terço das vezes é apenas um terço de uma ferramenta. — 01:22:13

  • data context layer (conceito): Camada que reúne em uma representação consultável o contexto estrutural, semântico e relacional necessário para agents trabalharem com sistemas de dados.
  • Context Graph (conceito): Grafo de nós e relações que permite recuperar e percorrer dependências relevantes sem reconstruir todo o contexto a cada tarefa.
  • grain (conceito): Significado de cada linha de uma tabela, cuja alteração pode distorcer métricas e agregações downstream.
  • eval-driven development (conceito): Abordagem que usa avaliações repetíveis de tarefas reais para medir o desempenho, a consistência e o uso de ferramentas por agents.
  • Typedef (empresa): Empresa que desenvolve uma camada de contexto para agents que acessam e operam sistemas de dados.
  • Yoni Michael (pessoa): Co-fundador da Typedef que apresenta a motivação e a arquitetura da camada de contexto de dados.
  • Brandon Callender (pessoa): Engenheiro fundador da Typedef que conduz o workshop sobre grafos de contexto para codebases e evals.
  • Qdrant (ferramenta): Codebase usado no workshop para demonstrar a extração de símbolos e relações de um projeto Rust.
  • Neo4j (ferramenta): Banco de dados de grafos usado no workshop para armazenar e consultar relações entre símbolos.
  • fenic (ferramenta): Biblioteca open source da Typedef que combina operações tradicionais de dataframe com operações de LLM.

00:13 Vamos começar. Meu nome é Yonyi Michael. Sou um dos cofundadores de uma startup chamada Typede. Estamos aqui para conduzir este workshop junto com Brandon Calendarer, nosso engenheiro fundador. Temos um horário de duas horas. Vamos tentar manter tudo o mais interessante e divertido possível. Sei que é muito tentador agora. Todos vocês estão com os laptops abertos, e a Alemanha está perdendo por um a zero na partida da Copa do Mundo, mas prometemos tentar tornar tudo o mais divertido possível, para que vocês não sintam necessidade de assistir à transmissão. Então, o que vamos abordar

00:47 hoje é o seguinte: antes de entrarmos na parte divertida, de arregaçar as mangas e fazer um workshop mais técnico, quero explicar um pouco o conjunto de problemas que estamos tentando resolver aqui na Typedev, apresentar o cenário e dar contexto sobre conceitos e princípios que serão aplicados mais tarde no workshop conduzido por Brandon. A ideia principal desta apresentação é explicar o contexto por trás da Typed Defeaf. Estamos construindo a camada de confiança para que agentes de dados possam

01:17 acessar plataformas e sistemas de dados e realizar tarefas de alta complexidade. O título da apresentação é “A camada de contexto de dados”, uma peça central de infraestrutura que construímos para nos ajudar nessa missão. Muita coisa vem da ideia de que talvez vocês não estejam tão envolvidos com a área de dados, mas, quando tentamos colocar agentes de programação ou agentes de engenharia de software para trabalhar com dados, o ambiente é bem mais complexo e cheio de nuances. Há muitas coisas

01:47 em que precisamos pensar ao criar o harness adequado e fazer engenharia de contexto para que esses agentes sejam realmente eficazes. É disso que vamos falar. A conclusão principal é que o modelo não é o gargalo. As capacidades dos modelos vão continuar crescendo. Mas o que falta para que esses agentes sejam realmente eficazes é o contexto. É isso que chamamos de camada de contexto de dados. Certo, então esta é a aposta que estamos fazendo como startup chamada Typed Defeaf. Todos nós passamos, digamos, os últimos dois anos, em geral,

02:19 no setor, pensando em engenharia de contexto para agentes de programação, certo? Temos empresas como Anthropic, Codex e Cursor, nas quais é possível entender como mapear repositórios de forma eficaz, fazer recuperação de informações e projetar ferramentas para que esses agentes de programação sejam realmente eficazes. Onde estamos ficando para trás e começamos a perceber uma necessidade maior é que o mesmo precisa ser feito para agentes capazes de acessar e interagir com os dados. E é justamente nos dados que, se um agente estiver confiantemente errado — o que acontece com frequência —, ele pode acabar

02:53 custando muito dinheiro, certo? Porque muitas dessas métricas e cálculos que pedimos à análise com agentes acabam em apresentações para o conselho, por exemplo. Ou são métricas de um modelo ou pipelines que falham silenciosamente, mas têm funcionalidades, métricas e medidas muito, muito importantes que estão tentando calcular. A aposta central é que os agentes serão operadores centrais desses sistemas de dados. Para que sejam realmente eficazes, precisamos garantir que, ao executar ações de leitura e escrita, consigam fazê-lo com segurança e

03:26 precisão, e que seja possível confiar nesses resultados em escala. Isso só funciona de verdade se pudermos fornecer ao agente a camada de raciocínio, para que ele consiga entender o sistema de dados de ponta a ponta, assim como uma equipe de engenharia de dados ou de dados, que tem o conhecimento prático necessário para construir o sistema de ponta a ponta, também consegue raciocinar sobre ele. Esse é o objetivo da camada de contexto de dados. O que descobrimos ao longo de, digamos, um ano e meio trabalhando nessa área é que existem três áreas concretas em que os agentes de programação atuais

04:00 costumam falhar quando tentamos torná-los eficazes com dados. A primeira é a análise de impacto: essencialmente, o que quebra se eu fizer esta alteração? A próxima é a recomendação de junções: esta junção é realmente segura para fazer entre os dados do meu sistema? E, de modo geral, alterações seguras no código — isto é, alterações seguras na lógica de transformação. Essa edição ainda quer dizer a mesma coisa? Ela preserva o significado desta métrica ou a definição da métrica vai mudar por causa deste commit de dados e desta alteração que está sendo feita? Essas não são tarefas incomuns.

04:33 São tarefas e workflows do dia a dia das equipes de dados, enquanto tentam descobrir como fazer os dados corporativos funcionarem com IA. Se o seu agente não consegue raciocinar e executar essas tarefas com confiabilidade, é muito difícil confiar nele. Muitas vezes, é muito difícil até mesmo confiar nele para lhe dar acesso de leitura. E o que importa é conseguir torná-los operadores centrais eficazes dos nossos sistemas de dados, não apenas agentes do tipo pergunta e resposta. Então, esta é a lacuna, a lacuna central que

05:07 conseguimos definir e realmente descobrir com nosso trabalho aqui. O contexto de que os dados precisam está distribuído por muitos sistemas diferentes, certo? Seu data warehouse pode armazenar as linhas e os próprios dados. Seu código de transformação contém a lógica de negócios, que é muito importante. Sua ferramenta de BI armazena as métricas, as dimensões e algumas definições de negócio. E há também os documentos, os canais e as conversas do Slack, além do conhecimento tácito que muitas vezes fica na cabeça das pessoas, certo? Mas o agente, na verdade,

05:39 precisa de algo que conecte tudo isso. Ele precisa entender o que um modelo significa, como se relaciona com outros modelos, o que quebra se eu fizer determinada alteração e quais operações são seguras, certo? Nos próximos 15 minutos, enquanto explico isso, vamos usar um termo chamado grain. Para quem não trabalha com dados aqui na sala, do ponto de vista de uma equipe de dados, grain significa… Desculpem: significa o que cada linha da tabela realmente

06:14 significa. O que cada linha representa de fato? Se o grain for cliente por mês e eu acidentalmente o transformar em cliente por semana, todas as somas subsequentes podem ficar erradas, mesmo que o SQL compile e tudo seja executado corretamente, certo? É isso que quero dizer quando falo em mudança de grain. Vamos tornar isso um pouco mais concreto em termos de confiabilidade: o que significa, na prática, se alguém perguntar a um agente por que a contagem de linhas de um pipeline aumentou de repente e saltou

06:47 40% da noite para o dia? O agente pode escrever SQL válido. Ele executa o código. Pode gerar um gráfico bonito, mas, no fim, vai informar a causa raiz errada, a análise errada do que realmente deu errado. Ele diz: “Parece que chegaram mais dados”. Mas o que aconteceu, na verdade, foi que uma coluna upstream transformou uma junção um para um em uma junção um para muitos, certo? Isso significa que o grain mudou. E o agente literalmente não consegue identificar um pico real de dados e diferenciá-lo de

07:18 uma mudança real de grain, porque o grain não está nos próprios dados, certo? O agente pode executar a consulta, obter algumas linhas de exemplo, fazer uma amostragem dos dados e ler muito bem o SQL para entendê-lo, mas não sabe o que cada linha deveria representar e que agora ela significa algo completamente diferente. Certo? É por isso que SQL válido não basta. A semântica é muito, muito importante quando tentamos tornar um agente eficaz.

07:49 Então, pensando nisso, digamos que há duas soluções óbvias para o que você pode fazer nesse tipo de situação. E é por isso que nenhuma dessas soluções óbvias funciona, certo? Você pode dar o código ao agent, certo? Mas o problema é que, se você for e der a ele, digamos, seu projeto DBD ou seu projeto Airflow, ele vai reconstruir o contexto do zero a cada prompt, em todos os prompts. Isso significa que o resultado é probabilístico, fica limitado pela context window, o que nem sempre funciona muito bem, e, cada vez que você inicia uma nova sessão, precisa reconstruir

08:21 esse contexto do zero. Então, digamos: tudo bem, vamos tentar dar acesso ao banco de dados. Vou conectar meu MCP do Snowflake ou do Databricks a ele. Em vez disso, ele consegue ver as linhas e pode ver uma amostra dos dados, mas não entende de fato o que cada linha significa. Então, consegue ler os números, mas não necessariamente explicá-los, certo? E, assim, na primeira situação, ele consegue ler o código, mas não tem memória para persistir o conhecimento do contexto que você passou muito tempo construindo, indo e voltando, e a

08:55 outra situação é que ele vê os dados, mas não associa o significado por trás deles. Portanto, nenhum dos dois tem um modelo persistente de como o sistema realmente se comporta, e essa é a missão que nos propusemos a resolver. Então, vamos dar um passo atrás e identificar exatamente o que está faltando no contexto que você precisa fornecer ao agent para que ele consiga raciocinar com eficácia sobre essas mudanças complexas nos dados. Digamos que há quatro fatos que determinam se uma resposta está certa, e nenhum deles está realmente registrado em algum lugar que o agent possa ler ou inferir. A primeira coisa é o que uma linha

09:28 realmente representa em uma tabela. É o que chamamos de granularidade. A próxima coisa é saber se um join é seguro ou não. Depois, há o que uma métrica realmente significa e o que se relaciona com o quê. Certo? As dependências são muito importantes em sistemas de dados. E um agent consegue ler seu SQL sem problema. Mas não consegue ver essas quatro coisas com facilidade no momento da consulta, justamente quando é mais importante reunir o contexto exato de que precisa. Então, o objetivo do que fizemos foi ver como podemos tornar essas informações computáveis e consultáveis, para

10:00 que o agent consiga obter, na prática, apenas o contexto de que precisa, exatamente quando precisa, sem ter que reconstruí-lo em cada sessão. Então, os próximos slides falam sobre como fizemos isso. Vamos usar como exemplo a ideia de join. Esse é o segundo fato de que falamos no slide anterior, e é um bom exemplo porque ilustra com clareza por que a sintaxe, por si só, não basta para tornar o agent eficaz. Há um join, que chamamos de equijoin, acontecendo aqui, escrito de uma determinada maneira, mas

10:32 ele pode ter quatro resultados completamente diferentes, dependendo dos dados subjacentes, certo? Pode preservar suas linhas, buscar uma dimensão ou replicar silenciosamente cada linha quando passa de uma relação do tipo um para muitos. O mesmo vale para a cláusula BETWEEN, que aparece com frequência em SQL. Ela pode ser uma consulta segura do tipo “as of” ou uma dimensão de datas que, na verdade, explode a granularidade, certo? Então, o agent consegue ver essa sintaxe, mas não consegue entender qual desses cenários vai resultar dela. Isso não é realmente um problema de modelo ou de capacidade.

11:04 É um problema de contexto que estamos tentando solucionar para o agent. O quarto fato aqui — e esse é um dos que mais gosto de mostrar — é entender quais modelos e ativos de dados se relacionam entre si. Certo? Por exemplo, quais modelos pertencem ao domínio de receita e quais pertencem ao domínio de produto. Muitas vezes, isso está relacionado aos perfis das pessoas que tentam acessar ou executar os conjuntos de dados para acessar as métricas ou os números principais desses domínios. Hoje, muitas vezes, as equipes

11:38 organizam isso com base nos nomes das pastas e, muitas vezes, é conhecimento tribal entender quais modelos se relacionam com quais unidades de negócio. Mas, na verdade, é possível descobrir isso pela estrutura dos dados. Por exemplo, você pode observar como os modelos se conectam, analisando a linhagem de tabelas, a linhagem de colunas, as chaves compartilhadas e os nomes compartilhados. O sistema os agrupa automaticamente em domínios para você. Assim, quando você pergunta ao agent: “Quais modelos pertencem ao meu domínio de receita para a equipe de operações de receita?”

12:13 isso se torna uma consulta que o agent pode executar, e não uma pergunta que ele precisa fazer — sabe, que exigiria uma conversa de ida e volta com uma pessoa para entender quais modelos estão no mesmo domínio. Esse é basicamente o mesmo princípio de engenharia de contexto que vemos repetidas vezes: você pega algo que é conhecimento tribal, que está na cabeça de alguém ou em alguma documentação em outro lugar, tenta computá-lo e depois torná-lo consultável pelo agent. Então, como produzir esses fatos? E o princípio fundamental de que falamos bastante

12:47 é que você deve sempre ser o mais determinístico possível primeiro e só então usar LLMs quando estiver tentando resolver ambiguidades ou fazer coisas em que os modelos são realmente bons, certo? Assim, você consegue computar a granularidade e a linhagem diretamente do próprio SQL. O que fazemos é pegar toda a lógica de transformação e analisá-la até o nível mais detalhado, para entender de fato como os dados estão estruturados. Dessa forma, o agent não pode se desviar da implementação real, porque a base do grafo e o contexto que estamos

13:19 fornecendo a ele são derivados da própria implementação. E então você pode fazer coisas como verificar se os joins são seguros com base nos dados reais. Certo? A chave do join é realmente única? Isso é algo que você pode fazer criando perfis das tabelas, certo? Você pode criar perfis das tabelas que tem no seu data warehouse. Isso permite que o agent não precise adivinhar coisas como cardinalidade. E então você pode usar o LLM para as partes que precisam ser legíveis para as pessoas, certo? Se estiver tentando entender nomes de negócio, resumir a utilidade dos modelos ou resolver alguma ambiguidade das

13:52 duas primeiras camadas, é aí que o LLM pode realmente entrar em cena. Esses fatos são computados e depois verificados em relação aos seus dados, para que o LLM apenas dê nomes a esses ativos de dados no grafo que estamos construindo. Isso permite garantir que não nos desviemos da implementação principal. Essa separação importa porque mantém os fatos principais vinculados à implementação real, como comentamos, e aos próprios dados.

14:23 Tudo isso é muito bom. Onde fica? Nós o chamamos de camada de contexto de dados. No fim das contas, é um grafo, certo? Ele é computado uma vez e, a cada commit, é atualizado atomicamente, certo? Então, modificamos incrementalmente esse grafo de contexto à medida que as equipes de dados fazem mais commits de dados nele. Assim, ele sempre representa o estado mais recente dos dados empresariais em produção. E o motivo é que se trata de um grafo. O motivo para ser um grafo, e não apenas um amontoado de texto ou um conjunto robusto de

14:54 arquivos Markdown se deve ao padrão de acesso, certo? Assim, o agent consegue buscar exatamente o subgrafo de que precisa em uma única travessia. E isso evita coisas como encher o prompt ou o contexto, além de escalar muito além de 500 modelos, porque você não fica relendo o projeto toda vez que tenta montar a camada de contexto. Certo? Nos últimos anos, obviamente vimos um enorme ressurgimento dos bancos de dados de grafos. E eu sempre me perguntei, mesmo antes da IA, qual era a principal utilidade deles, mas acontece que os modelos e

15:26 a IA são muito, muito bons em buscar contexto e em escrever Cypher, em escrever uma consulta para ele e depois percorrê-lo. E a natureza dos ativos de dados é muito baseada em dependências. Então, eles se prestam muito bem à criação desses objetos tipados, nós e arestas nesse grafo. Ah. Oh,

15:56 vocês não conseguem ver os slides. >> Vocês podem apagar as luzes? Não sei. Não. Ah, certo. Está melhor. Um pouco melhor. Podemos diminuir mais um pouco. Desculpem, pessoal. Eu devia ter pensado no fundo preto e nas outras cores. Mas posso enviar a apresentação para vocês depois também.

16:33 Certo. Desculpem por isso. Então, vamos ver. Vocês conseguem ver, ou talvez não consigam, mas aqui há um exemplo de um subgrafo. E o que está sendo representado é que as arestas carregam muitas informações de proveniência, certo? As arestas definem diferentes tipos de relações entre os próprios ativos de dados. Temos, por exemplo, “é materializado por”, “tem uma definição semântica” e esses são todos nós que representam diferentes tipos de ativos de dados no próprio grafo. Então, haverá um nó que representa, por exemplo, uma visão semântica, e depois haverá um nó que representa uma tabela física, que é a representação física real do código lógico da própria tabela. Vou enviar isso para vocês depois, para que possam dar uma olhada mais de perto, ou vamos descobrir como disponibilizar isso para vocês. E, sim, isso é essencialmente a aparência de um subgrafo. Então, se voltarmos e

17:05 pensarmos nos casos de uso relevantes que o agent pode resolver agora graças a essa camada de contexto que temos, o primeiro é a análise de impacto, certo? O agent propõe uma alteração, por exemplo, para modificar a granularidade de um modelo. Antes de fazer isso, antes de escrever uma linha de SQL para realizar essa mudança, ele pode percorrer o grafo a jusante e perguntar a cada nó que é uma dependência a jusante do nó que está tentando alterar: isso realmente quebra a granularidade representada neste nó aqui? Assim, consegue detectar que uma medida mensal de AR seria repetida nas linhas semanais se eu tentasse mudar da granularidade mensal para a semanal, e que, a jusante, há algum ARR que está somando todos os diferentes valores mensais, o que resultaria, digamos, em uma métrica com um valor quatro vezes maior do que o valor correto. E então ele poderia chegar, por exemplo, a tudo que está a jusante, como um dashboard de nível executivo, porque o grafo de contexto abrange todo o seu sistema de dados. Tudo, desde os sistemas de origem até a camada de BI e a camada de dashboards. Então, o

17:39 agent tem visibilidade e consegue percorrer a linhagem por todo o ciclo de vida dos dados, por assim dizer. Então, o principal é que tudo isso acontece antes da execução, certo? Não acontece no momento da compilação. Não acontece durante um incidente em produção. Ele conhece o raio de impacto antes mesmo de agir. E o segundo caso de uso são as recomendações de junção, certo? Então, este é o segundo ponto. O exercício aqui é que o agent quer atribuir o ARR por canal de marketing. Ele não escolhe um caminho com base em nomes de colunas que consegue

18:14 entender; na verdade, verifica se cada chave de junção é única, e isso é feito porque criamos perfis das próprias tabelas. E isso é verificado com base nos dados. Assim, ele recomenda o caminho de junção realmente seguro para fazer isso, sinalizando os caminhos que causariam uma expansão dos registros, como um aumento de sete vezes nas linhas, ou aqueles que fariam cair silenciosamente 12% das linhas a jusante. Então, o principal aqui é que fazemos recomendações com raciocínio e evidências reais. E elas são fundamentadas nos próprios dados. E, por fim, mudanças seguras no código. Este

18:46 é o terceiro ponto. Muitas vezes, você encontra definições de negócio aninhadas no próprio SQL, certo? Então, se você pedir a um agent, por exemplo, para simplificar uma medida ou métrica que tem, e ele remover um predicado, isso vai funcionar sintaticamente, certo? O SQL ainda vai compilar. Mas o que pode acontecer é que, embora pareça inofensivo, o predicado era, na verdade, uma parte muito importante da própria definição de negócio. Neste exemplo, ele está removendo a definição de “pago”, redefinindo silenciosamente o que a métrica inclui para também incluir o cancelamento de usuários em período de avaliação, em três dashboards a jusante que representam essa métrica. Então, essa camada de contexto detecta essa alteração nas definições e métricas de negócio antes de ela ser implementada. E é assim que você faz com que o agent trate essas definições como restrições de primeira classe. Não é apenas um comentário em algum arquivo YAML ou algo assim. Esse é o ponto principal. E essa é a diferença entre uma refatoração sintática e

19:18 uma mudança em que uma pessoa realmente pode confiar. Tudo muito bom, mas será que funciona de verdade? Certo? E isso precisa ser medido. Sempre que você está criando algo agentic, precisa conseguir medir. Praticamos o desenvolvimento orientado a evals, que é uma parte muito importante disso. Basicamente, criamos vários casos de uso, tarefas e workflows reais de engenharia de dados em produção, com níveis de dificuldade que vão do fácil ao cego, e cada um é executado várias vezes em clones isolados do

19:51 ambiente de dados real, o que é muito importante para que não haja vazamento entre esses ambientes. Depois, temos verificações de aprovação ou reprovação que conferem o resultado real. Também usamos algum LLM as a judge para coisas como análise de impacto. Mas a principal conclusão é que, embora os modelos e as tarefas sejam os mesmos, a única variável que muda é se o agent tem acesso à camada de contexto de dados. E vemos um aumento substancial quando ele tem o contexto certo. Ele consegue raciocinar sobre essas mudanças realmente complexas, e a taxa de sucesso nas tarefas aumenta

20:26 tremendamente. Ela fica quase 35 pontos percentuais mais alta quando o agent conta com a camada de contexto de dados. E esse benchmark específico obviamente vai continuar crescendo e evoluindo à medida que trabalhamos com mais equipes para entender os casos de uso específicos delas. Mas o sinal é consistente, certo? Quanto melhor e mais rico o contexto que você consegue dar ao agent, mais o resultado muda, em comparação com simplesmente trocar os modelos ou adicionar mais ferramentas genéricas, como ferramentas MCP, ao próprio modelo.

20:59 uma mudança em que um humano realmente possa confiar. Então, tudo ótimo, mas isso funciona de verdade? Certo? E, portanto, é preciso medir isso. Sempre que você está criando algo agentic, precisa conseguir medir. Então, praticamos o desenvolvimento orientado por evals, que é uma parte muito importante disso. Basicamente, criamos vários casos de uso, tarefas e workflows reais de engenharia de dados em produção, que variam em dificuldade, de fáceis a impossíveis de antecipar, e cada um deles é executado várias vezes em clones isolados do

21:31 ambiente de dados real, o que é muito importante para que não haja vazamento entre esses ambientes. Depois, temos verificações de aprovação ou reprovação que conferem o resultado real. Também temos algum LLM as a judge fazendo coisas como análise de impacto. Mas a principal descoberta aqui é que, embora sejam os mesmos modelos e a mesma tarefa, a única variável que muda é se o agent tem acesso à camada de contexto de dados. E, assim, você vê uma melhora significativa quando ele tem o contexto certo. Ele consegue raciocinar sobre essas mudanças realmente complexas, e a taxa de sucesso das tarefas aumenta

22:01 tremendamente. Ela é quase 35 pontos mais alta quando o agent tem a camada de contexto de dados à disposição. E, obviamente, esse benchmark específico vai continuar crescendo e evoluindo à medida que trabalhamos com mais equipes para entender seus casos de uso específicos. Mas o sinal é consistente, certo? Quanto melhor o contexto — quanto mais rico o contexto que você consegue fornecer ao agent —, mais o resultado muda, em vez de, por exemplo, simplesmente trocar os modelos ou adicionar mais ferramentas genéricas, como ferramentas MCP, ao próprio modelo.

22:32 Então, vou encerrar por aqui, mas, antes de encerrar, quero falar dos cinco padrões — e depois vou passar a palavra para Brandon, claro. Mesmo que vocês não estejam criando exatamente esse sistema, estes são alguns aprendizados importantes que, na verdade, só adquirimos do jeito difícil ao longo de quase um ano trabalhando nele, certo? A primeira coisa é: prefiram sempre o determinístico, quando possível, certo? Nunca tentem pegar todo o projeto dbt ou a lógica do Airflow e simplesmente jogar no modelo para ele tentar realizar essas tarefas difíceis e complexas. Isso não vai funcionar, certo? O que vocês realmente querem é conseguir entender profundamente e em detalhes a lógica de transformação

23:03 de forma determinística. Portanto, é muito importante analisar tudo até o nível necessário para entender a estrutura, as relações e as dependências. E também coisas como conseguir calcular a granularidade, certo? Isso é algo que vocês podem usar. Passamos bastante tempo aprimorando e usando álgebra relacional para calcular cada etapa do ciclo de vida dos dados. Depois, usem o modelo quando necessário, por exemplo, para resumir modelos ou diferentes ativos de dados, ou para categorizar ou classificar. Os modelos são muito bons nisso. A segunda coisa

23:36 é armazenar evidências, não conclusões. Muitas vezes, é melhor simplesmente manter os sinais e deixar o agent raciocinar, porque esses modelos são muito capazes. São muito inteligentes. Eles conseguem raciocinar efetivamente no momento da consulta. E a terceira coisa é que percorrer o grafo sempre supera enfiar tudo no prompt ou no contexto, certo? O ideal é deixar o agent buscar o subgrafo de que precisa. Não despejem o repo inteiro em tempo de execução. Ele vai buscar o subgrafo e poderá percorrê-lo para descobrir como resolver a tarefa em questão, porque todas as dependências e tudo mais estão claramente mapeados ali, e o contexto

24:09 naturalmente permitirá executar uma IA mais eficiente, porque o contexto será muito menor cada vez que esses agents tentarem resolver uma tarefa. E a quarta coisa é: controlem a superfície de ferramentas. O que descobrimos é que um conjunto de ferramentas selecionado com cuidado sempre supera um buffet de MCPs prontos para uso. Até mesmo nos MCPs que temos, que dão ao nosso agent acesso ao Snowflake MCP ou ao MCP do Databricks, acabamos reescrevendo boa parte deles para adequá-los às capacidades e funcionalidades que queremos alcançar. E, por fim, digamos que a quinta coisa seja

24:45 o que falamos sobre evals: são a coisa mais importante. Vocês não conseguem criar um agent confiável, seja qual for a tarefa que ele realiza, se não puderem medi-lo. Não podem aprender com nada se não conseguirem medir. Portanto, invistam cedo e com frequência na infraestrutura de evals. Ela é uma parte viva e contínua do software, que vocês precisam manter, atualizar e executar continuamente. Para encerrar esta parte do workshop — que, até agora, nem foi bem um workshop, só fiquei divagando —, vamos finalmente chegar à parte divertida. O que descobrimos é que os agents

25:20 não falham em tarefas de engenharia de dados ou tarefas com dados porque não conseguem ler ou escrever SQL. Eles fazem isso. Fazem muito bem. Eles falham porque não têm, ou não entendem, o que os dados realmente significam, como fluem ou o que deixa de funcionar quando eles mudam, certo? Então, se vocês tratarem esse contexto como algo realmente fundamental — extraí-lo, calculá-lo e persistir nele —, o agent poderá consultá-lo de forma muito eficiente, porque vocês têm um grafo. Assim, vocês conseguem passar de um agent que apenas lê o código para um que está realmente pronto para operar o sistema de dados. E

25:53 foi isso que criamos com essa camada de contexto de dados. Certo. Obrigado por essa breve apresentação. Quero passar a palavra para Brandon, que vai começar a conduzir vocês por um workshop mais prático, que retoma alguns dos conceitos que discutimos aqui. Não vou responder a perguntas agora, porque quero garantir que Brandon tenha tempo suficiente para apresentar o workshop inteiro, mas estarei por aqui depois. Sintam-se à vontade para falar comigo; vou ficar feliz em conversar mais sobre isso. Obrigado.

26:27 [aplausos] Muito bem. Certo. Como vocês estão hoje? Estão bem? Fantástico.

27:02 Desculpem, só um instante. Certo. O tema de que vou falar hoje está definitivamente relacionado ao que Yonyi estava falando. Vamos tentar reunir esses conceitos em uma forma que possamos colocar em prática no tempo que nos resta aqui. Meu nome é Brandon. Trabalho na Typed. Vocês vão ouvir muitas apresentações nesta conferência, ao longo desta semana, sobre como um agent só é tão bom quanto o contexto que vocês fornecem e como construir esse contexto é, na verdade,

27:32 o trabalho em si. E esta é basicamente uma versão prática exatamente dessa mesma lição. Nos próximos 90 minutos, vamos construir juntos uma camada de contexto para um agent de programação — uma camada real, baseada em uma base de código de exemplo real. Hoje vamos usar a base de código do Qdrant. Depois, vou mostrar a última parte: mesmo que vocês criem essas ferramentas incríveis, o agent provavelmente vai ignorá-las, a menos que vocês o orientem. Fazer com que ele realmente use a ferramenta é onde está o verdadeiro trabalho. Então, antes de começarmos, se

28:11 quiserem acompanhar, este código QR leva ao meu repo no GitHub, que contém todo o código relevante. Esse repo tem alguns notebooks do Jupyter para vocês acompanharem. Mais uma vez, não se preocupem se não quiserem configurar tudo. As dependências são Docker Compose ou Docker para executar o Neo4j. E, se quiserem experimentar ao vivo as partes que usam LLM, podem usar uma chave do OpenRouter. Mas isso não é necessário. Tudo será executado offline, se preferirem, ou vocês podem simplesmente acompanhar enquanto eu monto tudo aqui.

28:45 Vou deixar isso na tela só mais um pouquinho. Depois, vou falar um pouco para que as pessoas possam configurar tudo, se quiserem, e então vamos abrir os notebooks. Certo, todos conseguiram escanear? Prontos para começar? Vou dar mais um minuto. Ótimo. Vamos começar pelo ponto de partida de todo agent de programação: a busca de texto. Vou perguntar sobre esse

29:17 tipo para esta struct chamada posting list. É um tipo real no Qdrant. Então, quando o agent começa, ele executa o ripgrep. E vocês veem que ele encontrou duas correspondências exatas. Ambas são exatamente posting list. Mas há mais de uma dúzia de nomes parecidos: posting list view, posting list iterator e assim por diante. O agent vai fazer a mesma coisa. Vai executar o grep, receber esse monte de resultados e escolher um, certo? Pode escolher o correto ou pode escolher o errado. Vai explorar esse primeiro. Talvez tenha que voltar,

29:48 retroceder e ler o próximo para descobrir o que vocês querem, certo? Mas não há realmente como ele saber qual posting list vocês querem, certo? O próprio texto não tem nenhum conceito de identidade nem indica qual posting list é realmente importante. As coisas com que vocês se importam — quais métodos a usam, onde ela é usada, em qual crate está — não estão necessariamente só no texto; elas estão na própria estrutura do código. Acabamos de ver Yonyi apresentar um argumento muito parecido sobre isso.

30:19 para dados. E é problemático fazer o agent reler o sistema inteiro a cada execução, sem memória. Porque, antes de tudo, isso vai ser caro, certo? Ele vai ter que reler o mesmo conjunto de arquivos toda vez. Se você fizer a mesma pergunta cinco vezes, ele vai voltar e ler todos esses mesmos arquivos em cada ocasião, certo? Mas o importante é que isso é frágil, certo? Quando ele voltar e ler esses cinco arquivos, pode lê-los em uma ordem diferente na próxima vez, ou pode ler primeiro um arquivo que o leve à resposta errada e o faça sair numa busca inútil

30:51 por outro arquivo. Certo? Então, o que estamos realmente tentando alcançar ao projetar o contexto para esse tipo de agent é fazer com que esse processo seja determinístico e menos frágil, certo? O objetivo não é necessariamente fazer o agent ser muito mais correto ou muito mais eficiente, embora às vezes isso aconteça. O verdadeiro objetivo é torná-lo muito mais determinístico, certo? Porque estamos tentando aplicar o máximo de determinismo possível a esse sistema tão não determinístico. Sem esse modelo persistente e confiável do código, ele vai ficar adivinhando

31:24 toda vez. E o que vemos é que, com o tempo, os modelos têm ficado cada vez melhores em adivinhar, certo? Todos podemos ver a diferença entre quando executamos o Claude Code há um ano e quando o executamos agora, certo? A experiência é completamente diferente. Então, se quisermos criar uma camada de contexto para código, basicamente queremos seguir exatamente essa mesma receita, mas em um nível um pouco diferente. Vamos reduzir a escala para podermos montar um workshop e deixar algo que vocês possam executar hoje, em vez de tentar reproduzir todo o nosso produto

31:57 aqui. Então, basicamente, vamos pegar esse conceito de extrair, persistir e consultar e começar a extrair informações da nossa base de código. Vamos coletar informações sobre os símbolos — todo tipo, todo trait, toda função — com sua identidade e localização reais. Vamos obter as relações entre esses diferentes símbolos, as arestas do nosso grafo que podemos percorrer. Elas são calculadas uma vez e acabam sendo consultadas muitas vezes. Essa é a mesma

32:29 abordagem geral que usamos para lidar com uma arquitetura de dados extensa, só que com um exemplo pequeno o suficiente para executar nesta sala. Então, sim, era basicamente isso que eu estava dizendo. Vamos apresentar isso hoje. Esse é um conceito sobre o qual vocês vão ouvir bastante durante a conferência. Achei muito legal. Fiz um slide sobre três níveis de contexto e, quando olhei a programação, vi que alguém vai falar amanhã sobre quatro níveis de contexto. Então, sinto que levaram a melhor sobre mim antes mesmo de eu começar, mas ainda assim vamos passar por isso

33:01 hoje, porque acho que ainda é bem útil para este contexto. Então, dá para pensar no contexto de um coding agent em termos desses níveis. É assim que gosto de pensar. O nível um é mais ou menos o que vocês acabaram de ver: grep, glob, strings brutas, sem estrutura, certo? No nível dois ficam a maioria das soluções de RAG para código que temos hoje, certo? Você tem busca semântica, indexa o código, cria embeddings, certo? Faz uma boa busca lexical e semântica e obtém resultados ordenados, certo, de todas as localizações em que esse elemento foi mencionado. Mas, no fim das contas, isso ainda vai ser uma lista simples,

33:34 certo? Vai ser uma lista simples que fornece resultados mais relevantes. Ainda será uma lista simples. Não vai mostrar as relações entre as diferentes partes do código, certo? Então, na nossa opinião, o nível mais alto, ou seja, o nível três, é um grafo percorrível. Algo que vincula os símbolos com identidades reais e adiciona arestas tipadas entre eles, pelas quais você pode realmente navegar. Então, acho que, na próxima parte deste workshop, vamos construir cada um desses níveis e, ao final,

34:04 vamos chegar a um agent capaz de responder a perguntas sobre a base de código usando esse grafo percorrível. E, sim, vamos organizar isso. Enquanto construímos, haverá alguns passos intermediários para chegar ao entendimento do nível três sobre como fazemos as coisas. O degrau zero é o nível sintático. Primeiro, é preciso analisar o código que você está tentando entender. A maneira mais fácil de fazer isso é com algo independente de linguagem, como tree-sitter. Isso fornece uma decomposição de alto nível bem básica, em AST, das

34:39 diferentes partes da sua base de código e de como elas interagem. No entanto, isso ainda não sabe lidar com colisões, certo? A lista de postings, o símbolo que está em posting_list.rs, e a lista de postings, o símbolo que está em quant.rs, são duas listas de postings diferentes. Ainda assim, elas vão colidir no degrau zero. Então, o nome do jogo é o degrau um. Resolução é algo que o grep realmente não consegue fazer, certo? É o que transforma essas duas listas de postings ambíguas em dois tipos distintos e

35:10 identificáveis, certo? Com Rust, é um pouco trapacear. Você meio que ganha o degrau um de graça, porque o compilador Rust faz boa parte desse trabalho para você. Basicamente, vamos usar rustdoc JSON para obter todas essas informações prontas. Isso é trapaça? Sim. Escolhi Rust de propósito porque precisava montar este workshop e, sabe, criar um parser inteiro para outra linguagem ou tentar construir algo do zero é um pouco mais difícil. No entanto, no final, vou falar de um caso mais útil com Python, e há um notebook de exemplo no repo

35:43 para vocês consultarem, caso não consigamos chegar a isso durante o workshop de hoje. A próxima camada é o degrau dois, para o qual há um notebook — não tenho certeza se vamos conseguir chegar a ele hoje. Esse degrau trata do significado real dessas diferentes coisas que estamos extraindo, certo? Essas duas primeiras camadas são inteiramente determinísticas. Estamos tentando sempre fundamentar nosso entendimento em uma abordagem tão determinística quanto possível. Então, construímos essas duas primeiras camadas usando tree-sitter e rustdoc JSON e, se tivermos tempo, vamos explorar como construímos

36:14 essa camada do degrau dois: como pegamos essas informações e as passamos para um LLM para classificar, resumir e fazer as coisas em que o LLM é bom, em vez de simplesmente jogar tudo lá dentro e pedir que ele entenda. Ótimo. Agora vamos abrir o notebook 01 e

36:46 vou ficar confuso. Vou trazer isto para cá. Ah, na verdade, já está aqui. Certo.

37:17 Tudo bem. Vocês conseguem ver isto? Certo. Ou apareceu aí do lado de vocês? Ótimo. Este notebook vai mostrar o processo de construção daqueles níveis diferentes de contexto para o nosso projeto Quadrant. Hoje não vamos indexar a base de código inteira. Ela é um pouco grande. Vamos analisar uma amostra de, acho, cinco crates. Isso nos dá um bom corpus de código para examinar nos nossos testes aqui.

37:49 Então podemos começar a executar estas células. Primeiro, fazemos várias configurações. Agora que a configuração está pronta, podemos começar a olhar para… Sim. Ah, está no GitHub. Sim, posso colocar o link de novo rapidinho.

38:22 mostrá-lo. Então, o link está aqui em cima. Se não quiser acompanhar localmente, há links do Colab em cada uma das diferentes opções ali. Isso. Para o Colab do notebook, se quiser usar o grafo, você vai precisar de uma conta ou de um login no Neo4j. Certo, isso é só uma particularidade dessa opção. Beleza. Então, podemos ver aqui que há um monte de código adicional em

38:57 aqui para fazer todo o parsing dentro do próprio Rust. Não vou necessariamente explicar isso hoje, mas está tudo disponível no repo para você consultar quando quiser. Hoje, temos apenas uma função mágica aqui, extract_rust_gate, que extrai as informações ao executar o tree-sitter na base de código Rust. E depois, outra função que faz a mesma coisa com as nossas informações do rustdoc. Então, podemos ver aqui que, a partir dos primeiros crates, extraímos um monte de símbolos do tree-sitter e, em seguida, um monte de símbolos — as versões resolvidas desses

39:30 símbolos do rustdoc. Então, agora podemos começar a executar um código que vai tentar entender por que estamos diferenciando entre o nível 1 e o nível 0. Podemos ver que, quando perguntamos sobre algo como posting list no nível 0, vemos essa versão exportada de posting list, mas não sabemos de onde ela vem. Não conseguimos resolver isso de volta até o local exato onde foi definida. Então, é para isso que serve o nível 1,

40:03 certo? Ele vai conseguir dizer: isto é posting list. O local canônico é aqui: posting list, posting list, posting list; e o local exato está neste caminho, nesta linha, certo? Isso vai nos permitir descobrir quando algo é reexportado para fora de um crate e vinculá-lo de volta ao crate original. O agent vai conseguir percorrer todas as relações entre essas coisas diferentes quando construirmos o próprio grafo. Então, agora vamos construir rapidamente o nível 2 de que falei antes, em que temos uma espécie de índice no qual criamos um, bem, um índice muito simples, em que

40:39 basicamente pegamos todos os símbolos e deixamos uma função fazer uma busca de texto completo. Certo? Fazemos uma busca e temos o mesmo problema que vimos com o grep. Podemos pesquisar posting list e obter informações muito úteis. Podemos ver que isto é a reexportação. Isto é a struct. Isto é a struct. Mas ainda não vamos conseguir ver as ligações entre essas coisas diferentes, certo? E o agent pode inferir isso e perceber que, provavelmente, se posting list é uma struct, ela provavelmente é reexportada aqui em cima. Então, essa provavelmente é

41:10 a base disso. Mas você não quer que ele tenha que dar esse salto lógico toda vez, porque vai haver aquela vez em que ele vai errar e acabar seguindo um caminho completamente diferente e sem sentido. Então, nosso próximo passo é começar a construir o próprio grafo. Pegamos todos aqueles símbolos e informações diferentes que acabamos de extrair e os transformamos em um monte de nós e arestas que podemos inserir no nosso grafo do Neo4j. Depois, vamos em frente e

41:45 carregamos tudo isso. Quando tudo está no grafo, podemos até abrir o Neo4j. Podemos ver um grafinho muito bonito com referências a todas as diferentes partes da nossa base de código. Agora que temos esse tipo de grafo, podemos criar ferramentas que aproveitem especificamente os recursos do grafo para dar ao nosso agent acesso a algo

42:16 que facilite o trabalho dele. Por exemplo, podemos criar uma ferramenta para analisar uma função e ver o que vai mudar, o que vai quebrar se eu alterar essa função. Então, podemos criar esta função de raio de impacto: você passa o nome qualificado de algo, o nome canônico, e ela percorre o grafo por meio das diferentes arestas que definimos, tentando descobrir exatamente o que depende dessa coisa e o que vai quebrar se ela for alterada. Assim, você pode pedir ao

42:48 agent que responda a uma pergunta que exigiria várias pesquisas com grep em vários arquivos diferentes e, em tese, ele pode responder de uma só vez. Claro que não funcionou — por que funcionaria? Demonstrações ao vivo, pessoal! Certo, podemos ver aqui que, quando dizemos que vamos passar o raio de impacto desta struct, podemos dizer: legal. Ela está vinculada aqui por meio de algo que a retorna, por

43:21 isso. Há um método que faz isso; ela é retornada aqui. Assim, podemos ver todas as ligações que conseguimos estabelecer só por termos o conteúdo dentro do grafo. Finalmente, conseguimos resolver nosso problema de colisão. Agora, se dissermos que temos duas posting list diferentes e quisermos ver a diferença entre elas, podemos chamar nossa nova ferramenta de raio de impacto para esta posting list e para aquela posting list, e obteremos resultados diferentes. Alterar uma posting list

43:52 não vai resultar no mesmo nível de mudanças que alterar a outra posting list. Então, embora essas coisas tenham o mesmo nome subjacente, a resolução canônica delas nos permite dizer com muito mais precisão o que vai mudar quando você alterar essa coisa, em vez de apenas usar grep, ler todos os arquivos e tentar adivinhar. Agora podemos pegar o que construímos até aqui e criar um agent de função bem pequeno e limitado para usar essa nova ferramenta. Agora que temos a ferramenta de raio de impacto,

44:26 podemos conectar um agent bem simples. Esse agent é muito simples. É apenas um agent do Pydantic, configurado com um system prompt que diz que ele pode executar estas diferentes ferramentas. Escrevemos algumas ferramentas bem básicas para que ele possa usar o raio de impacto e os métodos dos implementadores, que trabalham com consultas Cypher bem básicas para obter as informações. Quando você tem essas informações no grafo, escrever as consultas para extraí-las fica bem simples. Se você conseguir

44:58 expressar o que quer fazer, fica bem fácil escrever consultas Cypher, ou a linguagem equivalente no grafo que estiver usando, para extrair esses dados. E isso é um sinal de que você criou o schema certo para a tarefa. Agora, se você executar este agent, ele vai passar por algumas etapas, chamando as ferramentas e tentando entender o que está acontecendo. Depois, vai tentar responder à nossa pergunta. Por enquanto, podemos perguntar: “O que quebra se eu alterar posting list? Liste cada símbolo dependente, com o arquivo e a linha.” Então, ele vai me dizer

45:31 exatamente o que vai quebrar, com base no resultado do raio de impacto. E podemos ver aqui quais ferramentas ele chamou. Ele começou com uma busca, fez algumas buscas antes de chamar o raio de impacto e, depois, fez outra chamada ao raio de impacto. Podemos ver que, se você tiver um agent com habilidade suficiente no uso de ferramentas, ele pode usar essas novas ferramentas para desenvolver seu entendimento da base de código. Agora, esta parte é mais interativa, em que vocês podem

46:03 construir qualquer ferramenta que você quiser. Basicamente, isso funciona assim: podemos escrever uma ferramenta que executa qualquer consulta Cypher. Então, aqui estamos escrevendo uma consulta Cypher que diz: encontre todos os símbolos neste caminho de arquivo e simplesmente os liste. Essa ferramenta fará exatamente isso. Ela encontrará todos os símbolos neste caminho de arquivo e os listará para mim. Sim. Então, se eu quisesse escrever uma nova

46:35 ferramenta, eu nem necessariamente precisaria conhecer Cypher, certo? Eu poderia dar ao agent acesso a um system prompt com um esquema, certo? Que diz ao agent: “Certo, estes são os diferentes nós, estas são as diferentes arestas”. E eu poderia escrever minhas solicitações em linguagem natural, como: “Quero ver toda struct que implementa uma trait, junto com a trait que ela implementa”, certo? E, se eu executar isso, vai levar um segundo porque

47:05 o agent vai tentar descobrir como fazer. E consegue. Ele realmente gera essa consulta Cypher, que pode executar, e consegue extrair essas informações do grafo. Não vamos chegar ao nível dois hoje, mas podemos ter uma pequena amostra dele. Até agora, tudo tem sido muito determinístico. Você obtém sua estrutura e sua resolução. Aquele terceiro nível de que eu estava falando era o significado semântico propriamente dito. Você pode fazer um LLM resumir esses símbolos e gerar embeddings deles. E a Typedef tem uma biblioteca open source para isso, chamada

47:38 fenic. É uma biblioteca de data frames para combinar operações tradicionais de data frame com LLMs. Aqui, estamos apenas fazendo uma configuração rápida do fenic, dizendo que vamos conectá-lo ao OpenRouter. Vamos buscar algumas informações. Vamos tentar classificar este item específico de Rust, trazendo o tipo, o nome qualificado e a docstring; depois, vamos perguntar, em uma frase concisa: para que serve este item de Rust? Então, quando executamos o fenic sobre este corpus, sobre

48:08 essas linhas, recebemos os resultados resumidos pelo LLM. A ideia, a longo prazo, seria incorporar essas informações ao grafo, para que também fiquem disponíveis para o agent. E o bom disso é que o processo de geração é auditável. Então, se você olhar para isso e disser: “Ei, esta explicação do que esta função faz está errada”, podemos corrigir isso. Se o agent descobrir na hora o que a função faz e estiver errado, você só vai saber disso muito mais tarde, quando ele escrever um código que

48:39 não funciona. Ótimo. Sim, claro. Esse foi o notebook um. Temos mais algumas coisas para discutir, mas fico feliz em responder a perguntas sobre o conteúdo apresentado. Vou colocá-lo na tela de novo rapidinho. Sim, vejo duas pessoas lá atrás. Vamos voltar com um microfone.

49:12 >> Testando. >> Sim. O grafo é recriado em runtime, sempre que vocês o constroem ou percorrem? >> Defina runtime. Runtime no sentido de quando o PR ou o CI/CD é executado, ou… >> Sim, no mundo ideal — não vivemos de construir esse produto, mas, no mundo ideal — isso ficaria em cache e salvo em uma forma canônica, como, por exemplo, a versão principal, baseada na branch main, certo? E, conforme você ajusta e modifica as coisas

49:44 localmente, provavelmente precisa manter uma cópia local, que idealmente seria atualizada de forma incremental, para não ter que fazer tudo de uma vez. >> Sim. Minha pergunta é parecida. Como vocês pensam no custo de manter essas estruturas atualizadas em comparação com o aumento de desempenho e de fidelidade? Porque, pelo menos no meu caso, uso IA para

50:18 fazer grandes mudanças. Então… >> É difícil para mim saber de imediato se realmente seria melhor ter que manter essas estruturas. >> Sim, com certeza. Acho que o principal é que muitos desses problemas podem ser simplificados e processados de forma incremental, porque, em muitas das coisas que você faz, não é necessário reindexar o que já foi indexado se não mudou, certo? E, quando

50:50 você faz mudanças abrangentes na sua base de código, altera muitos arquivos, mas também há muitos arquivos que não altera, certo? Então, se formos inteligentes em relação à reindexação incremental — garantindo que só acompanhamos o que mudou e reindexamos apenas essa parte —, o que vimos até agora é que, de fato, vale a pena fazer essa troca. Isso é ainda mais verdadeiro à medida que aumenta o tamanho do contexto que você precisa manter indexado, porque o custo de fazer isso ao vivo, toda vez, fica ainda maior. Muito obrigado. Isso foi muito

51:22 útil. Estou me perguntando como isso funciona numa base de código de verdade, numa linguagem complicada, em que, por exemplo, há despacho dinâmico ou nem todos os tipos estão disponíveis em tempo de compilação — ou antes disso, quando você simplesmente analisa o código. Vocês já testaram? Quais são as limitações? Alguma dica? >> Sim. Tenho um terceiro notebook aqui que trata especificamente disso. Basicamente, ele explica como fazer isso numa linguagem como Python, que não oferece o mesmo acesso fácil aos tipos como grafo que Rust.

51:54 Podemos usar outras ferramentas. Podemos usar ferramentas como Griffe e tree-sitter. O tree-sitter ainda vai fornecer uma… e o Griffe vai fornecer resolução estática de algumas outras coisas, como os tipos. Você também pode usar algo como ty para fazer verificação de tipos e obter mais dados para indexar. Sim, isso fica exponencialmente mais difícil com linguagens diferentes. Vou falar um pouco mais sobre isso daqui a pouco. Mas, sim, pensei nisso. É um problema muito mais difícil, e foi por isso que escolhi resolver hoje um problema um pouco mais fácil.

52:27 Sim.

52:59 Não acho que vocês devam construir isso, para deixar claro. [risos] Este é um exemplo prático para fins educacionais. As pessoas constroem esse tipo de coisa com grafos de código regularmente, e eu não tentaria fazer uma do zero só para analisar código. Não sei se isso responde à sua pergunta. Acho que vale a pena construir essas ferramentas, mas não sei se vale a pena cada pessoa construir a sua. Provavelmente já há várias pessoas nesse meio

53:29 fazendo esse tipo de análise só na camada de código. Então, eu diria que provavelmente não vale a pena uma pessoa construir isso com o objetivo de transformá-lo em um produto. Provavelmente vale a pena construir com o objetivo de aprender a criar um agent, ferramentas e grafos de contexto. Acho que é na escala desse problema que isso se torna mais útil. Isso responde à sua pergunta? >> Ótimo. >> Minha pergunta é sobre a camada de contexto de dados que você mencionou antes. Estou supondo que ela é útil para um desenvolvedor. Você desenvolve essa camada de contexto e ela ajuda a produzir procedimentos SQL melhores e coisas do tipo. Tenho duas perguntas. Primeiro: vocês viram se ela também ajuda em runtime, no momento da inference? Essa é a primeira. E, segundo, vocês têm resultados de benchmark para demonstrar isso? >> Sim, com certeza. Então, o Yoni…

54:03 que seja útil para um desenvolvedor. Então, você desenvolve essa camada de contexto, e ela ajuda a produzir procedimentos SQL melhores e coisas do tipo. Então, duas perguntas. A primeira: você viu se isso também ajuda em tempo de execução? No momento da inference, por exemplo. Essa é a primeira. E a segunda: você tem resultados de benchmark para demonstrar isso? >> Sim, com certeza. Então, o que Yonyi estava

54:33 Falando sobre o que ele mencionou na palestra, temos resultados de benchmark para isso. O que fazemos é tentar encontrar arquiteturas de dados representativas, o que é meio difícil, porque você pode montar um exemplo simples, tipo um exemplo de brinquedo, e dizer: “Ah, meu agent acerta 100% dos meus testes no meu corpus de 30 modelos”, e aí é como: “Tá, isso não é muito impressionante”. Então, o que tentamos fazer foi encontrar exemplos open source de arquiteturas de dados maiores. No fim, decidimos usar o Mattermost. Não sei se alguém já ouviu falar dele.

55:03 Eles são uma empresa de mensagens instantâneas e chat. Na verdade, eles disponibilizam como open source todo o projeto DBT, com cerca de 300 a 350 modelos. Então, conseguimos baixá-lo, simular dados nele e, em seguida, executar evals com cenários especificados. Pedimos que ele realize uma tarefa específica, que quebre alguma coisa ali e, depois, pedimos que conserte. Pedimos que construa um conjunto de novas tarefas para criar coisas novas. E conseguimos ver que, ao construir nossa camada de avaliação e adicionar evals de quão correto, quão completo e quão eficiente ele é

55:39 — certo? Conseguimos ver que, com o contexto adequado, nossos agents conseguem fazer um trabalho melhor no geral e, em certas categorias específicas, realizar essas mesmas tarefas melhor do que o Claude Code funcionando sozinho. >> E quanto à pergunta anterior, ou à outra pergunta sobre o tempo de inference? >> Então, sim. Você está falando de quando o agent está realmente em execução? Sim, quando o agent está realmente em execução. >> É nesse momento que vemos o maior benefício, porque, quando o agent está

56:11 em execução, ele consulta todas as tools que criamos, e é aí que o valor real entra em jogo, certo? Se o agent consegue consultar tudo isso em tempo de execução, não precisa derivar tudo de novo de uma só vez, certo? >> Então, vocês já testaram isso com benchmarks de NL2SQL, como Bird ou Spider, ou algum desses? >> Sim. Quer dizer, sim e não. Sim, já testamos em alguns desses benchmarks. Acho que a parte complicada é que o objetivo do produto

56:41 não é necessariamente escrever SQL analítico. O que estamos tentando construir para as pessoas está mais voltado à engenharia de dados. Acho que é algo muito comum dizer: “Ah, vamos criar texto para SQL, vamos construir isso”. Acho que todo mundo está tentando fazer isso, certo? Mas acho que é mais importante dizer que queremos criar as tools que vão permitir que você realmente confie em texto para SQL; não necessariamente que vamos conseguir entrar e escrever consultas analíticas melhores para você. Queremos garantir que, quando você

57:12 estiver fazendo seu trabalho diário de manter sua arquitetura de dados, possamos ajudar você. Isso respondeu à sua pergunta? Muito bem. Voltando aos slides, se eu conseguir encontrar o mouse. Voltando aos slides. Então

57:44 o que vimos no final, certo, é que conseguimos distinguir essas diferentes listas de postings. Isso porque podemos consultar nosso novo contexto de grafo. E conseguimos descobrir todas as dependências de um determinado símbolo com um único salto, sem precisar percorrer tudo de novo. A consulta funciona assim: ela corresponde ao nome totalmente qualificado e, em seguida, encontra todas as dependências. E é isso, pessoal. Ótimo workshop, terminamos

58:17 por hoje. Não. A questão é que isso é legal. É real. Essa tool é realmente ótima, certo? Com uma chamada, ela consegue fazer o que certamente exigiria vários grafos. Não acho que você conseguiria fazer isso em uma única busca, mesmo que tivesse a busca perfeita, certo? Mas o problema é que você precisa de fato entregar isso a um agent. Então, quando você dá essa tool ao agent, e dá essa tool ao agent junto com todas as tools existentes que ele já tem, e sabe, temos essa tool muito legal

58:47 e já a vimos fazer coisas incríveis, mas ele executou grep mesmo assim. Se você der acesso às tools de grafo e ao grep, ele vai executar grep mesmo assim. Demos a ele tools incríveis, um modelo do codebase pré-computado e percorrível, e ele executou grep mesmo assim. Simplesmente usou glob, leu arquivos e executou grep. Às vezes, consultava o grafo e ficava confuso, e depois voltava a percorrer aquelas coisas do grafo. E é aqui que ninguém realmente conta para você. Todo mundo diz: “Ah, ele criou essas tools legais e atraentes, e elas fazem coisas incríveis”. Mas o agent precisa realmente usá-las, certo? Você precisa

59:19 fazer a engenharia de prompt, todo o resto do trabalho difícil e as avaliações. Essa é a outra parte, essa é a parte difícil, certo? Você consegue criar esse notebook. Consegue criar seu contexto de grafo incrível em uma tarde. A parte difícil é fazer o agent usá-lo de verdade. Sim. E por que ele faz isso? Bem, é por causa do pós-treinamento. Os modelos são treinados e projetados para chamar essas tools, certo? Há muitas situações no treinamento em que a resposta é executar grep e ler, certo? E você não vai conseguir necessariamente

59:50 lutar contra isso só criando uma tool melhor. Você pode criar uma tool melhor, mas a memória muscular do agent não vai deixá-lo usá-la. É muito parecido com os humanos, certo? Você pode criar uma tool melhor para as pessoas e, se tentar obrigá-las a usá-la, elas não vão ficar felizes. Talvez isso seja um pouco atual. Não sei. Mas o importante é que você não está necessariamente competindo com base no mérito de sua tool ser melhor do que todas as tools do conjunto de tools do agent, certo? Você está tentando lutar contra os próprios reflexos dele. Então, como você

1:00:23 faz isso, afinal? Porque acho que já não há dúvida de que, sim, se ele pudesse consultar o grafo, seria mais eficiente e melhor. Vamos provar isso depois, mas como fazer com que ele faça isso de verdade? A resposta é não lutar contra a experiência prévia do modelo, mas, de certa forma, aproveitá-la, certo? Fazemos o grafo parecer mais com aquilo que o modelo já usa, como o grep, certo? Você pega uma tool que talvez se chame query search, query graph ou query qualquer coisa e simplesmente a chama de search, certo? O agent vai usá-la de uma certa maneira. O que descobri é

1:00:55 que, se você chamar uma tool de search, o agent basicamente sempre vai fazer uma busca com várias palavras-chave nela, mesmo sem treinamento. Então, você precisa encontrar um meio-termo com o modelo, certo? Você pega o modelo, dá a ela o nome de search, leva em conta como o modelo vai consultá-la e ajusta a tool para isso. Depois, retorna os resultados do grafo no mesmo formato que o ripgrep, certo? Você usa a mesma estrutura de tool e de resposta do ripgrep e devolve isso ao modelo, certo? Porque foi com isso que o modelo foi treinado. Ele sabe lidar com essas coisas, e você não vai conseguir fazê-lo mudar completamente por conta própria só

1:01:28 prompting, certo? E a última coisa é que suas ferramentas precisam dar resultado logo na primeira chamada, sempre, porque, quando algo acontece, quando o agent segue um caminho e encontra um beco sem saída, ou algo que ele considera um beco sem saída — ele fez uma busca e não obteve resultados. Bem, às vezes ele vai achar que a busca está quebrada. Se não estou obtendo resultados e deveria haver resultados, talvez a busca esteja quebrada. Devo tentar outra ferramenta, certo? Ele não vai voltar e usar aquela ferramenta de novo. E, se voltar, talvez tente usá-la de outra forma. Não dá para prever isso necessariamente,

1:02:00 certo? Então, você precisa fazer o possível para garantir que suas ferramentas retornem algo muito útil logo de cara. Caso contrário, eles simplesmente vão voltar ao grep e nunca mais vão voltar. Então, o segundo notebook é mais curto. É um exemplo um pouco mais focado, e é todo sobre evals, certo? Como você mostra de fato que o agent está fazendo o que você acha que deveria fazer, certo? E

1:02:31 como você prova de fato que sua ferramenta é melhor do que as ferramentas existentes que talvez já estejam disponíveis, certo? Então, tudo isso já está armazenado em cache. Eu executei essa avaliação. Se quiser executar o notebook localmente, você pode definir o tamanho da amostra como um valor maior que zero e executar a avaliação ao vivo. Vai levar bastante tempo. Não diga que eu não avisei. Mas executei isso durante a noite, com cinco tentativas e 30 perguntas no meu conjunto de e-mails. Então é daí que vêm esses resultados para este notebook. Então

1:03:05 a primeira coisa que fazemos depois dessa configuração é preparar nossos casos de teste, certo? Vamos ter um agent com nossas novas ferramentas de grafo. Vamos dar ao agent acesso ao mesmo modelo e às nossas ferramentas de grafo. Também teremos nosso agent de controle, que terá acesso ao mesmo modelo e a um conjunto de ferramentas de sondagem do sistema de arquivos mantido pela Pydantic. Então, esses serão nossos dois agents de teste, e depois vamos montar um corpus de perguntas. Cada pergunta é

1:03:39 uma tarefa real. Vamos pensar em perguntas como: “Quais tipos públicos quebram se eu mudar a lista de postagem?”. Há duas listas de postagem. Qual delas devo usar? Sabe, coisas desse tipo. Tentamos dizer: “Esta deveria ser uma pergunta muito boa para o grafo”; “esta provavelmente é ótima para o grep”. Sabe, há perguntas de controle que provavelmente seriam boas para ambos, certo? Os dados de referência são, na verdade, extraídos do grafo; fazemos uma referência cruzada com o código-fonte. Assim, não estamos contaminando o contexto dos dados de referência com a saída do grafo. Precisamos fazer essa referência cruzada para garantir que

1:04:11 os dados de referência não estejam trapaceando. Então, agora podemos carregar nossos dados de referência e começar a configurar a estrutura da avaliação propriamente dita. E aqui está o que define, basicamente, uma boa pergunta de avaliação em comparação com uma pergunta ruim. Em um mundo ideal, sua pergunta de avaliação deveria poder ser pontuada de forma determinística. Acho que LLM as a judge é fantástico para muitos casos de uso, mas costuma ser usado em excesso quando, na verdade, você deveria se esforçar ao máximo para que a pontuação seja o mais

1:04:44 determinística possível. Isso vai reduzir a variância nas suas execuções, reduzir o custo e deixar você mais satisfeito no longo prazo. Você quer criar perguntas cujas respostas possam ser extraídas e estruturadas de modo que haja uma resposta de referência válida. Você consegue comparar a resposta com a referência. Não está apenas pedindo ao agent que dê sua opinião e, depois, pedindo a outro agent que rebata essa opinião com a própria opinião. Essas perguntas normalmente não funcionam bem como evals. Certo. Então, isto é um pouco de

1:05:17 texto técnico. Não precisa se preocupar muito com isso. Basicamente, é só montar os casos de teste que mencionei. Esta é a parte determinística, em que verificamos se ele cita todas as evidências que esperamos que cite. Estamos apenas criando uma lista de exemplos, uma lista de coisas que ele deveria citar e, depois, verificando se citou todas. Para as evals em si, estamos usando uma configuração do Pydantic Evals, porque é muito fácil de configurar. O funcionamento é o seguinte: montamos nossos dois agents e, depois, criamos

1:05:48 o executor do agent e, em seguida, um avaliador, certo? É ele que vai avaliar o agent com base nas tarefas que temos diante de nós. Basicamente, vamos comparar o que o agent disse com o que esperamos dele em cada tarefa, certo? Assim, conseguimos comparar as respostas e os resultados entre nosso agent do grep e nosso agent do grafo. Certo? É assim que conseguimos estabelecer, primeiro, qual é a utilidade real das nossas skills e, segundo, como podemos melhorar ou

1:06:23 o que podemos fazer melhor com o passar do tempo. Então, vamos executar. Certo. Não vou executar as evals de novo, porque isso levaria cerca de 30 minutos. Mas podemos ver que executamos a avaliação uma primeira vez, com k igual a 1. Então, para cada tarefa do nosso conjunto de dados, usamos o agent do grafo uma vez. E, quando fazemos isso, o agent do grafo não se sai muito bem. Dá para ver que o agent do grafo supera o agent do grep por uma pequena margem, mas o resultado não é muito consistente e não é realmente muito bom — não é o que queríamos, certo? Mas precisamos entender que o objetivo nunca foi necessariamente construir algo mais correto que o grep. Todos nós que usamos o grep no dia a dia, quando usamos nossos agentes de programação, sabemos que ele é muito bom no que faz. Ele usa o grep por um motivo. Não vai errar muitas vezes, certo? Mas o importante não é que ele acerte nesta única vez em que tentei, e sim que, quando eu tentar 5, 10, 15, 20 vezes seguidas, ele

1:06:56 acerte todas as vezes, certo? Então, precisamos começar a amostrar esses testes dessa forma. Agora, executamos esse mesmo conjunto de testes sobre o mesmo corpus cinco vezes para obter nossas métricas pass@k. Agora, temos uma resposta um pouco diferente. Certo? Temos duas estatísticas diferentes: pass@k e pass^k. pass@k indica se, ao longo dessas cinco execuções, o resultado esteve correto alguma vez. Certo? Nesse caso, os resultados ainda são muito parecidos. Na verdade, o grafo tem uma pequena vantagem. O grafo não amostra nenhuma das variáveis privadas, apenas a parte pública dos crates. Então,

1:07:29 há algumas perguntas de controle em que o grafo sempre vai se sair melhor. Mas o que é realmente interessante é quando mudamos a métrica e perguntamos se ele faz o trabalho certo em todas as execuções. Quando fazemos isso, os números se invertem completamente: de repente, o agent do grafo é duas vezes melhor em fazer a coisa certa nas cinco execuções do que o agent do grep. Certo? E podemos ver que, além de fazer essas coisas, ele também as faz com mais consistência e eficiência, certo? Ao longo de todas as nossas execuções, em média, a abordagem de grafo levou cerca de

1:08:02 há algumas perguntas de controle em que o grafo sempre se sai melhor. Mas o que é realmente interessante é quando você muda a métrica e pergunta se ele faz o trabalho certo em todas as execuções. Quando você faz isso, os números se invertem completamente: de repente, o agent de grafo tem o dobro de chances de fazer a coisa certa nas cinco execuções do que o agent de grafo. Certo? E podemos ver que, nesse agent que está fazendo essas coisas, ele também as faz com mais consistência e eficiência, certo? Podemos ver que, ao longo de todas as nossas execuções, em média, a abordagem de grafo levou cerca de

1:08:35 três e 3,1 vezes menos tokens. A última parte é: certo, como fazemos o agent usar o grafo de fato? É aqui que entram seus evals, porque, basicamente, você precisa escrever um eval que diga: “Ei, examine todas as ferramentas no meu trace. Com que frequência meu agent está usando as ferramentas que eu quero que ele use?” E então você pode obter seus números de adoção, certo? É assim que você chega a um ponto em que pode dizer: “Ah, escrevi um prompt que não tinha nenhuma instrução para usar as ferramentas. Tive 34% de uso das ferramentas. Voltei e mudei meu prompt. Agora tenho 72% de uso das ferramentas.” Certo? Então, os evals são a única

1:09:07 maneira de conseguir entender de fato se o agent está usando as ferramentas que você quer que ele use. Isso é só um teste desse conceito exato. Temos dois prompts diferentes, ambos tentando responder à mesma pergunta com o mesmo conjunto de ferramentas. Se você orientar o prompt — se simplesmente deixá-lo ingênuo, ele vai em frente e busca, e então começa a fazer outras coisas. Se você orientar o prompt, o cenário ideal é que ele faça a busca de fato e use get symbol para descobrir a resposta correta. [fungada] E então, a receita para construir evals realmente sólidos e confiáveis é

1:09:40 extrair candidatos a tarefas reais do seu corpus. Crie um avaliador determinístico e bem ancorado, tanto quanto possível. E primeiro teste o avaliador com testes unitários. Gosto do Pydantic Evals. Há muitas outras ferramentas de eval por aí, mas a estrutura central de todas elas é a mesma. Você tem um conjunto de dados, casos e um avaliador. Certifique-se de executar seus evals muitas vezes e de relatar pass@k, pass@k e todas as suas métricas diferentes. E certifique-se de

1:10:14 incluir exemplos de coisas em que seu agent talvez não seja necessariamente bom, porque você não quer que ele passe em 100% dos casos. Não é útil se o seu agent passa no seu eval 100% das vezes. Seu eval é fácil demais. Seu agent deveria estar quase falhando nos seus evals. Seus evals são a oportunidade de ver como ele se sai na prática e o que seu agent vai fazer nas circunstâncias mais difíceis possíveis. Então, não ajuda ninguém simplesmente dar a ele evals muito fáceis e simples. Certo, alguém tem alguma pergunta sobre este notebook de eval antes de eu voltar aos slides para a última parte da palestra?

1:10:46 Eu só estava pensando no exemplo, quando você estava falando sobre como o agent costumava usar o GPT em vez do grafo. Então você teve que reformulá-lo para que fosse uma ferramenta de busca. Houve alguma tentativa de pegar o grafo e destilá-lo em uma estrutura de arquivos, talvez com alguma perda, mas uma estrutura de arquivos que o GPT naturalmente pesquisasse, para que você não precisasse definir nenhuma

1:11:17 ferramenta separada? >> Sim. Não por enquanto, simplesmente porque o tempo estava um pouco apertado, mas acho que essa é definitivamente uma abordagem razoável. Quer dizer, acho que é isso que você vê em wikis corporativas de LLM, com todas essas coisas diferentes, em que você consegue muitos desses benefícios só com o corpus em Markdown. Mas acho que isso é definitivamente verdade e que você ainda obteria muitos dos mesmos benefícios com essa abordagem. Só que, no Markdown, em uma wiki como a Ele, por exemplo, ainda acho que o problema vai ser

1:11:50 que você quer arestas e proveniência reais entre esses elementos, certo? E essas conexões vão ser muito difíceis, porque mesmo quando você faz algo como o Grain, por exemplo, ele constrói tudo em Markdown, mas também cria um grafo por cima para você ver a linha do tempo, as conexões e coisas do tipo, certo? Então, acho que, em algum nível, se sua tarefa depende de entender a relação entre essas coisas diferentes, você vai querer algum tipo de grafo. Embora eu também ache que dá para obter um nível de sucesso muito parecido com uma abordagem como essa.

1:12:20 Sim. Você acha que falta alguma coisa em uma configuração como essa para permitir que pessoas não técnicas, que não são engenheiras de dados, abram um PR com isso como guia para navegar por uma base de código? Você já tentou fazer isso ou acrescentaria algo antes de dar a essas pessoas a possibilidade de trabalhar dessa maneira? >> Ah, como uma ferramenta? >> Sim. >> Ah, sim. Acho que é uma boa pergunta. Sim. Acho que

1:12:53 a parte mais fácil provavelmente seria começar a integrá-la como um MCP às ferramentas que você já usa. Então, você teria esse grafo de código junto com o Claude Code, por exemplo, e pediria ao Claude Code para chamar o grafo e fazer essas outras coisas. Depois, escreveria skills para que ele realmente usasse isso corretamente. Acho que essa seria a melhor abordagem. Mas, se você for menos técnico ou não técnico, acho que o objetivo seria

1:13:25 promover essas técnicas de engenharia de prompt de que estou falando aqui a skills e fornecê-las ao seu agent de programação, se quiser configurá-lo para usar essas coisas no contexto de programação real. >> Sim. Assim, esse PR chega ao engenheiro pela primeira vez já com uma primeira versão razoavelmente boa, como resultado de uma orientação mais determinística. >> Sim. E acho que a parte determinística ajuda porque, como falávamos antes, em resposta à pergunta lá no fundo, você pode comparar incrementalmente o que mudou, certo? Analisar isso

1:13:57 de forma determinística e obter uma conclusão muito, muito rápida sobre o que está acontecendo e o que mudou desde a última vez, em vez de esperar o agent ler tudo, examinar todas as alterações e devolver a mesma comparação. >> Tem alguém lá atrás. Acho que… >> Você experimentou ou pensou em diferentes níveis de resolução? Uma coisa em que eu estava pensando, em algumas das palestras anteriores, é que você faz previsão do próximo token e tem o prefixo, acho que as pessoas

1:14:32 disseram, que é basicamente toda a string de tudo que existe na base de código. Aqui, você tem os símbolos, mas os símbolos têm uma certa granularidade; haveria níveis mais altos de granularidade implícitos na base de código. Tornar esses elementos pesquisáveis e acessíveis a um agent talvez trouxesse benefícios. Eu queria saber se você pensou nisso ou se tem ideias de como abordaria isso. >> É uma pergunta fantástica. Pensei nisso. Há… e vou

1:15:03 falar disso no final da apresentação. Com certeza, há algumas coisas que eu faria em seguida. Se você estivesse tentando construir isso de verdade, usaria algo como SCIP para rastrear os grafos de chamadas dentro da base de código. Você consegue ver o que chama o quê e como isso interage, porque pode rastrear símbolos e como eles se movem pela base de código. Mas, se quiser rastrear de fato o que chama o quê e como tudo está conectado, esse é o nível mais alto de que você está falando, e você precisa de uma abordagem um pouco ampliada, mais um nível de análise determinística. E dá para fazer isso com…

1:15:38 Tem algo como Rust ou SEIP. Há saídas SEP semelhantes para TypeScript ou Python. Eu ia fazer tudo isso também, mas fiquei sem tempo. [risos] Mas, para responder à sua pergunta, sim, definitivamente há mais estrutura que podemos extrair de uma determinada base de código. E é mais ou menos isso que tentamos fazer na Type Def: encontrar todas as maneiras diferentes de extrair o máximo possível de contexto dos dados que temos. Tenho uma pergunta rápida: como você diria que esse conceito de camada de contexto de dados

1:16:12 se combina com algo como um catálogo de dados? O catálogo de dados ajudaria a alimentar um grafo? Eles são meio redundantes para coexistirem em uma empresa? >> Não acho que sejam redundantes. Acho que respondem a perguntas diferentes. O catálogo de dados é uma espécie de entendimento fixo do que está presente no seu data warehouse, exatamente, e acho que isso é uma solicitação um pouco diferente do que o grafo de contexto está

1:16:45 tentando extrair, certo? Acho que eles coexistem. Você lê as informações do catálogo para formar o grafo de contexto. Então, o que você tentaria fazer é basicamente dizer: certo, digamos que eu tenha meu projeto dbt, que tem vários modelos. Esses modelos criam tabelas e views no Snowflake, certo? Então, podemos dizer que vamos ingerir esse projeto DBT. Vamos trazer todo aquele SQL. Vamos analisá-lo, conectar tudo e, então, vamos dizer: ah, certo, este modelo corresponde a esta tabela. Vou

1:17:17 consultar o catálogo, obter informações sobre essa tabela e também trazê-las para o grafo. Assim, podemos dizer: certo, este modelo produz esta tabela física, e esta tabela física tem estas colunas, e estas colunas têm estes perfis, e essas informações vêm todas do catálogo, certo? Então, esse catálogo de informações, o esquema de informações, os dados de criação de perfil, tudo isso é essencial para o que fazemos, e está tudo no catálogo. Então, eu diria que eles coexistem, em vez de um substituir o outro.

1:17:54 >> [limpa a garganta] >> Para voltar à engenharia de dados e a esse conceito de eval, você pensa nisso da mesma forma, em termos de fazer um conjunto de perguntas sobre dados e depois observar como elas se comportam ao usar ou não a ferramenta? >> Sim. >> É. É. Exatamente. Ah, você tem mais alguma coisa? Desculpe. >> Desculpe. >> Ah, não sabia se você tinha terminado. Sim, com certeza. Usamos, como eu estava dizendo, basicamente tentamos encontrar um corpus de dados o mais real possível e, então, tentamos

1:18:27 encontrar vários tipos diferentes de cenários, certo? Você tem o caso básico de corrigir um bug, em que o cenário é: vamos inserir um diff na base de código que introduz um bug, certo? Então, você pede ao agent para tentar corrigir esse bug. E aí você pode testar se ele examinou os lugares certos, se verificou as coisas certas e se, ao corrigir o bug, não o corrigiu no upstream nem no downstream, certo? Quer dizer, sem colocar um curativo, certo? Ele realmente encontrou o modelo certo e corrigiu o problema certo. E você pode tornar esses testes mais fáceis ou mais difíceis, certo? Pode mudar a linguagem do prompt

1:18:59 para dizer: “Ah, acho que há um problema com o modelo XYZ”. Ou, num exemplo mais difícil, você pode dizer: meu dashboard Y está com este problema: este número está duplicado. Não sei por quê. Assim, você força o agent a trabalhar de trás para a frente, a partir daquilo de que o número depende, subindo pela árvore para entender de onde vem a causa raiz do problema e, então, verificar se ele realmente entra e corrige o problema, em vez de deixá-lo como está. Isso é realmente difícil, e o problema é que os agents sempre vão trapacear. Então, no seu eval harness, você precisa ter muito cuidado para reforçá-lo. Por exemplo,

1:19:32 se você disser que meu caso de teste é: o agent vai fazer checkout de uma branch e depois tentar corrigir o problema, o agent vai trapacear de bom grado e usar o git para voltar à main e tentar descobrir, a partir daí, como você introduziu o problema nessa branch. E ele vai encontrar muito facilmente o nome da branch que diz que é um teste e pensar: “Ah, isto é um teste. Ah, posso trapacear aqui.” Então, você precisa ter muito cuidado ao reforçar o seu harness para que ele realmente represente as tarefas que você quer que o agent execute. Esse é sempre o

1:20:04 perigo. >> Para esses evals, você vê as pessoas geralmente criando-os do zero ou… >> Existem alguns frameworks existentes para isso? ADE bench é ótimo. Provavelmente é o padrão atual da indústria para esse tipo de benchmark de engenharia de dados. Eles têm uma abordagem muito parecida para criar problemas em repos usando diffs e, depois, fazer o agent ler dados do warehouse e dados do código e tentar corrigir o problema. Então, esses frameworks existem. As pessoas estão conectando tudo, e acho que isso está

1:20:34 se tornando cada vez mais comum. Acho que a parte mais difícil é sempre a estrutura em volta, porque, para código, você só precisa da sua base de código, mas precisa de um banco de dados em execução. Ele precisa estar preenchido com os dados com os bugs certos. Esses dados precisam ser internamente consistentes. Você não quer que o agent fique dando voltas tentando descobrir: “Ah, por que os números são de 2024?”. É que só geramos dados de 2024 no nosso conjunto de amostra. Então, acho que você se depara bastante com esse tipo de problema. É por isso que as pessoas estão tentando padronizar isso no AD. Acho que a parte difícil é construir um benchmark assim que outras pessoas possam usar e que tenha um conjunto de dados grande o suficiente.

1:21:08 Essa vai ser a parte complicada, e isso ainda não foi feito, pelo menos não de um jeito que me satisfaça. Mas, enfim, voltando: este é apenas um resumo dos mesmos resultados que acabamos de ver. Vemos que, à medida que aumentamos a capacidade, à medida que instruímos o agent a usar mais as ferramentas, o agent consegue responder

1:21:40 ao mesmo conjunto de perguntas com mais consistência e consegue responder a esse mesmo conjunto de perguntas com mais eficiência. Um exemplo de por que é muito importante executar essas avaliações várias vezes: não se trata da sua precisão por tentativa, mas do seu determinismo. E o que realmente importa é como garantir que o agent esteja usando as novas ferramentas. Então, você realmente quer garantir que seus números de adoção estejam aumentando e medir a adoção das ferramentas, porque é isso que vai fazer você chegar ao fim do caminho, certo? Sabe,

1:22:13 uma ferramenta elegante que é usada só um terço das vezes é apenas um terço de uma ferramenta. E todo ganho de capacidade que você pode obter com ferramentas melhores depende desse número. É por isso que criamos o eval: não para provar que a ferramenta é boa, mas para provar que ela não está sendo deixada sem uso. A parte difícil desta palestra toda foi basicamente admitir que o grafo funciona muito, muito bem. Eu fiz todo esse trabalho, construí esse grafo de contexto e executei os evals, e deu empate. Foi meio frustrante, e você meio que quer continuar otimizando e construindo a coisa e tentando conseguir…

1:22:44 cada vez mais, e isso meio que vira uma armadilha, certo? Acho que esse instinto de construir mais pode, às vezes, levar a um custo irrecuperável, certo? Tipo, o agent tem o próprio reflexo. Eu tenho o meu, sobre o que acho que ele deveria estar fazendo. E estamos tentando chegar a um meio-termo, certo, e de fato encontrar uma maneira de avançar com isso, certo? E aí você pensa: “Ah, talvez seja só uma questão de fazer mais testes, mais evals ou mais alguma coisa, certo?” E acaba com essa falsa sensação de que só preciso de mais rigor, mais testes e

1:23:14 aí o agent vai ficar melhor em usar as ferramentas. Mas talvez você realmente precise perguntar: “Ei, por que o grep está funcionando tão bem?”, em vez de dizer: “Por que ele não está usando minhas ferramentas?” Bem, talvez a resposta seja que o grep já funciona muito bem, certo? Então talvez você não vá vencer tentando lutar contra isso. Talvez tenha que se adaptar um pouquinho. O agent vai usar grep de qualquer maneira. Então, quando você continuar tentando superar o grep com engenharia, vai acabar tendo uma experiência frustrante. Portanto, se você conseguir projetar de acordo com o que já funciona

1:23:47 tanto quanto possível e ajudar os agents a seguirem o que já querem fazer, isso vai ajudar bastante. Já fiz várias palestras em que cheguei a uma conclusão, terminei a palestra e disse: “Deixe-me executar de novo um conjunto de evals”. Aí tive que refazer a palestra inteira porque as conclusões eram completamente diferentes. O que aconteceu foi que escrevi minhas evals, construí todo esse harness e fiz isso errado, e obtive respostas que apontavam na direção certa. Elas diziam que o grafo vencia o grep, mas eu queria investigar um pouco mais a fundo.

1:24:17 E, quando você investiga um pouco mais a fundo, descobre que, ah, aquela ferramenta que criamos retorna o caminho para o arquivo, mas não o caminho correto. Então, às vezes, quando chamavam a busca, ela encontrava o caminho errado e seguia por um caminho errado ao tentar ler esses arquivos. Por isso, você precisa examinar todos os seus diferentes traces, certo? Acesse o logfire, ou o lugar para onde você está enviando seus traces, e confira se está mesmo lendo-os, porque não basta olhar para o placar. Se você só olhar para ele, pode obter respostas que parecem apontar na direção certa ou que parecem ótimas, mas talvez estejam

1:24:49 ocultando problemas subjacentes muito maiores e um problema bem mais sério na sua arquitetura. Então, a solução nunca é simplesmente fazer o número ficar maior ou mais bonito. É criar uma eval mais difícil. Leia os traces, encontre o artefato, torne o harness mais robusto e execute tudo de novo. Nunca confie no seu próprio placar. E, sim, esses são só alguns dos outros pensamentos que tive. Os modelos só estão melhorando. Então, podemos continuar acreditando que eles terão menos variância e tenderão cada vez mais a conseguir fazer o

1:25:23 trabalho que queremos que façam. Então, a parte fácil talvez seja construir o contexto e as ferramentas. A parte difícil vai ser mesmo criar o ciclo de evals, implementar o monitoramento e a observabilidade e garantir que o agent esteja realmente fazendo o trabalho que você quer, com as ferramentas que quer que ele use. E tudo isso está disponível: a pontuação, o ciclo, está tudo no repo, caso alguém queira explorar por conta própria. E, sim, foi disso que eu estava falando antes. Eu trapaceei usando Rust. O

1:25:55 compilador de Rust já fez uma delas, certo? É lição de casa. Escolhi a linguagem que era gratuita, porque a lição era que você consegue construir uma versão bem projetada de algo para uma instância relativamente fácil de um problema. E, em geral, se conseguir fazer as coisas funcionarem em um problema um pouco mais fácil, fica um pouco mais fácil ampliar isso para uma questão maior. Então, agora podemos dizer: “Uau, fizemos essa coisa realmente fácil para Rust. Quero fazer o mesmo para Python.” E Python é simplesmente mais difícil, certo? Tipo, não dá para realmente, se

1:26:25 você olhar para essas linhas de código, não vai chegar a lugar nenhum, certo? Se fizer grep em “client”, vai ficar preso em um mar de 50 mil clients. Você pode usar análise estática para carregar essas coisas e tudo mais, mas vai ser difícil. E é aí que um grafo de código realmente se mostrou valioso, certo? Num caso como esse, em que as bases de código são maiores e as linguagens são menos bem estruturadas, um grafo de código é ainda mais eficaz do que grep. Porque, em Rust, se eu quero ver o que implementa essa trait, tudo bem, é só pesquisar por “imple”.

1:26:57 Certo? É muito fácil. Mas, em Python, isso fica bem mais difícil, certo? Então, é aí que você realmente precisaria construir algo mais robusto. Mas a estrutura do grafo e das ferramentas não precisa necessariamente mudar, certo? O que muda é a estrutura de como ingerimos e carregamos os dados. E, se você tornar isso modular, pode expandir para quantas linguagens ou projetos quiser, certo? Então, essa é mais ou menos a estrutura subjacente de como a Typedef funciona: tentamos criar o maior número possível desses diferentes processos de ingestão para extrair dados de muitos lugares diferentes e depois sintetizar tudo em um só lugar.

1:27:28 Há outro notebook sobre isso. Não vou passar por ele agora, porque já estamos aqui há um tempo, mas fiquem à vontade para explorá-lo por conta própria e tentar aprender mais, se quiserem. A última coisa de que eu estava falando era, basicamente, o degrau do significado. A estrutura mostra o que está conectado a quê, mas o significado mostra mais ou menos o porquê. Há comentários no código, docstrings e todas as outras informações. Incorporar todas essas informações nos ajuda a

1:28:01 entender a intenção dos programadores quando escreveram o código, além da estrutura real do código que existe atualmente. Então, basicamente, gostaríamos de adicionar pequenas etapas como esta para resumir as informações do código e entendê-las linha por linha, de modo que a busca possa responder o que o código faz, além de indicar sua localização. E a parte importante, quando você está construindo

1:28:31 algo assim, é que está construindo uma análise semântica auditável que ficará sobre a sua análise determinística. O importante é fazer muitas perguntas pequenas, não uma pergunta enorme. A resposta não é escrever uma única… Então, quando fazemos análise em SQL, não escrevemos só um prompt muito, muito bom e jogamos o SQL nele, dizendo: “Ei, aqui está um modelo pedante com 10 campos; me dê todas as informações que conseguir extrair do SQL.” Isso não vai funcionar. Um modelo vai alucinar. Vai inventar informações. Não vai coletar tanta informação quanto conseguir. Vai

1:29:03 acabar não coletando tanta informação quanto poderia. Em vez de perguntar ao modelo 10 coisas sobre um trecho de código de uma só vez, você faz 10 perguntas diferentes sobre 10 trechos menores do código, cada uma respondendo a uma pergunta específica. Certo? Você analisa a estrutura de forma determinística e depois faz muitas perguntas pequenas e focadas, que aproveitam os pontos fortes do modelo. Em vez de pedir ao modelo que examine um bloco inteiro de SQL e diga o que significam todas as colunas, você analisa o SQL e diz: “Ei, aqui está uma coluna. Aqui está o seu

1:29:33 tipo. Aqui estão os comentários. Você sabe: isso é uma medida, isso é uma dimensão, isso é blá-blá-blá. Então, nesse ponto, você pode pedir ao agent para fazer classificação e sumarização, porque é nisso que ele é bom, certo? E, se quiser dar mais peso a isso, você fornece mais entrada de prompt para essa pergunta específica. Não tenta enfiar mais perguntas. Você dá mais contexto para que ele consiga responder àquela pergunta específica. Então: operações semânticas pequenas, fundamentadas e auditáveis sobre a nossa estrutura. É para isso que serve a nossa biblioteca fenic e é assim que a usamos como parte da Typedef. Então, aquele último degrau, o último nível da escada,

1:30:07 que é basicamente termos tudo isso em camadas e sabermos como tudo se conecta. Agora, o que isso realmente significa? Podemos começar a extrair parte desse entendimento semântico do que as coisas realmente significam usando os LLMs na última camada. Sim, como eu estava dizendo antes, há muito mais que você pode fazer aqui. Você pode, na verdade, criar um agent para analisar o grafo de uma base de código. Pode usar grafos de chamadas para entender quem está chamando isto e quem faz referência a isto, certo? E ele pode ler os comentários dentro e fora do código e incorporar

1:30:39 informações de fora do código. Pode incorporar issues do GitHub, suas issues do Linear, seus tickets e coisas assim. É assim que você transforma isso em algo que realmente seja mais útil e fundamentado: começa pequeno, começa com a menor coisa possível que você pode fazer, que é: como extrair a estrutura desta base de código? Depois, passa para: como pegar essa estrutura e acrescentar a ela o entendimento semântico que realmente vai ajudar a construir uma base útil para que os agents trabalhem nesse espaço e

1:31:09 façam alterações. E esse é, mais ou menos, o objetivo final. Enfim, muito obrigado, pessoal. Essa foi a minha palestra: “O agent vai agarrar de qualquer forma”. Sou Brandon Callender, da Typedef. Aqui está de novo o link para o workshop e para o meu LinkedIn. Então, obrigado. Posso responder a perguntas ou falar de outras coisas, mas muito obrigado a todos. [aplausos] Alguma pergunta? Posso passar o microfone para mais uma rodada.

1:31:45 Vocês já experimentaram esse tipo de workflow entre repositórios, em que as coisas podem ser microsserviços ou atravessar limites de HTTP? Funcionou bem? >> Não particularmente. Acho que isso se aplicaria a um único repositório. Agora, fazemos isso em escala no nosso produto de arquitetura de dados de maior porte. Então, acho que pode funcionar. Basicamente, a parte difícil é, obviamente, descobrir os limites entre as diferentes coisas. Então, seria preciso escrever

1:32:17 classificadores de limites que tentassem entender… Eu estava tentando dar um exemplo com Python e Rust, usando maturin, para mostrar algo parecido, mas não tive tempo. Basicamente, você precisaria escrever alguma forma de entender quando código escrito em Rust chama Python, ou vice-versa, certo? Para tentar construir essa camada de conexão entre eles. Então, sim, daria para fazer. Só acho que exigiria bastante trabalho a mais. >> Mais alguma pergunta?

1:32:49 >> Desculpa. >> Pode falar. >> O Cursor faz bastante coisa do nível dois. Por exemplo, quando você vê o Cursor pesquisando na sua base de código, geralmente ele está fazendo busca semântica, como busca por embedding vetorial, e isso funciona muito bem. Acho que há um motivo para existirem todos esses produtos de RAG para código que fazem busca semântica ou recuperação, certo? E acho isso ótimo. Só que essa abordagem permite encontrar todos os lugares do código que você quer examinar com o agent.

1:33:22 Mas você ainda precisa examinar todos esses lugares com o agent, certo? O objetivo desta abordagem é poder dizer, de uma vez: sei que estou olhando para esta função aqui, certo? Basicamente, quero poder clicar na coisa na IDE e ver todos os pontos em que ela é chamada, mas dentro do meu agent, sem que ele precise buscar em todo lugar. Faz sentido? Mais alguma pergunta? Certo, ali na frente. Um instante.

1:34:00 >> Imagino que metade da diversão de construir isso seja acabar com, não sei, uma espécie de vetor do grafo que você pode recuperar. >> Sim. Estou muito curioso sobre cache e armazenamento quando você começa mesmo a pensar em escalar isso, já que garantir que o agent acerte na primeira chamada é muito importante para orientar o processo. >> Sim, faz sentido. Acho que, à medida que você começa a ampliar seu banco de dados de grafos, vai perceber que as coisas podem ficar bem complicadas muito rápido. E

1:34:33 você tem toda a razão. Acho que o armazenamento é menos preocupante, porque, sim, esses grafos tendem a se espalhar um pouco, especialmente à medida que crescem, e fica mais difícil fazer a coleta de lixo. Mas me preocupa menos o armazenamento e mais a quantidade de computação necessária para executar análises de grafo em todas essas coisas diferentes. Então, acho que, ao tentar escalar isso, o principal em que você precisa se concentrar

1:35:05 é, como eu dizia antes, o processamento incremental: descobrir como reconstruir isso sem regenerar tudo. Acho que essa é provavelmente a parte mais importante, e é algo com que temos dificuldade na Typedef ao tentar construir isso, sabe, em maior escala, para uma arquitetura de dados grande. Porque você não quer ter que consultar seu warehouse 500 vezes de novo, certo? Você não quer ter que verificar, sabe, compilar todo o código SQL de novo, certo? Você quer conseguir atualizar as coisas à medida que chegam. Então, acho que a maneira mais importante de lidar com isso é projetar tendo a indexação incremental

1:35:37 como prioridade desde o início. Essa é mais ou menos a única maneira de chegar ao ponto em que você tenha que lidar com os outros problemas de escala. Acho que você precisa lidar com o problema de sincronizar novamente as informações muito antes de ter que lidar com o problema propriamente dito de escala: como armazenar tudo ou como fazer computações sobre tudo isso. Sim.

1:36:09 Descobri que, para nós, criar o conjunto de dados de referência e mantê-lo é sempre o maior problema. >> Sim, concordo 100% com você, e essa é uma questão para a qual não tenho uma boa resposta. Acho que… Na verdade, tenho duas respostas para isso. Uma delas é que o conjunto de dados de referência não precisa necessariamente… Se eu estivesse construindo isso

1:36:43 e estivesse testando em alguma coisa, escolheria um snapshot específico daquela base de código, certo? Assim, poderia dizer que este é o meu snapshot e que, quando executo meus evals, tenho um conjunto determinístico de perguntas de referência, certo? Isso é meio que uma forma de escapar da sua pergunta, certo? Mas é o que eu faria se estivesse projetando isso: projetaria com base em um snapshot. Assim, minhas perguntas de eval seriam determinísticas e não mudariam com o tempo, certo? Porque, no mundo ideal, seu agent ficaria cada vez melhor em responder às mesmas perguntas. Mas

1:37:16 Mudar as perguntas com base nas mudanças no corpus não ajuda, de fato, a avaliar se seus agents estão melhorando. Certo? A melhor maneira de avaliar se seus agents estão melhorando é fazê-los executar exatamente a mesma tarefa de novo. Certo? Então, de certa forma, a resposta é: você não faz isso, porque, para fins de evals, seu padrão-ouro deve permanecer constante em relação a um corpus constante. Faz sentido? Agora, se você estiver testando em produção, sim, precisa conseguir encontrar novas maneiras de testar isso em relação à sua base de código. Mas acho que isso e seu conjunto fixo de evals são coisas diferentes. Faz sentido?

1:37:47 >> Sim. Mas, tipo… >> Sim. Sim, mas quero dizer, se… Se usarmos o exemplo da base de código e digamos que você tem um bug meio complicado, que envolve uma função chamando outra função, e há um bug em uma dessas funções, ou há um if, como você disse, e então você tem uma eval que testa se, quando esse bug é introduzido, o agent foi até esta e aquela função e corrigiu o problema. Mas aí você refatora o código, e essa função nem sequer é mais usada

1:38:19 Então a eval falha. >> Ah, entendi o que você quer dizer. Nesse caso, acho que continua… Acho que continua igual, porque o que estou dizendo é que, quando monto minha eval, diria que ela é uma combinação de um commit específico em que meu código está, certo? O diff específico que quero introduzir para ver se o agent consegue corrigi-lo e, depois, o conjunto de mudanças resultante daquele prompt, certo? Isso deve sempre permanecer constante. Quer dizer, entendo o que você está dizendo, porque, sim, o código vai

1:38:51 mudar e não será mais necessário corrigir aquele problema, mas não é isso que você está testando, certo? Você não está testando a capacidade do agent de reagir ao novo código. Está testando a capacidade do agent de fazer aquela mudança com base nas circunstâncias em que ele estava quando você escreveu a eval, certo? Não acho que essa tarefa tenha mudado, mesmo que não seja mais relevante; ainda está testando algo útil. Faz sentido? Pergunta. >> Ótima palestra. Adorei. Hum…

1:39:26 Isso me lembra um pouco ontologia, hum… >> Classes, e é nisso que a Palantir está ganhando muito dinheiro, na verdade. >> Sim. Então, depois de construir a ontologia e as dependências, podemos conversar com o LLM usando essas estruturas de nível mais alto? >> Sim. >> Com essas classes. >> Sim. >> E é isso que você estava meio que insinuando neste momento? >> Sim. Então, isso é basicamente o nosso produto, certo? É esse conceito de construirmos o contexto…

1:39:57 …graph, construirmos a camada de ontologia por cima, na qual tentamos organizar todas as informações em torno da arquitetura dos seus dados: para onde as coisas apontam, onde ficam, o que significam. E então damos ao agent acesso a esse conjunto de ferramentas para que ele possa responder a perguntas e fazer mudanças na sua arquitetura de dados. Com licença. Certo. Isso talvez seja um pouco ingênuo, mas, voltando ao produto que vocês estão desenvolvendo e ao que demonstraram aqui: muitas das coisas que vocês…

1:40:28 …mostraram aqui são muito parecidas com o que a Neo4j apresentou em um workshop anterior, >> que é algo como: “Certo, aqui está um grafo de conhecimento. Você, como desenvolvedor com conhecimento do domínio, vai criar um grafo de conhecimento nesta área, e eles vão disponibilizá-lo para um agent, que então vai consultar o Cypher para obter informações semânticas.” Sou responsável por — vou chamar isso de um ecossistema de dados vasto — uma grande quantidade de software como serviço de terceiros. Temos outras coisas internas. Minha equipe é responsável por trazer muitas informações desses sistemas. Alguns deles são altamente configuráveis, como o Salesforce. Você traz as informações, coloca no…

1:41:01 …data warehouse etc., etc., e, por fim, tem business intelligence do outro lado, além da interação. Então, o objetivo é: como posso fazer com que minha equipe, bem pequena e formada por pessoas muito boas, defina as relações semânticas entre tudo isso em um só lugar, uma única vez, com um bom controle de versão, para que eu possa fornecer contexto e ferramentas a agents de IA, principalmente para trabalhos analíticos, mas também, ocasionalmente, para escrever de volta, e documentação para as pessoas que…

1:41:33 …precisam usar esses sistemas, seja para onboarding ou em um só lugar. Ainda não descobri qual é a maneira certa de fazer isso. >> Vamos conversar. >> Então, vocês vão me dar isso? Porque você também disse que não vão criar o catálogo de dados, >> certo? >> Não, não vamos… Bem, a questão é que acho que o que quero dizer é que não vamos nos limitar ao catálogo de dados, porque não vamos ler apenas o catálogo do Snowflake, nem apenas as informações do seu warehouse. O objetivo é ler do Salesforce, do… do… do Fivetran, do… do Airbyte, do que quer que seja, de todos os mecanismos de transferência…

1:42:05 …que você tem, do Airflow, do que quer que seja. Incorporar tudo isso a uma única árvore de contexto compreendida — um context graph — que possamos percorrer. E, a partir daí, o objetivo é extrair disso as coisas de que você estava falando. O principal problema das camadas semânticas é que ninguém quer manter uma camada semântica, certo? É quase um palavrão, porque, quando você cria uma, é tudo muito legal, bacana e atraente, mas depois você precisa continuar atualizando tudo sempre que adiciona modelos, e isso é um pesadelo. Então, o objetivo da Typedef é…

1:42:36 Isso foi ótimo. Obrigado, porque você explicou muito bem o que queremos que o produto seja capaz de fazer, que é exatamente o que você disse: conseguir raciocinar sobre todas essas diferentes camadas da sua arquitetura de dados. Construir a partir de um único grafo, pelo qual você consegue rastrear o fluxo dos dados, do Salesforce até a camada de BI, certo? E então, quando o agent pergunta — quando você pergunta ao agent — algo como: “Ei, de onde está vindo o AR?”, ele consegue dizer: “Ah, sim, isso se origina lá em cima, nos dados do Stripe, e depois é…

1:43:07 …associado aos dados de leads do Salesforce aqui embaixo para descobrir, sabe, essa coisa aqui embaixo, que é onde os dados são realmente calculados. Sim, essa linhagem é muito útil, mas, para nós, ela serve em parte à equipe de dados que está fazendo esse desenvolvimento. Ótimo. Mas quero que, no fim das contas, isso fique acessível para a área de negócios, certo? Assim, eles podem olhar o relatório e dizer: “Isso está errado. Que diabos?” E, em vez de vir até a minha equipe para gritar comigo, podem dizer: “Ah, isso é responsabilidade dessas pessoas, neste sistema e neste registro; é com esta pessoa que tenho que falar.” >> Então… >> manter tudo junto. >> Concordo 100% com você. E acho que…

1:43:39 …uma coisa que estamos tentando fazer — uma das coisas que o LLM nos permite fazer, em alto nível, algo que talvez não fosse possível antes — é ser essa camada de tradução entre as pessoas da área de negócios, os contadores, os analistas e, depois, os analistas de dados, os engenheiros ou seja lá quem for, certo? Porque o que queremos dizer é: certo, aquele analista de negócios diz: “Ei, aquele número no painel de receita está errado.” Então, o processo para corrigir isso seria: “Certo, preciso abrir um ticket…

1:44:10 em algum lugar, onde é registrado em algum sistema. Alguém pega isso, faz a triagem, tenta descobrir quem é responsável por aquilo, certo? Encaminha para essa pessoa. Eles tiveram que falar com a pessoa que abriu o chamado. Então, eu… é louco, né? Trabalhei na Salesforce com isso, então entendo um pouco do assunto em alto nível. Então, o objetivo da Typedef, certo, é permitir que aquele analista de negócios consiga entrar e dizer: “Ei, meu dashboard está com problema, certo?” Ele pode informar o nome do dashboard. Podemos abrir o dashboard, sabe, na sessão em que ele está. Podemos rastrear o problema e dizer: “Ah, sim, acho que estou vendo qual é o problema aqui. Provavelmente, sabe, algumas camadas atrás, isso está”

1:44:43 acontecendo. Consigo detectar que essa equipe é responsável por esse modelo aqui. Posso abrir esse chamado para você, já com as informações da solução incluídas, porque já consigo ver qual é a causa raiz: voltei três camadas e vi que esse SELECT estava errado, certo? Posso rastrear isso até uma alteração que entrou na semana passada. Ah, é por isso que isso aqui parou de funcionar, certo? E também podemos ver quando isso aconteceu, porque houve uma execução do Airflow aqui que, na verdade, preencheu esses dados, certo? O que estamos tentando alcançar é uma visão unificada de como tudo funciona, do runtime à linhagem, permitindo que o agent realmente responda a esse tipo de

1:45:15 perguntas e atue entre essas diferentes camadas para tornar o processo mais eficiente. Porque, se, em vez de o chamado ficar parado e sendo passado entre cinco equipes diferentes, o problema for entendido imediatamente e encaminhado logo para a equipe certa, essa equipe pode, até mesmo — num mundo ideal, em que nosso produto seja onipresente e funcione perfeitamente —, simplesmente dizer: “Sim, essa solução parece boa. Marca como aprovado e pronto; aí ela é implementada.” Certo? Então esse é o sonho. Mas é mais ou menos isso que estamos tentando alcançar, e já avançamos bastante nesse caminho. Ficarei feliz em conversar.

1:45:49 >> Certo. Mais alguma pergunta final? Beleza. Estaremos por aqui se vocês quiserem entrar em contato pelo LinkedIn ou a qualquer momento. Adoramos conversar sobre context graphs em geral. Então, muito obrigado por terem vindo, e até mais. >> Obrigado a todos. >> [música]

Transcrição original

[00:13] We’ll get going. Uh my name is Yonyi Michael. I’m one of the co-founders of a startup called Typede. Uh we’re here to do along with uh our founding engineer Brandon Calendarer this workshop. Um we have a two-hour time slot. We’re going to try and keep it as interesting and fun as possible. Uh, I know it’s very tempting right now. You guys all have your laptops out and Germany’s down one- nothing in the World Cup game, but it’s our promise to try and make it as fun as possible, so you don’t feel the need to be streaming that. Um, so, um, what we’re going to talk about

[00:47] today, uh, is we’re going to do like before we actually get into the fun, roll up your sleeves, do more of a technical workshop, I wanted to walk through a little bit around the problem set that we’re trying to solve here at Typedev. um and kind of set the stage and give some context for concepts and principles uh that will later be applied to the actual workshop that Brandon will lead us on. Um so the main idea behind this talk um is the background on typed defeaf. We’re building uh the enter the trust layer for data agents to be able

[01:17] to access and perform uh high complexity tasks on data platforms and data systems. Um and so the title of the talk is the data context layer which is a core piece of infrastructure um that we’ve actually built to help us with that mission. Um, and so, uh, a lot of it, uh, comes from the idea that perhaps maybe you guys aren’t as much into the data space, but once you try and unleash, uh, coding agents or software engineering agents on the data, it’s actually a lot more of a nuance, complex environment, and there’s a lot of things

[01:47] that you need to think about when building the right harness, when context engineering for these agents to be really effective. That’s what we’ll be talking about. The punch line is like the the model isn’t the bottleneck. The model’s going to continue to grow in capabilities. Um but the missing context to make these agents actually effective is the important part in this. Um so this is what we call the data context layer. Um all right so here’s the bet that we’re taking as a startup called typed defeaf. Um all of us have spent let’s say like uh the last two years generally

[02:19] in the industry um going and thinking about context engineering for coding agents right so we have like the the anthropics the codeexes of the world the cursors um where you’re able to understand how to map repositories effectively do retrieval and then tool design for these coding agents to be really effective. um where we’re falling short and where we’re starting to see there’s more of a need for is that the same thing needs to be done for agents that are able to access and touch the data and the data is exactly where uh if an agent is confidently wrong which it often time is it can actually end up

[02:53] costing a lot of money right because a lot of these metrics and calculations that we’re asking agentic analytics to actually do wind up on things like board decks um or their metrics in a model or their pipelines that fail quietly that have really really important important features and and um and metrics and measures that they’re trying to calculate. Um so the core bet that we’re saying is that agents are going to be core operators of these data systems. In order for them to be really effective, we need to be able to ensure that when they do read and write actions, they’re able to do these actions safely and

[03:26] accurately and be able to trust these results at scale. And that only really works if you’re able to give the agent the reasoning layer so that it can actually understand the data system end to end very much like how a data engineering or a data team that actually has working knowledge that was able to build the system from end to end is able to also reason about it. So that’s the goal of the data context layer here. Um, so where I’m going to say what we found over let’s say the last year and a half of us building in this area is that there are three general concrete areas where today’s let’s say coding agents

[04:00] fail over when you’re trying to get them to be effective in data. The first thing is impact analysis. So essentially like what breaks if I actually make this change. Um the next thing is join recommendations. So if the is this actually is this join actually safe for me to make across my data system and then safe code changes generally as well right safe transformation logic changes. So does this edit still mean the same thing? Is it preserving the meaning of this metric or is this metric definition now going to change because I’m making this data commit and this data change that’s happening. These aren’t fringe

[04:33] tasks, right? These are like the day-to-day uh tasks and workflows of like data teams um as they’re thinking of how to actually get enterprise data to be working with AI. And if your agent can’t reason and do these tasks reliably, it’s very hard to actually trust it. It’s very hard to actually trust it to give it right and even read access many times. U and that’s what actually matters is be able to get them to be effective core operators of our data systems and [clears throat] not just like question and answers types of agents. So here’s the gap the core gap that

[05:07] we’ve been able to define and like really discover through our work here. Um this context that the data needs is uh is uh distributed across many different systems, right? So your warehouse can have the store. It stores the the rows and the actual data itself. Your transformation code has the actual business logic that’s really important. Your BI tool stores the metrics, the dimensions, some business definitions in there. And then you have your docs and Slack channels and um and Slack threads and like tribal knowledge that oftentimes lives in like people’s heads as well, right? But the agent actually

[05:39] needs something in between all of that. It needs to actually understand what a model means, how it relates to other models, what breaks if I make this certain change, um and then which operations are actually safe to make, right? Um and for the next let’s say 15 minutes while I’m going over this like we’ll be using like um a term that’s called grain. So for all of you like non-data folks in the room what grain actually means from like a data team perspective is um is uh um sorry is like what does each row of the table actually

[06:14] mean? What does each row actually represent? Um and so if the grain is customer by month uh and I accidentally turn it into customer by week then every downstream sum after that uh can become wrong even though the SQL compiles and everything runs correctly right so that’s what I mean when I talk about grain change um and so let’s make this a little bit more concrete like in terms of reliability what does it actually mean if someone asks an agent why a pipeline’s row count row count suddenly spiked and it jumped

[06:47] 40% overnight. Um, the agent can go and write valid SQL. It runs it. It could render a beautiful chart, but at the end it’s going to report the wrong root cause, the wrong analysis of what actually went wrong. It says, “Looks like there’s more data that came in. Um, but what actually happened is there was an upstream column that uh turned a onetoone join into a one to many join, right? So that means the grain actually changed. uh and the agent literally cannot tell a real spike like a real data spike and differentiate between

[07:18] that and like an actual grain change because the grain isn’t in the data itself, right? Um and so the agent can run the query, it could get some sample rows, it could sample the data, it could read the SQL very well to understand, but it didn’t know that what one row was actually supposed to mean and that now it means something completely different. Right? So this is why valid SQL isn’t enough, right? Right? The semantics are actually very very important when you’re trying to get an agent to be effective.

[07:49] So then let’s say like the like thinking on it, there maybe two obvious fixes for what you can do for this type of scenario. And this is why neither obvious of like neither of these obvious fixes actually works, right? So you can give the agent the code, right? But the problem with that is if you go and give it let’s say like your DBD project or airflow project it’s redriving the context every time from scratch right on every single prompt which means it’s probabilistic um it’s bounded by the context window which doesn’t always work very well and then every time you start a new session you have to go and rebuild

[08:21] that context from scratch. Then let’s say okay let’s try and give it database access. So I’m going to connect my snowflake or datab bricks MCP to it. um and instead it can see the rows and then it can pro it can it can see sample some data but it doesn’t actually have an understanding of what each row actually means. So it can read the numbers but not necessarily explain it right. And so the first situation is one can read uh uh one can read the code but doesn’t actually have memory to be able to persist the knowledge of the context that you spent a lot of time going back and forth and having bu build up and the

[08:55] other sees the data but doesn’t actually associate the meaning behind it. So neither has like a persistent model of how the system actually behaves and that’s the mission that we set out to try and solve. So let’s like take a step back and name exactly what’s missing from the context that you need to actually give the agent to be able to reason effectively across these uh uh complex data changes. There are four facts let’s say that decide whether an answer is right and none of them are actually written down anywhere that the agent can read or infer. So the first thing is what what one row

[09:28] actually represents in a table. This is what we talked about the grain. The next thing is whether a join is safe or not. And then there’s what a metric actually means and what relates to what. Right? The dependencies are very important um in data systems. [snorts] And so an agent can read your SQL just fine. Um but it can’t see these four things very easily at query time when it’s the most important time for it to gather the the exact context that it needs. So the goal of what we did was let’s see how we can make them computable and queryable so

[10:00] that the agent is effectively able to grab just the context it needs just at the time it needs and not have to rebuild it during every single session. So the next few slides are about how we actually went about doing that. Um and so let’s take for example the join idea. This is like the the second fact that we talked about in the previous slide and this is because let’s this is a good example because it’s a cleanest example of like why syntax isn’t actually enough to be able to get the agent to be effective. So there’s one join, we call it like an an equa join that’s happening here that’s written a certain way, but

[10:32] it could have four completely different outcomes depending on the underlying data, right? So it can either preserve your rows, it can look up a dimension, or it can silently replicate every single row when it goes from like a one to end type of relationship. The same thing is for this between clause that happens oftentimes in SQL. So it could be a safe as of lookup or a date spine that actually explodes your grain, right? So the agent can see this syntax but it can actually understand which of these scenarios is going to result because of this syntax syntax. That’s not really a model or capability

[11:04] problem. That’s like a context problem that we’re actually trying to arm the the the agent with. The fourth fact in here and this is one of the more fun ones that I like to um to show off is that um understanding of like what models what data assets relate to other ones. Right? So this is which models are in the revenue domain for example, which are in the product domain and those oftentimes correlate to the personas of the people that are actually trying to access or run the datas to access these uh the core metrics or numbers that are in these uh in these domains. Oftentimes today like teams

[11:38] will organize it based on folder names and a lot of times it’s tribal knowledge to understand which models relate to which um business units. But you can actually discover it from the you can actually do more and actually discover it from the structure of the data. So for example you can take a look at how the models actually connect looking at lineage for example table level lineage column level lineage looking at shared keys looking at shared naming. So the system clusters them automatically for you into the domains so that when you go and ask the agent okay which models are in my revenue domain for the revops team

[12:13] um it becomes a query that the agent can actually run and not a question it has to ask like you know that has to go back and forth with the human to be able to understand which are in the same domain. So this is kind of the same principle for context engineering that you see over and over again. It’s you take something that is tribal knowledge that lives in someone’s heads or lives in some documentation elsewhere and you try and actually compute it and then make it queryable for the agent. Um, so how do you actually produce these facts, right? And the key principle here

[12:47] that we talk about a lot is like you want to always be as deterministic as possible first and then only use LLMs when you’re trying to resolve ambiguity or do things that models are actually really good at, right? And so you’re able to compute grain and lineage straight from the actual SQL itself. So what we do is we actually take all the transformation logic and we parse it all the way down to the as to actually understand how the data is actually structured right and that way the agent can’t drift from the actual implementation itself because the the basis of the graph and the context we’re

[13:19] giving it is actually derived from the implementation itself. Um and then uh and then you can do things like verify join safety against the actual data. Right? So is the join key really unique? This is something that you can do by profiling the tables, right? So you’re able to go and profile the the tables that you have in your warehouse. Um, so that allows the agent to not really guess about things like cardality. And then you can use the LLM for things where the human readable parts of it, right? So if you’re trying to understand business names or summarize the utility of models or resolve any ambiguity from

[13:52] like the first two layers in there, that’s where the LLM can really come into the picture. um these facts are computed and then checked against your data so that the LLM um uh just kind of names these data assets at the end on the graph that we’re building and that allows it to make sure that we’re not actually drifting from the core implementation. That separation matters because um it keeps the core facts tied to the actual implementation kind of like we talked about and to the data itself.

[14:23] So that’s all good and great. Where does this all live? What we call we call it the data context layer. At the end of the day, it’s a graph, right? And it’s computed once and then on every commit, it’s refreshed atomically, right? So, we’re incrementally modifying the uh this context graph as data teams are making more data commits to it. So, it’s always representative of the the freshest state of uh your production enterprise data. Um, and the reason is a graph. The reason it’s a graph and not just a pile of text or the robust set of

[14:54] markdown files is because of the access pattern, right? So the agent’s able to pull exactly the the subgraph it needs in one traversal. Um, and it avoids things like prompt stuffing or context stuffing and it scales way past 500 models because you’re not like rereading the project every time you’re trying to build the context layer. Right? over the last few years, we’ve obviously seen a huge resurgence of like graph databases. And I’ve always like wondered even like before AI like what the main utility of it is, but it turns out that models and

[15:26] AI is very very good at being able to to grab context and be able to write cipher, be able to write a query against it and then traverse. Um, and the nature of how data assets are, it’s very dependency based. So it yields really nicely to be able to create these uh typed objects and nodes and edges in this graph. Um. Oh,

[15:56] you guys can’t see the slides. Oh, >> can you guys turn off the lights? I don’t know. No. Oh, okay. Um, oh that’s better. Getting a little better. We dim them a little bit more. Sorry guys. I should have uh thought of the the black background and other colored. Um, but I can happy to send this out afterwards also the deck.

[16:33] Okay. Um, apologies for that. Um so um yes let’s see so you can see or maybe you can’t see I guess um but there there’s like a sample of like a subgraph in here and you can and what you’re is actually represented is that the edges carry a lot of provenence right so the edges are defining different types of relationships amongst the the data assets themselves we have like for example um uh is is materialized by has

[17:05] a semantic definition and these are all nodes that are representing different types of data assets in the actual graph itself. So there’ll be like a node that represents a semantic view for example um and then there will be a node that will present like a physical table which is like the actual physical represent representation of the logical code um of the table itself. Um I’ll send this to you guys afterwards so you can take a closer look at it or we’ll figure out how to get it to you guys. Um and uh and so yeah and so this is like essentially like what a subgraph will look like. Um and so if we go back and

[17:39] think about the use cases that are relevant that the agent can now solve because of this context layer that we have um is is impact analysis is the first one right so the agent proposes a change um for modifying like a model’s grain for example right before it actually does that before it writes a line of SQL to make this change it can actually walk downstream the graph and ask each node that is a downstream dependency of this node it’s trying to change does this actually break the grain that you have represented in this node here. So here it’s able to catch that um uh a monthly AR measure would

[18:14] get repeated across weekly rows if I’m trying to go from the monthly grain change to the weekly grain change and that downstream there’s some ARR that’s actually summing all of the different um values for this on a monthly basis which would result in let’s say the metric having like a 4x um of the actual value amount that it’s actually accurate right and then it could land on let’s say everything downstream on like a board level dashboard that you have because the context graph is inclusive of your entire data system. Everything from like source systems all the way down to the BI layer in the dashboards layer. So the

[18:46] agent has visibility and able to walk the lineage all the way across the the data life cycle if you will. So the key part is this all happens pre-flight, right? It’s not at build time. It’s not in a production incident. It knows a blast radius before it actually even acts on it. And then the second use case here is join recommendations, right? So this is fact two. Um so this is like the agent wants to attribute the the exercise here is like the agent wants to attribute a arr by marketing channel right so it doesn’t pick a path because of like column names that it’s able to

[19:18] understand it actually checks whether each join key is actually unique and this is done because we’re profiling the actual tables themselves um and it’s verified against the data right so then it has the recommendation of what the actual safe join path is to make this happen while flagging the ones that will actually cause like a fan out or like a 7x um in in rows or one that will suddenly like silently drop 12% of the rows downstream as well. So we’re making recommendations with actual reasoning and evidence is the key here and it’s grounded in the data itself. And then safe code changes, right? This

[19:51] is the third fact. Oftentimes you’ll find like business definitions. The reality is they’re oftentimes nested in the SQL itself, right? And so if you’re asking an agent to let’s say tidy up some measure or some metric that you have and it removes a predicate um syntactically that will work right the the SQL will still compile um but what it’s actually happening and it it could could look harmless but the predicate was actually a very important part of the business definition itself right so here in this example it’s dropping the definition of um of paid right so it it’s silently redefining what the metric

[20:26] uh the metric to include trial churn uh across all the way downstream three dashboards that are actually happening representing this metric. Um so this context layer actually catches this uh change in business definitions and business metrics before it’s actually implemented. Um and so this is like if you’re making the agent ser like have these as like first class constraints. It’s not just a comment that is in some YAML file or something like that. That’s the key here. And that’s the line between like a a syntactic refactor and

[20:59] a change that a human can actually trust. So all good and great, but does it actually work? Right? Um and so you have to you have to measure this. Anytime you’re building anything agentic, you need to be able to measure it. Um and so we practice like eval driven development, which is a very important part of it. We’re essentially going and creating a bunch of like real production data engineering use cases and tasks and workflows that range in difficulty from like easy to blind and they each run multiple times and they’re is they’re each running on isolated clones of the

[21:31] actual data environment which is a very important part so that there’s no actual leakage that happens across these environments and then we have pass fail that’s running checking the actual output of it. We have some LLM as judge doing like impact analysis things as well. Um, but the key findings here is that although it’s the same models, it’s the same task, the only variable that’s changing is where the agent has the access to the data context layer. And so you see a material lift um when it has this right context. It’s able to reason across these really complex changes and the task success rate goes up

[22:01] tremendously. It’s almost 35 points higher when it’s armed with the data context layer. And this exact benchmark is obviously going to keep growing and keep evolving as we’re working with more teams to figure out their kind of um bespoke use cases in there. Um but the signal is consistent, right? The better context um the more rich context you’re able to give the agent, the outcome changes tremendously than just let’s say like swapping models or adding more generic like tools, MCP tools um to to the model itself.

[22:32] So, I’ll wrap up here, but before I wrap up, the the five patterns, and I’ll hand it off to Brandon, obviously, but the five patterns that are even if you’re not building this exact system, these are some key takeaways that we’ve really learned the hard way actually over the last like year of us building this, right? The first thing is like deterministic always when you can, right? Don’t ever try and take like your entire DBT project or your Airflow logic and just throw it to the model to be able to try and do these tough complex tasks. That that won’t work, right? Um what you really want to do is be able to get a really deep and detailed understanding of the transformation

[23:03] logic in a deterministic way. So parsing it all the way down to the as to understand the structure the relationship the dependencies are very very important. And then things like being able to compute grain right like that’s something that you can use. We spent a lot of time refining and like using like relational algebra to actually calculate and compute for each stage of the data life cycle. Um and then use the model when you need to when it’s like summarizing models or summarizing the different data assets or categorizing or classifying models are really great for that. The second thing

[23:36] is store evidence not conclusion. So it’s oftentimes good to just keep the signals and then let the agent be able to reason because these models are very capable. They’re very smart. They can reason at query time effectively. And then the third thing is that traversal always beats prompt stuffing or context stuffing, right? um it’s going to want to let it pull the subgraph that it needs. Don’t dump the entire repository at runtime. It’ll pull the subgraph and be able to traverse to figure out how to solve the task at hand because all the dependencies and everything is clearly mapped out there and the context when

[24:09] naturally you’ll have be running more efficient AI because the context is going to be a lot smaller every time um the task is trying to be solved by these agents. And then the fourth thing is like own the tool surface. So like what we found is a curated a curated type tool set always beats like a buffet of just like off-the-shelf MCPS. Um even like for the MCPS that we do have that give our agent access to Snowflake MCP or data bricks MP we found ourselves like rewriting a lot of it to kind of match the capabilities and functionality of what we’re trying to accomplish. Um and then um let’s say the fifth thing is

[24:45] we talked about like eval right this is uh the most important thing you can’t really build a reliable agent regardless of what the agent is doing if you can’t measure it you can’t learn from anything you can’t measure um and so invest in like the eval infrastructure early and often and it’s a continuous like living breathing uh piece of your software that you have to always maintain and up and and update um and run continuously. So to close uh this portion of the workshop, this hasn’t been a real workshop so far. It’s just me kind of rambling, but we’ll get to the fun part now. Um what we found is the agents

[25:20] don’t really fail at data engineering or data task because they can’t read or write SQL. They do that. They do a very good job of that. They fail because they don’t have or they don’t understand what the data actually means, how it flows, or what breaks when it changes. Right? So if you make that that context really a first class citizen um you extract it, you compute it, you persist it, and then the agent’s able to query it in a really efficient way because you have a graph. Uh then you’re able to cross a line between from an agent that only reads your code to one that’s actually ready to like operate your data system. And

[25:53] that’s like what we’ve built with this like data context layer. All right. Um so thank you for that like little brief talk um that we’ve given here. I want to hand it over to Brandon who’s actually going to like start walking you guys through more of like a hands-on workshop that carries over some of the concepts we talked about in here. Um, I won’t take any questions out because I want to make sure that we have enough time for Brandon to go through his entire workshop, but I’ll be around like after this and whenever like feel free to reach out to me. I’m happy to chat more about this. Thank you.

[26:27] [applause] Good job. Okie dokie. How is everybody doing today? You’re doing good. Fantastic.

[27:02] Sorry, give me one moment. All righty. So, the topic that I’m going to talk about today um is uh definitely related to what Yonyi was talking about. We’re going to try to bring these concepts together in a form that we can actually do in uh the rest of our time here. Um so, my name is Brandon. I’m at Typed. Um, and you’re going to hear a lot of talks here at the conference this week about how agent is only as good as the context you give it and that building that context is in fact the

[27:32] actual job. And so this is basically a hands-on version of that exact same lesson. Um, over the next 90 minutes, we’re going to build a context layer for a coding agent together, a real one over a real example codebase. We’re going to be using the quadrant codebase today. Um, and then I’m going to show you uh the last part, which is that even if you build when you build these amazing tools, um the agent’s mostly going to ignore it unless you help it along the way. Um, getting it to actually use the tool is going to be uh where the work really is. Um, so before we go ahead and dive in, um, if

[28:11] you want to go ahead and follow along, this QR code links to my GitHub repository, which does have all of the relevant code. Um, that repository has a few Jupyter notebooks for people to follow along with. Again, don’t worry if you don’t want to have set that set everything up. The dependencies there are uh docker compose or docker for running neo4j. Um and if you do want to try out the llm portions of it live, you can use an open router key. However, that’s not required. It’ll all run offline if you desire or you can just follow along with me uh as I build it up here. Um

[28:45] so, uh I’m going to leave this up for just a little bit longer and then I’m going to start doing a little bit of talking for people to be able to set things up if they want to and then we’ll dive into the notebooks themselves. Okay, everyone got the scan in. Ready to go? Give it one more minute. All right, perfect. So, let’s start where every coding agent starts, which is text search. Um, I’m going to ask about this

[29:17] type for this strct called posting list. Uh, it’s a real type in quadrant. Um, so when the agent goes, it runs rip grip. Um, and you see great, it found that we have these two different um, answers that are exact matches. They’re both exactly posting list. Well, we see it has a dozen more look alikes. You know, we have a posting list view, a posting list iterator, etc., etc. Your agent’s going to do the same thing. It’s going to GP. It’s going to get this wall back and it’s going to pick one, right? It may pick the right one. It may pick the wrong one. It’s going to explore that one first. Maybe it has to go back,

[29:48] backtrack, read the next one to actually figure out what you want, right? But it doesn’t really have any means of knowing which posting list you want, right? It it it can’t the text itself doesn’t have any concept of identity or you know which posting list is actually you know meaningful. Um this the the things you care about you know what methods use it where it’s used what crate that it’s in that’s not necessarily just in the text it’s in the actual structure of the code itself. Um, so we just watched Yonyi make this, you know, a very similar case

[30:19] for data. And it’s it’s problematic to have the the agent redive the entire system for every run with no memory. Um, because for one thing, it’s going to be expensive, right? It’s going to go through it’s going to have to reread the same set of files every time. If you ask it the same question five times, it’s going to go back and read all those same files every time, right? But the important thing is that it’s fragile, right? when it goes back and it reads those five files, it might read them in a different order next time or it might read one file first that gives it the wrong answer and sends on a goose chase

[30:51] to another file. Right? So, it’s that issue of actually having this thing be deterministic and not as fragile uh that we’re really trying to accomplish when we’re trying to engineer context for these kinds of agents, right? The goal isn’t necessarily to get the agent to be a lot more correct or a lot more efficient, even though that’s sometimes the case. The real goal is to have it be a lot more deterministic, right? because we’re trying to apply as much determinism as we can to this very non-deterministic system. Um without you know this persistent trustworthy model of the code, it’s going to guess um

[31:24] every time. And you know what we see is that over time the models have been getting better and better and better at guessing, right? You know, we can all see what happened when we ran cloud code a year ago versus running it now, right? It’s completely different experience. So like if we want to build a context layer for code, we basically want to follow this exact same recipe um but um just at a slightly different uh level, we’re going to scale this down so that we can build a workshop and you can leave it something that you can actually run today um instead of trying to replicate all of our uh our entire product up

[31:57] here. So we’re basically going to take um that that concept this exact this extract persist and query um to basically go ahead and start extracting information out of our codebase. So we’re going to pull out information about the symbols you know every every type every trait every function uh with its real identity and location. Um we’re going to the relationships between these different symbols get uh edges inside of our graph that we can traverse. These get computed once and they end up being uh queried many times. This is the same

[32:29] uh overall approach as what we do when we’re tackling a sprawling data architecture just with a small enough example to run in this room. Um and so yeah, that’s basically what I was just saying. Uh so we’re going to send it up today. So uh this is going to be a concept that you’re going to hear about a lot during the conference. Um I thought it was I thought it was really cool. I I brought up I made the slide of three levels of context and look at the schedule and someone has a talk about four levels of context tomorrow. So I feel a bit preemptively oneuped but we’re still going to go through this

[33:01] today because I think it’s still uh pretty useful for this context here. Um so you can think about context of a coding agent in sort of this terms of levels. Uh that’s how I like to think about it. So level one is sort of what you just saw. It’s the GP, it’s the glob raw strings, no structure, right? Level two is where you get, you know, most of the code rag solutions that we have today, right? you get um semantic search, you index the code, you embed it, right? You do some nice lexical search, semantic search, you get ranked results, right, of all the, you know, the locations that this this thing was mentioned in. But at the end of the day, that’s still going to be a flat list,

[33:34] right? It’s going to be a flat list that gives you more relevant results. It’s going to be still a flat list. It’s not going to show you the relationships between these different code, right? So the the top level or or the highest level at level three is in our opinion a traversible graph. Um something that links the symbols with real identity and adds typed edges between them that you can actually traverse. Um so I think for the next part of this workshop we’re going to go through and build up through these different levels and by the end

[34:04] we’ll get to an agent that can answer questions about the codebase um using this traversible graph. Um, and uh, yeah, let’s uh, let’s let’s lay it out. So, as we’re building this, there’s going to be a few different uh, stepping stones or rungs to get to that level three understanding of how we do things. So, rung zero is sort of the syntactic level. First, you need to actually parse the code that you’re actually trying to trying to understand. And so, the easiest way to do that is with something that’s language agnostic like treesitter. And that gets you a very basic high-level ASD breakdown of

[34:39] the different um parts of your codebase and how things interact. Um, however, that still doesn’t really have any idea how to deal with collisions, right? Posting list, the symbol that’s in posting list.rs and the posting list, the symbol that’s in, you know, quant.rs are two different posting lists. They’re still going to collide at rung zero. So, the name of the game is going to be rung one. So resolution is the one thing that GP can’t really do, right? It’s the thing that turns those two ambiguous posting lists into two distinct

[35:10] identifiable types, right? So with Rust, it’s a little bit cheating. You kind of get wrong one for free because Rust compiler will do a lot of this work for you. And uh you know, we’ll basically be using Rust do.json to give us all that information basically out of the box. Now, is this cheating? Yes. Um I picked rest on purpose because I needed to build this workshop and you know building an entire parser for a whole other language or trying to build something out is a little bit harder. However, at the end I will talk about a more useful case of this on Python and there’s an example notebook in the repo

[35:43] for you guys to peruse on that if we don’t get to it uh during this workshop today. Um the next layer uh that we’re there is a there is a notebook for I’m not sure we’ll get to today is rung two which is the actual meaning behind um these different things that we’re extracting. Right? So these first two layers are solely deterministic. We’re basically always trying to be ground our understanding in as deterministic a base approach as possible. So we build out these first two layers using tree sitter and rest.json JSON and then if we have some time we’ll explore um how we build

[36:14] out that rung two layer like how do we take that information feed it into an LLM to classify and summarize and do the things that the LLM are good at as opposed to just trying to throw everything in there and asking it to make sense of it. Um fantastic. So, now we’re going to go ahead and open up our uh notebook 01 and

[36:46] going to be confused. Bring this over here. Oh, actually it’s already here. Okay.

[37:17] All right. Can folks see this? Okay. Or did I bring them up on your side? Great. So this uh this notebook, what this notebook is going to do is it’s just going to walk through the process of building up those uh those levels of different context for our uh our quadrant project. We’re not going to be indexing the entire codebase today. It’s a little bit big. Um we’re taking a look at uh a sampling of I think five crates. Um it gives us a good corpus of code to look over uh for our testing here. Um,

[37:49] so we can start going ahead and running these cells. And so what we do first, we just do a bunch of setup. Um, and now that the setup is good, um, we can go ahead and start looking at the, um, yes. Ah, it is in the GitHub. Yes, I can put up the link again to that real quick.

[38:22] bring that up. Um, so the link is up here. Uh, if you don’t want to follow on locally, there are collab links in each of the different um, things there. Yes. Uh, for the collab for the notebook one, if you do want to use the graph, you will need a Neo4j or a login. Okay, that’s just a that’s just a special thing for that. Okie dokie. So, we can see here um there’s a bunch of additional code in

[38:57] here to actually do all of the um parsing inside of uh Rust itself. I’m not going to necessarily go over that today, but it’s all available in the repo for you at your leisure. Um we just have a magical function here today uh extract rust gate that will uh pull out the information from running trees sitter across the rust codebase. Uh and then another one that will go ahead and do the same thing but for our information from uh rest dock. So we can see here that from the first couple of crates we’ve pulled out um a bunch of symbols from freesitter and then a bunch of symbols uh resolved versions of those

[39:30] symbols from Rustto do. So now we can go ahead and start running some code that’s going to start trying to understand why we’re differentiating between rung one and rung zero. So we can see when we ask about something like posting list in rung zero we see that okay we see this exported version of posting list we don’t know actually where that originates from though we can’t resolve that backward into the actual um location where it was defined. Um, so that’s what rung one is for,

[40:03] right? It’s going to be able to say this is posting list. This canonical location is here at posting list posting list posting list and that actual location is at this path at this line, right? So this is what’s going to be able to let us figure out when things get reexported outside of a crate, link it back to the original crate. The agent’s going to be able to traverse all the relationships between those different things um once we actually build out the graph itself. Um so now we’ll just build very quickly the level two that I was talking about before. Um where we have a kind of index where we build a uh very you know where

[40:39] we basically take all the symbols and then we let um a function go that just does a full text search. Right? So we do a search and we get the same problem that we saw in grap. Right? We can search for posting list and we can get very useful information. We can see this is the reexport. This is the strruct. This is the strruct. But it’s still not going to tell us the linkages between those different things, right? And the agent can infer that and it can, you know, make the make the figure out, oh, probably that if the posting list posting list is a strruct, it’s probably reexported up here. So that’s probably

[41:10] the basis of it. But you don’t want it to have to make that logical leap every single time because it’s going to be that one time where it gets it wrong and then it’s going to end up on a completely different wild gase. So our next step here, we build out the we start building out the actual graph um itself. So we take that all those different um uh symbols and uh information that we just extracted, we transform that into a bunch of nodes and edges that we can populate into our Neo4j graph. Um and then we go ahead and

[41:45] actually um load all of that in. So once that’s in the graph, right, we can even go into Neo4j. Um we can see we have you know a beautiful little graph of u references to all the different parts of our uh of our of our codebase here. Um so now that we have this sort of graph, we can build tools that specifically harness the capabilities of the graph to give our agent access to something

[42:16] that’s going to make it easier for it to do its job. So if we build a tool and we want to say okay I want to look at a function and I want to see what’s going to change what’s going to break if I change this function right so we can build this function blast radius where we say okay you pass in the qualified name of something the canonical name it’s going to go in and it’s going to try to figure out walk down the graph through those different edges that we defined try to figure out exactly um what depends on this thing what will break if this thing gets changed right and so in that sense you can ask the

[42:48] agent to answer a question that would have taken multiple grapes across multiple different files and in theory um the agent can just answer it in one in one go. So of course that didn’t work because why would it why would it work live demos everybody? Okay, so you can see here when we say we’re going to pass the blast radius to this strruct, we can say, okay, cool. It’s linked here via it gets returned by

[43:21] this um there’s a method that does this um it gets returned here. So we can see all the linkages that we’re able to establish um just by having the content inside of the graph. So finally um we get to resolve our collision problem. Um now if we say we have two different posting lists and we want to see the difference between them um we can call our new tools blast radius for this posting list and this posting list and we get a different set of results. Changing one posting list is

[43:52] not going to in fact the same level of changes if you impact change the other posting list. So even though these things have the same underlying name, the actual canonical resolution of them results in us being able to more definitively say what’s going to change when you break this thing more than just going a grap reading all the files and then trying to guess. Um so now we can actually take what we built so far and build a very small limited function agent to actually use this new tooling. Um now that we have uh this blast radius

[44:26] tool um we can wire up a very quick little agent. Um this agent is very simple. It’s just a pyantic agent um that uh is just configured with a system prompt that says you have the ability to uh run these different tools and we wrote some very basic tools for it to be able to use blast radius the implementers methods of um that work with very basic cipher queries in order to get the information out. So once you have this information in the graph, writing the queries to actually pull it out becomes quite simple. Um if you can

[44:58] express the idea that you want to uh do then it’s pretty easy to write cipher or the equivalent language in whatever graph you’re using um to actually pull that data out. And that’s a sign that you have created the right schema for the job. So now if you go ahead and run this agent, this agent will actually spend a few turns um calling the tools and trying to figure things out. Uh and then it will go ahead and uh try to answer our question. So for now if we can say what breaks if I change posting list tell me each dependent symbol with its file line and then it will tell me

[45:31] exactly uh what will break um based on the blast radius finding and we can see here what tools it called. It started with search did a couple of searches before calling the blast radius uh and then doing another blast radius call. So we can see that if you have an agent that is uh skilled enough in tool usage um it can use these new tools to uh build out its understanding uh of the codebase. Now this part is a uh more of an interactive section where you can

[46:03] build whatever tool that you would like. Um, how this basically works is we can write a tool that basically executes arbitrary arbitrary cipher. Um, so here we’re wr writing some cipher that says find me all of the um symbols within this file path um and then just lay them out. So this tool will do just that. It will find all of the symbols in this file path and lay them out for me. Um, yes. So then if I wanted to write a new

[46:35] tool, I wouldn’t even necessarily have to know cipher, right? Um I could give the agent access to a system prompt that has a schema in it, right? That tells the agent, okay, these are the different nodes, these are the different edges. And I can write um I can write my um my requests in natural language like I want to see uh every strct that implements a trait along with the trait that it implements, right? And if I run um if I run that uh it’ll take a second because

[47:05] the agent’s going to actually try to figure it out. And it does. It actually generates this cipher query that it can run and it can go ahead and pull that information um out of the uh out of the graph. Um we’re not going to get to uh rung two today, but we can get a little taste of rung two. So so far everything has been very deterministic. Um you get your structure and you get your resolution. Um that third rung I was talking about was the actual semantic meaning. um you can actually have an LLM summarize these symbols and uh embed them. And uh typed defaf actually has an open source library for this called

[47:38] fenic. It’s a dataf frame library for u marrying traditional dataf frame operations and LLMs. Um so here we’re just doing a quick configuration of fenick where we say we’re going to have it talk to open router. Um we’re going to pull in some information. We’re going to say uh we’re going to try to classify this specific rust where we’re going to pull in the kind and the qualified name and the dock string and then we’re going to ask in one concise sentence what is this rust item for and so when we run fenic over this over this corpus over

[48:08] these rows we get back um the summarized outputs from the LLM. So in the long run, right, the idea would be to bake this information up into your graph so that this information is also available um for the agent. And the nice thing about this is that this is auditable when you generate it. So if you’re looking at this and you say, “Hey, this explanation of what this function does is wrong, we can go ahead and actually get that fixed.” If the agent on the fly figures out what that function does and it’s wrong, you’re not going to know that it’s wrong until much later down the line when it writes code that

[48:39] doesn’t work. Um, awesome. Yeah, sure. Um, that was uh notebook one. We have a little bit more stuff to talk about, but I’m happy to take any questions about the content that was there and I’ll put it back up real quick. Yeah, I see two in the back. will come back with a with a microphone

[49:12] >> testing. >> Yeah. Is the is the graph reestablished at runtime when whenever you’re uh building these a uh traversing them? >> Define runtime. runtime as in uh when the PR or CI/CD comes through or >> yeah in an ideal world I mean we don’t build this product for a living but like in an ideal world that would be uh cached and saved in the a canonical form as like say the gold version based on main for example right um and then as you tweak stuff and modify things

[49:44] locally you need to probably maintain a local copy of that that would ideally be incrementally refreshed so you wouldn’t have to do it all up front >> yeah I My question is similar. How do you uh think about the cost of keeping these structures up to date versus you know whatever the increase in performance and fidelity um because at least for me you know I’m using AI to

[50:18] make uh large changes. So >> um so it’s hard for me to know um offhand you know whether it would really be better if you have to maintain these structures. >> Yeah, absolutely. I think I think the key to that is [snorts] um a lot of these problems can be boiled down and incrementalized because a lot of the things that you’re doing is you you don’t necessarily have to reindex the things that you’ve already indexed if they’re not changing, right? And when

[50:50] you’re making broad sweeping changes across your codebase, like you are changing a lot of files, but there also are a lot of files you’re not changing, right? So if we can be smart about incremental reindexing, making sure we’re only keeping track of what’s changed, only reindexing that part, then what we’ve seen so far is that the trade-off is in fact worth it. Especially as the context size of what you need to keep indexed grows, it actually becomes more useful because the cost of doing it live every time just becomes even more expensive. Um, thank you very much. That was super

[51:22] useful. Um, I I’m I’m wondering how it works on kind of like a real code base in a in a messy language where uh for example you have dynamic dispatch or like not not all types are available at you know at compile time or before sorry when you just analyze code. So have you tried like what are the limitations any any any tips there? >> Yes. Um we I do have a third notebook here which is talking about that in particular. Um which is basically how you do this thing on a language like Python which doesn’t have the same uh uh

[51:54] easy access to the types as graph as as rust. Um we use we can use other tooling. We can use tools like griff and same type sitter. Uh types will still give you a and griff will give you static resolution of some of the other stuff like the types. You can use something like tai uh to do type checking as well um to gain more index there. Yes, it does become exponentially harder with different languages. Um, so the and I will talk about that a little bit later on. Um, but yes, the answer to your question is yes, I did think about it. It is a much harder problem which is why I chose to solve a slightly easier

[52:27] problem today. Yeah.

[52:59] I don’t think you should be building this to be clear. [laughter] This this is this is a this is a worked example for educational purposes. um people build these kind of coding uh graph things like on the reg on the regular and I wouldn’t try to build one from scratch um just for doing code analysis. Uh I don’t know if that answers your your question and I think they are worth it to build. I don’t know if it’s worth it for everyone to build their own. I think there is probably a and there are a bunch of folks in this game already

[53:29] that are are doing this sort of you know analysis just on the code layer. Um, so I I would say it’s probably not worth it for a single person to build if for the purpose of being a product. It is probably worth it for someone to build for the purpose of learning about how to build an agent and how to build tooling and how to build context graphs. Um, I think the scale of this problem is like where it that’s where it’s more useful. That answer your question? >> Awesome. >> So uh my question is about the data context layer uh that you talked about earlier. Uh so uh I’m assuming that

[54:03] that’s useful in a for a like a developer. So you develop this this context layer and it helps uh produce uh better SQL procedures whatnot. Um so uh two questions number one um does it have you seen if it helps at runtime as well? Uh like inference time uh that’s number one. And number two, um do you have any benchmark results to demonstrate? >> Yeah, absolutely. So what Yonyi was

[54:33] talking about in his talk, um we do have benchmark results for that. Um what we do is we’ve uh we’ve tried to find a representative data architectures which is kind of hard because you can build an easy tour example like in your in your in your and say oh my agent aces 100% of my tests on my 30 model corpus and it’s like okay that’s not very impressive. Um, so what we tried to do is find uh open source examples of data architecture that was larger. Um, so we ended up settling on using uh I don’t know if anyone’s heard of Mattermost.

[55:03] They’re a instant response and chat company. Um, they actually open source their entire DBT project with like several 300 350 models in it. Um, so we’re actually able to pull that down, simulate data in there, and then run evaluations against specified scenarios inside of that. Um, asking it to do a specific task, break something in there, and then ask it to fix it. ask it uh build out a set of you know new tasks for it to build new things. And we’re able to see that uh by building out our evaluation layer and layering out you know evaluations of how correct it is, how complete it is, um how efficient it

[55:39] is, right? We are able to see that uh with the appropriate context our agents are able to do a better job uh overall and in certain particular categories uh to uh like accomplish those same tasks better than just cloud code running alone. Um >> and what about the earlier question or the other question about inference time? >> So uh it does so you’re talking about like when the agent is actually running. Yes, when the agent is actually running >> that that that’s when we find the most use out of it because when the agent is

[56:11] running, it’s going to be querying all of the tools that we built and that’s where the real value is coming into play, right? If the agent’s able to query all this stuff at runtime, it doesn’t have to rederive it all at one time, right? >> So, um, so then have you tried this with like NL2SQL benchmarks like Bird or Spider or one of those? >> Yeah. So, I mean, yes. And no like yes we have tried it on some of those benchmarks. I think the the tricky bit is that like the goal of the product

[56:41] isn’t necessarily to write analytical SQL. The goal of what we’re trying to build for people is more on the data engineering side. So it’s I think you know it’s it’s sort of a very common thing to say oh we’re going to build text to SQL and we’re going to build that. I think everyone’s trying to build that, right? But it’s I think it’s more important to say we want to build the tools that are going to make it possible for you to actually trust text to SQL, not necessarily that we’re going to be able to, you know, come in and write better analytics queries for you. Like we want to make sure that when you

[57:12] are actually going in and doing your day-to-day work of maintaining your data architecture that we’re going to be able to help you with that. Did that answer your question? All righty. So, popping back into if I can find my mouse. Popping back into the slides. So

[57:44] what we saw there at the end, right, is that when we uh we can actually go ahead and tell these different posting lists apart. Um because we’re able to uh query our new graph context. Um and we’re able to actually figure out all the dependencies of a given symbol with one hop uh without any regrabing. Um and so how the query works you know it matches on the qualifi the fully qualified name and then it goes and finds all the dependencies and that’s it guys great workshop we’re done

[58:17] for the No. So the thing is like this is cool. This is real. This tool is really great right? This tool can do in one call what it would certainly take multiple graphs to do. I don’t think you could accomplish this in one grab even if you did the perfect grab, right? But uh the problem is the agent you have to actually go and give it to an agent. So when you give this agent this tool and you give it this agent this tool and you give it alongside all the existing tools the agent already has and you know we have this really cool

[58:47] tool, we’ve seen it do this amazing stuff and it ran GP anyway. Um, if you give it access to the graph tools and GP, it’s going to run GP anyway. And we gave it amazing tools, a precomputed traversible model of the codebase, and it just ran grip anyway. It just globbed and read files and and ran grip and it would sometimes, you know, poke the graph and get confused and then get back to, you know, waiting through the graph stuff. And this is where no one really tells you. Everyone’s like, “Oh, he built these cool sexy tools and they do this amazing stuff.” But the agent has to actually use them, right? you have to

[59:19] do the prompt engineering and the rest of the hard work and the evaluations. That’s the rest that’s the hard part, right? You can build that notebook. You can build the your amazing graph context in an afternoon. The hard part is actually making the agent use it. Um like the uh yes. Um and why does it do this? Well, it’s because of the post- training. The models are trained and designed to call these tools, right? There are so many circumstances in their training where the answer is to GP and read, right? And you’re not going to be

[59:50] able to necessarily fight that um just by making a better tool. You can make a better tool, but the agent’s muscle memory isn’t going to let it use it. It’s very similar to humans, right? You can build humans a better tool and if you try to force them to use it, you’re going to get they’re going to not be happy about it. This may be a little bit topical. I don’t know. Um, but so the the important thing is that like you’re not necessarily competing on the merits of your tool being better than all the tools inside of the toolb tool set of the agent, right? You’re trying to fight against its own reflexes. So how do you

[1:00:23] actually do that, right? Because I don’t think there’s a question anymore that yes, if it could query the graph, it would be more efficient and better. We’ll prove that later, but how do we actually get it to do that? And the answer is to not really fight the the prior experience of the model, but to kind of lean into it, right? We we make the graph feel more like the model is using grab, right? You know, you you you take this you take a a tool that maybe called query search or query graph or query whatever and you just call it search, right? The agent will use it in a certain kind of way. What I found is

[1:00:55] that if you call a tool search, the agent will basically always do multi keyword search into it with no training. So you have to you have to kind of meet the model halfway, right? You take the model, you name it search, you account for it to adjust for how the model is going to query it and then you just, you know, you get back the results from the graph and you return the results from the graph in the same form as rip grab, right? You take the same tool structure and response as rip grip and you return it back to the model, right? Because that’s what the model’s been trained on. It knows how to handle this stuff and you’re not going to get it to completely change its own self um just by

[1:01:28] prompting, right? And the last thing is that you need to have uh your tools pay off in the first call uh every time because when something when the agent goes down a path and it finds a a dead end or something that it thinks is a dead end. It called search didn’t get any results. Well, sometimes it’s going to think search is broken. If I’m getting no results and like I there should be results, maybe search is broken. I should try another tool, right? It’s not going to go back and use that tool again. And if it does, maybe it tries to use a different way. you can’t really necessarily predict that,

[1:02:00] right? So, you have to do your best to make sure that your tools are returning something that’s super useful uh right away. Otherwise, they’re just going to go back to GP and they are uh never going to come back. So, the second notebook is uh shorter. Um it’s a little bit of a more focused example and this is all about evals, right? How do you actually show that the agent is doing what you think it is supposed to be doing, right? And

[1:02:31] how do you actually um prove that your tool is better than the existing tooling that may already exist, right? So um all of this stuff is already pre-cached. I did this run. If you do want to run the notebook locally, you can absolutely set sample size to um something greater than zero and run the eval live. It will take a long time. Don’t say I didn’t warn you. Um but um I did run this overnight with uh five retries and uh 30 questions in my email set. So that’s where these results are coming from for this uh for this notebook. So

[1:03:05] the first thing that we do um after we do this setup is um we basically uh set up our our test cases, right? So we’re going to have an agent with um our new graph tools. So we’re going to give the agent access to the same model and our graph tools. We’re going to have our control agent that’s going to have access to the same model and a set of file system probing tools that’s maintained by paid by dantic. Um so uh that’s going to be our two test agents and then we’re going to build up a corpus of questions. Um each question is

[1:03:39] a real task. We’re going to figure out okay questions like what public types break if I change posting list? There are two posting lists. What do I use? You know this kinds of thing. um where we try to you know say okay this this should be a question that should be really good for the graph this is a question that’s probably great for for for GP um you know there’s a control questions that are probably be good for both right so the gold data for this is actually we do pull the mind data from the graph we do cross reference it back to the source code so it’s not that we’re polluting the context of the gold with the output of the graph we do have to cross reference it to make sure that

[1:04:11] the gold isn’t cheating um so now we can go ahead and load up our uh our gold and we can basically start trying to set up the actual evaluation structure itself. Um and so this is what lays out what’s basically a good eval question versus a poor eval question. Um you want an eval question that can be in an ideal world your eval should be able to be deterministically scored. I think LM is judge is really fantastic for a lot of use cases but it tends to be overused when you really should be trying as hard as possible to have your scoring be as

[1:04:44] deterministic as possible. It’s going to reduce variance in your runs and it’s going to reduce cost in your runs and it’s going to make you happier in the long term. Um, so you want to create questions where the answers are pullable from the you you’re able to structure an answer to them that’s gold that works. You’re able to check the answer against the gold. Um, you’re not just asking the agent to provide its opinion and then asking another agent to counterman that opinion with its opinion. Those questions don’t really tend to ever work out as emails. Um, all right. So, this is this is some

[1:05:17] craft text. You don’t need to worry about this too much. This is just basically building out the um the the test cases that I was saying. This is the deterministic part where we check to see if it cites all the evidence that we expect it to site. Um, we’re basically just creating a list of examples, creating a list of uh uh things it should be citing, and then checking to see if it’s citing all of them. For the eval themselves, we’re using a configuration in Fidantic Evals just because it’s really easy to set up. Um, how this works is we build out uh our two different agents and then we build

[1:05:48] out um the agent runner itself and then we build out an evaluator, right? So, this is what actually will evaluate the agent based on um the given tasks that we have in front of it. We’ll basically compare what the agent has said to uh what we expect it to for each given task, right? And that way we’re able to compare the responses and the results between our prep agent and our graph agent. Right? So that’s how we’re able to establish um one you know what is the actual utility of our skills and two um how do we uh what what can we improve or

[1:06:23] or what can we do better as time goes on. So um go ahead and run. Right. So I’m not going to go ahead and run the eval again because that would take about 30 minutes. But um we can see that we run the eval the first time and we run it with uh k equals 1. So we just say for every task in our data set onehot graph agent graph agent and we do that the graph agent doesn’t do very well. You can see that the the graph agent it does edge out the the graph

[1:06:56] agent a little bit but it’s not really consistent and it’s not really like very it’s not what we wanted, right? But what we have to understand is that the goal wasn’t ever necessarily to build something that’s going to be more correct than Grap. The goal because all of us that use Grap on a day-to-day basis when we use our coding agents know that it’s very good at what it does. Like it uses GP for a reason. It’s not going to be wrong a lot of the time, right? But the important part is not that it’s right the this one time I tried it, but that when I try it 10, five, 10, 15, 20 times in a row that it

[1:07:29] gets it right every time, right? So that’s how we need to actually start sampling these tests. Now when we say we run these same set of tests over the same corpus five times to get our our pass at K metrics. Now we get a slightly different um answer. Right? So we have two different statistics. Our pass at K and our pass hat K. Our pass at K says across these five runs was this ever correct? Right? In that case they’re still they’re very similar. Actually graph has a little bit of an edge there. Um the graph doesn’t sample any of the private variables only the the private part of the crates only the public. So

[1:08:02] there’s some control questions that the graph will always do better on. Um but what’s really interesting is when you change the metric and say is it doing the right job on every run and when you do that the numbers completely flip right now all of a sudden the graph agent is twice as good at doing the correct thing all five runs than the than the graph agent. Right? Um, and we can see that within that ad that’s doing that stuff and it’s doing it more consistently, it’s also doing it more efficiently, right? We can see that over the course of all our different runs, on average, the graph approach took about

[1:08:35] three and a 3.1 times fewer tokens. The last part is, okay, how do we actually get the agent to use the graph? This is where your evals come into place because you basically have to write an eval that says, hey, examine all the tools in my trace. How often is my agent using the tools that I want it to be using? And then you can get your sort of adoption numbers, right? So that’s how you can get to a place where you can say, “Oh, I wrote a prompt that didn’t have any instructions in it to use the tools. I got 34% of tool usage. I went back. I changed my prompt. Now I have 72% tool usage.” Right? So the eval are the only

[1:09:07] way that you’re going to be able to actually understand that the agent is using the tools that you want it to use. Um, this is just a test of that exact concept. we have uh two different prompts that are both trying to answer the same question with the same tool set. Um if you if you nudge the prompt uh if you just naive it will just go ahead and grap um and then we’ll start doing some other stuff. If you nudge it the ideal world is that it will go and actually run the search and use get symbol to go and figure out the actual answer. [snorts] And so the recipe for building, you

[1:09:40] know, really solid, honest evales is to mine real task candidates out of your corpus. Uh build a deterministic as much as you can, an anchored scoreer. Uh and unit test the scorer itself first. Um I like pantic eval. There’s many many other eval things out there, but the core structure of them is all going to be the same. You have a data set, you have cases, you have an evaluator. Um, make sure you run your evals many times over and reports pass at K, pass at K, uh, and all your different metrics. And, uh, make sure that you are,

[1:10:14] um, putting in examples of things that your agent may not necessarily be good at because you don’t want it to pass 100%. It’s not a useful if your e if your agent’s passing your email 100%. Your email is too easy. Your agent should be close to failing your emails. um like your emails are your chance to see where the metal hits the road and what your agent’s going to do in as hard as possible circumstances. So it doesn’t really do a good anyone any service by just uh giving it very easy and very simple evals. All right, anyone have any questions on this eval notebook before I jump back

[1:10:46] into the slides for the last part of the talk here? Uh I I was just wondering in the example when or when you were discussing how you the agent would often use GP instead of graph. So you had to retool it to be this search tool. Uh was there ever any uh uh like attempt to take the graph and distill it into a file structure maybe like with some loss but a file structure that then GP would naturally search over so you didn’t have to define any

[1:11:17] separate tool at all. >> Yeah. Not today. um just because uh there was a bit of a time crunch but I think that’s definitely a reasonable approach. I mean I think that’s what you know that’s corporate llm wiki you know with all these different things where like you can get a lot of these different benefits just from the markdown corpus but like I and I think that that’s that’s definitely true and I think that you would still get a lot of the same like benefit out of it but I don’t like in the markdown like the ele wiki that kind of stuff right I still think that that your problem is going to

[1:11:50] be like you want real like edges and provenence between them right and like those connection connections are where it’s going to be really hard because even when you do something like Grain or something like that, right? It yeah, it builds all the stuff in markdown, but it does build a graph on top of it for you to see the timeline and the connections and stuff, right? So, I think at some level, you know, if your task is based in needing to understand the relationship between these different things, then at some level you’re you will want some kind of graph. Even though I do think that you can get a

[1:12:20] very similar level of of of of success out of out of an approach like that. Yeah. Um, do you think there’s anything missing from a setup like this for letting non-technical, non-data engineer folks like kick off a PR with this as like the guide through a codebase? Have you tried that or like is there a something you’d add before you empower people to do it that way? >> Oh, like for this as a as a tool? >> Yeah. >> Oh, yeah. I think um that’s a good question. Yes. I I think

[1:12:53] the the the easiest part I mean would probably be like starting to hook it in as an MCP to the tools that you’re already using. So you would have you know this code graph in addition to your your cloud code and you would ask your cloud code to call the graph and do those other things and then you would write skills in order for it to actually use that that that properly. I think that would be the the best approach there. Um, but I would think that yeah, if you’re less technical or non-technical, I I would think that, you know, the the the goal would be that you

[1:13:25] would if you want to engineer the agent to be using these things in the context of actual coding, you would promote these prompt engineering things I’m talking about here into skills and give those to your coding agent. >> Yeah. Like that PR lands at the engineer for the first time with like a halfdeent first pass as a result of being more deterministically guided by this. >> Yes. Yeah. And and I think that the the deterministic part is helpful because you can like like we were talking about before with for the question in the back like you can incrementally just diff over what’s changed, right? Analyze that

[1:13:57] deterministically and get a really really fast conclusion back about what’s what’s happening and what’s been changing since the last time as opposed to waiting for the agent to go and read everything and look at all the changes and and give you back that that same diff. >> That’s one back there. I think >> um did you uh experiment or think through like different uh levels of resolution like one thing I was thinking of um in some of the earlier talks is like you do next token prediction and you will have the prefix I think people

[1:14:32] said which is just like >> you know it’s all at the level of like this is the whole string of everything in the codebase here you have the symbols but like the symbols is a certain granularity, there would be higher levels of granularity that are imminent in the codebase and making those things searchable and accessible to an agent, you know, might have payoffs and I was wondering if you thought about that or if you have thoughts on how you’d approach that. >> It’s a fantastic question. I did think about this. There are uh and I will

[1:15:03] actually talk about it at the end of the at the end of the the presentation. Um there was definitely some follow-ups I would do here. Um if you were actually trying to build this um you would uh uh use something like SCIP to actually trace the call graphs inside of the codebase. You can actually see what is calling what and how that interacts because you can track um like symbols and how they move across the codebase. But if you want to actually track, you know, what is calling what and how it’s all connected, that is the higher level that you’re talking about and you need a you need a slightly augmented approach, another rung of of of deterministic parsing. And you can do that um with uh

[1:15:38] there’s like a Rust or SEIP. Um there’s similar SEP outputs for like TypeScript or or Python. I was going to do all of that too, but I I ran out of time. [laughter] But to answer your question, yes, there’s uh there’s definitely more structure we can extract from a given codebase. And that’s sort of what we try to do at Type Def is find all the different ways that we can pull as much context as possible out of the data that we have. Um I have a quick question for how would you say like this concept of the data

[1:16:12] context layer um combines with like something like a data catalog like is it the data catalog would help feed a graph are they kind of redundant to both have in an enterprise >> I don’t think they’re redundant I think they answer different questions um your data catalog is a a sort of fixed understanding of exactly what is present in your data warehouse which I think is a kind of a different ask than what the context graph is

[1:16:45] trying to extract right um I think they they coexist you read information from the catalog to form the the context graph um so what you’ll try to do is basically say okay I have my say my dbt project that has you know a bunch of models in it those models build out uh uh tables and views in Snowflake, right? So we may say we’re going to ingest that DBT project. We’re going to pull in all that SQL. We’re going to parse it, link everything together, and then we’re going to say, oh, okay, this model is corresponding to this table. Let me go

[1:17:17] query the catalog, get information about that table, pull it into the graph as well. So we can say okay this model is uh producing this physical table and this physical table has these columns and these columns have these these profiles and that information all comes from the catalog right so that catalog of information the information schema the profiling information all that stuff that’s essential to what we do and that’s all in the catalog so I would say they coexist more than it’s a replacement

[1:17:54] >> [clears throat] >> to try to bring this back to like data engineering and this concept of like eval. Do you think about the same way in terms of like asking a set of like data questions and then seeing how those react uh to using the tool or not using the tool? >> Yes. >> Yeah. Yeah. Precisely. Oh, so do you have more? Sorry. >> Sorry. >> Oh, I didn’t know if you were done. Um yes absolutely. Um we do use uh like I was saying we basically try to find like as as real a data corpus as we can and then we try to

[1:18:27] find multiple different kinds of scenarios right so you have your basic fix a bug um where the scenario is we’ll inject a diff into the codebase that introduces a bug right and then you’ll ask the agent to go and try and fix this bug um and then you can you can test you know that it looked at the right places that it checked the right things that when it fixed the bug, it didn’t fix it upstream or fix it downstream, right? By putting a band-aid on it, right? It actually found the right model and fixed the right issue. And you can make those tests easier or harder, right? Like you can you can change the prompt language

[1:18:59] to say, “Oh, I think there’s a problem with model XYZ.” Or you can say for the harder example, my down my dashboard Y is having this issue where this number is doubled. I don’t know why. So forcing the agent to work backward from what that depends on upward through the tree to understand like where the actual root cause the issue is coming from and then asserting that it actually goes in and fixes that problem instead of leaving it be. Um so that’s what’s really hard and the problem is that the agents are always going to cheat. Um so in your eval harness you have to be really careful about hardening it. Um like for

[1:19:32] example your agent if you if you if you say that oh my test case is the agent will check out a branch and then it will go and try to fix the problem. The agent will very happily cheat and use git to go back to main and try to figure out how you introduce the problem in this branch from from there. Um and it’ll go and very happily find the branch name that says this is a test and be like oh this is a test. Oh I can cheat on this. Um, so you have to be very careful about hardening your your your harness to actually uh be representative of the tasks that uh you want your agent to do. Um, because that’s always going to be

[1:20:04] the danger there. >> For these evals, do you find people like encoding them from scratch for the most part or like >> there are there are some existing frameworks for this? Um, ADE bench is is a great one. Um, it’s probably the current industry standard for this kind of like data engineering benchmarks. They have a very similar approach um for uh uh creating problems in repos using diffs and then having the agent go and read data from the warehouse and read data from the code and try to fix the problem. Um so those do exist. People are wiring them up and I think it’s

[1:20:34] becoming more and more of a thing. I think the harder part is always the scaffolding around it because for code you just have your codebase but you need a running database. You need it to be you know populated with the right buggy data. you need that data to be internally consistent. So you don’t want the agent to be spinning around trying to figure out, oh, oh, why are the numbers from 2024? It’s we only generated 2024 data in our sample set. So, um, I think you run into those kind of issues a lot. Um, which is why people are trying to standardize it in AD. I think the hard part is building out a benchmark like that that other people can use that actually has a big enough

[1:21:08] data set. That’s going to be the tricky part and that’s not something that has been done yet. Uh, at least to me. to my satisfaction. Um but yeah, the uh you know, we get back the the this is just a summarization of the same results we just saw. Um, we see that as we uh increase the amount of ability, as we tell, as we get the agent to use the tools more, um, the agent is able to be more consistent in its ability to answer

[1:21:40] the same set of questions and is able to answer those same set of questions um, more efficiently. Um, and an example of why it’s quite important to uh, run these eval multiple times. It’s not about your per shot accuracy, it’s about your determinism. Um, and the number that actually matters was how do you actually make sure that your agent is using the new tooling. And so you really want to make sure that you’re getting your adoption numbers up and that you’re measuring your adoption of the tools because that’s really what’s going to get you um the right the last part of the way, right? You know,

[1:22:13] an elegant tool that gets reached for a third of the time is just a third of a tool. Um, and every capability win that you can get from better tools is gated by this number. Um, so this is why we built the eval, not to prove that the tool is good, but to prove that it’s not going unused. So the hard part about this whole talk was basically admitting that graph works really, really well. Um, I did all this work and I built this context graph and I ran the evals and it was a tie and it was like it was kind of frustrating and you kind of want to keep optimizing and building the thing and trying to get

[1:22:44] more and more out of it and it’s kind of a trap, right? That instinct is like to build more I think can sometimes lead to a sunk cost, right? like the agent has its own reflex. I have mine what I think it should be doing. And like we’re we’re trying to meet in the middle, right, and actually figure out a way to move forward with this, right? And it’s like, oh, you think that maybe it’s just that you just need more testing or more eval or or or more something, right? And you end up with this this false feeling of I just need more rigor and more tests and

[1:23:14] then the agent will be better at using the tools. But you really maybe you need to ask, hey, why is GP working so well as opposed to saying, why isn’t it using my tools? Well, maybe the answer is that GP actually does work really well already, right? So maybe you’re not going to win by fighting it. Um maybe you’re going to have to lean into it just a little bit. So the agent is going to GP anyway. Uh so when you keep trying to out engineer GP, um you’re just going to have a kind of frustrating time. Um, so if you can design as with the grain

[1:23:47] as much as possible, help the agents lean into what they want to do anyway, uh, it will help you out quite a bit. Um, I’ve now done multiple talks where I I I had a conclusion, finished the talk, I said, “Let me rerun one set of evals and then I had to redo the whole talk because the conclusions were completely different.” Um, what had happened was I wrote my evals and I built this whole harness and I built it wrong and I got answers that were directionally correct. They said that graph beat GP, but I wanted to look into it a little deeper.

[1:24:17] And when you look into it a little deeper, you’ll find that oh, that tool that we made, it returns the path to the file, but it’s not like the right path. So sometimes when they call search, it would find the wrong path and it would go down a wrong path to trying to read these files. So you actually have to go in and you read all of your different traces, right? Go into logfire, make sure or go into wherever you’re you’re you’re you’re sending your traces in, right? and make sure that you’re actually reading them because you’re not supposed to just if you just look at your scoreboard, you might get answers that look directionally correct or or that look great for you, but they may be

[1:24:49] burying underlying issues that are actually much larger and and a much bigger problem inside of your inside of your architecture. Um, so the fix is never just to make the number higher or nicer. It’s to build a harder eval. Uh, read the traces, find the artifact, harden the harness, and then rerun. Never trust your own scoreboard. Um and yeah, these are just some of the other thoughts I had. The you know, the the models are only getting better. Um so, you know, you can you we can continue to believe that the models are going to have less variance and more drift towards being able to uh do the

[1:25:23] work that we want them to do. So, the easy part may actually be building the context, building the tools. The hard part is really going to be building out your eval loop, building out your monitoring observability, and making sure that your agent is actually doing the work that you want it to do with the tools you want to be using. Um, and so all the stuff is there, the the score, the loop, uh, it’s all in the repo if anyone wants to play with that um on their own time. And uh, yeah, so this is what I was talking about before. You know, we did I did cheat by using Rust. Um, the Rust

[1:25:55] compiler already did run one, right? It’s homework. uh picked the language that was free because the lesson was you can build a well-engineered version of something uh on a relatively easy instance of a problem. Uh and in general, if you can get things to work on a slightly easier problem, it’s slightly easier to go and scale that back up to a larger issue. Um so we now can say, oh wow, we have this really easy thing that we did for Rust. I want to do for Python. And Python is just harder, right? like you can’t really if

[1:26:25] you look at this these lines of code you’re not going to get anywhere right if you GP client you’re going to get stuck in a land of 50,000 clients you can use static analysis to to load this stuff and things like that but you know you’re going to have like a a difficult time and so this is where a code graph really earned its keep right in a case like this where the code bases are larger and the the languages are are less well structured a code graph is actually even more effective than grap in that case Because for Rust, you know, I want to see what implements this trait. Okay, cool. I just search imple,

[1:26:57] right? Like it’s really easy. But for Python, that becomes a lot harder, right? So that’s where um you really would need to build something out that’s more robust. Um but the structure of the graph and the structure of the tools doesn’t necessarily have to change, right? It’s the structure of the way that we ingest and load the data. And if you make that modular, then you can build that out with for as many languages or as many projects as you want, right? So that’s kind of the underlying structure for like how type def works is we try to build out as many of these different injest processes as

[1:27:28] we can to pull data from many different places and then sort of synthesize it all into one. Um there is another notebook for this. I’m not going to go through it now because we’ve we’ve all been here for a little while and um but feel free to go through it yourselves and uh try to learn more if you want. Um, last thing I was talking about was uh basically the meaning rung. Um, the structure is what’s telling you what’s connecting to what, but the meaning is telling you sort of why. Uh, so there’s comments in code and dock strings and all the other information. And folding all of that information in helps us

[1:28:01] understand the intent that the coders had when they wrote the code in addition to the actual structure of the code that currently exists. So um we would basically want to add in small uh passes like this to say basically summarize the information from the code um and uh understand it at a at a rowbyrow basis so that search can now answer what the code does in addition to um its location. Um, and the important part when you’re building

[1:28:31] something like that, um, you’re building this sort of auditable semantic analysis that’s going to go on top of your deterministic analysis. Um, the important part is to ask many small questions and not one big question. The answer isn’t writing, you know, one really. So when we do analysis in SQL, right, we don’t just write one really really good prompt and throw your SQL at it and say, “Hey, uh, here’s a pedantic model with 10 fields like give me all the information you can out of the SQL, right? It’s not going to work.” Um, a model is going to hallucinate. It’s going to invent information. It’s going

[1:29:03] to not collect as many as much information as it can. You want to basically instead of asking the model 10 things about a chunk of code at once, you ask the question, you ask the model 10 different questions about 10 different smaller chunks of the code that answer specific questions. Right? You parse the structure deterministically and then ask many small focused questions that play to the model strengths. Right? Instead of asking the model to look at an entire block of SQL and tell you what all the columns mean, you parse the SQL and you say, “Hey, here’s one column. Here’s its

[1:29:33] type. Here’s the comments.” you know, is this a measure, is this a dimension, is this blah blah blah, then at that point, you can ask the agent to do classification and summarization, and those are the things that it’s good at, right? And if you want to weigh it more, um, you give it a more you give it more, um, prompt input to that single focus question. You don’t try to cram more questions in. You give it more context for it to be able to answer that single question with. So, small, grounded, auditable, semantic operations over our structure. Um, that’s what our library fenic is for, and that’s what we use it for as part of typed. Um, so that last ladder, the last rung in the ladder,

[1:30:07] which is basically we have all this stuff layered in, we know how everything links together. Now, what does it actually mean? Now, we can start pulling some of that semantic understanding of what things actually mean by using the LLMs in the last layer. Um, yes, so as I was talking about before, there’s a lot more that you can do here. you actually want to build a coding uh codebase graph analysis agent um you can use you can leverage call graphs to understand who’s calling this and who references this right and you know it can read the comments inside and outside of the code and it can fold in

[1:30:39] pros from outside of the code it can fold in GitHub issues and and and your your your linear issues and your ticket and stuff so that’s how you build this into something that’s actually more useful and and and grounded is you start small you start with the smallest possible thing you can do which is how do extract structure out of this codebase and then you move on to how do I take that structure and layer into it the semantic understanding that’s really going to help um build out a substrate that’s useful for agents to actually work inside of this space and

[1:31:09] make changes um and that’s sort of what the uh where the end goal is there so anyway thanks so much guys uh this has been my talk the agent will grip anyway um I’m Brandon calendar from typed defev here is the link again for the workshop and my LinkedIn. So, uh, thanks. I can take any questions or or any other things, but thanks so much everybody. [applause] Any any questions? I can pass around the mic for one last round.

[1:31:45] Have you tried this kind of workflow like across repositories where things might be either microservices or like going across HTTP boundaries um with any success? >> Uh not particularly no. I think where where this would where this would come into play is on a single repository. Now we do that at scale for our like larger data architecture product. So I think it can work. um you basically just need to the hard part there is obviously figuring out the boundaries between the different things. So you basically need to write

[1:32:17] boundary classifiers that basically try to understand um I was trying to do an example with Python and Rust like with maturin so you could have a similar example of that but I didn’t have time to do that but you basically would need to write some way of understanding when rust called code is calling Python or vice versa right to try to like build out that linking layer between them. Um, so yes, you could do it. I just think it would it would require quite a bit more work. >> Any other questions?

[1:32:49] >> Sorry. >> I got you. >> Cursor does a lot of the um level two stuff. So like when you see like cursor doing searching through your codebase, it’s usually doing semantic search like vector embedding search um and that’s that’s that that does work really well. Like I think that there’s a reason why there’s all these like code rag products that do um that do semantic or retrieval, right? And I think that that’s that’s really great. It’s just that you know that approach does get you

[1:33:22] the ability to find all the places in the code that you want to then examine with the agent. But you still have to go and examine all those places with the agent. Right? The goal of this approach is if I want to be able to in one shot say I know that I’m looking at this function right here, right? I basically want to be able to do the IDE click on the thing to see all its call sites but within my agent without my agent needing to grab everywhere. That make sense? Any other questions? All right, one over here. One sec.

[1:34:00] >> I imagine half the fun of building this is that you end up with uh I don’t know, sort of a a vector of the graph that you can recall. >> Yeah. Um, I’m really curious about caching and storage as you really start thinking about scaling this given that uh making sure the agent gets it right on the first call is so key in steering. >> Yeah, that makes sense. I think um as you as you start to scale out your um your graph database, you’re going to start finding that it’s uh it can get very hairy very fast. And

[1:34:33] you’re you’re absolutely correct. Um, so I think storage is less of a concern, I think, because like these like yes, these graphs do tend to uh get a little sprawly, especially as they grow and they’re harder to kind of garbage collect for, but they do um I’m less concerned about storage and more concerned about like the amount of compute that it takes to actually run like graph analysis across all these different things. Um, so I think the as you try to scale it up, I think the main thing you need to focus

[1:35:05] on like I was saying earlier is the incrementalism like figuring out how to rebuild this thing without regenerating all of it I think is probably the most important part and that’s something we fight with at typed death for trying to build this you know at a larger scale across a large data architecture because you don’t want to have to go and query your warehouse you know 500 times again right you don’t want to have to go check you know compile and all the SQL code again right? You want to be able to update the stuff as it’s coming in. So, I think the most important way to get around that is design for incremental

[1:35:37] indexing first as a first class citizen. Um, that’s kind of the only way you’ll be able to get to a point where you have to deal with the other scaling problems. I I think you have to deal with the problem of reyncing the information long before you have to deal with the actual scaling problem of how to store it or how to compute across it all. Yeah.

[1:36:09] I found that for us the building of the golden data set and maintaining it is always the biggest problem. >> Yeah, I I agree with you 100% to that and I I that’s that is one I don’t have a great answer for. I don’t think because I I think I think so. I have two answers to that actually. One of them is that like the the gold data set isn’t necessarily if I was if I was actually building this

[1:36:43] and I was testing it on something, I would pick a specific snapshot of that codebase, right? So I could say that this is my snapshot and that when I run my evals, I have a deterministically set of my gold questions, right? That’s that’s kind of a copout to your question, right? But that’s what I would do if I was actually designing this, right? Is I would I was I would design this against a snapshot. So my eval questions are deterministic and aren’t changing over time, right? Because the in an ideal world, right, your agent is going to get better and better at answering the same questions. But

[1:37:16] changing the questions based on changing the corpus doesn’t actually help you evaluate if your agents are getting better. Right? The best way to evaluate if your agents are getting better is have them do the exact same task again. Right? So it’s sort of your answer is you don’t because you if for the purpose of evals your gold should be constant against a constant corpus. Does that make sense? Now if you’re testing live yes like you need to be able to figure out new ways of testing it against your codebase but I think that and like your fixed eval set are different things. Does that make sense?

[1:37:47] >> Yeah. But like >> Yeah. Yeah, but I mean like if you so if we go with the codebase example and let’s say you have a kind of like a complicated bug that is like a function that is calling a different function and there’s like a in one of those functions there’s like a bug or there’s like a if like you said and then you have an eval that tests that if this bug was introduced then the agent went to this and this function and fixed it but then you do a refactoring and then this function is actually not even used

[1:38:19] anymore. So then the eval breaks. >> Oh, I see what you’re saying. So in that case, I I think it’s still I think man is still the same because what what I’m saying is that I when I’m building my eval, I would say my eval is a combination of a specific commit that like my code is at, right? The specific diff that I want to introduce to see if the agent can fix it and then the resultant set of changes from that prompt, right? that should always stay constant. Like I I I see what you’re saying because like yeah, the code’s

[1:38:51] going to change and it’s not going to need to fix that issue anymore, but that’s not what you’re testing, right? You’re not testing the agents ability to react to the new code. You’re testing the agents ability to make that change based on that circumstance that it was in when you wrote the eval, right? Like that task I don’t think has changed. even if like it’s not relevant anymore, it’s still testing something that’s useful. Does that make sense? Question. >> Great talk. Loved it. Um

[1:39:26] this kind of reminds me of ontology um >> classes and this is what Palunteer is making a killing out of actually. >> Yeah. So um once you build the ontology and the dependency um can we talk with the LLM in these higher constructs now? >> Yes. >> With these classes. >> Yes. >> And is this is essentially what you were kind of insinuating at this point in time. >> Yes. So that that’s that’s basically our product, right? Is is is that concept of we we build um we build the context

[1:39:57] graph, we build the ontology layer on top of it where we try to organize all the information around your data architecture, where things are pointed, where things live, what things mean. Uh and then we give the agent access to that tool set to be able to answer questions and and make changes within your data architecture. Excuse me. All right. So, this may be a little naive, but so going back to kind of the product you guys are building, what you demonstrate here. So, a lot of stuff you

[1:40:28] showed here is very similar to what Neo4j showed at an earlier workshop, >> which is like, okay, here’s a knowledge graph. You as the developer with domain expertise are going to build a knowledge graph in this area and they’re going to provide it out to an agent and then make that agent query cipher to get some semantic information. I’m responsible for I’ll call it a sprawling data state where there’s a lot of you know thirdparty software as a service. We’ve got other things that are internal. My team is responsible for you know bringing in a lot of information for those things. Some of them are highly configurable like a salesforce. You bring the information in you put in the

[1:41:01] data warehouse etc etc and eventually you have business intelligence on the other side and also interaction. So the goal is how can I get my pretty small team of pretty good people to define the semantic relationships between all of this stuff in one place one time with good version control so that then I can both provide context and tools to AI agents for mostly analytic work but also write back occasionally and documentation for human beings that have

[1:41:33] to use these systems whether that’s onboarding or in one place I haven’t figured out the right way to do that yet. >> Let’s chat. >> So, are are you gonna are you going to give me that? Because you also said like you’re not going to do the data catalog, >> right? >> No, we’re not gonna Well, the thing is I think I think what what I what I mean to say is we’re not going to be limited to the data catalog because we’re not going to be reading just your Snowflake catalog, just your, you know, just your warehouse information. The goal is to be reading from your Salesforce, from your from your from your fiverr, from your your um your air bite, from your whatever, from all the transfer

[1:42:05] mechanisms that you have, your airflow, your whatever. Incorporate all of that into a single understood context tree uh context graph that we can actually traverse. And then from there, the goal is to actually um distill out of that the things that you were talking about. The the main problem with semantic layers is no one wants to maintain a semantic layer, right? It’s kind of a dirty word because once you build one, it’s all really cool and and sick and sexy, but then you have to actually keep updating it every time you add models and it’s it’s a nightmare. So, the goal of of typed defe is you

[1:42:36] really just it was great. Thank you for like gave a great understanding of exactly what we want the product to be able to do, which is exactly what you said is to be able to reason across all those different layers that you have in your data architecture. build off a single graph that you can actually trace how data is flowing from Salesforce all the way down to your BI layer, right? And then when your agent asks when you ask your agent a question like hey um where is my like where is AR being coming from it’s able to say oh yeah great that originates up here in the Stripe data actually and then it gets

[1:43:07] joined into your Salesforce lead data down here um to like figure out you know this this thing down here which is where the actual data is being calculated. Yeah, like that that lineage is very useful, but and for us it’s, you know, partially for the data team that’s doing this development. Great. But I want that to eventually be accessible to the business, right? So they can look at their report and we’re like, that’s not right. What the hell? And then rather than coming to my team and to yell at me, they can say, “Oh, well that’s actually owned by these people on this system and this record and like this is the person I have to talk to.” >> So >> keep it all together. >> Agree 100% with you. And I think that

[1:43:39] what we’re trying to like one thing that the LLM sort of let us do at a high level that wasn’t necessarily possible for is be that kind of translation layer that sits between the business folks the the accountants the analysts right the and then the data analyst the the engineers or whatever things like that right because what we’re saying is what we want to say is okay that business analyst says hey that number in that revenue dashboard is wrong right so the process for them to fix that would be Okay, I need to go file a ticket

[1:44:10] somewhere that gets sat in some system. Someone picks it up, triages it, tries to figure out who owns that thing, right? Sends it over to them. They had to talk to the original. So I it’s crazy, right? I worked at Salesforce for this, so I I get a little bit of that on a high level. Um, so the goal with type defeaf, right, is for able that that that business analyst to be able to go in and say, “Hey, my dashboard is broken, right?” They can put in the name of the dashboard. We can pull up the dashboard, you know, in that session where they are. we can trace back and say, “Oh, yes, I think I see what’s the problem here. It’s probably that, you know, I’m multiple layers back, this is

[1:44:43] happening. I can detect that this team owns this this model up here. I can go ahead and file that ticket for you with the resolution information already encaptured in it because I already can see what the root cause is because I looked back three layers and saw that this select was wrong, right? I can trace that back to a change that landed last week, you know, oh, that’s why this broke here, right? And oh, we can see when that happened because there was an airflow run over here that actually populated that data, right? That’s what we’re trying to get to is that unified vision of how everything works from runtime through lineage and being able for the agent to actually answer those

[1:45:15] kind of questions and sit between those different layers and make that process more efficient because if if instead of that ticket sitting around and bouncing between five different teams, if it’s immediately understood understood what the problem is and that gets routed to the right team right away, that team can even just in an ideal world where our product is ubiquitous and works perfectly, that team can just say, “Yeah, that solution sounds good check plus and then it goes and implements it. Right? So that’s that’s the dream, right? So but that that’s kind of what we’re trying to to get to and we’re we are definitely part of the way there. So be happy to chat.

[1:45:49] >> All right. Any final questions? Cool. We’ll be around if you guys want to connect with us on LinkedIn or anytime. We love chatting about context graphs generally. So thank you guys so much for coming through and we’ll catch you guys later. >> Thanks everyone. >> [music]