Het probleem
Een documentassistent is alleen iets waard als je hem kunt controleren. De meeste antwoorden in een nette alinea met daaronder een voetnoot die een bestandsnaam noemt, en dat is ongeveer net zo nuttig als geen voetnoot. Wat je wilt is de zin die hij gelezen heeft. Wat je krijgt is een bestandsnaam.
Wat we bouwden
Een pdf naast een chat. Het antwoord stroomt binnen met genummerde bronverwijzingen, en elke verwijzing draagt de pagina en het tekenbereik die het zoeken echt opleverde, de matchscore, en een citaat dat woord voor woord tegen de brontekst is gecontroleerd. Een klik scrollt de pdf naar die pagina en licht de passage op in de eigen tekstlaag van het document, opgemeten in het lettertype dat in de pdf zit in plaats van geschat met een kader. Het zoeken is dicht en dun tegelijk: bge-m3-embeddings in Vectorize naast BM25, samengevoegd met reciprocal rank fusion, en zo opgeknipt dat een passage nooit een paginagrens overschrijdt en het paginanummer kwijtraakt dat hij nodig heeft om naar te verwijzen. Een feitenpaneel vat het document samen met bij elk feit een paginalink. Heeft het model niets, dan zegt het dat in plaats van een alinea te schrijven.
Het resultaat
Het voorbeelddocument is het NIST Cybersecurity Framework 2.0, een Amerikaans overheidswerk in het publieke domein, en een bezoeker kan in plaats daarvan een eigen pdf uploaden. Een upload verlaat de browser nooit in zijn geheel: pdf.js haalt de paginatekst er in de browser uit en alleen die tekst wordt verstuurd, de passages en hun vectoren leven een uur, en een cron ruimt ze op.