Skip to content

Andrija Birtić

Software developer | Tech lead | Architect

Designing and shipping production ML systems end to end.

Check out my Work

A documentation assistant

Production assistant over thousands of dense technical documents. Hybrid retrieval, research loops, an improvement loop driven by real feedback.

  • LangGraph
  • Vespa
  • BGE-M3
  • evals
Read the case study

An entity deduplicator

1.3 million guest profiles from multiple property systems, a quarter of them belonging to someone who already has another one. Hybrid retrieval as blocking, a calibrated neural resolver, guarded merges. All inside a streaming pipeline.

  • PyTorch
  • entity resolution
  • hybrid retrieval
  • calibrated nnet
Read the case study
incoming profile
Anna O'Neill14.02.1991
anna.oneill91@mail.com
Northbrook
search index
idle
nnet
waiting
  • Anna ONeill14.02.19910.97dupe
  • Anna McNeill22.09.19870.11dupe
  • A. O'Neill14.02.19910.93dupe
  • Alice O'Neill03.05.19910.15dupe
  • Anna O'Neill30.06.19690.07dupe
  • Anna O'Neill14.02.19920.62dupe

a new profile arrives

Hospitality recommender at streaming scale

Multiple source systems unified into a canonical model with Kafka connectors and ksqlDB, streaming services that clean and deduplicate through a fuzzy lake index and a neural deduplicator, and a two tower recommender answering inside a five second contract.

  • Kafka
  • ksqlDB
  • two tower
  • dedup nnet
Read the case study
guest lab
visitsfirst timerreturningregular
partysolocouplefamily
seasonspringsummerautumnwinter
  • 1pool morning0.74
  • 2bike rental0.71
  • 3guided hike0.70
  • 4late checkout0.58

Document AI scanner

Identity documents read on a webcam or a cheap flatbed, no dedicated scanning hardware. A pipeline of custom vision models and OCR in one era, one fine tuned vision language model in the next.

  • object detection
  • classification
  • segmentation
  • OCR
  • VLM
  • vision
Read the case study
scannerv1, model pipeline
national IDchecksum okmrz cross checked
Republic of Memeland
national identity card
surname
O'NEILL
name
ANNA
doc no
N0482913
birth
14.02.1991
expiry
08.11.2031
nationality
MML
I<MMLN0482913<8<<<<<<<<<<<<<<<
ONEILL<<ANNA<<<<<<<<<<<<<<<<<<
document classifiednational ID
extracted fields
  • surnameO'NEILL
  • nameANNA
  • doc noN0482913
  • birth14.02.1991
  • expiry08.11.2031
  • nationalityMML
  • mrzchecksum ok
fields fused
model output
{
"surname": "O'NEILL",
"name": "ANNA",
"document_number": "N0482913",
"birth_date": "14.02.1991",
"expiry": "08.11.2031",
"nationality": "MML"
}
valid JSON

version one starts, the document is detected in the frame

Sentiment analysis platform

Type a business name, pick the platforms. A nightly job scrapes the public reviews and an agent loop distills large quantities of them, across languages, into one report of severity ranked issues and concrete fixes.

  • LangGraph
  • scrapers
  • agent loop
  • automation
Read the case study
sentimentreview, croatian
public reviewHR

Soba je mirisala na vlagu, klima nije radila, a na recepciji nitko nije pomogao.

Das Frühstück war fantastisch, aber das WLAN fiel ständig aus.

Posizione perfetta vicino al mare e la colazione è davvero ricca.

Great pool and friendly staff, but check in took almost an hour.

HRDEITEN
extracted
languagecroatian
sentimentnegative
  • roomnegative
  • air conditioningnegativehigh
  • receptionnegative
suggested fix
service the AC units, retrain the front desk

Automated migration pipeline

parked

The legacy system mapped into a Neo4j graph, an agent planning and executing the migration, a human in the loop on every consequential step.

  • LangGraph
  • Neo4j
  • agent
  • human in the loop
Read the case study
migration

Check out my Research

Memory of How

active

