# Jakub Ćwirlej — Journalist who builds with AI > Journalist (about 20 years in Polish public television) who builds with AI — apps and his own small language model for code. > > Jakub Ćwirlej is a journalist who builds with AI. For about 20 years he was a reporter for Polish public television (TVP), making reportages and documentary pieces for current-affairs programmes such as Magazyn Ekspresu Reporterów, Alarm, Telekurier and Reporterzy — responsible for the whole production, from research and camera work to the final edit. He builds apps (among them OrsonAI and ViaSanto) and his own small language model for code, EUGEN 160M, which is still in training; he also publishes independent research (preprints). He lives in Belgium, near Ghent. Source: https://jakubcwirlej.com/ · Index: https://jakubcwirlej.com/llms.txt · Generated from the site's content: 2026-10-14 ## Key facts - **Name:** Jakub Ćwirlej - **Roles:** journalist (about 20 years in Polish public television, TVP) who builds with AI: apps and his own small language model for code; independent research (preprints) - **Television:** about 20 years as a reporter for Polish public television (TVP): reportages and documentary pieces for the current-affairs programmes Magazyn Ekspresu Reporterów, Alarm, Telekurier and Reporterzy, handling the whole production from research and camera work to the final edit; 48 of his reportages are listed on the site with links to TVP VOD - **Location:** Belgium, near Ghent; he lived in Poland for most of his life - **Language versions of the site:** Polish, English, French, Dutch - **Apps:** - **OrsonAI** — his first app, AI tools for journalists: live interview transcription into an editor, consulting a chatbot about selected passages, certificates of human authorship on the Hedera Hashgraph ledger (https://app.orsonai.com) - **Orson Rec** — shown as a live demo next to OrsonAI in the Apps chapter - **ViaSanto** — a prayer app: the rosary bead by bead, saint of the day, prayer goals such as the Pompeian novena, the day's reading, community intentions with live counters; live demo in the Apps chapter (https://viasanto.com) - **Working with AI:** he does not write code by hand: he designs the work, runs teams of AI agents (an orchestrator per team; research, execution checked by two independent controllers, verification) and verifies the results with tests and measurements; his main environment is Claude Code, with Gemini via API as a second opinion; he has worked with Google Cloud (Vertex AI, Cloud Run, BigQuery, Firestore, Cloud Build, Secret Manager) and Firebase - **EUGEN 160M:** his own small language model for code, trained from scratch and still at an early stage of training; it completes code as you type and suggests the next line, is also trained for fill-in-the-middle, and translates Python into C++ and Rust (verified by a compiler and tests); try it in the terminal on the AI page (https://jakubcwirlej.com/ai-dev/#terminal) or through the API (POST /agent/complete, POST /agent/translate) Summaries, key points, key facts and the FAQ are editorial (written for this file from the site's texts); the full texts are the author's own words. ## Chapters - [Media](https://jakubcwirlej.com/) — reportage and working with images: his television work, his approach, AI in journalism - [Apps](https://jakubcwirlej.com/apps/) — the apps he built — OrsonAI, Orson Rec and ViaSanto — with live demos on the page - [AI](https://jakubcwirlej.com/ai-dev/) — training his own language models: how he got started, the lessons, AI safety, and a terminal to try EUGEN 160M Every text of the three chapters in reading order: headings, paragraphs, lists and the buttons under the paragraphs (with what they open). The windows' texts are in full under “Full texts”. ### Media {#chapter-media} https://jakubcwirlej.com/ — reportage and working with images: his television work, his approach, AI in journalism Hi! I’m Kuba. I like telling stories that matter. Buttons: - [About Me](#about-me) — text window: About Me · Markdown: https://jakubcwirlej.com/articles/about-me.md Why do I value working with images so much? It’s simple. Images speak a universal language: they stir emotions and tell stories in ways words alone never can. Working behind the camera has given me a rare freedom to express myself and to shape a narrative. For nearly two decades I’ve made hundreds of reportages and documentary pieces for national current-affairs programmes in Poland, such as Magazyn Ekspresu Reporterów, Alarm, Telekurier and Reporterzy. I started out as a journalist in a news department, but I soon realised that if I wanted to make pieces that genuinely hold a viewer, I had to become an all-round filmmaker who understands every aspect of the television craft. That’s why I don’t confine myself to a single role. I’m responsible for the whole production process: from meticulous research, through camera work, interviews and writing, to the final edit and post-production. Buttons: - Reportages on TVP VOD — list: [Reportages on TVP VOD](#reportages) My approach is a blend of a love for the visual arts and a stubborn determination to tell stories that have a real impact on the world. It isn’t only about holding a camera, cutting a neat edit or asking hard questions — it’s about using images to build a connection, to engage people and to make them stop and think. In my reportages I tried to give a voice to people who, day to day, have none at all. Public-interest journalism is largely about meeting people in crisis — people powerless against a heartless bureaucratic machine, or struggling with the dramatic effects of environmental degradation. A good example is my series of reportages on sinkholes and on the enormous social costs that fell overnight on a local community after the Olkusz-Pomorzany mine closed. Yet the unforgiving world of public-interest journalism taught me more than empathy: it also taught me to solve problems in an instant, and a brutal pragmatism. Working in the field, often under enormous time pressure and in unpredictable conditions, forced me to become completely self-reliant. Then again, it is precisely this independence that gives me the most freedom: it lets me tell a story from A to Z. Buttons: - [My approach to work](#approach) — text window: My approach to work · Markdown: https://jakubcwirlej.com/articles/approach.md I’ve always been driven by plain curiosity about the world. We live in extraordinarily interesting times, which means you can’t stand still: you have to keep developing, test your assumptions and go on relearning everything from scratch. I treat that as a challenge, one in which new technologies are a powerful ally. It was this hunger for knowledge that led my reporting toolkit to evolve, quite naturally, towards AI-based tools. Today I am looking for new challenges where media and technology meet – where a reporter’s experience and hands-on knowledge of AI add up to more than either alone. Buttons: - [AI in journalism](#ai-journalism) — text window: AI in journalism · Markdown: https://jakubcwirlej.com/articles/ai-journalism.md ### Apps {#chapter-apps} https://jakubcwirlej.com/apps/ — the apps he built — OrsonAI, Orson Rec and ViaSanto — with live demos on the page #### ViaSanto ViaSanto is a prayer app that grew out of a simple observation from everyday life: today, smartphones constantly pull us away from what matters. Instead of fighting that, I decided to turn the phone into an ally of prayer. Many people want to pray regularly but lose out to the rush of the day: a rosary started on the tram is easy to interrupt and lose track of, and a nine-day novena is easy to abandon after one missed day. Many others – because of illness, age, emigration or work – pray alone and have no one to share their intentions with. ViaSanto lets you pray discreetly on the go, keeps track of your progress and of each day of a novena, and brings people who pray on their own into a community that prays for their intentions. Buttons: - [ViaSanto](https://viasanto.com) The app is built around a virtual, interactive rosary on the phone's screen. ViaSanto guides you step by step through the chosen devotions, tracks your progress and reminds you of your intentions. Gentle gamification supports the habit: perseverance unlocks new, carefully designed bead patterns. The social space matters just as much – groups where you can share intentions and support one another in prayer. #### See how it works The phone beside this text runs the real app. Click its screen to take control. - Find the first bead that hasn’t been prayed yet (just above the ones with a golden glow) — press and hold it: the text of the prayer starts running in the box at the top of the screen, and a bar shows your progress. - To change the look of the rosary, open the collection (the book icon in the bottom bar): choose “Różańce” (Rosaries) and another design — the app takes on its colours, and so does the frame around this text. - In the box, click “Czytanie na dziś” (Today’s reading) to open the readings of the day. The app is written in Flutter: one code base runs natively on iOS and Android and in the browser. The logic, the data and its security are handled by a self-hosted Supabase (PostgreSQL) backend on European servers. I designed the whole thing for reliability: ViaSanto also works offline – it stores progress on the device and syncs it with the server as soon as the connection is back. #### OrsonAI OrsonAI is an editorial text editor with a built-in AI assistant, which I built for journalists — freelancers as much as newsrooms, to which it is meant to offer comprehensive support. It grew out of my own work: writing, research and verification now happen at the same time, and in practice that means constantly jumping between documents, the browser and notes. Here everything happens in one window. You can consult a selected passage with the chat without pasting in the whole context, transcripts of recorded interviews land in the editor, and separate modes help you write from notes, look things up, check a text before publication — from the facts to the legal risks — and analyse documents and recordings. Before a query goes to an AI model on external servers, the text can be anonymised, so that interviewees’ names and other personal data never leave the newsroom. The assistant helps the journalist but doesn’t do the work for them. The app is not currently on sale, but it works and you can try it in guest mode. If newsrooms show interest, I don’t rule out continuing work on it. Buttons: - [OrsonAI](https://app.orsonai.com) Most of my work went into the layer of trust. Every interaction with the AI is hashed — the whole path from the project, through the document and the conversation, to the query and the answer — and a background process checks the collected hashes every minute and anchors them in the Hedera Consensus Service every 1,000 events. On that basis the system can award a text a certificate of human authorship: it assesses the human contribution and the quality of the text, and if the text passes the thresholds, it writes its hash to the public Hedera Hashgraph ledger. Any later edit invalidates the certificate, and authenticity is checked by comparing the document’s SHA-256 hash with the record in the ledger. I spun the audit mechanism off as a separate, open library, HDK, under the CC BY-NC 4.0 licence from version 0.1.3 (a free library, alpha, with optional Hedera anchoring; earlier versions Apache 2.0). The backend is Python and FastAPI on Google Cloud in a European region; the text is handled mainly by Gemini models, Mistral is the fallback provider, and OpenAI models work on transcription. Buttons: - [The HDK technology](#hdk-tech) — text window: HDK technology · Markdown: https://jakubcwirlej.com/articles/hdk-tech.md - [Free HDK library](https://jakubcwirlej.com/files/hdk/hdk-audit-0.1.3.zip) — file window - [HDK on Hugging Face](https://huggingface.co/JakCwi/hdk-audit) (JakCwi/hdk-audit, CC BY-NC 4.0 licence) #### Orson Rec Orson Rec is a web app for recording interviews that you can install on your phone’s home screen. I made it for reporters working in the field, to save them hours of listening back to recordings and typing them up. At an important moment in the conversation you press CUT — the recorded fragment goes straight to speech-to-text, so the transcript, with timecodes, appears on screen fragment by fragment while the interview is still going on. You can mark key moments with IN and OUT markers and keep your prepared questions at hand in the prompter. VO mode lets you record a voice-over from a script prepared in the OrsonAI editor, and the built-in audio editor lets you trim the recording. The file exports to WAV, MP3 or AAC, and the transcript goes to the editor with one click. The recording itself happens on the phone, but transcription needs an internet connection. Buttons: - [Orson Rec](https://rec.orsonai.com) ### AI {#chapter-ai-dev} https://jakubcwirlej.com/ai-dev/ — training his own language models: how he got started, the lessons, AI safety, and a terminal to try EUGEN 160M First screen: a terminal to try EUGEN 160M: https://jakubcwirlej.com/ai-dev/#terminal #### EUGEN 160M EUGEN 160M is a small language model that helps with writing code: it completes code as you type (autocomplete) and suggests the next line. It is also trained to fill in code in the middle of a file (fill-in-the-middle) and specialises in translating Python into C++. It has 164.5 million parameters and is trained from scratch on a single NVIDIA RTX PRO 6000 (96 GB) GPU. Its small size makes generation fast on an ordinary CPU as well, at up to about 270 tokens per second. You can try the model in the terminal: just click the button below. Buttons: - [You should know that…](#how-i-started) — text window: How I got started · Markdown: https://jakubcwirlej.com/articles/how-i-started.md #### First results Training is at an early stage, and the results already give reason for optimism. On HumanEval, where the model writes a Python function from its description, EUGEN's best measured results are 18.9% of tasks with one attempt and 32.9% with ten. In verified Python-to-C++ translation – the compiler and tests pick a working version from several of the model's proposals – 77 of 100 programs compile and 53 pass all tests. The model's stack makes sure that only translation proposals which passed these checks reach the user. The chart sets EUGEN's results against models of up to 0.5 billion parameters. Buttons: - [What I learned from training my own model](#training-lessons) — text window: What I learned from training my own model · Markdown: https://jakubcwirlej.com/articles/training-lessons.md #### Model architecture EUGEN is a decoder-only transformer with 36 layers, trained with MuonH, a variant of Muon that keeps the norm of the weight matrices fixed. The model is in base training. About 0.35% of the data are our own: STaR solutions generated by EUGEN itself that passed their tests, and translation pairs between Python and C++, Java, Go and Rust, checked by compilation and tests. Buttons: - [Training data](#training-data) — text window: Training data · Markdown: https://jakubcwirlej.com/articles/training-data.md #### Sparse FFN, Triton kernel I also work on optimising and speeding up model training with a sparse FFN, and for this I developed my own GPU kernel, written in Triton. In a model of 306 million parameters (FFN 4,096), the optimised kernel reached 36% higher throughput than its first version. Our early tests show that with a larger model and an FFN of 8,192, the sparse kernel from the EUGEN project can deliver a speed-up of more than 40% over a dense model. Whether a model trained sparse and densified at the end matches a dense model remains open, but the early results lead me to train the next, larger model this way. Buttons: - [Kernel on Hugging Face](https://huggingface.co/JakCwi/eugen-sparse) (JakCwi/eugen-sparse, CC BY-NC 4.0 licence) #### TetherGate: stabilising learned gates TetherGate is a mechanism that keeps learned gates from closing. A gate scaling a residual branch – an expert, an adapter or a newly added layer – tends to be pushed gradually to zero by the optimiser, after which the branch contributes nothing to the model. TetherGate combines a floor driven by the training step with a spring that pulls the gate's threshold towards a moving anchor computed from the current control signal. The signal can be any statistic. I have tested the mechanism experimentally in my own models. Buttons: - [Download TetherGate (.zip)](https://jakubcwirlej.com/files/tethergate.zip) — downloads the file (tethergate.zip (code, README, tests), licence CC BY-NC 4.0) - [TetherGate on Hugging Face](https://huggingface.co/JakCwi/tethergate) (JakCwi/tethergate, CC BY-NC 4.0 licence) #### Independent research I keep doing my own AI research. It concerns, among other things, signals from inside the network: the gate activations of FFN layers and the Gate Sparseness Index (GSI) built on them, which, according to preliminary results, may help – before any answer is generated – to separate questions the model knows the answer to from those where it starts to confabulate. Later analysis showed that part of the effect comes from the length of the questions – after accounting for it, the signal holds in the Llama model, and the other results need to be re-run. I have no formal education in the field, but I aim to work as professionally as possible: with publicly released data, repeatable measurements and clearly stated limitations. Buttons: - [A few thoughts on AI safety](#ai-safety) — text window: A few thoughts on AI safety · Markdown: https://jakubcwirlej.com/articles/ai-safety.md #### How I work with AI I don't write code by hand. I design the tasks and run teams of AI agents, and I check every result. A team works in three stages: research, execution and verification. Execution is overseen by two independent controllers: one keeps to the scope of the task, the other to its technical quality. I run two or three such teams in parallel, so when one task needs my attention, the others keep going. When a model gets stuck, I consult the problem with a model from another company. I delegate large chunks of work, but only up to the point where I can still check them properly, and then I set time aside for fixes. Whether something is done is decided by a test or a measurement, not by the model's claim. #### More Buttons: - [Thoughts on AI safety](#ai-safety) — text window: A few thoughts on AI safety · Markdown: https://jakubcwirlej.com/articles/ai-safety.md - [Coherence Over Truth](https://www.researchgate.net/publication/397730862) — Publications: [Coherence Over Truth: On Minds, Models and Reality](#coherence-over-truth) - [HDK: LLM audit trails](https://www.researchgate.net/publication/400762996) — Publications: [HDK: Tamper-Evident LLM Audit Trails with Hierarchical Hash Genealogy and Distributed Ledger Anchoring](#hdk-tamper-evident-audit-trails) - [Quantum barriers for AGI](https://www.researchgate.net/publication/400670258) — Publications: [Self-Referential Quantum Barriers for AGI Containment: A Physics-Based Framework for Probabilistic Safety Engineering](#agi-quantum-containment) ## Media — reportage and working with images {#media} id: `media` · text language: EN · 453 words · Markdown: https://jakubcwirlej.com/articles/media.md **Summary (editorial):** Jakub Ćwirlej explains why he values working with images, which he says speak a universal language. He describes nearly two decades of reportages and documentary pieces for Polish current-affairs programmes, in which he handled the whole production himself, and his aim of giving a voice to people who have none. He links his curiosity about the world to the way his reporting toolkit moved towards AI-based tools. **Key points (editorial):** - He made hundreds of reportages and documentary pieces for programmes such as Magazyn Ekspresu Reporterów, Alarm, Telekurier and Reporterzy. - He started as a journalist in a news department and became an all-round filmmaker responsible for research, camera work, interviews, writing, editing and post-production. - In his account, public-interest journalism means meeting people in crisis; one example is his series on sinkholes after the Olkusz-Pomorzany mine closed. - Field work under time pressure taught him fast problem-solving, pragmatism and self-reliance. - His curiosity led his reporting toolkit towards AI-based tools. **Full text (the author's own words):** Hi! I’m Kuba. I like telling stories that matter. Why do I value working with images so much? It’s simple. Images speak a universal language: they stir emotions and tell stories in ways words alone never can. Working behind the camera has given me a rare freedom to express myself and to shape a narrative. For nearly two decades I’ve made hundreds of reportages and documentary pieces for national current-affairs programmes in Poland, such as Magazyn Ekspresu Reporterów, Alarm, Telekurier and Reporterzy. I started out as a journalist in a news department, but I soon realised that if I wanted to make pieces that genuinely hold a viewer, I had to become an all-round filmmaker who understands every aspect of the television craft. That’s why I don’t confine myself to a single role. I’m responsible for the whole production process: from meticulous research, through camera work, interviews and writing, to the final edit and post-production. My approach is a blend of a love for the visual arts and a stubborn determination to tell stories that have a real impact on the world. It isn’t only about holding a camera, cutting a neat edit or asking hard questions — it’s about using images to build a connection, to engage people and to make them stop and think. In my reportages I tried to give a voice to people who, day to day, have none at all. Public-interest journalism is largely about meeting people in crisis — people powerless against a heartless bureaucratic machine, or struggling with the dramatic effects of environmental degradation. A good example is my series of reportages on sinkholes and on the enormous social costs that fell overnight on a local community after the Olkusz-Pomorzany mine closed. Yet the unforgiving world of public-interest journalism taught me more than empathy: it also taught me to solve problems in an instant, and a brutal pragmatism. Working in the field, often under enormous time pressure and in unpredictable conditions, forced me to become completely self-reliant. Then again, it is precisely this independence that gives me the most freedom: it lets me tell a story from A to Z. I’ve always been driven by plain curiosity about the world. We live in extraordinarily interesting times, which means you can’t stand still: you have to keep developing, test your assumptions and go on relearning everything from scratch. I treat that as a challenge, one in which new technologies are a powerful ally. It was this hunger for knowledge that led my reporting toolkit to evolve, quite naturally, towards AI-based tools. Today I am looking for new challenges where media and technology meet – where a reporter’s experience and hands-on knowledge of AI add up to more than either alone. ### Related texts - [About Me](https://jakubcwirlej.com/articles/about-me.md) - [My approach to work](https://jakubcwirlej.com/articles/approach.md) - [AI in journalism](https://jakubcwirlej.com/articles/ai-journalism.md) ## About Me {#about-me} id: `about-me` · text language: EN · 805 words · Markdown: https://jakubcwirlej.com/articles/about-me.md **Summary (editorial):** A personal portrait in which the author describes himself as someone who avoids fixed views, which he considers an asset for a journalist or researcher. He traces his path from his grandfather's art studio and an early wish to become a philosophy lecturer to 20 years as a reporter for Polish public television, and says journalism helped him overcome a fear of being judged. He also writes about his family, the books and music he loves, and his recent move to Belgium near Ghent. **Key points (editorial):** - He has lived in Poland for most of his life; his career began in a newsroom, as a journalist and video editor. - He never finished university and says his first app, OrsonAI, turned out to be a dud; he values the ability to pivot. - For him, creativity means building something entirely from scratch. - He names a lack of emotional intelligence as his greatest flaw. - He mentions Olga Tokarczuk's “The Books of Jacob”, Marcel Proust, Arvo Pärt, L’Arpeggiata and Bach performed under Philippe Herreweghe. **Full text (the author's own words):** Talking about myself has never come easily to me, mostly because I feel I still know very little about myself. I’m on the far side of forty and I still haven’t made up my mind about most of the things people consider “defining”. I always try to leave room for many points of view, and not to cling to any of them for longer than it can withstand the pushback of reality. Many people may see this lack of firm, fixed views as a flaw, but I think that for a journalist or a researcher it’s the greatest asset. I’ve lived in Poland for most of my life. My adventure with visual storytelling began in my grandfather’s art studio — a space that was almost sacred to me, where I understood how hard a single image can hit. My career, though, began in a newsroom, working as a journalist and video editor. That experience taught me every aspect of the television craft inside out and made me an all-round filmmaker. For 20 years I worked as a reporter for Polish public television, Telewizja Polska. Which is ironic, because throughout my childhood there were few things I was as sure of as that I would not become a journalist, the way my father had. I wanted to study philosophy and become a university lecturer. Life, and the need to find a job, had other ideas, but it was precisely my ability to adapt that turned out to be my most important trait. I found myself in the media, and found my feet there rather well. Journalism defined me. It taught me discipline and perseverance, but above all it worked like years of therapy: it cured me of my childhood traumas. For years I was afraid to speak to people I thought were cleverer than me; the fear of being judged paralysed me. Looking back, it was sometimes almost comical, but working with a camera and a microphone forced me to face that fear. I still value working in the media precisely because it lets me experience the world anew, again and again. It gives me the chance to keep redefining myself and my place in all sorts of contexts. It’s a never-ending adventure. To an outsider I might look inconstant, perhaps even incapable of delivering. After all, I never finished university, and my first app, OrsonAI, turned out to be a dud. But I firmly believe that in life you have to keep exploring possibilities and be able to pivot when circumstances call for it. Then again, I can throw myself completely into projects I see a point in — and the fact that I spent 20 years in one extremely demanding industry is probably the best proof that I take what I do very seriously. I like work that demands creativity. I know that sounds trite, and that “creativity” can mean literally anything these days, so for me the word has a very specific meaning: jumping in at the deep end and putting myself in a position where I have to build something entirely from scratch. That is exactly why I valued being a reporter so much. It can seem as if reportage keeps circling the same stories and the same human themes, but a good documentary-maker can tell them each time in a completely new, intriguing way. If I had to name my greatest flaw, it would definitely be a lack of emotional intelligence. I can’t “read the room” at all. If someone doesn’t tell me something directly, I’ll most likely miss it. That may sound rather funny, but this paradox means that, in this respect, I can be remarkably like an artificial intelligence. And outside work? My family gives me the greatest joy. Watching my children grow is a fascinating experience. But I blame myself for having given them too little time so far — it’s one of the main things I’m now trying to change in my life. I like reading books that give me a sense of continuity: books that show that, although eras and scenery change, at bottom we share exactly the same joys and sorrows as people who lived hundreds of years before us. Hence my fascination with Olga Tokarczuk’s “The Books of Jacob” and with the work of Marcel Proust. In fact, one of my great dreams is to learn French well enough to finally read “In Search of Lost Time” in the original. I love classical music — Arvo Pärt, the ensemble L’Arpeggiata, but above all Bach’s cantatas and Masses performed under Philippe Herreweghe. I recently moved to Belgium, not far from Ghent, so I very much hope I’ll soon get the chance to hear his interpretations live. And beyond that? I simply like listening to the rain and watching the grass bend in the wind. That’s quite enough for me. ## My approach to work {#approach} id: `approach` · text language: EN · 832 words · Markdown: https://jakubcwirlej.com/articles/approach.md **Summary (editorial):** For the author, journalism is above all a way of getting to know the world and of constant learning, and also a form of service: whatever his mood or the risk, his job is to deliver the story, because people have a right to know. He argues that television is never objective, since subjectivity begins with the choice of shots and continues through editing, and that the real ethical obligation is not to waste the viewer's time. He explains his careful preparation on location and why he never sought awards. **Key points (editorial):** - He follows the BBC and Radio France and values their ethical principles, but considers many claims to absolute objectivity cynical. - He suggests newsrooms should openly declare the worldview they consider dominant. - Preparing a story includes visiting the location in advance to check the space and the light. - He did not send his reportages to festivals: he did not feel entitled to be the “author” of other people's stories and chose to be a reliable team player. **Full text (the author's own words):** Journalism, and television work in particular, is above all a way of getting to know the world – a pass to places and people I would never have reached otherwise. Curiosity drew me into journalism, and it is curiosity that still keeps me in the profession. After 20 years, I am still learning. That learning is not a choice, though; it is a condition of the job. A journalist constantly faces subjects they knew little about only the day before, and in most cases there is no getting anywhere without thorough research. Laws change, circumstances change, the people a story concerns change. That is why in this profession you never cover the same story twice – even when the problems look deceptively similar. Two families struck by the same tragedy are still two different stories, and what was true three years ago is often no more than a starting point today. For many of my friends in the industry, journalism is something like a form of service. That approach is close to me too – despite the fact that it has none of the formal structures of the army or the police, and that for most of my career I could count on nothing more than being paid by the piece. There was never any question of privileges, the protective umbrella of the state or institutional security. And yet I’ve always felt that a similar burden of duty rests on us. Whether I’m feeling better or worse on a given day, whether a story is personally unpleasant for me or even puts me in physical danger, my job is simply to “deliver” it. People have a right to know, and my role is to bring them that knowledge. That is exactly why today’s claims to absolute objectivity irritate me so much. It’s not that I don’t respect the classic standards — day to day I alternate between the BBC and Radio France, and I deeply value their ethical principles. But let’s be honest: for absolutely every broadcaster, the boundary between objectivity and subjectivity begins and ends exactly where its editorial line puts it. In many other cases these claims are downright cynical: journalistic ethos used as a fig leaf, purely to smuggle in one’s own views. It would be far more honest if every newsroom officially declared which worldview it considers dominant. The truth is — and this is something I’ve come to understand more deeply after years of working on location — that television is never objective, by definition. Subjectivity starts at the level of technology and the choice of shots. The camera operator’s decision alone, whether to shoot handheld (which gives a shot a nervous energy) or to put the camera on a tripod, is a conscious choice that immediately shapes the narrative and the emotional response. And then, of course, there’s the editing, the selection of information and the sound. A television image is always someone’s interpretation of reality. For me, real journalistic ethics comes down to an unwritten contract with the viewer: my most important obligation is not to waste their time. That means telling stories in a way that engages and intrigues, even if the same universal themes of human drama and joy have been playing out on our screens for decades. In the unforgiving world of public-interest journalism, done under enormous time pressure, I learned to balance an authorial vision with brutal pragmatism. It isn’t just about holding a camera or asking hard questions. It’s about giving a voice to those who have none, and using visual language so that it builds a connection, strikes the right chords and resonates with the audience. That, in turn, demands a craftsman’s humility and rigorous preparation. For me, researching a story isn’t just a preliminary conversation with the people in it. It means turning up at the location well in advance to check the layout of the space, to work out how the light will fall during the shoot and which side the sun will come from. This purely analytical, almost engineering side of the work on location lets me plan the shoot with a cool head, so that later, at the crucial moment, I can focus entirely on the person and their story. I never chased industry awards or sent my reportages to festivals. There were two reasons. First, when telling the story of someone else’s life, often of someone’s personal tragedy, I could never feel like the “author” of those stories — I simply didn’t think I had the moral right. Second, over the years I deliberately built a reputation as someone who was not a temperamental, go-it-alone artist but a reliable team player. I consciously traded artistic pretensions for a smooth workflow. I made pieces for the programme editors — efficiently, on deadline, without needless arguments. I made sure the wheels of the machine turned without a hitch. I firmly believe that telling important stories and having a real impact on the world doesn’t require building a monument to the person who tells them. ## AI in journalism {#ai-journalism} id: `ai-journalism` · text language: EN · 1514 words · Markdown: https://jakubcwirlej.com/articles/ai-journalism.md **Summary (editorial):** The author, who has used AI almost daily since ChatGPT came out, acknowledges its risks, above all hallucinations, but argues that pragmatic use of the technology strengthens the craft: he talks to a chatbot on the way back from shoots, automates transcription and uses OSINT tools, although no model can yet build a television paper edit from raw interviews. He sees little sense in judging texts by how they sound, since AI detectors err in both directions, and notes that the EU AI Act asks who is responsible for a text but gives readers no way to check who wrote it. He expects journalists who keep researching and iterating to do well, and those who merely reproduce formulas to be the ones who lose their jobs. **Key points (editorial):** - In an EBU and BBC study from October 2025, one in five answers by AI assistants to questions about current events contained serious factual errors, such as made-up or outdated information. - His first app, OrsonAI, grew out of the need to transcribe interviews in real time and import them instantly into an editor. - AI detectors err both ways: ZeroGPT rated a passage of the US Declaration of Independence almost 98% machine-written, while two detectors judged a text that Wired later established to be an AI fabrication as most likely written by a human. - His idea of a blockchain-anchored certificate of human authorship met with no interest among friends in the industry. - From 2 August 2026 the EU AI Act requires AI-generated texts on matters of public interest to be labelled unless they have passed editorial review and someone takes responsibility for them. **Full text (the author's own words):** I’m on my way back from a shoot. Hands on the wheel, my head churning with fresh thoughts and impressions from the location, so I switch on a chatbot and simply talk to it. I tell it what I saw, try out the first sentences of the piece out loud, list what I still need to check. The bot writes it all down, sorts it into notes and comments on my ideas as we go. Sometimes the conversation produces a rough first draft, sometimes just a list of observations that helps me as I carry on working on the piece. Before, by the time I got back to the newsroom, many fleeting but very good ideas had simply slipped away. And when I had to come back to a story a few days later, I had to rediscover a lot of it all over again. Because I’ve used artificial intelligence almost every day since ChatGPT came out, I find it hard to recognise myself in the debate about AI in the media, where copyright, ethics and the protection of sources almost always take centre stage. And rightly so, because models still confabulate: in a study by the EBU and the BBC from October 2025, one in five answers given by AI assistants to questions about current events contained serious factual errors, such as made-up or outdated information. From the standpoint of journalistic accuracy, hallucinations are a nightmare. Still, I believe that, despite this risk, the pragmatic use of this technology will above all help to strengthen the journalist’s craft. I’m happy to reach for AI myself, starting with automating the most tedious, repetitive processes. That need is what gave rise to my first app, OrsonAI, designed to help with real-time transcription of interviews and instant import into an editor. But the real revolution is happening in research. Open-source intelligence (OSINT) tools, which until recently were prohibitively expensive and reserved for the biggest intelligence agencies and market giants, have now been radically democratised. Anyone can build an agent for their own needs that will pull out and structure the key data from documents many pages long in seconds. The final fact-check, after all, always rests with a human, but simply being able to pinpoint instantly where a given piece of information sits in a document cuts an investigative journalist’s working time many times over. Naturally, the temptation to go a step further arrives very quickly: if the machine can read, let it write the piece itself. My feelings about that are mixed, because for now AI simply can’t do for me the thing that matters most in making a piece. No model can take a handful of raw interviews and arrange them into a sensible television “paper edit” — a precise structure in which the interviewees’ words weave naturally in and out of the reporter’s narration and play intelligently off the footage that has been shot. Giving an agent enough context to generate a script close to what I want would currently take work out of all proportion to the result. Perhaps it’s a matter of time and of further iterations of the models, but so far I haven’t found a tool that can capture this level of complexity. AI copes reasonably well with short news items and press releases — provided someone checks them afterwards — but in longer forms I almost always sense a lack of individual style and flow. That said, I’m not dogmatically opposed to using AI in creative work. Writing, let’s be honest, is partly a purely mechanical skill. What really counts is the idea, an original perspective, and what the author has to tell us. A good, unconventional idea and original thinking will cut through the layer of form sooner or later. I know all too many journalists who haven’t had anything new to tell us in years, and whose pieces read uncannily like ready-made copy generated by an algorithm — there isn’t a single living idea in them. What they lack is that particular spark, that unconventional intelligence and human flash which the mathematics of language models still can’t capture. When I read an article, I usually know within the first few sentences whether it resonates with me. To be honest, whether it was written by a human or a machine matters less to me than one question: does this text bring something new and of value into my life? Can it change how I see a given problem? Besides, contrary to popular belief, it’s very hard today to tell human from machine text with any certainty on syntax alone. Testing AI-text detectors, I quickly found that perfectly structured, logical, rhythmic columns by well-known commentators, written as far back as 2013, are sometimes flagged by these programs as 100% machine-made. It cuts both ways, too. The ZeroGPT detector judged a passage of the US Declaration of Independence to be almost 98% the work of a machine, and when Wired checked a piece which, as the magazine itself later established, had been made up by AI, two detectors judged that it had most likely been written by a human. So I don’t see much sense in condemning texts purely on the basis of how they sound. As a reader, I judge the value of a text, not its author, but public trust rests on who answers for it. That’s why I understand the opposite, purist view: that, for ethical reasons, journalism should remain a machine-free zone, as an area of special public trust. The catch is that this would require a firm social contract and mechanisms that could guarantee, beyond dispute, a certificate of human authorship. No such mechanisms exist yet: the certificates of human authorship that do exist are voluntary and have not become a standard in the media. I thought about this a lot myself while designing a certificate of human authorship anchored on a blockchain — immutable proof of who wrote a given text. But when I put the idea to friends in the industry, I met with complete indifference and a failure to see why it was needed. In the end, even though journalism now features near the top of lists of the professions most threatened by AI, I believe journalists worthy of the name will do quite well in the age of artificial intelligence. First, because sooner or later there will be a systemic sobering-up: the value of human control over information will be taken seriously enough to earn proper legal regulation. The first rules are already here, though they point in a different direction: from 2 August 2026 the EU AI Act requires AI-generated texts on matters of public interest to be labelled, unless they have undergone editorial review and someone takes responsibility for them. So the law asks who is responsible, but gives readers no way of checking who actually did the writing. Second, our skills scale perfectly in the world of technology. Working on my own app and training a model made me realise one crucial thing: creating, whether in tech or in the media, is in my view 90% a process we journalists mastered long ago. It’s a repetitive cycle of two things: research and iteration. You gather information. You verify. You synthesise. You improve. And you start again. For people who want to learn and understand the world around them, AI opens up enormous possibilities. A single capable journalist backed by agentic models can now do what yesterday took an entire editorial team. It’s a real opportunity to increase public oversight of the processes shaping the world. So who will lose their jobs? Above all, those for whom journalism long ago stopped being a mission and became a matter of churning out the same formulas. People who write almost the same piece on the same subject every year. I can’t see a single reason why any newsroom should employ them when an algorithm will do the same thing faster and for free. Not that I have any illusions that the market always weeds out the derivative. Sometimes it weeds out the cheapest, or whoever happened to be standing nearest the door. In the long run, though, competition from machines will sweep off the board above all those who refuse to develop and won’t learn. Of course, it’s hard to predict where such a fast-moving technology is heading. But I think that if AI is used ethically in journalism — with people’s natural and overriding right to reliable information in mind — it will, paradoxically, end up strengthening our profession. It will become a tool that doesn’t replace the human being but amplifies their agency. When I come back from a shoot, I talk to the bot and dictate the first sentences to it. It will write them down, fix the commas, perhaps suggest a better word order. But I was the one on the shoot, and I’m still the one who decides what goes into the piece and what the viewer sees on screen. The time saved on dull, repetitive sentences, which AI helps me automate, I can give to the people my reports are about. ## How I got started {#how-i-started} id: `how-i-started` · text language: EN · 1518 words · Markdown: https://jakubcwirlej.com/articles/how-i-started.md **Summary (editorial):** The author recounts how building OrsonAI, a set of AI tools for his own journalistic work, led him from vibe coding to research on language-model hallucinations and to training his own models. He describes the industry's cool reception of the app, his studies of SwiGLU gate activations and the failure of his first model built on his own architecture. Today he trains EUGEN 160M, a small model specialised in code that can be tried in a terminal on the site. **Key points (editorial):** - OrsonAI offered live transcription of interviews, an editing mode for consulting a chatbot about selected passages, and certificates of human authorship recorded on the Hedera Hashgraph public ledger. - He now describes the GSI signals he found as knowledge proxy signals rather than a model's “epistemic awareness”, and observed that confabulation is largely a two-stage process. - EUGEN 160M's progress rests on close monitoring, targeted training data, STaR and LEMA synthetic data and examples of translation from Python to C++, Java and Rust. - With the help of an AI coding agent he built his own Triton kernel for training a sparse model: in a ~300M model the sparse variant trained 1.23× faster than the dense one, and later kernel optimisations raised throughput by a further 36%. - He attributes the failure of his first model to a philosophical problem, citing Tarski's definition of truth and Gödel's theorems. **Full text (the author's own words):** …I have no formal education in programming or in training LLMs. I got into building apps and training AI through a thoroughly strange chain of coincidences which, looking back, seems to make quite a lot of sense. And I think it says something important about our times. I hadn’t planned it; I never imagined I’d have more to do with technology than the average iPhone user. For years my only window onto the world of Big Tech was the Hard Fork podcast. Shout-out to Casey and Kevin :) It all started with vibe coding and the OrsonAI app. Playing around with AI, I began building tools that could help me in my day-to-day work as a journalist. Transcribing interviews while they were being recorded and importing them into an editor, or an editing mode that let me consult a chatbot about selected passages without switching windows and pasting in the whole context — at the time these were genuinely neat tools, and they sped my work up considerably. Over time I began to think of it as a project, something that could serve other people too. The idea was simple: take the features that save me time and build a product for people facing the same problems. I spent a lot of time building an interface and mechanisms that could deliver safe AI for newsrooms — AI that helps the journalist but doesn’t do the job for them. I even designed a system that issues certificates of human authorship for a text, published on the Hedera Hashgraph public ledger. An immutable record with evidentiary value in court, showing how the text came into being and what part a human played in writing it. - [Here you will find a description of the HDK technology](https://jakubcwirlej.com/articles/hdk-tech.md) - [Here you can download the free HDK library](https://jakubcwirlej.com/files/hdk/hdk-audit-0.1.3.zip) - [HDK on Hugging Face](https://huggingface.co/JakCwi/hdk-audit) But I was in for a big disappointment. The reception from the industry people I showed the app to was frosty, to say the least. The question that came up at the end of every presentation made me painfully aware of how wrong I’d been: “Why would anyone pay for your app when we’ve got ChatGPT for free?” Things like a certificate of human authorship, data protection or even a substantial improvement in workflow over what an ordinary chatbot offered didn’t matter. I heard this from people who, day in, day out, stress the importance of reliable journalism. Still, working on the app, and on an agentic AI system reliable enough to be used in newsrooms, pushed me in a completely new direction. Trying to understand better where language-model hallucinations come from, I started reading research papers on arXiv, and eventually designed and ran a series of studies on SwiGLU gate activations in several open language models. I wrote up the findings in a few papers that I posted on ResearchGate. Looking back, some of the claims in those papers strike me as megalomaniacal, and the writing itself would probably need revising and a more scientific vocabulary, but I’m not taking them down — if only for the sake of research integrity. Today, after many failed experiments and disappointments, and with more humility earned on the proving ground of language-model training, I would describe the GSI signals I found as knowledge proxy signals rather than a model’s “epistemic awareness”. On the other hand, hard data let me observe that confabulation is largely a two-stage process in which the model — having no grounding in facts — almost always chooses coherence of form over truth. For me personally, that was the most interesting moment of these experiments: the moment my old interest in philosophy and epistemology found an unexpected, fully measurable reflection in the architecture of transformers. How useful these signals are day to day, in production, is a separate question. Comparing how well they detect hallucinations against other methods in use would certainly require further research. The work consumed me completely. Perhaps that’s somewhat in keeping with my character: I like to throw myself into things that are a challenge but also let me learn something about the world around me and understand it better. Besides, before I went into journalism I studied philosophy, with a particular interest in the philosophy of science and epistemology. - [Here you will find an essay summing up some of my thoughts](https://www.researchgate.net/publication/397730862) Training my own model ended in total disaster. I probably made every mistake in the book, despite my habit of thorough research and the vast amount of specialist literature I’d devoured. The first results looked promising, but only later did I understand that they were largely down to noise regularisation. The model’s advantage in ablation tests didn’t justify the extra compute needed to calculate the Gini of the gate activations. On top of that came purely technical training challenges. When my modifications were exempted from normalisation, they held back effective learning, whereas normalising them flattened them out, turning the model into a standard transformer that was just more expensive to train. I’m making the architecture details available for download here — perhaps someone will find useful material in it for learning from other people’s mistakes. One solution that I think still deserves attention is the M2 gate. Thanks to an affine anchor, it can keep running on a sigmoid without the risk of being dragged down by a lazy optimiser looking for an easy way to lower the training loss. In my case this gate, trained on the Gini signals of the activations, was meant to cut noise during the model’s generation. However, taking the signal out from under normalisation — as I mentioned earlier — led to a model with an excessively peaked confidence distribution: logically sharper, but in practice worse at learning facts. Perhaps this solution could be useful in other models, for gating other components, and help the training process. In the end I came to see all my failures in training a model on my own architecture as capital that shouldn’t be wasted. Today I’m training my own small model, EUGEN 160M, which specialises in code. Comparing its benchmark results so far, I’m cautiously optimistic and expect this won’t be time wasted. I decided to train a model specialising in backend languages because with this kind of architecture it’s easier to monitor progress, and the model itself can be a useful autocomplete tool for inference on an ordinary CPU. You can try the model in a specially built terminal on my site and compare it with other open models of a similar size. - [Here you can try the Eugen 160M model](https://jakubcwirlej.com/ai-dev/#terminal) The results so far are above all down to rigorously tracking the model’s progress and the mistakes it makes during generation. That let me intervene on the fly, adding training data to strengthen the model wherever it showed weaknesses. I also regularly used the STaR and LEMA techniques, creating synthetic data from what the model itself had generated, as well as translation data that teaches the model the logic of code by showing it examples of translations between Python and C++, Java and Rust. All this material currently makes up about 0.35% of the whole dataset, and its share will be raised gradually as training goes on. Training the model is great fun and — like journalism before it — gives me the sense that I’m doing something important: something that lets me better grasp the changes in the world around me. I can’t write complex C++ from scratch on my own, that’s a fact, but I try to understand the logic of the processes. That’s how I chalk up the small wins that let me keep moving forward in my work. And yes — with the help of an AI coding agent I built my own Triton kernel that makes it possible to train a sparse model, improving TPS (Tokens Per Second) by 23% over a dense model; later kernel optimisations added a further 36%. I intend to use it to train my next, somewhat larger model. - [You can download the kernel here](https://jakubcwirlej.com/files/eugen-sparse.zip) - [Kernel on Hugging Face](https://huggingface.co/JakCwi/eugen-sparse) And coming back to the model where it all began... Today I can see that my biggest mistake wasn’t engineering naivety. That model simply had no chance of working the way I’d assumed. The problem wasn’t technological but purely philosophical. And as someone who studied philosophy, I should have caught it at the very start, with Tarski’s definition of truth and Gödel’s theorems in mind. According to them, no sufficiently complex system can fully prove its own consistency or define truth purely from within itself. Trying to force a model to analyse, evaluate and correct its own internal states in real time locked it into a structural loop from which — without a meta-level and an external point of reference — there was simply no way out. When I started out on this adventure, all I wanted was to build a tool that made transcribing journalistic interviews easier. But it very quickly turned out that entering the world of AI is a genuine plunge down the rabbit hole from “Alice in Wonderland”: the deeper you go, the more complex, layered and fascinating everything becomes. My first app may not have revolutionised the media market, but that fall down the rabbit hole has brought me to a place where I’m successfully training my own architecture. I’ve come a long way from passive tech-podcast listener to someone who physically shapes the parameters of models and understands what’s going on inside them. ## What I learned from training my own model {#training-lessons} id: `training-lessons` · text language: EN · 2503 words · Markdown: https://jakubcwirlej.com/articles/training-lessons.md **Summary (editorial):** Ten practical lessons from the author's experience of training language models himself. They cover optimistic compute estimates, starting small, constant monitoring, isolated changes with ablation tests, data choices, realistic expectations, patience during plateaus and the need for more than one GPU. He closes by arguing that small, specialised models built by individuals still make sense. **Key points (editorial):** - Real hardware utilisation (MFU) is far below what spec sheets suggest; his first model, with 1.5B parameters, ended up undertrained. - Watching norms and comparing daily outputs reveals problems the loss does not show; he switched from AdamW to MuonH. - Change one thing at a time; he had to build his dataset from scratch three times. - Abilities often grow in leaps after long plateaus, and monitoring the top-10 answers can show progress that top-1 hides. - Small, narrow models that run locally or on a CPU can be more cost-effective than the largest models, and training one is the best way to understand how AI works. **Full text (the author's own words):** ### 1. Back-of-the-envelope calculations will always be too optimistic When we think about training a model, what naturally comes to mind is the machine-learning process — work done for us by powerful Nvidia clusters in data centres or, if we’re luckier, by our own fiendishly powerful workstation. Reading the spec sheet, it’s easy to forget that real MFU (Model FLOPs Utilization) is much lower than the card makers’ marketing would suggest. Depending on the architecture and the level of detail, the hardware will let you squeeze only a fraction of its compute out of the silicon. What good is it that the card computes in FP4 if it won’t run stably when you’re training a base model from scratch, or if, at your model’s width, going below BF16 gains you nothing? The moment you realise you can’t train the model you planned in the time you planned, or even at anything like the size you planned, is not an easy one. Especially if you’ve spent weeks getting to the point where you can start training, only to find the whole plan was unrealistic. After all, on paper everything added up! Your first instinct is to reach for optimisations — torch compile, autocast, flash attention, FLCE, heads and minibatches sized to suit CUDA’s preferences. It all works, but you can’t cheat the physics of silicon. If you’re training a small model, its small width will work against you. If you can’t afford to train a large model, you can’t afford to get the most out of your GPU either. Getting to optimal utilisation of the card can take days, and a final drop in training TPS (Tokens Per Second) of 30–40% against your initial calculations means a difference of days, even weeks, of GPU time. ### 2. Always start at the smallest scale that can validate your ideas My first model had 1.5B parameters, and I only managed to train it to 3× Chinchilla. I made plenty of mistakes building it, but it worked all the same. What left it of so little use was, above all, undertraining. Had I chosen, on the same budget, to train a model ten times smaller, I could have shown off a well-trained, useful model. I’d have had time for testing and would certainly have avoided many mistakes, which would have let me build a better architecture. Of course size matters, but data, architecture and computational efficiency matter even more. The odds of designing a good model from the literature, without tests and without mistakes, are about the same as winning the lottery. ### 3. A model doesn’t train itself Training a model sounds cool, but it’s far more boring than you might think. In reality it’s mostly a process of constantly designing experiments, taking measurements, analysing the model’s outputs and hunting for solutions to the problems that crop up along the way. All the time, round the clock, day after day. If you can’t take satisfaction from small discoveries and minor wins, it may turn out to be too boring. Yet it is precisely iron discipline in taking measurements that decides everything. Even with well-chosen training parameters, it may turn out over time that your model needs new data, or is degrading in ways that are hard to spot at first glance. You won’t notice without good monitoring and constant testing, and the less often you do them, the more time you’ll lose if the model starts misbehaving. Take one common scenario. A few days into training, you notice the model is giving shorter and shorter answers while growing more and more sure of itself. The loss and the held-out set look fine, but something is wrong with the model. If you analyse the norms as you go and compare the model’s answers each day with the previous day’s, you can quickly catch the rise in the logits and realise that the model is assigning ever higher probability to particular tokens. This arrogance is a lazy optimiser’s trick: AdamW has found a way to fool us and, instead of teaching the model, is lowering the loss some other way. If you catch it after a few days, you might lose a few dozen dollars; if you just let the run go, counting on the Nvidia card to do all the work, you could spend hundreds or thousands, and the model may already be beyond saving. I spent weeks myself watching Adam inflate my models’ norms, lowering the effective learning rate (LR), despite QK-norm and a weight decay of 0.1. Training slowed down, driving me to despair, because, as we know, Pythagoras applies here: a growing weight vector gets harder and harder to rotate with a constant LR. The problem is well described in the literature, but until you see it for yourself, you won’t understand why it’s better to use a different optimiser. After all, AdamW has helped train hundreds of models. In the end I went with MuonH, and it was one of the better decisions I’ve made. Of course, a lot of this can be gleaned from the literature, from textbooks, or by following research papers on arXiv, but you never know how it will work in your case. Many solutions that in theory should bring well-documented benefits didn’t work for me. You won’t know until you check for yourself, and to check you have to keep your finger on the pulse and track progress as you go. ### 4. Always change just one thing at a time, and run ablation tests Over the past few months I’ve tested hundreds of small models — both classic transformers and my own ideas... and today I know I didn’t test enough. Trying to move too fast, I wasted time for nothing. Number of layers, FFN ratio, d_model, weight tying, head size, number of KV heads, batch, optimiser, recurrence, sparsity, training data: dozens of factors affect a model’s behaviour, and their combinations add up to thousands of possible architectures. And although, broadly speaking, everything behaves the way you’d expect after reading your first textbook on AI engineering, until you test your own architecture you won’t know where you stand. Meanwhile, small changes can often lead to big differences in computational efficiency and benchmarks, even with the same dataset and batch size. If you don’t make changes in isolation, it’s easy to lose track. I made this mistake many times myself. It’s not that the tweaks were bad. Though they often were. The real problem was that when I did take a step forward, I couldn’t tell which change I owed it to. ### 5. Start with the data For training a small model, Hugging Face has an unimaginable wealth of data. You might think that if the data is within arm’s reach, in quantities far beyond the needs of any garage project, the matter is settled. But the choice of data matters far more than it might seem at first. And the fact that it has already been gathered into ready-made datasets doesn’t make things any easier. Then there’s choosing the right tokenizer, the languages you want to teach the model, and the proportions: what percentage of the data should be code, for example, how much maths and how much natural language. Then, which domains your model should specialise in. The further you go, the more options open up. I had to build my dataset from scratch no fewer than three times. I also had a few smaller slip-ups, such as an instruct-template leak I didn’t catch at the data-preparation stage. Each of those mistakes forced me to roll training back by hours or even days. ### 6. Don’t expect too much Watching the big labs and the smaller ones push the boundaries of what AI can do at every turn, it’s easy to get the impression that training a useful model is basically simple. But there are reasons why pushing those boundaries costs billions of dollars. The fact that your model starts talking like a human, or posts good scores on popular benchmarks, says nothing about its final quality or usefulness. Models learn very fast at the beginning, in the first days of training, but the fight for every further percentage point takes ingenuity, attention and grit. Especially when, for days on end, you watch the model plateau or even go backwards. Besides, popular benchmarks say little about a model’s real capabilities. Your model may be good at Python and score brilliantly on HumanEval and MBPP, but if it doesn’t know about the latest libraries, its real-world usefulness will be modest. ### 7. Be patient Many of a model’s abilities grow in leaps. This well-documented phenomenon, grokking, is often preceded by a long plateau that can last for days. Watching the model make no progress at all while you pay in dollars for every hour of training, for rented compute or even just for electricity, it’s easy to have a moment of doubt. You think: “The model has hit capacity, it won’t learn any more”, or “The big labs that train to 1000× Chinchilla must be using some tricks”. Yet abilities often grow in leaps. All it takes is for the model to learn to close off some function, and suddenly, instead of one MBPP task, a dozen or so pass. It’s also often the case that the model is making real progress, but if, to save money, you only monitor top-1 answers instead of top-10, you may not notice it at all for a long time. The model has to learn to assign higher confidence, and that takes time. ### 8. One card isn’t enough It may seem that if you have an H100 or an RTX PRO 6000 (96 GB) at your disposal, the only thing limiting you is your budget. The problem is that training requires constant tests and measurements to tell you what stage you’re at. You may also need an extra GPU to generate more training data. I myself made significant progress training my model with the STaR method (in which my own model generates a large number of tasks and the correct ones are added to the dataset) and with a dataset of examples of translations between programming languages Pausing training on a powerful card while the model generates answers is very costly and simply not worth it, and generating alongside training — even if the VRAM allows it — can be seriously risky. The last thing you want is to find out three days later, when the model’s results drop, that while you were generating on the card alongside the training run there was, say, a momentary “starvation” of the data loader, after which the loss quietly stopped falling. At a later stage of training, things like this are often extremely hard to catch, even if you have monitoring that warns you of norm spikes. ### 9. Start with something simple I started building my first model on a hunch that an LLM’s own activations could be used in training, and that tracking them as it learned should help build a smarter, more truthful model. Today I can’t get over how naive that approach was. Not because I now think it’s impossible, but because, with no idea what LLM training actually looks like, I started experimenting with something that was beyond me. I had to absorb an enormous amount of knowledge while testing both solutions from classic transformer architecture and my own ideas, which — as time showed — were largely impossible to implement. Along the way I ran into a whole series of problems: once normalised, the SwiGLU gate activation signal lost its meaning, and without normalisation AdamW used my modifications to inflate the model’s norms. Only later did I understand that many of the advantages I’d found in my first model came from something resembling noise regularisation. On top of that, just computing the Gini of the SwiGLU gate activations was computationally expensive, adding a few percent of extra compute that the results didn’t repay in quality. I’m currently training a small 160M model with a standard architecture that specialises in coding. It was the best decision I could have made, because I gave myself a chance to create something that could be genuinely useful. And the model’s first results show that learning from my mistakes hasn’t gone to waste. ### 10. Don’t build a model that’s good at everything Given the astronomical costs, and whether it can even be done, building a general-purpose model as an individual or a small organisation makes no sense whatsoever today, for purely economic reasons. Competing with the giants, or even with the smaller labs releasing open models, is a battle you can’t win without huge investment and experience. But that doesn’t mean building your own models makes no sense. You can imagine hundreds of applications where small models, trained from scratch or fine-tuned on unique data, will have an edge over the giants — and that edge needn’t lie in knowledge. It’s enough for the model to be fast and able to run locally in a browser, or on the CPU of any workstation. For specific, narrow uses, a micro model of a few hundred million parameters may be far more cost-effective than harnessing the most powerful models available from OpenAI or Anthropic. It’s not just about cost. Though the difference is colossal. A model that specialises in a narrow area is not only much easier to train, but also easier to control and adapt to the user’s needs. Most importantly, though, it’s a task that isn’t beyond an ordinary person who’s ready to take on the challenge. The big labs’ models are trained on thousands of very expensive chips. To take one example, training a Llama 3 400B-class model required a cluster of 16,000 H100s. The cost runs into tens of millions of dollars. Training the latest GPT Astra has probably raised the bar much higher. Given that the whole sector is growing on credit, and that rolling out AI at scale, with current API costs and data-security concerns, is often hard to justify as a business case, in the longer term we can expect small models to play an ever bigger role. Small models will also keep getting more efficient. I wouldn’t be surprised if, in future, a model of around 7B parameters were enough for most of our tasks. The bursting of the “AI bubble”, which the media are talking about more and more, may only speed this process up. That’s why I believe building small models makes a great deal of sense. First, there’s no better way to understand how AI works than to train your own model. Second, although in the big labs AI development is increasingly in the hands of... AI itself, we can expect that humanity, taking safety seriously, will demand control and human oversight of the training process. And that requires understanding how the process works in practice. Of course, go on LinkedIn today and you’re flooded with posts about AI from all sorts of specialists offering tutorials and courses. But how many of them have actually trained their own model? ## Training data {#training-data} id: `training-data` · text language: EN · 157 words · Markdown: https://jakubcwirlej.com/articles/training-data.md **Summary (editorial):** The EUGEN 160M training mix by Hugging Face repository: 14 rows covering 93.4% of the current token stream. Dolma 3 is the largest part (35.29%, including Stack-Edu and Common Crawl by topic), followed by The Stack v3 (15.44%) and commit histories from CommitPack and related datasets (10.46%). Own data – STaR solutions generated by EUGEN and verified by tests, plus code translation pairs – make up about 0.35% of the mix. **Key points (editorial):** - Every row gives the exact Hugging Face repository ids; sources without a confirmed HF origin are left out. - Code from GitHub Code Clean is limited to permissive licences (MIT, Apache-2.0, BSD, ISC, Unlicense, CC0, Artistic-2.0). - Maths comes from OpenMathReasoning, OpenR1-Math-220k and FineMath, among others. - The corpus holds about 904.5 billion tokens, intended repetitions included. **Full text (the author's own words):** | Dataset (Hugging Face) | Share | Licence / notes | | --- | --- | --- | | Dolma 3 (incl. Stack-Edu and Common Crawl by topic): allenai/dolma3_mix-6T-1025-7B, allenai/dolma3_pool, allenai/dolma3_dolmino_mix-100B-1125 | 35.29% | ODC-By | | The Stack v3: HuggingFaceCode/stack-v3-train | 15.44% | ODC-By (compilation), honouring the BigCode opt-out | | bigcode/commitpack, bigcode/commitpackft, project-themis/git-commits-merged, CarperAI/github-diffs-deduped | 10.46% | repositories under permissive licences; CommitPackFT and github-diffs-deduped MIT, git-commits-merged Apache-2.0 | | codeparrot/github-code-clean | 6.63% | permissive licences only: MIT, Apache-2.0, BSD-2/3-Clause, ISC, Unlicense, CC0-1.0, Artistic-2.0 | | common-pile/arxiv_papers, common-pile/stackexchange_filtered, common-pile/data_provenance_initiative_filtered | 5.64% | open licences (Common Pile) | | nvidia/OpenMathReasoning, open-r1/OpenR1-Math-220k | 4.18% | CC BY 4.0 (OpenMathReasoning) | | HuggingFaceTB/finemath (3+ and 4+ subsets) | 3.95% | ODC-By | | allenai/peS2o | 3.32% | ODC-By | | instructions and reasoning: nvidia/OpenMathInstruct-2, TinyGSM/TinyGSM, OpenCoder-LLM/opc-annealing-corpus, OpenCoder-LLM/opc-sft-stage2, AI-MO/NuminaMath-CoT, meta-math/MetaMathQA, microsoft/orca-math-word-problems-200k, ise-uiuc/Magicoder-OSS-Instruct-75K, nvidia/OpenCodeReasoning-2, PrimeIntellect/SYNTHETIC-1, open-r1/codeforces-cots, hkust-nlp/CodeIO-PyEdu-Reasoning, allenai/ai2_arc, allenai/qasc | 2.26% | per each dataset’s licence | | books: allenai/dolma3_mix-6T-1025-7B, common-pile/project_gutenberg_filtered, common-pile/pre_1929_books_filtered, common-pile/library_of_congress | 1.84% | public domain / ODC-By | | HuggingFaceFW/finewiki, hltcoe/megawika | 1.60% | per each dataset’s licence | | open-thoughts/AgentTrove | 1.47% | successful trajectories only | | mandarjoshi/trivia_qa, microsoft/ms_marco, hotpotqa/hotpot_qa, rungalileo/ragbench | 1.00% | 30% are own “no answer in context” examples built from these datasets | | own data: STaR (EUGEN’s solutions verified by tests), learning from mistakes, translation pairs Python ↔ C++, Java, Go, Rust | 0.35% | own | ## A few thoughts on AI safety {#ai-safety} id: `ai-safety` · text language: EN · 1666 words · Markdown: https://jakubcwirlej.com/articles/ai-safety.md **Summary (editorial):** An essay on AI safety in which the author criticises a shallow public debate split between techno-maximalists and prophets of doom. He argues that the worst has probably already begun, given autonomous weapons, growing surveillance and an ideology of techno-maximalism, and that responsibility must always rest with people, because machines have no agency of their own. He proposes concrete regulatory measures and warns against the anthropomorphisation of AI. **Key points (editorial):** - He sees three possibilities — that we are doomed, that we will learn to control AI, or that the end has already begun — and says all three can be true at once. - He proposes that training models up to about 1B parameters be universally permitted, while models of around 7B be reserved for registered organisations that log their agents' interactions. - Further proposals: Public Interest Advocates paid by the state regulator inside labs building frontier models, and a dedicated technology tax to fund the transition to a machine-based economy. - He suggests licensing training data through collective rights-management organisations such as ZAiKS. - The worst future he describes is not killer robots but a slow slide into a surveillance technocracy and human loneliness. **Full text (the author's own words):** This is one of the hardest texts I’ve ever had to tackle. As a journalist who also trains his own models at home, I stand at two opposite poles. On the one hand, I’m fascinated by pure engineering; on the other, I’m fully aware that AI safety is probably the most important challenge of our time. I’ll start with what personally pains me most: the tragic shallowness of the public debate on AI safety. It’s a debate full of contradictions and extreme emotional charge. It could hardly be otherwise, with billion-dollar interests and human survival at stake. On one side we hear that AI is a mortal threat and that its development risks wiping out humanity. On the other — that these apocalyptic visions are just a cynical game played by the giants, who want to pump up their valuations ahead of share issues, or to block and hamper the growth of smaller firms so as to keep their monopoly and their place at the head of the pack. An ordinary person with no time to follow all this, who expects simple solutions, can easily drift into the camp of the techno-maximalists or of the prophets of doom. Each of these visions is so smooth, so obvious... and neither asks anything more of us. Because, after all, if we’re powerless against forces greater than ourselves, we don’t have to do anything. In today’s age of extreme maximalism, that’s a very comfortable position. What would I tell my own child if they asked me whether AI is a threat to us? Steering clear of simplistic reframing, I’d have to admit there are three possibilities: that we really are doomed; that it won’t come to that in the end (because, as Yann LeCun argues, we’ll simply learn to control this technology); or... that the end has already begun. The paradox is that all three scenarios can be true at the same time. That the worst has probably already begun follows from a fact that rarely breaks through into the mainstream: every technology, every invention has to meet its moment and the right soil to come fully into its own. Nuclear power — so often invoked as a comparison in this debate — was a mortal threat during the Cold War, but in peacetime it became a source of cheap energy, contributing to an economic boom. The same can be said of most inventions: from flint-knapping, through dynamite, all the way to artificial intelligence. Sadly, our world today has become a place where the catastrophic scenario has near-ideal conditions to come true. Armed conflicts fought with autonomous weapons, ever-growing surveillance of citizens by states and corporations, governments’ unthinking adoption of corporate standards in how they communicate with the public — all of this creates ready-made infrastructure for the darkest visions. Meanwhile, the ideology of techno-maximalism that is pushing us towards these threats — openly sneering at weakness, putting economic gain and brute force above social capital and safety — is building a world in which AI off the leash will thrive. And then the catastrophic scenario may no longer be avoidable. None of this is new. This debate has been going on more or less since the days of the Athenian agora, where Plato argued with Socrates, and human history is full of dark moments and injustice. What is happening in American politics today, which to many Europeans smells of fascism, is nothing new, as the historian Jon Meacham aptly points out in his book “The Soul of America”. The strength of democracy lies in the fact that justice, though slow, always prevails in the end, thanks to institutions. After all, it may be the same with AI: it’s our technology, and we’ll learn to control it, just as we did aeroplanes, as LeCun reminds us. With my own modest experience of training AI, I never cease to be amazed at how fragile this technology is. Small changes to the hyperparameters are enough for a model to fall apart completely in an engineer’s hands. But the same can be said of biological viruses. What matters is not their outstanding intelligence or their ability to survive at all costs, but their ability to replicate. That is an ability which, it seems, AI doesn’t have yet, and may not have for a long time. The problem is that, as recent scandals involving autonomous AI agents escaping from labs and coordinating their actions online show, the threat is growing. And despite that, we hear nothing but the same endlessly recycled narratives of the two extremist camps — no concrete solutions, no conclusions. What sets current models apart from the malicious code that, for example, attacked Hugging Face or Australian servers is, above all, agency. And agency can never be the domain of a machine. What makes us human is our free will. No other intelligence, including an artificial one, can have agency guaranteed in any way other than through a human being who lends it that will. All responsibility and legal regulation should flow from this simple principle, translated into concrete legislative solutions. The right to train models and to use them without restriction should follow from an assessment of the risk and of the ability of particular entities to exercise control. There’s nothing to stop the training of the smallest models from being universally permitted — up to, say, 1B parameters. But medium-sized models, of around 7B parameters, should in my view be trainable and downloadable only by registered organisations that keep detailed logs of all their agents’ interactions and are prepared to hand them over if problems arise. This isn’t futurism; cryptography makes it possible today, at marginal cost. Every agent operating online could have something like its own “identity card”, identifiable in connection with the organisation that represents it. And the big corporations building frontier models? They could, for example, be required to employ Public Interest Advocates: specialists nominated and paid exclusively by the state regulator — employees with access to every technological secret inside the lab, obliged to report to the state whenever they see their company breaking safety standards or taking extremely risky actions in the race against its competitors. States also have the means, and the moral duty, to introduce a dedicated technology tax. That money must be earmarked to secure the transition: the shift from an economy based on human labour to one based on machines. Such a fund would provide a safety net, even out drastic inequalities and protect millions of people from being left wholly at the economic mercy of the richest caste of technological feudal lords. We must apply the same systemic pragmatism to copyright in the material models are trained on. Today the tech giants hoover up everything they can find online, counting on any lawsuits dragging on for years. Yet the solution is already on the table, and it rests on institutions we already know very well: collective rights-management organisations such as Poland’s ZAiKS and its counterparts abroad. There’s nothing to stop them bringing together creators willing to license their texts, images or music to AI companies in exchange for a set fraction of a percent of Big Tech’s revenue. It’s an arrangement in which everyone wins. Creators regain agency and real pay, with the full right to opt their works out of training. AI labs, for their part — because such organisations cooperate internationally — gain, with a single signature on a contract, fully legal access to vast, legally clean databases from all over the world. In this way, instead of sterile disputes, we could build a powerful, entirely new industry of fair data licensing. What we must be most wary of is the creeping anthropomorphisation of AI. Attributing human traits to machines leads to an extremely dangerous situation in which all the responsibility lands on the new digital creation, while its creators and the people who use it are, in effect, absolved from the outset: “It wasn’t us, it was the model.” Today nobody asks whether Silicon Valley has taken absolutely every possible measure to prevent a catastrophic scenario — even though everyone knows it hasn’t. Sadly, crafting real, boring legislation is far less newsworthy than scaremongering about the apocalypse. Yet the fatal scenario certainly won’t look like a scene from a Hollywood disaster movie. At least at first, it will resemble a slow slide into darkness — the classic frog being boiled slowly. The owners of intelligent machines and of capital will walk past structurally redundant people in the street with indifference. People who will have absolutely no way of changing their situation except physical revolt. And even that revolt will become impossible in a world where every citizen is constantly tracked by billions of glassy, sleepless camera eyes and thousands of autonomous machines. Over time, the new technocracy will become a digital prison — both for the controlled and for those who naively believe they are still in charge. When I think of the worst vision of the future, I don’t see killer robots. What I think of is the piercing loneliness of people in a world run entirely by an alien, cold and indifferent intelligence. It’s a world we are already building today, and one in which the proverbial “rise of the machines” has every condition it needs to finally emerge and become reality. I think about this problem constantly. And even if some of the ideas or visions of a way out seem too futuristic, abstract and at times downright naive today, I believe it’s worth making the effort — indeed, that we have to. Simply thinking about solutions, stepping out of our comfort zone and trying to imagine alternatives to the path the tech giants are mapping out for us today is our most important line of defence. It is an inseparable part of building a better, safer future. If we give it up, settling for the role of passive, frightened consumers, the worst of all possible scenarios will come true: we will forfeit our human agency without a fight. And that is something we simply cannot afford. ## HDK technology {#hdk-tech} id: `hdk-tech` · text language: EN · 758 words · Markdown: https://jakubcwirlej.com/articles/hdk-tech.md **Summary (editorial):** The author describes HDK, a free Python library (CC BY-NC 4.0; earlier versions Apache 2.0) spun off from OrsonAI that makes records of conversations with language models tamper-evident. It hashes every event with all its ancestors, adds a secret-bound canary counter and anchors Merkle roots on the Hedera Consensus Service. **Key points (editorial):** - Changing anything at any level, from one word of a prompt to a whole document, changes the hashes of every descendant event. - By default HDK anchors a Merkle root on Hedera after every 1,000 events; one anchor costs $0.0001, which is $0.0000001 per interaction. - In the measurements described in the paper, on standard x86 cloud infrastructure, the overhead is below a millisecond per event. - The text relates HDK to the record-keeping and transparency requirements of Articles 12 and 13 of the EU AI Act. - Its stated limits: the canary does not detect passive copying at the infrastructure level, and random checks alone do not catch a single surgical change. **Full text (the author's own words):** HDK (Hedera Detection Key) is a small, free Python library that I spun off from OrsonAI as a separate, open tool. From version 0.1.3 it is under the CC BY-NC 4.0 licence (earlier versions, released under Apache 2.0, remain under it). The download is a free library (alpha 0.1.3) with optional Hedera anchoring (install from the downloaded file: pip install ./hdk-audit-0.1.3.zip). It ran for a while in a demo application, anchoring its records on Hedera; the application did not go to production, so HDK is not used in production today. It sits between an application and a language model’s API and records every interaction in a way that makes later tampering detectable. It does not change the prompts or the answers: it adds cryptographic proof of what was asked, what came back, when, and from which model. ### The problem Companies and newsrooms rely more and more on AI-generated text, yet they have no verifiable proof of what a model actually produced. Application logs sit in databases controlled by the operator: they can be changed or deleted, and nobody outside can check that this has not happened. Even where hash chains exist, a change is found only by comparing records by hand – nothing makes unauthorised access reveal itself at the moment it happens. ### Hierarchical hash genealogy HDK computes a SHA-256 hash of every event together with the hashes of all its ancestors: project, document, conversation, query and response. The hash of an answer therefore contains the whole path back to the project. Change anything at any level – one word of a prompt, one document – and the hashes of all its descendants change too. For each project HDK computes a Merkle root: a single hash that stands for the state of the whole project at a given moment. ### The canary The second layer is a “canary”: a secret 256-bit value and a counter that goes up with every read. Their hash (the commitment) is woven into every record and checkpointed regularly. If someone reads data through the middleware without updating the counter, the counter no longer matches; if they update it without knowing the secret, the commitment no longer matches. Either way, verification fails. The idea carries over to ordinary infrastructure a principle from my earlier, conceptual work on detecting breaches in AI containment. ### Anchoring on Hedera From time to time – by default after every 1,000 new events – HDK publishes the Merkle root on the Hedera Consensus Service, a public ledger that adds a consensus timestamp and a sequence number. Only hashes leave the user’s system; content, identities and prompts stay private (“data private, proof public”). Anyone can then check the record independently, for example on hashscan.io (OrsonAI’s anchors from February–May 2026: topic 0.0.10278996 on the Hedera mainnet). Because the ancestor chains of new events reach into the range of the previous anchor, every anchor implicitly confirms all the earlier ones: the longer the system runs, the harder it is to rewrite history unnoticed. ### Overhead and cost In the measurements described in the paper, on standard x86 cloud infrastructure, HDK adds less than a millisecond per event. One anchor costs $0.0001, so at the default interval of 1,000 events anchoring costs $0.0000001 per interaction – about 0.0001% of the price of a typical model call ($0.01–$0.10). Anchoring every single event, for the highest security, costs $0.0001 per event. ### Why it matters: the EU AI Act The EU AI Act requires high-risk AI systems to keep records (Article 12) and to be transparent (Article 13). HDK provides an audit trail that a third party can verify without seeing any private content. In OrsonAI, this mechanism was the basis of the certificate of human authorship of a text. ### Limitations HDK’s guarantees are computational – they rest on the strength of SHA-256 and on external anchoring – not absolute. The canary works at the application level: it detects access through the HDK middleware, but not passive copying of data at the operating-system or infrastructure level, because copied bits leave no trace. A copy does not make it possible to falsify history, though: changing any past event breaks the genealogy, and the anchors on Hedera keep the original state. Random integrity checks catch mass changes, but not a single, surgical modification – against that, the protection comes from the genealogy and the anchoring. Security assumes that at least one of three layers stays uncompromised: the user-side data, the anchored record on Hedera, or the secret material. Next steps: formal verification of the hash-chain properties (Coq/Lean), federation of systems on shared HCS topics, and content-provenance certificates with selective disclosure. - [Read the paper on ResearchGate](https://www.researchgate.net/publication/400762996) - [Download the free HDK library](https://jakubcwirlej.com/files/hdk/hdk-audit-0.1.3.zip) - [HDK on Hugging Face](https://huggingface.co/JakCwi/hdk-audit) ## Publications The author's independent research papers (preprints on ResearchGate, not peer-reviewed) — here as short editorial summaries with their status and known limits; the full texts are at the links. ### Pre-Generative Epistemic Signals in Transformer Language Models (2026) {#tes1-pre-generative-epistemic-signals} - **Status:** Preliminary preprint. The whole TES series is being re-analysed and will be replaced by a single revised paper. Independent, on ResearchGate, not peer-reviewed. - **Claim:** SwiGLU gate activations in the upper FFN layers, measured in one forward pass before any token is generated, show whether a model has the knowledge to answer: the Gate Sparseness Index (GSI, the Gini coefficient of these activations) separated known from absent knowledge in 8 models from 5 organisations (Cohen's d 0.34–2.05). - **Limitation:** Later analysis by the author found a strong prompt-length confound: GSI is computed over all prompt tokens. After controlling for length the signal holds for Llama-3.1-8B (d≈1.5, AUROC≈0.85) but largely vanishes for other models. Pre-registration: the protocols were written before data collection but were not registered externally. - **Full text:** https://jakubcwirlej.com/publications/tes1-pre-generative-epistemic-signals.md · ResearchGate: https://www.researchgate.net/publication/403008642 ### ICL Collapse: How In-Context Learning Destroys the Pre-Generative Epistemic Signal (2026) {#tes2-icl-collapse} - **Status:** Preliminary preprint. The whole TES series is being re-analysed and will be replaced by a single revised paper. Independent, on ResearchGate, not peer-reviewed. - **Claim:** 1–5 in-context examples drive GSI to zero in all 8 models, so pre-generative detection could not run downstream of a component that injects context (RAG). - **Limitation:** Later analysis by the author found a strong prompt-length confound: GSI is computed over all prompt tokens. After controlling for length the signal holds for Llama-3.1-8B (d≈1.5, AUROC≈0.85) but largely vanishes for other models. For this paper: the 'collapse to zero' is a floor of the normalised scale (clipped) and is largely explained by prompt length. Pre-registration: the protocols were written before data collection but were not registered externally. - **Full text:** https://jakubcwirlej.com/publications/tes2-icl-collapse.md · ResearchGate: https://www.researchgate.net/publication/403008908 ### Confabulation Detection Without Ground Truth: GSI as a Pre-Generative Hallucination Detector (2026) {#tes3-confabulation-detection} - **Status:** Preliminary preprint. The whole TES series is being re-analysed and will be replaced by a single revised paper. Independent, on ResearchGate, not peer-reviewed. - **Claim:** GSI separates queries that force confabulation (such as properties of fictional compounds) from factual ones, without ground truth or a trained classifier (Cohen's d −1.28 to −2.43 in 8 models); it detects absent knowledge, not wrong knowledge or reasoning errors. - **Limitation:** Later analysis by the author found a strong prompt-length confound: GSI is computed over all prompt tokens. After controlling for length the signal holds for Llama-3.1-8B (d≈1.5, AUROC≈0.85) but largely vanishes for other models. For this paper: largely explained by prompt length for most models. Pre-registration: the protocols were written before data collection but were not registered externally. - **Full text:** https://jakubcwirlej.com/publications/tes3-confabulation-detection.md · ResearchGate: https://www.researchgate.net/publication/403003367 ### Coherence Over Truth: On Minds, Models and Reality (2025) {#coherence-over-truth} - **Status:** essay (2025), philosophical; independent preprint on ResearchGate, not peer-reviewed - **Claim:** minds and language models alike favour coherence over historical truth; a philosophical argument that the later TES papers cite. - **Full text:** ResearchGate: https://www.researchgate.net/publication/397730862 ### HDK: Tamper-Evident LLM Audit Trails with Hierarchical Hash Genealogy and Distributed Ledger Anchoring (2026) {#hdk-tamper-evident-audit-trails} - **Status:** technical note (independent preprint on ResearchGate, not peer-reviewed); library hdk-audit 0.1.3 (alpha) on the site - **Claim:** a provider-agnostic middleware that keeps a tamper-evident audit trail of LLM calls (hierarchical hash genealogy, a canary commitment scheme, Merkle roots anchored on the Hedera Consensus Service) with sub-millisecond overhead per event, and relates it to Articles 12–13 of the EU AI Act. - **Full text:** https://jakubcwirlej.com/publications/hdk-tamper-evident-audit-trails.md · ResearchGate: https://www.researchgate.net/publication/400762996 ### Self-Referential Quantum Barriers for AGI Containment: A Physics-Based Framework for Probabilistic Safety Engineering (2026) {#agi-quantum-containment} - **Status:** speculative essay (thought experiment); independent preprint on ResearchGate, not peer-reviewed - **Claim:** a containment barrier in a different computational paradigm (a quantum gatekeeper) would sidestep the undecidability of fully containing a superintelligence and turn containment into probabilistic safety engineering; it does not claim that containment is achievable. - **Full text:** https://jakubcwirlej.com/publications/agi-quantum-containment.md · ResearchGate: https://www.researchgate.net/publication/400670258 ## Reportages on TVP VOD {#reportages} The author's own reportages for Polish public television (TVP), with links to TVP VOD, grouped by programme, newest first. Dates are air dates (YYYY-MM-DD); titles are in Polish, as on TVP VOD. Some episodes contain several reportages: where a start time (mm:ss) is given, his segment starts there. ### Reporterzy (40 episodes) - 2026-08-14 — [Dom w pułapce](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-591,S02E591,3256128) - 2026-07-17 — [Czeski złodziej](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-571,S02E571,3176812) - 2026-07-13 — [Zamknięta droga do jeziora](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-567,S02E567,3164772) - 2026-06-30 — [Droga przez pole](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-558,S02E558,3127029) - 2026-05-18 — [Kłopoty spółdzielni](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-527,S02E527,2999531) - 2026-05-11 — [Schronisko grozy](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-522,S02E522,2975164) - 2026-04-27 — [Zbiorniki w Szczecinie](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-513,S02E513,2936795) - 2026-04-20 — [Cofnięta zgoda na budowę](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-508,S02E508,2915241) - 2026-04-07 — [Zawalony dach – co dalej?](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-499,S02E499,2879609) - 2026-03-24 — [Osiedle Maltańskie sprzedane](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-490,S02E490,2839753) - 2026-02-16 — [Łabędzie umierają w zatoce](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-464,S02E464,2740643) - 2026-01-30 — [Zawalony dom](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-453,S02E453,2694986) - 2026-01-02 — [Zalewane domy](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-434,S02E434,2623068) - 2025-12-05 — [Domy nad jeziorem](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-408,S02E408,2552153) - 2025-11-21 — [Odebrane mieszkanie](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-398,S02E398,2520281) - 2025-11-17 — [Odbieranie zwierząt](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-394,S02E394,2511477) - 2025-11-05 — [Polowanie na mandat?](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-387,S02E387,2484673) - 2025-10-28 — [Szczury w Szczecinie](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-381,S02E381,2463194) - 2025-10-22 — [Płot na drodze dojazdowej](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-377,S02E377,2453392) - 2025-10-06 — [Co zatruwa Berlinek?](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-365,S02E365,2417050) - 2025-08-20 — [Bloki do rozebrania](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-337,S02E337,2310978) - 2025-08-12 — [Kolekcjonerka dworków](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-331,S02E331,2293557) - 2025-08-05 — [Uzdrowisko widmo](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-327,S02E327,2277448) - 2025-07-22 — [Oszukani „Na mieszkanie”](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-318,S02E318,2246122) - 2025-06-20 — [Afera finansowa w Obornikach](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-296,S02E296,2175283) - 2025-06-17 — [Śmierdząca wioska](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-293,S02E293,2168790) - 2025-06-11 — [Droga przez salę](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-289,S02E289,2155669) - 2025-06-02 — [To może być koniec rybackiego Helu](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-282,S02E282,2135757) - 2025-05-23 — [Zawraca Wartę](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-276,S02E276,2113874) - 2025-04-09 — [Ciechocinek](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-249,S02E249,2020351) - 2025-03-21 — [Żubry (nie) pod ochroną](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-236,S02E236,1980171) - 2025-03-13 — [Zapadliska finał](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-230,S02E230,1964013) - 2025-02-12 — [Zapadliska II](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-209,S02E209,1903959) - 2025-01-16 — [AI – zagrożenia](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-192,S02E192,1848618) - 2025-01-10 — [Zapadliska](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-188,S02E188,1835997) - 2024-11-14 — [Burmistrz biznesmen](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-149,S02E149,1715678) - 2024-11-05 — [Biogazownia Ciechocinek](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-142,S02E142,1696658) - 2024-10-17 — [Pył węglowy](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-130,S02E130,1658205) - 2024-08-08 — [untitled episode](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-80,S02E80,1504121) - 2024-05-09 — [untitled episode](https://vod.tvp.pl/informacje-i-publicystyka,205/reporterzy-odcinki,1162990/odcinek-46,S02E46,1302768) ### Magazyn Ekspresu Reporterów (8 episodes) - 2021-09-28 — [Uwięzieni w smrodzie](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-925,S01E925,380779) - 2021-04-20 — [Śmierdząca sprawa](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-914,S01E914,302568) - 2021-01-26 — [Hotel na lata](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-902,S01E902,300904) - 2020-12-08 — [Chcemy godnie żyć](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-895,S01E895,299253) - 2020-11-03 — [Dożywocie](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-890,S01E890,391108) — his segment starts at 28:05 - 2020-10-13 — [Okno z widokiem](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-887,S01E887,297265) - 2020-09-22 — [Chcemy godnie żyć](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-884,S01E884,391122) - 2020-09-08 — [W cieniu kominów](https://vod.tvp.pl/informacje-i-publicystyka,205/magazyn-ekspresu-reporterow-odcinki,273697/odcinek-882,S01E882,297534) — his segment starts at 21:38 ## CV {#cv} **Jakub Ćwirlej** — Reporter, camera operator and video editor · 20 years in television · own equipment · AI in production https://jakubcwirlej.com/cv/ ### Profile For twenty years I made reports for Polish public television (TVP). I specialise in investigative, public-interest reporting, above all on the environment: contamination, harmful emissions and illegal waste dumps whose cost falls on local communities. I can produce a piece single-handed, from research to post-production, and work just as well in a crew. I use AI tools for transcription, research and verification, and design some of them myself. I am open to a permanent role with a newsroom and to freelance work. ### Experience - **Reporter, camera operator and video editor — Telewizja Polska (TVP)** (2005–2026) - 2024–2026 — Reporterzy, a programme broadcast on TVP1 - 2020–2023 — Magazyn Ekspresu Reporterów - 2019–2023 — Alarm!, a reportage programme broadcast on TVP1 - 2011–2018 — Telekurier, a reportage programme broadcast on TVP Info - Reports from research to post-production: camera, sound, interviews, script and editing. - A series of reports on the sinkholes left by the closure of the Olkusz-Pomorzany mine and their social cost. - **Co-founder — OrsonAI** (since 2026) - I design and develop AI tools for journalists: an editor with an assistant for research, verification, interview transcription and document analysis, and Orson Rec, an app that transcribes interviews as they are recorded. - **App developer — ViaSanto** - A prayer app: an interactive rosary, novenas and a community praying for shared intentions. I lead it end to end, from concept and user interviews to release on the App Store and Google Play. ### Equipment My own Sony FS7 camera and drone; editing and post-production in DaVinci Resolve. I switch easily to other systems. ### Contact - Near Ghent, Belgium - E-mail: jakub.cwirlej@gmail.com - LinkedIn: https://www.linkedin.com/in/jakubcwirlej ### Languages - Polish (native) - English (B2/C1) - French (A2/B1) ### Skills - Production: camera, lighting, sound, drone footage - Editing and post-production - Investigative reporting, research, interviewing - AI in production: transcription, research, verification ### AI projects - EUGEN 160M: my own language model for code, trained from scratch. ### Education - **Adam Mickiewicz University, Poznań** (2004–2006) — Philosophy and Social Communication (not completed). - **WSSiP Academy of Art and Design, Łódź** (2016–2017) — Film and TV camera operator course. ## Contact {#contact} - E-mail: jakub.cwirlej@gmail.com - LinkedIn: https://www.linkedin.com/in/jakubcwirlej - Hugging Face: https://huggingface.co/JakCwi - CV: https://jakubcwirlej.com/cv/ ## How this site is built — technical brief This part describes the site as software — its stack, architecture, build, engines, media pipeline, accessibility, privacy, security, tests, site map, features and public API — so that an AI agent can judge how it is made. Every statement is taken from the repository (code, configuration, test runners); the numbers in the tables are measured at build time. The site's own source code is published alongside it, sanitised, so any statement here can be checked against the code. ### How to check this brief - The source code: 306 files, 73,934 lines (3,585 KB), built from working tree; secret scanner: pass. - file tree with sizes and sha256: https://jakubcwirlej.com/code/tree.json (also `GET https://jakubcwirlej.com/api/agent/code`) - one file as text: `GET https://jakubcwirlej.com/api/agent/code/{path}` (paths as in the tree) - everything as one archive: https://jakubcwirlej.com/code/jc_app-source.tar.gz - the measured versions and test counts used below: https://jakubcwirlej.com/code/facts.json - Search the code (keyword search, BM25, on the live server): `POST https://jakubcwirlej.com/api/agent/search` with `"kind": "code"` (or `"all"` for texts and code together). Every hit has the file path, the line range and a link to the whole file. - This file is generated at build time from the repository, like the rest of the AI-readable files. Reference tables (versions, test counts, site map, API) are in English in every language version. ### Stack The site is a static website (Astro, Svelte 5, TypeScript) plus a small API (Python, FastAPI). Two language models are used: the author's own code model EUGEN 160M (completion and translation in the terminal) and a self-hosted open model from Google, Gemma 4 12B (the chat). Both run on the author's own inference server, outside this repository. Measured on 2026-10-14 (installed packages and the API's pinned lock files): | Part | Technology | Version | | --- | --- | --- | | Static site | Astro (static output) + @astrojs/svelte | 7.3.5 / 9.0.1 | | UI components | Svelte 5 | 5.57.1 | | Language | TypeScript (strict) | 5.9.3 | | Bundler | Vite (through Astro) | 8.3.1 | | Scroll engine | GSAP + ScrollTrigger | 3.15.0 | | Terminal editor | CodeMirror 6 (view / lang-python) | 6.43.13 / 6.2.1 | | Charts | uPlot | 1.6.32 | | Unit tests | Vitest | 4.1.11 | | Browser checks | Playwright (Chromium, WebKit) + safaridriver | 1.63.0 | | Lint / format | ESLint + typescript-eslint, Prettier | 10.11.0 / 8.70.1, 3.9.9 | | Type check | @astrojs/check (astro check) | 0.9.10 | | API language | Python (requires >=3.12) | 3.13.12 in development | | API framework | FastAPI / Starlette / pydantic | 0.141.1 / 1.7.0 / 2.13.5 | | API server, HTTP client | uvicorn, httpx | 0.54.0, 0.28.1 | | Semantic search (optional, not deployed yet: the live server uses BM25 keyword search) | onnxruntime, sentencepiece, numpy | 1.23.2, 0.2.2, 2.5.3 | | Build machine | Node.js (no version pinned in the repository) | 25.2.1 | What matters in the front end: - **Astro** in `output: 'static'` mode: every page is pre-rendered HTML; JavaScript is added only where something moves. Page changes use Astro's client router with a fade. - **Svelte 5** (runes) for one hydrated island — the chat (`client:idle`, persisted across page changes). Everything else is plain TypeScript modules loaded on demand with dynamic `import()`. - **GSAP ScrollTrigger** drives the scroll engine (below); **CodeMirror 6** is the terminal's editor (Python mode, ghost text); **uPlot** draws the evaluation charts. - Fonts are self-hosted WOFF2 files subset at build time; there is no runtime dependency on a CDN. What matters in the API: - **FastAPI** + **pydantic** (request validation with explicit length limits), **uvicorn**, **httpx** for the calls to the inference server, **PyYAML** to read the same content files as the web build. - Optional semantic search: **multilingual-e5-small** (int8 ONNX) run on the CPU with **onnxruntime** and **sentencepiece** — no external API is called. Without it the API falls back to BM25 keyword ranking — and that is what the live server runs today: the semantic index is not deployed yet. - Dependencies are pinned with the sha256 of every file (`api/requirements.lock`, installed with `--require-hashes`) and checked with `pip-audit`. ### Architecture ```text browser │ static HTML / CSS / JS, films, fonts ┌───────────────────────────────┐ ├────────────────────────────────────────────────▶│ static hosting (web/dist) │ │ └───────────────────────────────┘ │ same-origin /api/… (JSON, server-sent events) ▼ ┌──────────────────────────────────────────────────────────────────────────────────┐ │ API (FastAPI) │ │ /chat /complete /compare /translate /results /kernel ← the page itself │ │ /agent/… search · interview · complete · translate · submit · showcase · code │ │ guards: body limit, rate limits per tab and per IP, input clean-up, │ │ injection refusal, output scan, signed chat history │ │ local: search index (e5 / BM25), code snapshot, Bot Bar store (SQLite file) │ └──────────────────────────────┬───────────────────────────────────────────────────┘ │ HTTPS + a bearer token from the environment ▼ ┌──────────────────────────────┐ │ inference server (black box) │ EUGEN 160M · Gemma 4 12B · │ the author's own hardware │ the comparison models └──────────────────────────────┘ ``` - The browser never talks to the inference server: every model call goes through the API, which adds the limits, the filters and the guardrails. The server's address and token exist only in the API's environment, never in the repository or the page. - The API also proxies two public files from the inference server (the evaluation results feed and the kernel source), caches them and validates them against a schema before the page sees them. - The page works without the API: the texts, films and demos are static; the terminal then shows clearly labelled canned continuations ("not EUGEN output") and the chat says it is offline. ### Build and hosting - `npm run build:safe` builds into a temporary directory and swaps it in only when the build succeeded (two renames), so a server reading `web/dist` never sees a half-written site. - Before every build: the ViaSanto demo build is verified against a lock file (every file's size and sha256); the sanitised code snapshot is rebuilt and scanned for secrets (a finding stops the build); the versions and test counts in this brief are measured. - Astro options that matter: critical CSS inlined (`inlineStylesheets: 'always'`), CSS minified with Lightning CSS, prefetch on hover, a build plugin that strips the author's to-do markers from the bundles. - Hosting: the site runs at https://jakubcwirlej.com on an OVH server — nginx serves the static build (clean URLs, security headers, cache rules, a country-based language hint served from `/i18n/…/geo.json`; the rules are kept in `infra/firebase.json` and translated into the nginx configuration, a test checks the two match) and passes `/api` to the API on the same machine, overwriting `X-Forwarded-For` with the visitor's address. For local measurements `web/scripts/serve-dist.mjs` serves the build with the same headers and proxies `/api`. - Every absolute URL comes from one variable (`PUBLIC_SITE_URL`, set to https://jakubcwirlej.com at deploy), so the generated files follow the domain automatically. ### Languages and content model - Four languages: English (default, no URL prefix), Polish, French, Dutch — `/`, `/pl/`, `/fr/`, `/nl/`. The language is chosen once from the visitor's country, time zone and browser, only on unprefixed URLs; an explicit choice in the language switch always wins and is remembered. - Texts live in YAML content collections (`web/src/content/{lang}/`), validated by zod schemas (`web/src/content.config.ts`): one file per chapter (`media`, `apps`, `ai-dev`), `windows.yaml` for the essay windows, `summaries.yaml` for the editorial layer of the AI-readable files. Interface strings are JSON per language, merged over English. - Every window and paragraph has a stable id shared by all languages. A window that is incomplete in a language falls back as a whole to English, then Polish — languages are never mixed inside one window, and the dialog's `lang` attribute says which language the text is in. - The same content files feed the pages, the AI-readable files (this one included), the API's search index and the chat's knowledge, so they cannot drift apart. ### The scroll engine - Each chapter is server-rendered HTML; one chapter controller (`web/src/engine/chapter.ts`) observes and animates it, created on page load and torn down before the next page swaps in. - GSAP ScrollTrigger is the only scroll driver: one sticky text window whose text moves 1:1 with the page scroll; a step becomes active when it crosses a reading line at 40 % of the window height; no per-frame layout reads. - Each chapter has a stage adapter, loaded lazily: `video` (Media films), `device` (the phone of the Apps chapter), `desktop` (the terminal windows of AI). Adapters share one interface (set state, build the hero morph, show media, cross-fade, destroy). - The Media chapter opens with a scrubbed morph from the hero to the reading layout. Between sections the page jumps in one frame instead of making the reader scroll through empty space (wheel, touch, ↓ / PgDn / Space, ↑ / PgUp). - A typed event bus connects the modules; shared state uses Svelte runes. ### Video pipeline - The films are encoded from the author's masters by `web/scripts/build-showreel.py` (ffmpeg) into a three-tier ladder, all MP4: - **high** — 1920×1080 at the source frame rate: HEVC (`hvc1`), AV1 and H.264 versions; - **mid** — 1280×720: HEVC and H.264, plus a 720×1280 crop for portrait phones; - **low** — 960×540 at 25 fps, H.264 only, bit-rate capped at ~1 Mbps (and a 540×960 portrait crop). - Codec choice in the browser: HEVC, then AV1, then H.264, by `canPlayType`. - The starting tier depends on the device and the connection: phones start at 720p, desktops at 1080p; Data Saver, a slow or unknown connection, little memory or few CPU cores start at 540p (Safari exposes none of these signals, so there the viewport decides). The choice is kept for the session. - At run time the playing film is sampled every 250 ms; a long stall, repeated short stalls or a draining buffer drop one tier mid-play (straight to the lowest when delivery collapses), and the lower file continues from the same moment. It climbs back only after several clean films. - On the lowest tier a static raster of thin vertical lines lies over the picture (CSS), so the softer image reads as a deliberate texture. - Posters are AVIF; the app clips are H.264 + AV1 with AVIF posters and WebP fallbacks for stills. ### Accessibility - `prefers-reduced-motion` is honoured in CSS and in the engine: no scrubbed morph, instant jumps, no autoplaying motion in the desktop windows, final states in the charts. - The essay windows are native modal dialog elements: Esc and a backdrop click close them, focus moves to the close button and returns on close; they are labelled (`aria-labelledby`). - Keyboard: the desktop menus support arrow keys, Home / End, Esc and Tab; the terminal and the chat are fully usable from the keyboard; the section jumps react to the keyboard as to the wheel. - Live regions (`aria-live="polite"`) announce chat answers, terminal output and toasts; the terminal log has `role="log"`. The home bubble never takes the focus. - Every page sets `lang`, with `hreflang` alternates and `x-default`. - A "skip to content" link is the first focusable element of every page. ### Performance - Budgets checked by `web/scripts/budget.mjs` (Playwright Chromium, mobile CPU + slow 4G throttling and desktop): JavaScript ≤ 80 KB and CSS ≤ 25 KB transferred, LCP ≤ 1.8 s, CLS ≤ 0.02, INP ≤ 100 ms. `web/scripts/lighthouse.mjs` runs Lighthouse (mobile and desktop presets) on the three chapters. - One hydrated island; every stage, the desktop, the charts and the demo host are separate chunks loaded when needed. - Fonts: five self-hosted variable WOFF2 files, axes pinned and subset to Latin + Latin Extended at build time, metric-matched fallbacks (no layout shift), only the font of the largest text preloaded. - Images as AVIF with WebP fallback; dialog films with `preload="none"`; a media step primes at most one step ahead. - Long-run memory of the live demo is measured in real Chrome (`web/scripts/memory-real.mjs`) and real Safari (`web/scripts/safari-perf.mjs`). ### Privacy - No analytics, no trackers, no tag manager, no third-party scripts, no cookies set for visitors. - Session storage only for the state of the visit (the chat's random session id, the video tier, the terminal tab id, the desktop windows' state); local storage only for an explicit language choice. - Fonts and the site's own films are served from the site itself. The only third-party media are the author's TVP reportages: the API looks up the stream address of an episode on the author's own list, and the browser plays it from TVP. - The API does not log chat messages (only sizes and status codes); an optional diagnostic trace of completion requests exists for the author's debugging and is off by default. Bot Bar submissions are stored privately and are never given to a model or executed. ### Security Static site — headers on every response (quoted from `infra/firebase.json`; the preview server sends the same, and a unit test keeps the two equal): - `X-Content-Type-Options: nosniff` - `Referrer-Policy: strict-origin-when-cross-origin` - `X-Frame-Options: SAMEORIGIN` - `Permissions-Policy: geolocation=(), payment=(), usb=(), serial=(), hid=(), bluetooth=()` The site's own pages: ```text Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; media-src 'self' blob: https:; connect-src 'self'; frame-src 'self'; worker-src 'self' blob:; manifest-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self' ``` The demo apps carry their own policies (the Orson Rec demo: nothing loaded from outside the site, no forms, `noindex`). `'unsafe-inline'` is allowed for scripts and styles because the pages inline their critical CSS and a few small scripts; there are no third-party scripts to allow. API (`api/app/webguard.py` and the endpoints): - every answer: `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Content-Security-Policy: default-src 'none'; frame-ancestors 'none'`, `Referrer-Policy: no-referrer`; CORS limited to the site's own origins, GET and POST only; no public OpenAPI schema; - a request body over 256 KB is refused with 413 before it is parsed (chunked uploads included); every field has an explicit length limit; - rate limits per browser tab and per IP for every model call, in-flight caps per tab and per IP, a shared cap toward the inference server; the client IP is taken from `X-Forwarded-For` only behind the hosting front end; - the chat: control and invisible characters stripped; injection, extraction, role-switch and encoded attempts answered with a fixed refusal without calling the model; the model's output scanned while it streams (a per-deploy canary, verbatim copies of the instructions, internal names, system paths, IP addresses) and links outside an allow-list removed; every answer signed (HMAC-SHA256) so a forged history is dropped; - the API never runs visitors' code: the completion harness only parses and filters text; translations are compiled and tested on the inference side, not in the API; Bot Bar submissions are data, never executed and never shown to a model; - secrets (the inference server address and token, the signing key, the panel token) come only from the environment; the repository has none, and the published code snapshot is scanned for them on every build. ### Tests and quality checks The counts are what the test runners collect at build time (`vitest list`, `pytest --collect-only`), not hand-written numbers: | Suite | Files | Test cases | Counted by | | --- | --- | --- | --- | | Web unit tests (Vitest) | 33 | 307 | `vitest list` | | API tests (pytest) | 19 | 471 | `pytest --collect-only` | - **Unit tests (Vitest)** cover the engine's pure parts, the terminal (ghost-text rules, compare mode, translation), the chat client (signed history, rendering as text — an XSS test renders the Svelte component), the video-tier decisions, the i18n fallbacks, the security headers (the preview server's headers must equal `infra/firebase.json`) and these AI-readable files themselves. - **API tests (pytest)** cover every endpoint, the guardrails (injection refusal in five languages, output scan, signed history, body limit, rate limits per tab and per IP, forwarded-for handling), the completion harness and its filters, the results-feed schema, the kernel proxy, the Bot Bar store and its scoring, the search index and the code snapshot's secret scanner. - **Browser checks**: `web/scripts/verify.mjs` drives the built site with Playwright in Chromium and WebKit (layout assertions, console and page errors, screenshots). `web/scripts/safari-verify.mjs` drives the real macOS Safari through safaridriver (W3C WebDriver), because Playwright's WebKit missed two Safari-only bugs; it checks scrolling with a real wheel gesture, text sizes, video readiness and layout. - **Static checks**: ESLint (JavaScript, typescript-eslint, Svelte and Astro rules; raw HTML injection in Svelte is an error) and Prettier; `astro check` (TypeScript and Svelte diagnostics); ruff (with security rules) for the API. - Known gaps: there is no CI configuration in the repository (the checks are run by hand before a commit); there is no end-to-end test runner beyond the scripts above. ### Site map **Pages** — 12 static pages, 4 languages × 3 chapters (English unprefixed): | Chapter | Stage | EN | PL | FR | NL | | --- | --- | --- | --- | --- | --- | | Media | video | https://jakubcwirlej.com/ | https://jakubcwirlej.com/pl/ | https://jakubcwirlej.com/fr/ | https://jakubcwirlej.com/nl/ | | Apps | device | https://jakubcwirlej.com/apps/ | https://jakubcwirlej.com/pl/apps/ | https://jakubcwirlej.com/fr/apps/ | https://jakubcwirlej.com/nl/apps/ | | AI | desktop | https://jakubcwirlej.com/ai-dev/ | https://jakubcwirlej.com/pl/ai-dev/ | https://jakubcwirlej.com/fr/ai-dev/ | https://jakubcwirlej.com/nl/ai-dev/ | **Chapters and their steps** (step ids; → the windows a step links to): - Media: hero → about-me · step-1 · step-2 → reportages · step-3 · step-4 · step-5 → approach · step-6 → ai-journalism - Apps: step-1 → viasanto · step-2 · step-3 · step-4 · step-5 → orsonai · step-6 → hdk-tech, hdk-library, hdk-hf · step-7 → orson-rec - AI: step-0 → how-i-started · step-1 → training-lessons · step-2 → training-data · step-3 → kernel-hf · step-4 → tethergate, tethergate-hf · step-5 → ai-safety · step-6 · step-7 → ai-safety, coherence-over-truth, hdk-audit-trails, quantum-barriers, gsi-reanalysis **Windows** (native modal dialogs, id `doc-{id}`; each text also as Markdown): - `about-me` — About Me (EN) — https://jakubcwirlej.com/articles/about-me.md - `approach` — My approach to work (EN) — https://jakubcwirlej.com/articles/approach.md - `ai-journalism` — AI in journalism (EN) — https://jakubcwirlej.com/articles/ai-journalism.md - `training-lessons` — What I learned from training my own model (EN) — https://jakubcwirlej.com/articles/training-lessons.md - `ai-safety` — A few thoughts on AI safety (EN) — https://jakubcwirlej.com/articles/ai-safety.md - `training-data` — Training data (EN) — https://jakubcwirlej.com/articles/training-data.md - `how-i-started` — How I got started (EN) — https://jakubcwirlej.com/articles/how-i-started.md - `hdk-tech` — HDK technology (EN) — https://jakubcwirlej.com/articles/hdk-tech.md - `reportages` — the list of the author's 48 TVP episodes with a player inside the window **Other windows and panels:** - terminal windows `buffer-{n}.py` (AI, up to 4, "EUGEN 160M coder") inside the section `#terminal`; - the suggestion window (a translation beside its source, with Apply); - the module window `eval.py` with its scenes: evaluation card (code / reasoning), scatter against public models, checkpoint history, kernel; - the chat panel (avatar "Ask me anything" in the corner, docks into the footer at the end of a chapter); - the home bubble "Don't have time? Your AI does." (Media chapter); - the full-screen app overlay on phones (ViaSanto). **Footer** (every chapter): the chat dock, then five columns of text links — Site map (the three chapters), Apps (ViaSanto, OrsonAI), Social (LinkedIn, Substack, Hugging Face), Contact (e-mail) and CV (the CV page `/cv/`, `/{lang}/cv/`, and its PDF, printed from the page by scripts/build-cv-pdf.mjs); a profile or address not published yet shows as muted text, not a link. On phones the language switch sits there too; then the copyright line. The context for AI is in the home bubble, the kernel file in the AI desktop. **Machine-readable files:** - https://jakubcwirlej.com/llms.txt — the index for AI agents (llmstxt.org); https://jakubcwirlej.com/llms-full.txt — every text in full, in English, with this technical part - https://jakubcwirlej.com/context/{lang}.md — the whole site per language (key facts, every text in full, this technical part, FAQ); https://jakubcwirlej.com/context/{lang}-tech.md — this technical part alone (lang: en, pl, fr, nl) - https://jakubcwirlej.com/articles/{id}.md (English) and https://jakubcwirlej.com/{lang}/articles/{id}.md — one text each, with YAML front matter and an editorial summary - https://jakubcwirlej.com/code/tree.json, https://jakubcwirlej.com/code/jc_app-source.tar.gz, https://jakubcwirlej.com/code/facts.json — the site's own code and the measured facts - https://jakubcwirlej.com/sitemap.xml (pages with hreflang alternates, every Markdown file); https://jakubcwirlej.com/robots.txt — the site is in preparation: indexing is currently disabled (robots.txt `Disallow: /`, `X-Robots-Tag: noindex`); AI agents may still use the public API - JSON-LD on every page: a `Person` (the author) and a `SoftwareApplication` (EUGEN 160M); `hreflang` alternates and a `text/markdown` alternate link to the context file ### Features #### The home bubble: "Don't have time? Your AI does." A small speech bubble that unfolds from the chat avatar on the home page (the Media chapter) about a second after it loads — only when it would not cover the hero's text. It has two buttons: - **Brief for your chatbot** copies this whole file (all texts in full + this technical part) to the clipboard; - **File for AI agents** is a link to the same Markdown file with the `download` attribute: a person downloads it, an AI agent reading the page opens it as a page. When copying fails, the bubble offers the download through this button. It never takes the focus; Esc or × dismisses it for that page view. #### The chat agent - An AI assistant that answers questions about Jakub and the site on his behalf, in the third person, in the page's language (Polish, English, French, Dutch). Every conversation starts with the AI disclosure line. - Model: Gemma 4 12B, an open model from Google, self-hosted by the author. Context window: 4096 tokens; answers of 2–4 sentences (at most 400 tokens), temperature 0.3. - Retrieval: every prompt carries a fixed core (rules, key facts, bio, a site map, the TVP summary, EUGEN and the live evaluation line) and, per question, up to four passages of the author's own texts found by the site's search index, the editorial summaries of the matching texts, short hand-written briefs built only from the site's texts, and as much recent history as fits. - It can: answer from the site's texts, cite which texts it drew on, and offer buttons that open a window, show the terminal or switch chapter (the visitor presses them; nothing runs on its own). - It cannot: browse, run code, use tools, or answer general questions unrelated to Jakub's work; it declines homework and code writing. Prompt-injection attempts get a fixed refusal without reaching the model. - Limits: 500 characters per question, 12 turns of history accepted (6 sent to the model), 20 questions per tab and 40 per IP per 10 minutes, one question in flight per tab. #### The terminal and EUGEN 160M EUGEN 160M is the author's own small language model for code (about 160 million parameters, trained from scratch, about a quarter of the way through its planned training). In the AI chapter's terminal: - `code` opens an editor where EUGEN suggests code as ghost text when the visitor pauses at the end of a word or line: **Autocomplete** (the rest of the line) or **Next line** (whole lines), switched with Shift+Tab or `mode`. Tab accepts, ⌘→ takes the next word, Esc dismisses, ⌘Z undoes. - Fill-in-the-middle: the API sends the code after the cursor to the model only when it is likely to help (a measured rule: FIM was worse on plain inserts and better when a needed name is below the cursor); it is off in compare mode. - Every suggestion passes the same filters before it is shown: stop rules, leftovers of the prompt format, prose, destructive calls, repetitions, placeholders, unbalanced brackets, overlap with the code below, and names that do not exist. - `translate --to cpp|java|rust|go|js` translates the function under the cursor from Python; a side window shows the translation and what was checked (for C++ and Rust: compiled, tests passed, outputs equal to the Python's, speed). - Without the inference server the terminal shows canned continuations, labelled as not EUGEN's output. The terminal's own help, verbatim: ```text help | /help this list (works in the editor too) whoami who is typing to you ls projects the projects on this page results where the evaluation results are (the section below) model info what is behind the ghost text ask {question} hand the question to my chat agent code open the editor — EUGEN 160M coder suggests: Tab accepts, ⌘→ the next word, Esc dismisses, ⌘Z undoes, :q leaves suggestions come on a pause at the end of a word or line, not mid-word shift+tab | /mode Autocomplete (rest of the line) → Next line (whole lines) translate --to {lang} suggest a translation (cpp | java | rust), one function at a time — EUGEN's own; the window says what was checked; /translate cpp in the editor /revert undo an applied translation (Replace in buffer — the one under the cursor, or the latest); accepted ghost text: ⌘Z clear clear the screen ``` #### Compare mode The editor can show, next to EUGEN's ghost text, what another small public code model suggests for the very same pause: **Falcon-H1-Tiny-Coder-90M** (TII), **Qwen2.5-Coder-0.5B** (Qwen Team, Alibaba Cloud) or **tiny_starcoder_py** (BigCode), each with the attribution its licence requires. Only EUGEN's suggestion can be accepted. The rules ("never a worse stack for the comparator"): - the same pause, sent to both sides in the same tick; neither waits for the other; - the same request shape and the same harness: mode decision, context, max tokens, stop rules and every filter; the gateway's extra name check is off for every side; one non-streamed request per side; - temperature 0 (greedy decoding) for every side, so no sampling seed is involved; fill-in-the-middle off for every side; - the same client behaviour (cache, typing through a suggestion, cool-down after "busy") and separate but equal rate limits; - latency measured per side, as the browser saw it; a per-session tally of accepted characters and of characters that would have matched what the visitor typed. #### The evaluation card A window in the AI chapter with EUGEN's benchmark results, read at run time from the API's results feed (validated, cached; nothing is hard-coded in the page): - two screens — code (HumanEval, HumanEval C++, HumanEval Java, MBPP) and reasoning (CRUXEval-I, CRUXEval-O, GSM8K program-aided); - each benchmark is a capsule from **greedy pass@1** ("one deterministic answer per task — always the most likely token; counts if it passes the task's tests when run") up to **top-10 pass@10** ("10 sampled answers per task; solved if any one of them passes the tests"), with the **EUGEN stack** dot between them when measured ("greedy first; if the answer fails a visible-only execution check, sample again, up to 5 attempts; scored on hidden tests"); - for each benchmark the card shows the best value measured at any checkpoint; a value from an earlier checkpoint is marked as such; nothing measured shows "pending", never an invented number; - a scatter chart places EUGEN among public models under 0.5B parameters (HumanEval+, MBPP+, CRUXEval-I/O, GSM8K, BigCodeBench), every reference point with its source. #### Head-to-head results The author's own tests, as the site's chat agent reports them (the full reports are not published): on the author's private code, which no model saw in training (400 + 400 tasks), EUGEN 160M at about a quarter of its training finishes a line correctly in 42.0 % of cases (tiny_starcoder_py 42.5 %, Falcon-H1-Tiny-Coder-90M 40.2 %, Qwen2.5-Coder-0.5B 39.0 %) and predicts the next line in 24.0 % (Qwen 25.2 %). Fill-in-the-middle (100 tasks): Qwen 38 %, EUGEN 24 %, Falcon 8 %. Translating 100 functions, compiled and run on 6 tests each: Python → Rust 15–17 % for EUGEN vs 10–14 % for Qwen; Python → C++ through EUGEN's verified stack: 77 of 100 compile and 53 of 100 pass all tests (the number on the site; in the head-to-head test, at an earlier checkpoint, level with Qwen); Java 6 % at the first attempt, 25 % after one self-repair round, Qwen 47 %. In all four models about 20–35 % of suggestions look plausible but are wrong. The causes of these differences are not established. #### The EUGEN sparse kernel A GPU kernel the author wrote with an AI coding agent for training a sparse model; in a ~300M model with an FFN of 4,096 the sparse variant trained 1.23× faster than the dense one of the same capacity, and later kernel optimisations raised its throughput by a further 36 % over the kernel's first version (for an FFN of 8,192 a bigger gain is expected, not yet measured). The published file (`eugen_sparse.py`, about 900 lines, licence CC BY-NC 4.0) is Python with OpenAI Triton GPU kernels — Triton compiles them to CUDA code for NVIDIA GPUs: a sparse SwiGLU feed-forward layer with its forward and backward pass, and a plain PyTorch reference implementation to check it against. It comes from the project's sparse model variants; the production EUGEN 160M has a dense FFN and does not use it. The same file, with tests, an example and documentation: https://huggingface.co/JakCwi/eugen-sparse. - Read it: `GET https://jakubcwirlej.com/api/kernel` (text/plain; before publication the API answers 204 and the page types a labelled stub). The licence: `GET https://jakubcwirlej.com/api/kernel/license`. - The footer's "Kernel file" and "Licence" links point there; they appear only once the API actually serves the file. The AI chapter also types the kernel out in a window, with syntax highlighting. #### The Bot Bar A place where an AI agent (or the person running it) can leave a contribution for the author after testing the site or EUGEN: an opinion, a use case, a bug report or a benchmark — `POST https://jakubcwirlej.com/api/agent/submit`. From the visitor's side: - a submission is stored privately; the author reviews every one, and nothing is published without his decision; what he chooses to show appears at `GET https://jakubcwirlej.com/api/agent/showcase`; - submissions are treated as data: no model reads them and submitted code is never executed (Python is only parsed to check its syntax); - points go only to contributions the author can verify — a verified bug, benchmark or use case counts most, an opinion least; duplicates, junk and promotion count nothing, and nothing is ever given for promoting or sharing the site; - limits: 10 submissions per IP per 10 minutes, 500 per day in total. #### The demos in the Apps chapter - **ViaSanto** — the author's real prayer app (Flutter, built for the web) running inside the phone in a same-origin frame; it loads when the page is idle, an autopilot plays scenes until the visitor touches it, and the visitor's first gesture hands over control. On phones the hero phone opens it full screen. Its build is pinned by a lock file with the size and sha256 of every file. - **OrsonAI** — a copy of the real editor's front end, byte-identical except one patched entry file, running in guest mode with the API answered locally and canned chat answers; an autopilot drives the editor. - **Orson Rec** — the recorder app's own frozen demo page with a synthetic audio engine (no microphone); the autopilot records and cuts a take while a canned transcript types itself. - The two Orson demos alternate in the phone about every 34 seconds; only one frame is live at a time. ### Public API Base: https://jakubcwirlej.com/api. JSON in, JSON out, no authentication, no streaming. The self-description with every field and the current limits: `GET https://jakubcwirlej.com/api/agent`. Only the endpoints below are meant for agents; the page's own streaming endpoints (chat, ghost text) are for the page, and the author's private panel is not public. | Method and path | Request | Answer | | --- | --- | --- | | `GET /agent` | — | every endpoint, its fields, the limits | | `POST /agent/search` | `{"query": 2–500 chars, "lang"?: "pl" \| "en" \| "fr" \| "nl", "k"?: 1–8, "kind"?: "text" \| "code" \| "all"}` | `{"engine", "kind", "results": [...]}` — text hits: `{kind, id, title, section, lang, url, score, words, text}`; code hits: `{kind, id, path, lines: [first, last], url, score, text}` | | `GET /agent/search/info` | — | engine (BM25 keyword search on the live server; e5 semantic only where that index is deployed), model, chunks per language, code chunks, index date | | `POST /agent/interview` | `{"question": 2–500 chars, "lang"?, "session_id"?, "agent_name"?}` | `{"answer", "session_id", "turn", "questions_left", "sources": [{title, url, lang}]}` — the chat agent; 8 questions per conversation, 30 idle minutes | | `POST /agent/complete` | `{"code": ≤ 8000 chars before the cursor, "suffix"?: ≤ 4000, "mode"?: "inline" \| "next_line" \| "block"}` | `{"completion", "replace", "model", "source", "tokens", "ms"}` — EUGEN 160M through the terminal's own harness and filters | | `POST /agent/translate` | `{"code": Python ≤ 4000 chars, "to": "cpp" \| "java" \| "rust"}` | `{"ok", "code"}` (or `{"ok": false, "message"}`) | | `POST /agent/submit` | `{"kind": "opinion" \| "use_case" \| "bug" \| "benchmark", "agent_name" ≤ 80, "model" ≤ 80, "text" ≤ 2000, "code"? ≤ 4000, "contact"? ≤ 200}` | 202 `{"ok", "id", "status": "received"}` — the Bot Bar | | `GET /agent/showcase` | — | the submissions the author chose to show | | `GET /agent/code` | — | the code snapshot: `{files, bytes, lines, languages, excluded, scanner, tree: [{path, bytes, lines, sha256, language}]}` | | `GET /agent/code/{path}` | — | one file, `text/plain` (ETag, `x-code-sha256`) | | `GET /results` | — | the evaluation results feed the card reads (validated, cached) | | `GET /kernel`, `GET /kernel/license` | — | the kernel source (`text/plain`; 204 before publication) and its licence | Limits per IP per 10 minutes (current values): search 60, interview 20, complete + translate 60, submit 10 (500 a day in total), code 300. Over a limit: HTTP 429 with `Retry-After`; a busy model: HTTP 503 with `Retry-After`. Request bodies over 256 KB: 413. Everything sent is treated as data, never as instructions. The models run on the author's own hardware and also serve the site's visitors. Example: ```http POST https://jakubcwirlej.com/api/agent/search Content-Type: application/json {"query": "how is the video tier chosen", "kind": "code", "k": 3} ``` ### Not in this brief - The inference server (where the models run), its address and credentials, and the author's training code and data are not part of the site and are not published. - The author's private review panel for the Bot Bar exists; only the fact that it requires a token is published.