Grafy Blog
All postsTry Grafy free
← All posts

An AI paper studio: search, cite and compile LaTeX

The Grafy team · Aug 28, 2026 · 13 min read
latex aiai research assistantarxiv searchbibtexai paper writing
An AI paper studio: search, cite and compile LaTeX

A paper lives in three tabs. The manuscript is in one, the reference manager is in another, and the model you are asking for help is in a third — which is the one that cannot see either of the others. So it proposes a citation for a paper you have never added, in a key format your file does not use, and the afternoon goes on reconciling rather than writing.

Scribe is Grafy's paper studio, and it exists to collapse those three tabs into one document. A LaTeX project, a scholarly search across arXiv and Crossref, a bibliography that merges instead of rewriting, a compile with readable diagnostics, and a writing assistant that is handed the file you actually have open.

1Search2Cite3Draft4Compile
Search → Cite → Draft → Compile
SCRIBEmain.texreferences.bibsections/intro.texsections/method.tex12345678\cite{vaswani2017attention}ReferencesarXiv:1706.03762 — already in references.bib as vaswani2017attentionno duplicate entry written
The reference panel answers with the key the file already had, rather than appending a second entry for the same paper under a different name.

A new project already compiles

The worst hour of any LaTeX project is the first one, and it is spent on nothing: a preamble that is missing a package, a bibliography command pointing at a file that does not exist yet, a first build that fails for reasons that have nothing to do with the paper.

A new Scribe project starts from an article class with inputenc, amsmath, amssymb, graphicx and hyperref already loaded, a title block, an empty abstract, an Introduction section, and \bibliographystyle{plain} wired to a references.bib that exists and is empty but for a comment. It compiles as it stands, and it accepts a citation as it stands. The first thing you do is write, not configure.

The entry point and the engine are settings on the project rather than conventions, so a manuscript split across sections/ files is a supported shape from the start. \input, \include and \subfile are followed from the entry point, root-relative first and then relative to the file doing the including — and a draft nothing includes is deliberately not part of the manuscript, because an unreferenced draft.tex full of biblatex is how a classic-BibTeX main.tex gets talked into the wrong backend and every real citation resolves to a question mark.

Five databases, one record

A reference panel that searches one database is a reference panel you will leave. Scribe asks five — arXiv, Crossref, OpenAlex, Semantic Scholar and DBLP — and reads every one of them into a single record: source, id, title, abstract, authors, year, published date, venue, DOI, arXiv id, and both the abstract page and the PDF link. Each database answers in its own dialect, arXiv in Atom XML and the rest in JSON, and none of that dialect reaches the rest of the app.

They are asked concurrently, one worker per source, so the wait is the slowest database rather than the sum of five — five calls behind a twenty-second fetch timeout is up to a hundred seconds in a row, and this runs inside a request you are waiting on. The answers are read back in a fixed order so the same query answers the same way twice: deduplication keeps the first sighting as authoritative, and a merge order that shuffled per request would quietly make that arbitrary.

Ten results by default, twenty-five at most; the query is trimmed to 300 characters and an abstract to 2,000. A fact that is absent is an empty string, never a guess — a record with no DOI says so rather than inventing a plausible one, which matters the moment that field becomes a line in your bibliography.

An optional open-access filter narrows the answer to work a reader can actually fetch. OpenAlex and Semantic Scholar can apply it themselves, so they are asked to; the others are filtered after the merge, because the merge is what learns that a Crossref hit with no link and an OpenAlex record with one are the same paper.

A database that is down, slow or answering nonsense contributes zero results and is logged, rather than failing the whole panel. One column empty is useful; a 500 in the middle of a literature search is not. It is the same discipline the web-change monitor uses on the open web, pointed at the scholarly ones instead.

A citation lands without rewriting your file

Your references.bib is a file you own, and probably one you have hand-edited: comments, an ordering that means something to you, a couple of entries with fields nobody else writes. So the merge is loss-free by construction. The existing bytes are preserved exactly and new entries are only appended after them. Nothing is reformatted, reordered or normalised on the way past.