A frozen model that learns a behavior from a few demonstrations and keeps it, no finetuning, no full training run. What it learned survives a full context wipe.

  • PyTorch
  • transformers
  • meta learning
  • continual learning
Read the research
case study

A documentation assistant

  • LangGraph
  • Vespa
  • BGE-M3
  • evals

Thousands of long, dense, interlinked technical documents, and the only working search engine was the support experts themselves. Every question waited in their queue. This assistant answers instead.

agent console

> en

An example run. Questions arrive in five languages, retrieval is cross lingual.

act 1

The problem

The software had thirty years of development behind it and around three thousand technical documents. Long, dense, interlinked. That volume is the first villain. No person holds it in their head, and keyword search never could. The support experts answering questions were the real search engine, and every question waited in their queue.

Solving the volume problem does not touch the second villain. Users ask in their own words, in Croatian, English, German, Italian, Slovenian. The docs answer in the software's words. The two vocabularies rarely meet.

act 2

The decision ledger

The system was built in iterations, side by side with the support experts who know the docs best. Each contender faced the same questions and kept its place or lost it.

the method

  • plain RAGdocs too dense and intertwined for one pass
  • knowledge graphsat par or below dense retrieval, too complex to maintain
  • RAPTORsame story, summaries lose the details
  • rerankers keptsmall but real gain, the M3 reranker stays in
  • hybrid three way retrievaldense, sparse lexical, and ColBERT fused in one ranking pass

the embedder

  • OpenAI text-embedding-3-largeweak on multilingual and this corpus, and no offline embedder, privacy says no
  • Cohere multilingualsame story, cloud only
  • Gemini embeddingsame story, cloud only
  • Mistral embedsame story, cloud only
  • Voyagesame story, cloud only
  • Qwen3 embedding, 0.6B to 8Bevery size worse than BGE-M3 here
  • BERT era variantsa generation behind on retrieval
  • BGE-M3best cross lingual scores of everything benchmarked, and one pass emits all three representations

the store

  • pgvectorno ColBERT support
  • Chromadoes not cover all three search paths
  • Weaviatehybrid BGE-M3 mode not fully supported at the time
  • Qdranttested fine, ranked slower than Vespa
  • Elasticsearchcould do it, slower and more complex for less gain
  • Milvusmatched Vespa on gain, lost on ease of use
  • Vespaarbitrary ranking math over all three representations, fast

the ingestion

  • rolling windowssteady but blind to document structure
  • sentence break chunkingclean cuts in the wrong places
  • recursive splittinggood generic default, still cut in the wrong places
  • semantic chunkinggood but slow, and still cut in the wrong places
  • structure based chunkingassumes cleaner docs than thirty years produce
  • custom chunkerbuilt around how the docs are written, significant boost, generalizes beyond them

the runtime

  • in house node framework lineagecame first, shaped how nodes are controlled and routed
  • LangGraphthe obvious choice once it matured, no reinventing warm water
act 3

The architecture

The entry proxy is the only exposed service, balancing into the web frontend with its admin panel and into the agent runtime. The embedding service and the search engine do retrieval, a cache holds the hot state, a durable store holds the rest. An mcp connector is available to the agent for integrations with the systems around it. The LLM provider is a slot, local models drop in. The same stack ran on AWS, on Azure, and on bare Hetzner. Watch the traffic:

internal networkuseradminproxywebagentembeddersearchmcpcachestore

The admin panel

Every knob and process in the system is operable by the support experts themselves, no dev in the loop. Ingestion, retrieval settings, learned improvements, evals, all behind a panel built for the people who answer questions, not for the people who wrote the code. I built the frontend and the panel by hand in React and TypeScript, working with UI/UX designers.

admin panel
retrieval weights
dense52
lexical18
late interaction30
agent
model per node
research budget74
answer language

A mockup.

Hybrid retrieval you can argue with

BGE-M3 emits three representations per text, a dense vector, a sparse lexical one, and ColBERT token vectors. Vespa ranks over all three in one pass, in two phases, a cheap hybrid ordering over the full candidate set, then a richer reorder of the head. Move the weights:

the head
denselexicalColBERT MaxSim

