Pular para o conteúdo

Transformers.js v4.3: saída estruturada no navegador

Ver no idioma original →

Autoria: Hugging Face · 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: Transformers.js v4.3: Structured Output in the browser · Canal: Hugging Face · 10:46 · Assistir no YouTube

Tópicos: Engenharia de prompts e contexto, Ferramentas de desenvolvimento com IA, Geração de conteúdo com IA, Modelos locais e open source

Transformers.js v4.3 traz a saída estruturada como principal novidade, além de melhorias na documentação, correções de bugs e recursos contribuídos pela comunidade. A saída estruturada usa constraint decoding para restringir os tokens gerados e fazer com que o modelo siga formatos como JSON Schema ou regex. O recurso foi implementado em um pacote separado, que se integra ao processo de geração por meio do logits processor. A implementação em JavaScript foi otimizada para reduzir o custo de desempenho e eliminar a necessidade de baixar uma biblioteca WebAssembly de 1 MB.

  • Transformers.js v4.3 inclui saída estruturada e melhorias no processo de geração da documentação.
  • Sem restrições, o modelo pode produzir JSON em formatos variados ou recorrer a XML e Markdown.
  • Constraint decoding filtra tokens que não atendem às restrições definidas pela aplicação.
  • O pacote Transformers structured output oferece suporte a JSON Schema e regex.
  • O JSON processor recebe o tokenizer e o schema esperado para orientar a geração.
  • O logits processor permite interferir na etapa de decodificação, antes da escolha do próximo token.
  • A saída estruturada permite integrar os resultados diretamente a aplicações que esperam uma estrutura específica.
  • A implementação JavaScript de LLM guidance eliminou a dependência de uma biblioteca WebAssembly de 1 MB.
  • Os testes apresentados indicam que o overhead de desempenho do constraint decoding é mínimo.
Início Capítulo Resumo
00:00 Novidades do Transformers.js v4.3 O vídeo apresenta o lançamento, destaca a saída estruturada e comenta as melhorias na documentação e as contribuições da comunidade.
02:09 Como funciona o constraint decoding A geração token a token é explicada como base para filtrar tokens e impor restrições à saída.
04:17 Integração com JSON Schema e regex O pacote novo se conecta ao logits processor para gerar JSON com estrutura definida ou texto compatível com uma regex.
06:55 Exemplo de gerador de receitas Um exemplo mostra como uma estrutura JSON previsível pode ser interpretada pela aplicação para preencher sua interface.
07:27 Implementação e desempenho O vídeo descreve a implementação JavaScript de LLM guidance, as otimizações e os resultados de desempenho, e encerra convidando o público a testar o pacote.

A ideia do constraint decoding é remover os tokens que não são permitidos. — 02:09

Agora ele não gerou Markdown; gerou apenas JSON. — 05:19

Eu conheço a estrutura exata que ele vai gerar e posso confiar nela. — 06:55

Quase não temos overhead. — 09:38

Adoraríamos receber seu feedback sobre esse novo pacote. — 10:12

  • Transformers.js (ferramenta): Biblioteca usada para executar modelos e geração de texto, que recebeu a versão 4.3 apresentada no vídeo.
  • Transformers structured output (ferramenta): Pacote adicional que permite aplicar restrições à saída gerada por Transformers.js.
  • structured output (conceito): Técnica para fazer o modelo gerar resultados que sigam uma estrutura especificada, como JSON ou uma expressão regular.
  • constraint decoding (conceito): Método que remove da seleção os tokens que não satisfazem as restrições definidas para a geração.
  • logits processor (conceito): Ponto de integração no processo de geração que permite filtrar os tokens possíveis antes da escolha do próximo token.
  • LLM guidance (ferramenta): Biblioteca mencionada como base para validar as saídas geradas em relação a restrições.
  • JSON Schema (conceito): Formato usado no exemplo para especificar a estrutura JSON que a geração deve seguir.
  • regex (conceito): Formato alternativo usado para restringir a estrutura do texto gerado.
  • Hugging Face (empresa): Empresa responsável pelo canal que apresenta o lançamento de Transformers.js v4.3.
  • Gemma 4 (modelo): Modelo citado nos testes de desempenho do constraint decoding.
  • Granite (modelo): Modelo citado nos testes de desempenho com diferentes tokenizers.
  • LFM 2.5 (modelo): Modelo citado nos testes usados para avaliar o desempenho do recurso.