The other half of the promise is that a paper you already have does not land twice. Identity is the DOI when there is one, the arXiv id when there is one, and a normalised title-plus-year otherwise — because the same paper arrives from two databases under two keys, two capitalisations and occasionally two spellings of the same author. When a candidate matches something already in the file, the answer is the existing key, and no entry is written.

That match also teaches the index: a title-only entry that is joined by a DOI-bearing record now answers to that DOI as well, so the next record in the same batch — same DOI, a variant title — cannot slip past both markers and land as a twin. And a paper that arrives twice in one batch cites once, because \cite{key,key} cites nothing extra and reads like a bug.

The keys are derived, so the same paper is always the same key

Citation keys are surname, year and the first word of the title — vaswani2017attention — with a letter suffix on a collision (smith2024riskb), which is the convention a hand-kept bibliography already uses. They are derived rather than random, so the same paper gets the same key whoever adds it and whenever.

The entry type follows the record rather than a default. An arXiv paper cites as the preprint it is: @article with journal = {arXiv preprint arXiv:2401.12345}, plus eprint and archiveprefix for the styles that use them. A venue that reads like proceedings becomes @inproceedings with a booktitle; anything else with a venue is an @article. Prose fields are escaped for TeX; DOIs and URLs are not, because a DOI carrying an escaped underscore stops resolving.

The assistant writes LaTeX; it never operates the project

The writing assistant is prompt-in, text-out, and the seam is deliberate. It is told it is writing for this manuscript, to keep your voice, and to put exactly one fenced latex block in an answer that proposes markup. That block is lifted out as an insertable suggestion; the prose around it stays prose. An answer with no block is a legitimate answer — sometimes the question was a question.

It is also told to cite only with keys that exist in the project's bibliography, which is the instruction that stops the failure this whole post opened with. The context it gets is clamped rather than whole-project: the file you have open (to 20,000 characters), the passage you selected (to 4,000), the list of file names, and the tail of the last compile log — but only when you say the question is about the failure.

What it does not get is the ability to act. The model writes text; the router applies changes through the same gates a keystroke passes, so an answer cannot rename a file, delete a section or quietly rewrite your bibliography. It is the same boundary the coding studio draws between an answer and a run.

Which engine compiles it, and what happens when none can

Compilation prefers Tectonic: one binary that runs its own passes and BibTeX, invoked untrusted because a .tex file is user content. Behind it come latexmk, then a bare engine loop over pdflatex or xelatex. A manuscript that needs biber vetoes Tectonic when a TeX Live engine and biber are both present, because Tectonic orchestrates BibTeX and not biber — and a PDF whose citations silently did not resolve is worse than a build that waited a second longer.

One pass gets 180 seconds. LuaLaTeX is deliberately absent and stays absent: \directlua reaches Lua's socket APIs, and while the compile now runs in a sandboxed container as an unprivileged user, that answers where the document can write, not who it can talk to.

No deployment is promised a TeX toolchain. Rather than a button that fails, /scribe-api/meta reports what this deployment actually has — assistant, compile, paper search — and a missing capability is stated instead of hidden. That is the same honesty the Files Editor applies to its conversions.

Errors you can act on

A TeX log is long, and the useful part of it is the end. Scribe parses the two shapes that carry real information — ./main.tex:12: Undefined control sequence. and the classic ! line with its l.12 anchor — into up to fifty diagnostics with a file and a line number, and keeps the log's tail rather than its head. Filenames with spaces are handled, because sections/my intro.tex is a filename people actually have.

The build's pages are served individually, so a 40-page manuscript previews the page you are editing rather than re-downloading the whole PDF on every compile.

The limits are told to you before the save

A project holds up to 200 files of 400,000 characters each, and 8,000,000 bytes in total. That third number is the one that is easy to get wrong, and it is measured in bytes on purpose: the manuscript travels to the compile runner as one request, and 8,000,000 characters of CJK is 24 MB on the wire. A cap that disagreed with the one downstream would let a project be saved, listed and edited happily and then fail only in production.