every candidate the query matched, unranked

marks documents the eval says should win this query.

  1. 1Persistence concepts
  2. 2Add thread level persistence
  3. 3Graph state reference
  4. 4Use Postgres checkpointer
  5. 5Runs API reference
  6. 6Run configuration reference
  7. 7Quickstart
  8. 8Cross thread memory store
  9. 9Durable execution
  10. 10Compiling a graph
  11. 11Time travel and forking
  12. 12Streaming graph outputs

The eval preset is what golden set tuning found for this docs corpus.

Candidate scores exported from the companion repo.

The improvement loop

The vocabulary gap never closes on its own, so the system keeps learning. Some feedback is direct, some is read from usage, and what it changes depends on where it lands in the system. Step through one loop:

1 / 5 A user pushes back

agent console

> How do I make the agent remember progress between runs?

I could not find a document about remembering progress. Closest match covers run configuration.

"assistant couldnt help me, my agent starts from zero every time and I dont want that"

Direct feedback, a downvote and the user's own words. The loudest signal, and the rarest.

Scripted frames, a mockup.

act 4

The evidence

The judge for every decision above is a customised eval harness. The golden question set was built with the support experts, from real user questions, graded easy to hard, each with known answer documents. Every change faces it before it lands: swapping the LLM, adding a node, tuning retrieval weights.

model bake offrunning the golden set
  1. candidate D10%
  2. candidate B6%
  3. candidate E5%
  4. candidate C3%
  5. candidate A0%

Every candidate model runs the same golden set. A scripted mockup.

plain RAG baseline, answers correct
40 to 50%
the shipped system, answers correct
85 to 90+%

Answer correct rate on the golden set.

What it does not do

  • Answers are only as good as the documents. Where docs contradict, the answer hedges.
  • Research loops trade latency for coverage. On heavily intertwined questions they trade it for accuracy too.
  • It answers the docs, not the world. Out of scope questions get declined, not improvised.
  • When retrieval comes back thin, it says so. A shrug beats a confident guess.
  • Improvements are learned per corpus. A fresh deployment starts unlearned and earns them.
  • It never edits the source documents. Learned phrasing lives beside the docs, not in them.
  • Cross lingual only reaches as far as the embedder does. New languages get benchmarked, not assumed.
  • Evals cover what the golden set covers. Blind spots move with it.
coming soon
case study

An entity deduplicator

  • PyTorch
  • entity resolution
  • hybrid retrieval
  • calibrated nnet

1.3 million guest profiles, one question: same person? Merging two strangers is a disaster, missing a duplicate is just noise, so every decision is three way. Merge, reject, or ask a human. Try beating the system at its own game:

pair judgepair 1 / 7
profile a
name
Anna O'Neill
born
14.02.1991
email
anna.oneill91@mail.com
phone
not on file
city
Northbrook
document
not on file
profile b
name
Anna ONeill
born
14.02.1991
email
anna.oneill91@mail.com
phone
not on file
city
Northbrook
document
not on file
same person?

Seeded fictional guests.

act 1

The problem

Front desks type fast, booking channels import blind, migrations copy everything. Across multiple property systems the guest base grew to 1.3 million profiles, and 26.3% of them belong to a person who already has another one. Histories fragment, loyalty misfires, the same guest gets the same offer three times.

A wrong merge poisons two guests' histories with each other's stays. A missed duplicate just repeats an offer. That is why the decision is three way, merge, reject, or ask a human.

act 2

Labels from the failure itself

There was no labeling budget, and none was needed. A proxy signal already sitting in the data turned out to be near ground truth for this problem, so the labels came for free, at corpus scale. What survives unlinked in the data is exactly what humans never caught. The model trains on the residue of the problem it solves.

act 3

The system

Blocking is the search index itself, lexical and vector retrieval fused into one candidate list, no hand built blocking keys, near total pair completeness. For the decision itself, four resolvers competed on one shared benchmark, a weighted heuristic, a small feature based neural net, a cross encoder, an LLM judge.