00:00 Finalmente lançamos o Transformers.js versão 4.3, e ela vem recheada de recursos muito legais. Sim, já fazia um tempo desde a versão 4.2, e o principal motivo foi que passamos bastante tempo trabalhando na nova biblioteca Hugging Face Canvas, lançada na semana passada. Mas vamos dar uma olhada nesta versão. Como vocês podem ver nas notas de lançamento, a grande novidade é a saída estruturada, e vou passar bastante tempo falando sobre isso no restante do vídeo. Também assumimos ou reformulamos completamente nosso processo de documentação, ou

00:31 o processo que gera a documentação do Transformers.js. Fazemos isso — ou, na verdade, sempre foi assim — gerando a documentação a partir dos comentários e do sistema de tipos que temos no nosso código. O problema é que isso dependia de algumas bibliotecas que estavam um pouco desatualizadas. Elas não entendiam completamente o sistema de tipos que temos. Então, agora temos um sistema totalmente novo, escrito em grande parte por nós, à mão, que faz isso por nós. Também temos algumas novas correções de bugs,

01:02 alguns novos recursos, e a maioria foi disponibilizada por pessoas como vocês, pela comunidade, e estamos muito felizes em ver tanto envolvimento da comunidade. Estamos muito felizes em ver tanto envolvimento da comunidade neste novo lançamento. >> [ofega] >> Mas vamos dar uma olhada na saída estruturada. Vamos ver este exemplo. Temos um system prompt que instrui o agent a extrair dados pessoais de um prompt de entrada e, em seguida, temos o prompt do usuário com informações sobre esse usuário.

01:34 Se clicarmos em gerar, ele aquece o pipeline e, em seguida, gera um trecho Markdown com um bloco JSON, e ali temos um JSON com nome e idade. Parece bom. Quer dizer, podemos analisar isso. Podemos remover as crases e o JSON, e então ficamos com o objeto. Podemos trabalhar com ele. O problema é que nunca podemos ter certeza de que ele sempre vai gerar o mesmo bloco JSON. Talvez mude a estrutura do JSON. Talvez nem use JSON, porque

02:09 pode querer usar XML ou Markdown. Simplesmente não sabemos. É aí que a saída estruturada tem um papel importante. Então, se olharmos para este pequeno gráfico, quando um LLM gera texto, ele faz isso token por token. Basicamente, uma execução do modelo retorna uma lista de tokens com as pontuações de probabilidade de cada um ser o próximo token. Aqui temos o texto “Quando Stanley

02:41 Boyle nasceu?”. E o que vocês podem ver no lado esquerdo é que temos alguns tokens que poderiam ser o próximo token. Temos born, birth, date, birthday e given. Outra coisa que podem observar é que alguns estão em verde e outros, em vermelho. Ou seja, alguns são permitidos e outros não. Born, por exemplo, não é permitido. Birth não é permitido. Birthday não é permitido. Então, ele precisa escolher date. O próximo token é of. Of é permitido, então ele pode escolher este. A ideia da decodificação com restrições, que é

03:12 basicamente o termo técnico para saída estruturada, é que, em vez de escolher um dos tokens disponíveis, ele remove alguns tokens que não são permitidos por determinadas restrições. Se olharmos para a implementação, podemos ver aqui o pipeline do Transformers.js. E agora temos um novo pacote chamado Transformers structured output. Vamos dar uma olhada no nosso repositório do GitHub. Na versão 4, também reorganizamos o repositório do GitHub.

03:46 E ali temos esta nova pasta packages. Durante muito tempo, dentro de packages, só tínhamos o pacote Transformers. Agora, também temos o pacote Transformers structured output. A ideia dessa reestruturação é que conseguimos — ou podemos — distribuir novos pacotes que podemos usar como plugins ou complementos para o Transformers.js. E este pacote de saída estruturada é basicamente o primeiro que distribuímos.