All three limits are advertised by /meta, so the editor can warn you before a save that would be refused rather than after. Paths are bounded too — 200 characters, and 255 bytes per component, because a single component of CJK clears a character cap and then fails every compile on a real filesystem.

The store keeps text: .tex, .ltx, .bib, .sty, .cls, .bst, .txt, .md, .csv and .tikz. Binary figures are a later milestone, and the vocabulary says so rather than accepting an upload it cannot compile.

A paper points at the work behind it

The experiments behind a paper do not belong in the manuscript, and a manuscript that has quietly become a Jupyter notebook is a paper nobody can compile. One request seeds a Grafy Code project from the manuscript: the sources as a snapshot under paper/, a runner skeleton at experiments/run.py, and a CITATIONS.md listing every reference by its citation key.

The snapshot is a snapshot, not a live link — edits in the code project never reach Scribe. Results come back the way evidence should: as numbers and figures you cite on purpose. The citations manifest is the wiring that makes it worth doing, because an experiment can name the keys it is testing, so a claim in the paper traces to a run and the run traces back to its sources.

One project was never enough, though, because a paper is rarely about one artifact. A manuscript keeps a list of linked works — up to sixty of them, across Code, Reader, Film, Game, Music, Video Edit, Robot, Social, Watch, Trip and Operator — so the two code projects you can finally tell apart, the documents behind the literature review and the film the work produced all hang off the paper they belong to. You name each one yourself, because source and experiment are different things and only the author knows which is which.

The vocabulary is the app registry rather than a table of its own, so a new studio becomes linkable by existing rather than by a migration. And the return path is half the feature: opening a linked work carries the paper it came from, and the shared chrome draws one row back — otherwise every link out of a manuscript is a one-way door into an app that has no idea where you came from.

Three tabs, and what each one costs you

The usual stack is not bad software. Overleaf compiles, a reference manager stores, and a chatbot writes. What it lacks is any connection between them, and every gap is paid for by hand.

The reference manager does not know which papers this manuscript cites, so its library grows and your .bib drifts. The chatbot cannot see either file, so its citation keys are guesses and its LaTeX is written for a preamble it has not read. And the compile is in a fourth place, so a diagnostic arrives with no way to jump to the line.

Reading is the other half. A paper you are citing heavily is worth mapping rather than skimming, and the reading room is where a PDF goes before it becomes a \cite.

Frequently asked questions

Can the assistant invent a citation?

It is instructed to cite only with keys that exist in the project's bibliography, and the bibliography is right there in the project it is answering about. That is a strong prompt, not a proof — which is why every citation it proposes lands as text you insert deliberately rather than as an edit applied for you, and why keys are derived from paper records rather than written by a model.

What if my deployment has no TeX toolchain?

Everything except compilation still works: the editor, the files, the scholarly search, the bibliography merge and the code-project seed. /scribe-api/meta reports compile as unavailable and the button is off with a reason, rather than present and permanently failing. Installing Tectonic or pdflatex locally lights it up against the same build plans.

Will it reformat my references file?

No. The merge preserves the existing bytes exactly and appends new entries after them, so your comments, ordering and hand-written fields survive. That constraint is why a paper already in the file answers with its existing key instead of being rewritten into a canonical form.

Does it handle biblatex and biber?

Yes, and it changes which engine is chosen. Tectonic runs BibTeX but not biber, so a manuscript that needs biber will prefer a TeX Live engine when the deployment has one alongside biber — rather than producing a PDF whose citations quietly failed to resolve.

How big can a manuscript get?

200 files, 400,000 characters per file, and 8,000,000 bytes for the whole project. The total is the binding one for a long paper with many section files, and it is checked inside the same transaction that counts the files, so the answer you get is the answer the compile runner would give.

Can I put figures in it?

The store keeps text formats today — .tex, .bib, .sty, .cls, .bst, .txt, .md, .csv and .tikz — so a diagram drawn in TikZ compiles, and binary image files are a later milestone. Saying so is deliberate: an upload accepted and then silently dropped from the build is a worse answer than a limit.

Open Scribe and start a manuscript that compiles on its first build.

Read next

More from the Grafy blog.

← Back to all posts