incoming profile
Anna O'Neill14.02.1991
anna.oneill91@mail.com
Northbrook
search index
idle
nnet
waiting
  • Anna ONeill14.02.19910.97dupe
  • Anna McNeill22.09.19870.11dupe
  • A. O'Neill14.02.19910.93dupe
  • Alice O'Neill03.05.19910.15dupe
  • Anna O'Neill30.06.19690.07dupe
  • Anna O'Neill14.02.19920.62dupe

a new profile arrives

A service

The resolver ships as one container image consuming a stream of profile events. Partitioning keeps each guest's events in order, scaling is adding a replica, and a poison message lands in a dead letter queue instead of stopping the stream. Verdicts flow out the same way they came in, as events.

topic, profile events
p0
p1
p2
p3
consumer group
dedup 1
p0p1
dedup 2
p2p3
dedup 3
merged identities 12,480
dlq

profile events stream in, one partition per guest key keeps their order

act 4

The evidence

Every resolver faced the same benchmark, held out clusters none of them had seen. The expensive favorites lost. The small feature based neural net reached 0.95 precision, trains in seconds on a CPU, and runs inference in milliseconds.

pairwise F1, held out clusters
0.973
cluster level B3 F1
0.988

The scores apply to a given corpus of guests.

What it does not do

  • It does not auto merge through doubt. Conflicting hard anchors are never trusted, doubtful matches go to human review.
  • It does not catch every duplicate. Some pairs carry too little evidence for anyone to link, model or human.
  • No online learning.
  • It makes mistakes. The verdicts are only as good as the data the source systems feed it.
case study
ongoing

Hospitality recommender at streaming scale

  • Kafka
  • ksqlDB
  • two tower
  • dedup nnet

A European hospitality group. 1.3 million guest profiles, 1.9 million stays, 10 million transaction lines, spread across multiple property systems. The main driving question, what should we offer this guest, this party, this season. All inside a five second contract.

front desk
Mia D'Arcy · spa visit
point of sale
M. Darcy · dinner
kafka, guest events
p0
p1
p2
p3
etl, canonical model1 quarantined
one schema
deduplicator
waitingMia D'ArcyM. Darcyone identity, merged
two tower model
guest tower
curated catalog
recommendations
  • 1.wine cellar tasting0.91
  • 2.spa morning0.84
  • 3.late checkout0.62

two property systems emit events about the same world, each in its own shape

Fictional guests, fictional catalog.

act 1

The canonical model

Nothing gets recommended while the sources disagree about who the guest is and what a stay even looks like. So the first thing built was agreement. A canonical data model for the whole domain. Kafka connectors pulling every property system into it. ksqlDB and a cleaner service standardizing events, quarantining the hopeless ones. And the deduplication layer on the stream, the deduplicator that earned its own case study.

act 2

Three architectures auditioned

LLM as the recommender

lost
guest history
vector storegraph db
LLM reasoner
promised
retrieval plus relations, a model reasoning its way to a recommendation
lost
prone to LLM failures

Transformer PoC

lost
guest history
transformernext item
promised
recommending as sequence generation, guest history in, next item out
lost
needs far more training data than the towers to reach the same quality

Two towers

shipped
guest
catalog
ranked list
promised
guests embedded on one side, products on the other, a dot product between them
won
cheap to serve, honest to evaluate, fast enough
act 3

Different ways to know a guest

No single description of a guest is enough. The model reads each guest through several views at once and learns to weigh them, leaning on whichever carries real signal for that particular guest. Build a guest below and watch the ranking follow:

guest lab
visits
party
season
popularity dial
personalpopular
cold start, popularity tier answering
the guest in semantic space
wellness
food
active
family
romance
comfort
  • 1pool morning0.74
  • 2bike rental0.71
  • 3guided hike0.70
  • 4late checkout0.58
  • 5spa morning0.55

Guests and catalog fictional.

act 4

The architecture

the platformsource Asource Bsource Ckafkacleanerdedupstoretwo towerguest
act 5

The evidence

in progress

What it does not do

  • No realtime features. The model sees the world as of the last snapshot.
  • Cold start answers in tiers, the highest quality tier that applies answers first, and quality degrades tier by tier below it.
  • Not a finished product yet. The system is still in development.