04:17 Então, podemos importar o processador de saída estruturada deste pacote Transformers structured output. Se olharmos para o código, podemos ver aqui o nosso processo de geração, que adiciona novos tokens ao streamer. E, no momento, não temos nenhuma restrição. Agora, também temos este novo processador JSON. O novo processador JSON vem do processador de saída estruturada. Precisamos passar o tokenizer e, em seguida, o esquema JSON, ou seja, o esquema exato que

04:48 esperamos. E o legal é que, para distribuir esta biblioteca, não precisamos — ou não precisamos fazer — nenhuma alteração no Transformers.js, porque o Transformers.js já oferece suporte ao processador de logits. Basicamente, ele permite conectar-se à última etapa de geração de um novo token, a etapa de decodificação. Na etapa de decodificação, temos todos os tokens que são — ou poderiam ser — possíveis próximos tokens. E podemos nos conectar aqui, e

05:19 basicamente remover todos os tokens cujo uso não queremos permitir. Então, podemos passar este novo processador JSON para o processador de logits. Pronto. Se eu executar agora o mesmo exemplo novamente, pronto. Vocês podem ver que agora ele não gerou Markdown; gerou apenas JSON, porque começa com chaves. E, em vez de name, escolheu full name. Por quê?

05:50 Porque dissemos a ele para usar full name como propriedade, e essa propriedade é obrigatória. Então, basicamente, forçamos o modelo a seguir exatamente o mesmo esquema. Também podemos fazer o mesmo com uma expressão regular. Assim, em vez de usar o tipo JSON schema, também podemos usar o tipo regex e passar a expressão regular. O que acontece em seguida é que, quando clicamos em gerar, ele gera uma expressão regular — ou gera a pessoa

06:22 como Bob Johnson, e a idade será 25. Então, basicamente, temos o mesmo system prompt e o mesmo prompt do usuário, mas, como forçamos uma estrutura muito específica, ele gera a estrutura que queremos. A decodificação com restrições nos permite ser muito específicos quanto à estrutura esperada. Se olharmos para um exemplo dife- diferente, criei aqui um pequeno gerador de receitas. E agora posso

06:55 gerar uma nova receita, e ele vai gerá-la seguindo exatamente a estrutura JSON que quero que siga. Temos um título, uma descrição e os ingredientes; e, se olharem para a resposta, esse é o JSON que ele gera. Posso então analisar o JSON na minha aplicação, e ele preencherá o layout que tenho. Isso só é possível porque sei exatamente qual estrutura ele vai

07:27 gerar e posso confiar na estrutura que ele vai gerar. Vamos dar uma olhada em alguns detalhes técnicos por trás desse recurso. Poder passar uma estrutura que ele precisa seguir é uma questão que está em aberto há alguns meses. Então, eu queria muito ver como poderíamos resolver isso. O principal problema é que temos a restrição fornecida pela aplicação e, então, precisamos ter

07:58 algum tipo de pacote que valide novas strings em relação a essas restrições. Então, já temos strings geradas, saídas já geradas, e precisamos validar todos os tokens em relação a essas restrições a cada etapa. Existe uma biblioteca chamada LLM guidance que já vem sendo usada para isso há algum tempo, e muitos projetos já se baseiam nela. A API de prompt a utiliza.

08:30 Também temos VLLM e llama.cpp. Então, ela é bastante conhecida na comunidade. O primeiro passo, como protótipo, foi basicamente criar um wrapper WASM em torno da implementação Rust do LLM guidance e, em seguida, criar também um pequeno adaptador que se conecta ao processo de geração dentro do Transformers.js. Fiz isso, e funcionou. Era bastante lento e também precisávamos baixar esta biblioteca de 1 MB como um arquivo WebAssembly, o que não era ideal. Então, o próximo passo é basicamente

09:05 criamos uma implementação de guidance para LLMs inteiramente em JavaScript, claro, com a ajuda de muitos agents de programação. E isso deu muito, muito certo e nos permitiu eliminar completamente a biblioteca JavaScript de WebAssembly de 1 MB. Mas não paramos por aí. Também investimos muito tempo no desempenho, porque, como você pode imaginar, sempre que um novo token é adicionado, precisamos basicamente regenerar toda a máscara de strings permitidas

