Rami ToumiDownload CV
← Rami Toumi

Do-Doc

The agent decides. It doesn’t execute.

2026 · Two-person project · Python, Next.js, Azure

Visit live site

Do-Doc is a web editor that turns plain writing into typeset LaTeX. You write, an agent edits the project, and a PDF preview updates beside you. Two people can work on the same document and each sees where the other’s cursor is. My friend and I built it together over 2026 — no fixed roles: we argued each problem out, split the implementation, then walked each other through what we had written.

Most of the decisions worth writing down turned out to be the same decision asked four times: where does work happen, and who is allowed to start it? The answer we kept arriving at was that the agent should decide things and almost nothing else. Every operation that is slow, stateful, or expensive lives outside it.

Leaving LangChain

We started on LangChain, because that is what you start on. It got us moving and then it started charging rent. The structure we wanted was mostly about boundaries — this runs in another process, that is only ever triggered by a human, retrieval is a service we call rather than a step in a chain — and the framework had opinions about all of it. Every boundary we wanted to draw became an argument with an abstraction designed for a different shape of problem.

So we removed it and wrote the agent loop in plain Python, on no framework. It is less code than the LangChain version and we can read all of it. The cost is real: we maintain our own tool dispatch and our own retries, and we gave up the integrations we would otherwise have had for free. It was worth it, because the thing we needed to control was exactly the thing the framework was abstracting away.

Tools run somewhere else

The agent’s tools — the operations that create, edit and delete files in a LaTeX project — do not run inside the agent process. They live in a separate service, and the agent calls an endpoint.

This was not a scaling decision, it was a correctness one. Two people can be editing the same document while the agent is working on it. If the agent mutated files in its own process, there would be two writers with two different ideas of the current state. Putting the tools behind a service meant there is one place where writes happen, and collaboration and the agent go through the same door.

Retrieval is a service, not a step

Users upload sources — reference PDFs, existing chapters, whole projects. Past a certain size, roughly a megabyte, putting that in the model’s context is not an option, so the content has to be indexed and retrieved instead. The size of the upload decides which path it takes.

We built retrieval as its own external service rather than a stage in the agent’s pipeline, because we expected it to be the part we would change most: chunking, embedding choice, how much gets pulled back, how it is ranked. Keeping it behind a network call meant we could rework the inside of it without touching the agent, and the agent’s view of retrieval stayed the same the whole time — ask a question, get passages.

Compilation belongs to the user

LaTeX has to be compiled to become a PDF, and compilation is slow. Files change constantly: the agent edits, two humans type, everything is stored on Azure. The question was whether the agent should be able to compile as part of its work, or whether compilation is something only a person starts.

We gave it to the user. The agent never compiles. An agent that can trigger an expensive job decides on its own how often that job runs, and on a document being edited by three parties at once that is a number nobody controls. Moving the trigger to a button means the cost is bounded by intent: a compile happens when someone wants to look at the PDF.

What collaboration forced

The shared-cursor feature is the constraint underneath most of the above. It is the reason there is one writer service instead of writes scattered across processes, and it shaped how files are stored, since the storage layer has to serve a live document and a background agent reading the same project without either seeing a half-written state. Real-time collaboration is usually described as a frontend feature. Almost all of its cost was in the back.

Open questions

The size threshold that routes an upload to retrieval is a fixed number, and a fixed number is a guess. A better version would decide from the token count and from whatever else is competing for the context on that turn.

The compile rule is deliberately blunt. There is a softer version where the agent may request a compile and the user approves it, which would help when the agent has made a change it cannot verify without seeing the output. We chose the blunt rule first, on the grounds that it is easier to loosen a restriction later than to claw one back.

← Back