coming soon
case study

Document AI scanner

  • object detection
  • classification
  • segmentation
  • OCR
  • VLM
  • vision

Identity documents read at the reception desk on a webcam or a cheap flatbed, no dedicated scanning hardware. Two generations of the scanner do it.

scannerv1, model pipeline
national IDchecksum okmrz cross checked
Republic of Memeland
national identity card
surname
O'NEILL
name
ANNA
doc no
N0482913
birth
14.02.1991
expiry
08.11.2031
nationality
MML
I<MMLN0482913<8<<<<<<<<<<<<<<<
ONEILL<<ANNA<<<<<<<<<<<<<<<<<<
document classifiednational ID
extracted fields
  • surnameO'NEILL
  • nameANNA
  • doc noN0482913
  • birth14.02.1991
  • expiry08.11.2031
  • nationalityMML
  • mrzchecksum ok
fields fused
model output
{
"surname": "O'NEILL",
"name": "ANNA",
"document_number": "N0482913",
"birth_date": "14.02.1991",
"expiry": "08.11.2031",
"nationality": "MML"
}
valid JSON

version one starts, the document is detected in the frame

A fictional document from an invented country, scanned by both generations.

act 1

The problem

Dedicated document scanners are expensive, lag years behind new document formats, and the rarer IDs and travel documents are often not supported at all. Every fix waits on the vendor.

So the scanner replaces them with whatever camera is present. Passports, national IDs, driving licences, and health cards, read at the desk and submitted as structured fields to every system that needs them. And training such a pipeline is hard for one reason above all, identity documents are sensitive personal data under GDPR. You cannot collect a training set of real ones.

act 2

The era of multiple models

Version one is a pipeline. Detect the document, warp it flat, fix the orientation, classify the type, find each field, read it, fuse the results. A model per stage, designed and trained in PyTorch, exported to ONNX, all running locally inside the desktop app.

detector labexported, ONNX
backboneneckheadConv stemC2f ×3cat P2detect P2C2f ×6cat P3detect P3C2f ×6cat P4detect P4C2f ×3cat P5detect P5SPPFfpn up, pan down
iteration 1 of 5
mAP500.91
latency14 ms
size12 MB

the baseline detector, a stock backbone neck head stack

An illustrative replay of the tuning loop.

The training data was fabricated, all of it. Fake identities rendered onto document templates with passport fonts, ink bleed, finger and stamp occlusions, and zero real personal data.

data factory
Republic of Memeland
national identity card
name
EMMA WHITFIELD
doc no
M1930412
birth
04.07.1989
training set
samples 117,204

a document template, no identity on it yet

How a training set exists with zero real people in it.

act 3

Building an OCR

The OCR was prototyped in house, a custom transformer OCR designed from scratch and trained on the synthetic lines, then benchmarked against a specialized classic engine. On par on accuracy, at a smaller memory footprint. A conv encoder reads the crop, a transformer decoder attends its way across it, character by character.

attention ocr
line crop
ANNA O'NEILL
ANNA O'NEILL
decoded
cropconv encodertransformer decodercharacters

a text line crop comes in

act 4

The era of texting with your scanner

Version one earned its place, around 90 percent balanced accuracy on exact field match, and in production it reads nearly flawlessly. At around a second per document. But its pain lived at the edges. A document type it had never seen, a border pass, came through degraded, and every improvement meant retraining a whole family of models.

Version two replaces the entire extraction stage with one fine tuned vision language model. The desk app literally texts it, a document photo goes in, one strict JSON object comes back. It is even trained to refuse bad shots, glare or a covered corner gets a warning to adjust the document instead of a bad read. In practice it generalises far better on unseen niche formats, exactly where version one degraded.

Models from 0.6B to 8B were tried across vendors, the sweet spot settling around 2B and 4B, similar in speed and accuracy, served on a single consumer GPU at 96.6 percent field accuracy, under three seconds a document. Version two is in final fine tuning and early testing.