09:38 seguintes e, então, precisamos comparar essa máscara com os tokens gerados pelo modelo. Foram necessárias algumas iterações, mas, como vocês podem ver, criei um pequeno caso de teste com o modelo Gemma 4, o modelo Granite e o modelo LFM 2.5. Então, temos modelos diferentes, tokenizers diferentes e casos de teste diferentes. E, se olharmos os tokens por segundo e a diferença, podemos ver que praticamente não temos sobrecarga. Então, sim, às vezes fica

10:12 um pouco mais lento quando o constraint decoding está ativado, mas a diferença é mínima. E temos muito orgulho de agora contar com um pacote super-rápido que deve funcionar com qualquer tokenizer. Agora, se formos ao repo, vocês podem ver que temos algumas instruções aqui. Todos os schemas compatíveis estão listados: temos JSON Schema e também regex. A documentação está aqui. Fiquem à vontade para testar. Adoraríamos receber seu feedback sobre

10:46 esse novo produto, esse novo pacote. E, sim, muito obrigado por assistir. Confira a nova versão, Transformers.js versão 4.3. Ela já está disponível. Experimentem e divirtam-se. Obrigado. Tchau.

Transcrição original

[00:00] We finally released Transformers.js version 4.3 and it’s packed with really cool features. Yes, it has been a while since 4.2 and the main reason for that is that we spent a lot of time with the new Hugging Face Canvas library that released last week. But let’s have a look at this one. As you can see from the release notes, the big one is structured output and I will spend a lot of time talking about this in the rest of the video. We also took over or made a complete overhaul of our documentation process or

[00:31] the process that generates the Transformers.js documentation. We do that or it has always been the case that the documentation was generated by the comments and the type system that we have in in our code. Problem is that it was based on a couple of libraries that have been a bit outdated. They didn’t fully understand the type system that we have. So now we have a completely new system mostly written by us by hand that does that for us. We also have a couple of new bug fixes,

[01:02] couple of new features and most of them have been provided by people like you, by the community and we are super happy to see that so much community engagement. Um Um that that we have so much community engagement in this new release. >> [gasps] >> But let’s have a look at structured output. Let’s take this example here. We do have a system prompt very instruct the agent to extract personal details from a given input prompt and then we have the user prompt with information about that user.

[01:34] If we then click generate, it warms the pipeline and then it generates a markdown snippet that has a JSON block and there we have some a JSON with name and age. That looks okay. I mean, we can parse that. We can strip out the backticks and the JSON and then we have the object. We can work with that. Problem is that we never or we can’t be sure that it will always generate the same um block of JSON. Maybe it will change the the structure of the JSON. Maybe it won’t use JSON at all because

[02:09] it wants to use XML or markdown. We we just don’t know. That’s where structured output plays a big role. So, if we have a look at this little graphic here, when an LLM generates text, it does that token by token. So, it basically one run of the model um returns a list of tokens with the scores how likely it is that this one should be the next token. Here we have uh a text “When was Stanley

[02:41] Boyle born?” And what you can see on the left-hand side is that we have a couple of tokens that could be the next token. We have born, birth, date, birthday, and given. Another thing that you can see is that some of them are green, some of them are red. So, some are allowed, some are not allowed. Born, for example, is not allowed. Birth is not allowed. Birthday is not allowed. So, it has to pick date. Next token is of. Of is allowed, so it can pick pick this one. So, the idea of constraint decoding, and that’s

[03:12] basically the technical term of structured output, is that instead of picking one of the available tokens, it will strip out some tokens that are not allowed by some constraints. If we look at the implementation, here we can see that we have the pipeline from Transformers AS. And now we have a new package called Transformers structured output. Let’s have a look at the um at our GitHub repository. In version four, we did some re-rearrangement in the GitHub

[03:46] repository, too. And there we do have this new packages folder, and inside packages, for the longest time, we only had the Transformers package. Now, we also have the Transformers structured output package. And the idea with this restructuring is that we were able or we are able to actually ship new packages, um, that we can use as plugins or as as add-ons to Transformers.js. And this structured output package is basically the first one that we that we shipped.

