vegman.dev
All posts

Five mistakes from my first year with language models

2026-08-06article

You've built your first assistant on top of a language model - a program that takes your request as text and writes the rest of the answer. It replies coherently, politely, and beside the point. Almost everyone's first thought is the same: the model is weak, I need a stronger one. I thought exactly that.

Over my first year of working closely with models I accumulated five mistakes. Each one cost me weeks, and none of them were about the model. Below are all five, with the mechanics: what exactly breaks, why it looks like a model problem, and what tells one apart from the other. By the end you'll be able to walk through your own project and check whether you're standing on one of these rakes right now.

flowchart TB
  A["Ответ неверный"] --> S1{"Что нашел поиск?"}
  S1 -->|"фрагменты не те"| F1["Чинить нарезку,<br/>разметку, поиск"]
  S1 -->|"фрагменты те"| S2{"Что ушло в модель?"}
  S2 -->|"фрагменты поданы плохо"| F2["Чинить промпт<br/>и формат подачи"]
  S2 -->|"подано верно"| F3["Только теперь - <br/>вопрос к модели"]
  A -.->|"соблазн: одна строчка<br/>в настройках"| F3
Порядок разбора: смена модели - последний шаг, а не первый

Mistake: believing quality comes down to the model

The search assistant was answering incorrectly - and the first thought was to swap the model. That's tempting, because it's one line in the settings. The real cause almost always turned out to be in how the data was prepared.

There were three typical causes:

  • Splitting documents without preserving context. A long text gets cut into pieces so they can be searched separately. If you cut blindly - by character count - the piece loses what it's even about: what's left is a paragraph of pronouns and numbers with no section and no subject attached to it.
  • No category labels. A fragment carries no marker for which section, product, or year it belongs to. Search can't narrow to the right area and pulls in word-similar material from the neighboring one.
  • Too narrow a search filter. The system cuts off the right material before the model ever sees it. The model didn't garble the answer - it simply wasn't shown the fragment it needed.

In all three cases, swapping the model changes nothing: a stronger model will answer just as carefully from the same broken data.

There's outside confirmation, too. An Nvidia study from August 2026: the same model on an interactive reasoning benchmark scores 30 percent without purpose-built scaffolding and 100 percent with it. Scaffolding is everything standing around the model: data preparation, retrieval, the order of steps, checks on the result. That's a spread from 30 to 100 within a single model. Keep that number in mind every time your hand reaches for the model setting.

Mistake: putting a model where a script would do

We had a regular check on a task queue. We hung it on a full agent - a program that decides for itself which steps to take and calls the model at every step. It sounded reasonable: let it be smart.

The entire job came down to two actions: read the data and drop it into a template. No decision was being made there at all.

We paid for that twice. First, every run requested the model's token ceiling from the provider instead of what it actually needed. A token is a small chunk of text, the unit both limits and money are counted in; reserving the maximum for a template fill is like booking a truck every time for a single box. Second, we took on a dependency on an external service where we didn't need one: when the balance ran low, a trivial check started failing every 30 minutes.

The rule is simple: if a step has no variability - if you can describe in advance what it does without the words "depending on the meaning" - it's a script. A script doesn't fail because of a balance, doesn't cost money per run, and doesn't make things up.

Mistake: trusting the prompt as a guarantee

A prompt is the instruction text you give the model: who it is, what it does, what it doesn't do. It feels like a rule written into the prompt is now being followed. In practice it's followed almost always, and all the difficulty hides in the word "almost."

The nastiest kind of violation isn't the one where the model produces obvious nonsense. The model confidently invented numbers of the same order as the real ones: it named an exit price of 8 dollars instead of 12. Not 800, not "around ten" - a plausible number that passes under your eye and raises no suspicion. Those are exactly the errors that reach the user.

You can only catch this with a mechanical cross-check against verified facts: numbers and names from the answer are matched against the source in code, not by a request in the prompt to be more careful. A prompt is a wish. A cross-check is a check.

Mistake: checking only the result, not the source

For a long time we checked only the answer text: looks plausible - good, doesn't - bad. Until we started looking at exactly which fragments search had found before the model began answering, debugging was guesswork.

Because behind a bad answer sit two completely different breakages:

  • Search didn't find the right fragment - what needs fixing is the data, the chunking, the labels, the filter.
  • Search found it and the model garbled it - what needs fixing is the prompt, the format the fragments are presented in, the cross-check.

From the answer text you can't tell them apart. From the list of retrieved fragments you can tell in seconds. Log what went into the model, not just what came out of it; it's the cheapest improvement of all five.

flowchart LR
  subgraph Q1["«Сколько осталось»"]
    A1["Баланс на счете"] --> A2["Видно, что счет вырос"] --> A3["11 дней до причины"]
  end
  subgraph Q2["«Куда ушло»"]
    B1["Вызовы: проект,<br/>модель, агент, день"] --> B2["Крупнейшая строка<br/>видна сразу"] --> B3["Минуты до причины"]
  end
  Q1 ==> Q2
Мониторинг отвечал на вопрос «сколько осталось» и не отвечал на вопрос «куда ушло» - разница измеряется в днях расследования

Mistake: having no answer to "where did the money go"

We had monitoring. It answered the question "how much is left" and did not answer the question "where did it go." The distance between those two questions is measured in days of investigation.

The bill grew. From the moment it became noticeable to the moment of "here's the cause," 11 days passed. That whole time, 45 percent of spending was going to the agent's housekeeping poll against an empty task queue: the system dutifully asked whether there was work, got "no" back, and paid for the question.

A total never shows you that. You need the breakdown: how much each scenario spent, each run, each step. Then "the bill grew" and "here's the cause" are one screen, not 11 days.

Put it to work

What to check in practice

The common denominator of all five mistakes is the same: the temptation to solve the problem at the model level, because that's one line in a config. The real fix almost always sits a level below - in the data, in the scaffolding, in the accounting. Hence the checks.

  • Take five real incorrect answers and for each one look at which fragments search found. Sort them into two piles: "didn't find it" and "found it but garbled it." Until you have the piles, you aren't debugging, you're guessing.
  • Open a dozen of the chunks your documents were cut into and read them as standalone texts. If a chunk doesn't make clear what it's about and what it belongs to, the problem is in the chunking, not the model.
  • Check whether fragments have category labels and whether the search filter is cutting off what you need. Turn the filter off on a trial run and compare.
  • Walk the list of places where the model is called and answer for each: is there any variability here? If a step comes down to reading data and filling a template - turn it into a script.
  • Look at what token limit is requested on each call. If it's the model's default ceiling - set it to the actual need.
  • Take ten answers containing numbers and names and check every one against the source by hand. If you find discrepancies, build an automatic cross-check.
  • Add a breakdown by scenario and step to your cost accounting. Separately, check what the regular background checks cost when there's no work.

How to tell it worked

There's one criterion, and it isn't about answer quality - it's about how fast you can diagnose.

Take a fresh incorrect answer and time yourself. If within a few minutes, without rerunning the model and without changing it, you can say which level the breakage is on - data, retrieval, prompt, scaffolding, or accounting - your system is observable, and from there it's ordinary engineering work. If instead you start cycling through models and rewriting the prompt at random, you're still fixing the top level, and a year will go into what a year went into for me.

The second sign, a simpler one: you can name last week's three most expensive line items from memory. Not the total - the line items.