desk app
scan_0142.jpg
model
model
{
"surname": "O'NEILL",
"name": "ANNA",
"doc_no": "N0482913"
}
valid JSON
desk app
scan_0143.jpg
model
model
glare over the document, adjust and rescan
desk app
scan_0143_retake.jpg
model
model
{
"surname": "O'NEILL",
"name": "ANNA",
"doc_no": "N0482913"
}
valid JSON

the desk app texts the model a scan

A fictional conversation.

act 5

Shipping it

The same system ships two ways. A desktop app with local inference where the hardware allows it, remote inference where it does not. Either way there is one point of control, a central server pushing delta updates, so every desk runs the version it should.

update serverdeskdeskdeskremote inferencelocal gpulocal gpuno gpu
act 6

The evidence

balanced accuracy, v1
≈90%
time per document, v1
≈1 s
balanced accuracy, v2
96.6%
time per document, v2
< 3 s

Measured on a held out document test set, 29 countries.

What it does not do

  • A language model can hallucinate no matter how accurate it is. Critical data such as the machine readable zone is cross checked against a dedicated deterministic reader.
  • Non Latin scripts are out of scope, the schema enforces Latin output.
  • The vision model needs its GPU box. The CPU fallback measured roughly ten times slower and was rejected.
coming soon
case study

Sentiment analysis platform

  • LangGraph
  • scrapers
  • agent loop
  • automation

Type a business name, pick the platforms, and that is the whole setup. From there it is a nightly job, scrapers collect the public reviews, and an agent loop distills large quantities of them, across languages, into one report with severity ranked issues and concrete suggested fixes.

sentimentreview, croatian
public reviewHR

Soba je mirisala na vlagu, klima nije radila, a na recepciji nitko nije pomogao.

Das Frühstück war fantastisch, aber das WLAN fiel ständig aus.

Posizione perfetta vicino al mare e la colazione è davvero ricca.

Great pool and friendly staff, but check in took almost an hour.

extracted
languagecroatian
sentimentnegative
  • roomnegative
  • air conditioningnegativehigh
  • receptionnegative
suggested fix
service the AC units, retrain the front desk

Seeded fictional reviews in four languages, click a language to replay one.

act 1

The problem

Review dashboards give a business a star average and a list of frequent words, a score with no instructions. The reviews themselves are scattered across platforms, written in several languages, and polluted by namesake businesses on other continents. And an LLM asked to summarize them will happily paraphrase, invent, and silently skip. The problem is getting from that mess to a to do list you can trust.

act 2

The robust solution

One failure mode is precision and scope. Scrapers are unreliable, so the platform is built to know its scrapers and control them. Several scrapers per platform, so one failing means a fallback.

Search a hotel name and you get the right one plus its doubles on three continents. A layered identity filter kills the impostors, a review only enters the analysis when every layer agrees it belongs to the right business.

The other failure mode is the LLM itself, and it is tamed with orchestration rather than hope. The current generation runs as a LangGraph flow with structured output at every step, which turns a moody generalist into a robust, well performing worker.

An LLM will still sometimes skip reviews in a batch, short and foreign language ones especially. The loop notices and recovers, so every review gets an answer and none get invented.

sentimentnightly job

resolve the business

One nightly job.

act 3

Provenance

Every claim the system makes must quote the review verbatim, an exact substring, checked. Every number in the report reconciles back to an extracted aspect, every aspect back to a quote, and every quote back to the original review on the platform it came from.

the report
the receipts
  • platform A#4f2c

    Das Frühstück war fantastisch, aber das WLAN fiel ständig aus.

  • platform B#a913

    Wifi kept dropping every evening, impossible to work.

  • platform C#07de

    Internet je bio prespor i za osnovne stvari.

Fictional reviews. Click an issue to open its receipts.

act 4

The evidence

The report is category cards, source and month breakdowns, rising and declining keywords, critical issues with severity, and deduplicated action items each with a suggested fix.

example run
reviews analyzed
≈700
total cost
≈$1.40

What it does not do

  • The LLM can make mistakes. The quotes are verbatim, but the judgment attached to them is still a model's.
  • Subtle sentiment can be missed. Sarcasm, irony, and mixed language reviews are harder than plain praise or complaint.
  • Coverage ends at the scrapers. What is not public, or not reachable, is not in the report.