[04:17] So, we can import the structured output processor from this uh Transformers structured output package. If we look at the code, we can see that here we have our generation process that adds new tokens to the to the streamer. And right now, we do have we have no constraints. Now, we also have this new JSON processor. And the new JSON processor is coming from the structured output processor. We have to pass the the tokenizer, and then we have the JSON schema, and the exact schema that we

[04:48] expect. And the cool thing is, to ship this library, we haven’t or we didn’t have to change anything in Transformers.js, because Transformers.js already did support or did support already the logits processor. And that is basically a way to hook into the last step of generating a new token, the decoding step. And in the decoding step, we have all the the the the tokens that are or that could be um possible next tokens. And here, we can hook in, and we

[05:19] can basically strip out all the tokens that we don’t want to support. So, we can pass this new uh JSON processor to the logits processor. There we go. And if I now run the same example again, there we go. You can see that now it didn’t generate markdown, it actually did generate just JSON, because it starts with the curly brackets. And instead of name it picked full name. Why?

[05:50] Because we told it to use full name as a property, and that property is actually required. So, we basically forced the model to follow an exact exact same schema. We can do the same thing with regex too. So, instead of using type JSON schema, we can also use the type regex and then pass the regex. And what will happen next is we then click generate, it will generate a regex, or it will generate the person

[06:22] equals Bob Johnson, and age will be 25. So, we basically have the same system prompt, the same user prompt, but because we force a very specific a very specific structure, um it generates the structure that we that we want. So, we have the constraint decoding that allows us to um to to be very specific about the structure that we expect. If we take a look at a diff- different example, here I built a little um a little recipe generator. And what I can do now is I can

[06:55] generate a new recipe, and it will then generate that by following the exact JSON structure that I want it to follow. So, we have a title, description, we have uh the ingredients, and if you look at the response, that’s the JSON it generates. And I can then parse the JSON in my application, and it will then fill basically the layout that I have. And that’s only possible because I know the exact structure that it will

[07:27] generate, and I can rely on the structure that um that it will generate. Let’s have a look at some of the um technical details behind the feature. So, being able to pass a a structure that it needs to follow has an issue or has been an issue that has been open since a couple of months. So, I really wanted to see how we could solve that. And the main problem is that yes, we have the constraint provided by the application and then we need to have

[07:58] some kind of package that basically validates new strings against those constraints. So, we have already generated strings, already generated output and then on each step we basically need to validate all the tokens against those constraints. There is a library called LLM guidance that has already been used for this since quite some time and a lot of projects already are based on that. So, we have the prompt API that uses that.

[08:30] We have VLLM, llama.cpp. So, it’s pretty well known in the in the community. So, the first step as a prototype was to basically create a WASM wrapper around the Rust LLM guidance implementation and to then also create a little adapter that hooks into the generation process in inside Transformers.js. I did that and it did work. It was quite slow and we also had to download this 1 MB library as a web assembly file which wasn’t wasn’t optimal. So, the next step is that we basically

[09:05] created a complete JavaScript only implementation of LLM guidance, of course with the help of a lot of coding agents. And that went really, really well and it allows us allowed us to completely eliminate the the 1 MB JavaScript web assembly library. But we didn’t stop there. We also invested a lot of time into the into the into the performance side because as you can imagine, whenever a new token is added, we need to basically regenerate the whole mask of allowed

[09:38] next strings and we need to then compare that mask to the tokens that the model generated. It did take a couple of of iterations, but as you can see now I created a little test case where we have um the Gemma four model, we have the Granite model and we have the LFM 2.5 model. So we have different models, different tokenizers and different test cases. And if we look at the um the tokens per second and the difference, we can see that we barely have any overhead. So yes, sometimes it

[10:12] adds it’s a little bit slower if we have constraint decoding enabled, but it’s very very minimal. And we’re super proud to now have a package that is super fast and should work with any uh with any tokenizer. Now if we head over to the repository, you you can see that we have some instructions here. So all the um the the schemas that are supported, we have a JSON schema, we also have regex, um we do have it documented here. Please feel free to try it out. We would love to get some get your feedback on

[10:46] that new product, that new package. And yeah, thank you so much for watching. Please check out the new version, Transformers Trace version 4.3. It is out now. You should try it and um have fun with it. Thanks. Bye.