coming soon
case study

Automated migration pipeline

  • LangGraph
  • Neo4j
  • agent
  • human in the loop

A legacy ERP, 725 modules in a proprietary format. The source ships as binary, unreadable by a model, and converting it yields 400 MB of markup, far more than any context window holds. 80% of that is generator boilerplate. A coding agent out of the box is useless here. The project is everything built around the agent so it stops being useless.

migration

A fictional module graph.

act 1

The problem

725 modules of binary source, converted to 400 MB of machine generated markup that is mostly noise. And nothing stands alone. A module reads tables another one writes, calls shared libraries, inherits from common ancestors. The real problem is separating behavior from boilerplate and cutting one module free of the tangle, not translating syntax.

act 2

The graph

Every module goes through a full grammar for the legacy language into a Neo4j knowledge graph. Clustering the graph finds whatever repeats, screen shapes, trigger layouts, data access patterns, and the corpus collapses into a handful of families. Migrate one member well and the same recipe carries across its family, biggest families first.

act 3

The agent

The agent never reads source files. It queries the graph through a small set of narrow tools. The graph hands it the full closure of what one module needs, its screens, its triggers, the tables it touches, the libraries it calls, so a single module migrates standalone, keeping context lean by design.

migrationthe fit test
context window
the whole corpus
400 MB
one module, raw source
too big
the graph closure
fits

the corpus does not fit any context window

Sizes illustrative.

act 4

The loop

The agent migrates a module, the result must build and pass its tests, then a human reviews and tests it for real. Findings feed the next cycle, and the cycles repeat until the migration is done.

What it does not do

  • It has not been run on the whole application, only on individual modules so far.
  • Complex modules still require significant human intervention.
  • The LLM can make mistakes no matter how good the harness is. Nothing ships without a build, green tests, and a human review.
coming soon
research
active

Memory of How

  • PyTorch
  • transformers
  • meta learning
  • continual learning

Imagine your assistant learning a behavior from a few demonstrations and keeping it, no finetuning, no full training run. The model stays frozen. And what it learned survives a full context wipe.

memory of how
frozen model
add an endpoint for user search
sure, starting with the route handler, I can add some tests afterwards if you want
demonstration
work like this. tests first, then code, one terse commit.
add pagination to the results
tests first. then pagination, one terse commit.
add rate limiting to the API
sure, I will write the limiter first and we can think about tests and commits afterwards
habit gone
context
frozen model + meta attn
add an endpoint for user search
sure, starting with the route handler, I can add some tests afterwards if you want
demonstration
work like this. tests first, then code, one terse commit.
add pagination to the results
tests first. then pagination, one terse commit.
add rate limiting to the API
tests first. then the limiter, one terse commit.
habit kept
context

two frozen models, the right one carries a tiny memory

A fictional dialogue.

Abstract

Language models exhibit effective in-context adaptation, but this adaptation does not persist beyond the context window. Existing approaches address this by editing model weights. We instead ask whether a frozen model can retain knowledge acquired in context and reuse it across sessions. We attach a compact learned memory module to frozen open-weight models spanning 0.6B to 8B parameters, and provide a small number of in-context demonstrations of behaviors such as coding practices, formatting styles, and naming conventions. After the context and KV cache are fully cleared, the behaviors persist. In preliminary runs, demonstrated habits survive at or near ceiling while arbitrary associations such as specific names or values collapse to zero. Transplanting one session's memory state into a new session carries the rule to content neither session contained. In its current form, the memory generalizes within the family of demonstrated behaviors and does not yet carry novel rules outside it. Results are preliminary, and the mechanism, full quantitative results, and ablations will appear in the preprint once the remaining experiments are complete.

ORCID 0009-0002-0399-4445

coming soon

About

Software developer, tech lead and architect. I mainly specialise in building dense neural networks and the systems that use them end to end.

I care about honest evals, clean architectures, and solutions that actually make a change.

Andrija Birtić

Contact

Write me directly.