newsmode
search
Меню
arrow_back Назад

Behind the Scenes: Building AI-Generated Opportunity Solution Trees

auto_awesomeКраткое саммари

Тереза Торрес рассказывает, как она — продакт-менеджер без инженерного бэкграунда — с помощью AI создала сервисы для генерации деревьев возможностей и решений (opportunity solution trees) для платформы Vistaly. После запуска альфа-версии (более 100 заявок, 8 дизайн-партнёров) команды захотели загружать больше интервью, и обновление готового дерева оказалось куда сложнее генерации с нуля. Главная проблема: древовидные диффы неоднозначны — операции вроде split и merge становятся невидимыми в обычном сравнении, поэтому пришлось заставить модель «показывать свою работу» в виде наборов изменений (change sets). Ошибки достигали 50% запусков, пока Тереза не пришла к ключевому инсайту: дать LLM самому исправлять свои ошибки через агентский цикл с валидацией-инструментом, который повторяет попытки до прохождения проверки. Решение заработало (исправление за 1–2 хода), и обновлённый сервис был отгружен Vistaly в понедельник в полдень. В выводах она признаёт, что после Opus 4.6 дала Claude слишком много свободы, но теперь снова держит его «на коротком поводке», и подчёркивает, что за 15 месяцев освоила новую профессию AI-инженера.

I just finished an all-out engineering sprint. That sounds weird to me.

Я только что завершила полноценный инженерный спринт. Мне самой это кажется странным.

I've been writing code on and off for several years. But I wouldn't call myself an engineer. I'm a product manager. And I used to be a designer. It's been a long time since I've written code for a living.

Я пишу код время от времени уже несколько лет. Но я бы не назвала себя инженером. Я продакт-менеджер. А раньше была дизайнером. Прошло много времени с тех пор, как я писала код профессионально.

But AI is changing what's just now possible. Not just for the products we build. But also for us. We can do more than we might have ever imagined. That's certainly been my experience.

Но AI меняет то, что стало возможным прямо сейчас. И не только для продуктов, которые мы создаём. Но и для нас самих. Мы можем сделать больше, чем когда-либо могли себе представить. Именно таким и был мой опыт.

Today, I want to share an engineering story with you. There will be some technical bits. Some of it might feel foreign to you. But I want you to know I had no idea how to do any of this a year ago. I learned it with the help of AI. My goal in sharing my engineering stories is to show you what's just now possible.

Сегодня я хочу поделиться с вами инженерной историей. Будут технические моменты. Что-то может показаться вам непонятным. Но я хочу, чтобы вы знали: год назад я понятия не имела, как всё это делается. Я научилась этому с помощью AI. Делясь своими инженерными историями, я хочу показать вам, что стало возможным прямо сейчас.

Creating AI-Generated Opportunity Solution Trees

Создание деревьев возможностей и решений, сгенерированных с помощью AI

In mid-February, I announced that I was building two services for Vistaly—AI-generated interview snapshots and AI-generated opportunity solution trees. We both made a call for alpha partners. We got over 100 applicants before we closed the round. We selected eight to be our initial design partners.

В середине февраля я объявила, что создаю два сервиса для Vistaly — сгенерированные с помощью AI снимки интервью и сгенерированные с помощью AI деревья возможностей и решений. Мы оба объявили набор альфа-партнёров. До закрытия раунда мы получили более 100 заявок. Мы выбрали восемь команд в качестве наших первых дизайн-партнёров.

Each team uploaded three customer interviews. We identified key moments and opportunities from each, then generated an opportunity solution tree from those three snapshots. I'm providing the AI services. Vistaly is building the snazzy UI and workflows around it.

Каждая команда загрузила по три интервью с клиентами. Мы выделили из каждого ключевые моменты и возможности, а затем сгенерировали дерево возможностей и решений на основе этих трёх снимков. AI-сервисы предоставляю я. Vistaly строит вокруг них эффектный UI и рабочие процессы.

Our initial feedback was strong. Teams started to ask us to let them upload more interviews. But we both had work to do to support this.

Наша первая обратная связь была сильной. Команды начали просить нас разрешить загружать больше интервью. Но нам обоим предстояло проделать работу, чтобы это поддержать.

Integrating New Interviews into an AI-Generated Opportunity Solution Tree

Интеграция новых интервью в сгенерированное с помощью AI дерево возможностей и решений

Updating an opportunity solution tree with new interview content is a much harder challenge than generating a new opportunity solution tree from scratch. I initially underestimated the complexity.

Обновление дерева возможностей и решений новым содержанием интервью — задача гораздо более сложная, чем генерация нового дерева с нуля. Поначалу я недооценила эту сложность.

Vistaly and I both have a shared goal. We don't want to produce AI-generated opportunity solution trees and have you take them as fact. We want you to engage with them, correct them, collaborate with the AI. We want to scaffold cross-interview synthesis, not do it for you.

У Vistaly и у меня есть общая цель. Мы не хотим выдавать сгенерированные с помощью AI деревья возможностей и решений и чтобы вы принимали их как факт. Мы хотим, чтобы вы взаимодействовали с ними, исправляли их, сотрудничали с AI. Мы хотим выстроить опору для кросс-интервьюного синтеза, а не делать его за вас.

To support this, we needed a way to communicate how your tree would change after you uploaded new interviews. We took inspiration from git diff and decided we needed the equivalent for opportunity solution trees. If you aren't familiar with git diff, it's how git—the most common version control software for coding—shows you what has changed in a file. It walks you through each change one step at a time.

Чтобы это поддержать, нам нужен был способ показать, как изменится ваше дерево после загрузки новых интервью. Мы вдохновились git diff и решили, что нам нужен его эквивалент для деревьев возможностей и решений. Если вы не знакомы с git diff: это то, как git — самая распространённая система контроля версий для кода — показывает вам, что изменилось в файле. Он проводит вас через каждое изменение шаг за шагом.

While this was absolutely the right decision to make, I had no idea how much work it would create for me. It wasn't enough for my service to generate an updated opportunity solution tree with the content from the new interviews. It also had to provide a step-by-step walkthrough of what changed.

Хотя это было абсолютно верное решение, я и понятия не имела, сколько работы оно мне создаст. Моему сервису было недостаточно просто сгенерировать обновлённое дерево возможностей и решений с содержанием новых интервью. Он также должен был предоставить пошаговый разбор того, что изменилось.

The Prototype Only Represents 20% of the Work

Прототип — это всего 20% работы

One of the themes that I hear often on my podcast Just Now Possible is that it's fairly easy to get to an impressive prototype with AI, but that it's much harder to get to a real production-quality product. This was exactly my experience.

Одна из тем, которую я часто слышу в своём подкасте Just Now Possible, заключается в том, что с AI довольно легко добраться до впечатляющего прототипа, но гораздо труднее дойти до настоящего продукта продакшен-качества. Именно таким и был мой опыт.

My opportunity solution tree service is comprised of two sub-services: 1) generate a new tree from scratch and 2) update an existing tree based on new interviews.

Мой сервис деревьев возможностей и решений состоит из двух подсервисов: 1) сгенерировать новое дерево с нуля и 2) обновить существующее дерево на основе новых интервью.

The first service was part of our initial alpha and was working well. The second service had to be built from scratch before any of our design partners could add a fourth interview.

Первый сервис был частью нашей первоначальной альфы и работал хорошо. Второй сервис нужно было построить с нуля, прежде чем кто-либо из наших дизайн-партнёров смог бы добавить четвёртое интервью.

On the surface, these don't seem that different, but they are. When you are updating a tree, you want to preserve as much of the existing structure as you can—unless the new content really warrants a structural change. You have to account for compound operations like merges, splits, and deletes. And you need to make sure you don't lose any data. On the data front, each node in the tree has both source opportunities—interview opportunities that provide supporting evidence for the tree opportunity—and children—tree sub-opportunities.

На первый взгляд они не кажутся такими уж разными, но это не так. Когда вы обновляете дерево, вы хотите сохранить как можно больше существующей структуры — если только новое содержание действительно не оправдывает структурное изменение. Приходится учитывать составные операции, такие как слияния, разделения и удаления. И нужно убедиться, что вы не потеряете никаких данных. Что касается данных, у каждого узла дерева есть как исходные возможности — возможности из интервью, которые служат подтверждающим доказательством для возможности в дереве, — так и дочерние элементы, то есть подвозможности дерева.

But like with many AI products, I was pleasantly surprised when I got something working reasonably well in just a few days. I shipped it and our design partners started playing with it.

Но, как и со многими AI-продуктами, я была приятно удивлена, когда всего за несколько дней у меня заработало нечто вполне сносное. Я выкатила это, и наши дизайн-партнёры начали с этим играть.

One design partner hit our beta limits quickly and asked if they could convert to a paid subscription so that they could get full access. We were thrilled. They showed a willingness to pay, converted, and immediately started uploading more interviews.

Один дизайн-партнёр быстро упёрся в наши лимиты беты и спросил, можно ли перейти на платную подписку, чтобы получить полный доступ. Мы были в восторге. Они проявили готовность платить, перешли на платный тариф и тут же начали загружать больше интервью.

However, once they hit the 14th, 15th, and 16th uploads, my initial service design started to show some cracks. We started to see some odd behavior.

Однако, когда они дошли до 14-й, 15-й и 16-й загрузки, в моём первоначальном дизайне сервиса начали проявляться трещины. Мы стали замечать странное поведение.

The Vistaly team noticed that the change sets—the step-by-step instructions emitted by my service—weren't always generating the final tree emitted by my service. Remember, we wanted to be able to walk the customer step-by-step through what changed. So my service returned both the step-by-step instructions (the change sets) and the final tree.

Команда Vistaly заметила, что наборы изменений (change sets) — пошаговые инструкции, выдаваемые моим сервисом, — не всегда генерировали итоговое дерево, которое выдавал мой сервис. Напомню, мы хотели иметь возможность провести клиента шаг за шагом через то, что изменилось. Поэтому мой сервис возвращал как пошаговые инструкции (наборы изменений), так и итоговое дерево.

The intent was that Vistaly could start with the current version of the team's tree, apply the change set in the order I provided, and end up with the updated tree that my service emitted. But sometimes the tree that the change set generated was different from the tree that the service outputted.

Замысел был в том, что Vistaly могла бы взять текущую версию дерева команды, применить набор изменений в том порядке, который я предоставила, и получить обновлённое дерево, которое выдал мой сервис. Но иногда дерево, которое генерировал набор изменений, отличалось от дерева, которое выдавал сервис.

Vistaly flagged this as a bug on the day I was flying to New Orleans for Jazz Fest. Looking back, I'm glad I had no idea what was waiting for me when I got home. I had about 80% of the work still left to do to make updating an opportunity solution tree a truly rock-solid product. Thankfully, I got to enjoy Jazz Fest first.

Vistaly пометила это как баг в тот день, когда я летела в Новый Орлеан на Jazz Fest. Оглядываясь назад, я рада, что понятия не имела, что меня ждёт по возвращении домой. Мне оставалось проделать ещё около 80% работы, чтобы сделать обновление дерева возможностей и решений по-настоящему надёжным продуктом. К счастью, сначала я успела насладиться Jazz Fest.

Uncovering the Full Scope of the Problem

Раскрытие полного масштаба проблемы

When I got home, I started to diagnose the problem and I was quickly overwhelmed. My service was a pipeline that combined LLM steps with deterministic code.

Когда я вернулась домой, я начала диагностировать проблему и быстро почувствовала себя погребённой под ней. Мой сервис представлял собой конвейер, сочетавший шаги с LLM и детерминированный код.

At a high level, here's how it worked: I had deconstructed updating an opportunity into a series of steps that could be done by an LLM. After those steps completed, deterministic code compared the output tree to the input tree and generated the change sets.

На высоком уровне он работал так: я разбила обновление возможности на серию шагов, которые могла выполнить LLM. После завершения этих шагов детерминированный код сравнивал выходное дерево с входным и генерировал наборы изменений.

As I started to dig into the bugs, I realized this was not a good approach. Tree diffs, unlike linear document diffs, are ambiguous.

Когда я начала копаться в багах, я поняла, что это не лучший подход. Диффы деревьев, в отличие от диффов линейных документов, неоднозначны.

If I'm editing a document and I add a sentence, the diff is simple. It shows that a sentence was added. If I delete a paragraph and rewrite it, the diff shows the removal of the old paragraph and the addition of a new paragraph.

Если я редактирую документ и добавляю предложение, дифф прост. Он показывает, что добавлено предложение. Если я удаляю абзац и переписываю его, дифф показывает удаление старого абзаца и добавление нового.

But if I split an opportunity A into two opportunities, A and B, and then later I merge B with C, the first move is hidden from the diff.

Но если я разделяю возможность A на две возможности, A и B, а затем позже сливаю B с C, то первое действие оказывается скрытым от диффа.

Let me walk you through why.

Позвольте объяснить, почему.

If the model decides to split an opportunity into two different opportunities, it has to figure out how to divide the original opportunity's source opportunities (the supporting evidence) and its children (the sub-opportunities) across the two resulting opportunities.

Если модель решает разделить возможность на две разные возможности, ей нужно сообразить, как распределить исходные возможности оригинала (подтверждающие доказательства) и его дочерние элементы (подвозможности) между двумя получившимися возможностями.

For example, if we have opportunity A and we want to split it into A and B, we have to figure out which source opportunities and children belong on A and which belong on B.

Например, если у нас есть возможность A и мы хотим разделить её на A и B, нам нужно понять, какие исходные возможности и дочерние элементы относятся к A, а какие — к B.

Suppose A has source opportunities 1, 2, 3 and children x, y, z. After the split, we might end up with A having source opportunities 1 and 2 and child x and B having source opportunity 3 and children y and z. When the model decides to split an opportunity it has to distribute the source opportunities and children appropriately.

Предположим, у A есть исходные возможности 1, 2, 3 и дочерние элементы x, y, z. После разделения мы можем получить A с исходными возможностями 1 и 2 и дочерним элементом x, и B с исходной возможностью 3 и дочерними элементами y и z. Когда модель решает разделить возможность, она должна соответствующим образом распределить исходные возможности и дочерние элементы.

Now suppose the model decides to merge B with C. When two opportunities are merged, we have to combine all of the source opportunities and children. Remember, B has source opportunity 3 and children y and z. If C has source opportunities 4 and 5 and children t, u, and v, then after the merge C will have source opportunities 3, 4, and 5 and children t, u, v, y, and z.

Теперь предположим, что модель решает слить B с C. Когда две возможности сливаются, нам нужно объединить все исходные возможности и дочерние элементы. Напомню, у B есть исходная возможность 3 и дочерние элементы y и z. Если у C есть исходные возможности 4 и 5 и дочерние элементы t, u и v, то после слияния у C будут исходные возможности 3, 4 и 5 и дочерние элементы t, u, v, y и z.

At the end, when we do a diff between our input tree and our output tree, we can't see both the split and the merge. Our input tree had A with source opportunities 1, 2, 3 and children x, y, and z and C with opportunities 4 and 5 with children t, u, and v.

В конце, когда мы делаем дифф между нашим входным деревом и нашим выходным деревом, мы не можем увидеть и разделение, и слияние одновременно. В нашем входном дереве было A с исходными возможностями 1, 2, 3 и дочерними элементами x, y и z, и C с возможностями 4 и 5 и дочерними элементами t, u и v.

Our output tree has A with source opportunities 1 and 2 and child x and C with source opportunities 3, 4, and 5 and children t, u, v, y, and z. It looks to our diff like some of A's sources and children moved from A to C. But we don't know why. The split and the merge conceptually explain what happened, but those moves are invisible to the diff.

В нашем выходном дереве есть A с исходными возможностями 1 и 2 и дочерним элементом x, и C с исходными возможностями 3, 4 и 5 и дочерними элементами t, u, v, y и z. Для нашего диффа это выглядит так, будто часть источников и дочерних элементов A переместилась из A в C. Но мы не знаем почему. Разделение и слияние концептуально объясняют, что произошло, но эти перемещения невидимы для диффа.

This is when I realized tree diffs were much harder than linear text diffs. We weren't just trying to show the user what changed. We wanted to show the user why it changed so they could weigh in. I needed a way to reconstruct each move—step by step.

Именно тогда я осознала, что диффы деревьев гораздо сложнее линейных текстовых диффов. Мы пытались показать пользователю не просто что изменилось. Мы хотели показать пользователю, почему это изменилось, чтобы он мог высказать своё мнение. Мне нужен был способ реконструировать каждое перемещение — шаг за шагом.

That meant I had to get the model to show its work. And that opened a whole new can of worms.

Это значило, что мне нужно было заставить модель показывать свою работу. И это открыло целый новый ящик Пандоры.

Asking the Model to Show Its Work

Просим модель показать свою работу

I decided to refactor all of my prompts to have the model generate both the final output and the step-by-step moves it took to get there.

Я решила переработать все свои промпты так, чтобы модель генерировала и итоговый результат, и пошаговые перемещения, которые она предприняла, чтобы к нему прийти.

My prompts already told the model what actions were possible: add, delete, reframe, merge, split, etc. But these were just guidelines. The model could do whatever it wanted to produce the final tree.

Мои промпты уже сообщали модели, какие действия возможны: добавить, удалить, переформулировать, слить, разделить и т. д. Но это были лишь рекомендации. Модель могла делать всё, что угодно, чтобы получить итоговое дерево.

But now, I needed it to show its work. And I needed it to show its work in the language of our change sets—specific moves that we predefined that make sense to our end user. In other words, split A into A and B and then merge B into C has much more semantic meaning than simply saying move some of A's sources and children to C arbitrarily.

Но теперь мне нужно было, чтобы она показывала свою работу. И мне нужно было, чтобы она показывала её на языке наших наборов изменений — конкретных перемещений, которые мы заранее определили и которые имеют смысл для нашего конечного пользователя. Другими словами, «разделить A на A и B, а затем слить B в C» несёт гораздо больше семантического смысла, чем просто «переместить часть источников и дочерних элементов A в C произвольным образом».

For each LLM step, the model had to output its recommendation and the change set it used to generate the output. This worked fairly well. But, of course, the model made mistakes.

На каждом шаге LLM модель должна была выдавать свою рекомендацию и набор изменений, который она использовала для получения результата. Это работало довольно неплохо. Но, конечно, модель допускала ошибки.

After a lot of testing and error analysis, I uncovered two categories of errors:

После множества тестов и анализа ошибок я выявила две категории ошибок:

  • The model tried to make a move that was not valid.
  • The model had a discrepancy between the change set and the recommendation. (In other words, the change set didn't generate the recommendation.)
  • Модель пыталась сделать перемещение, которое было недопустимым.У модели было расхождение между набором изменений и рекомендацией. (Другими словами, набор изменений не генерировал рекомендацию.)

    Category 1 was interesting. It felt like I was designing a game and the model was a very creative player. It kept finding ways to break my game. For each of these errors, I had to decide if this move should be allowed and if so, how the model should represent that move using our actions. If the move shouldn't be allowed, I had to let the model know.

    Категория 1 была интересной. Это было похоже на то, будто я проектирую игру, а модель — очень изобретательный игрок. Она постоянно находила способы сломать мою игру. Для каждой из этих ошибок мне приходилось решать, следует ли разрешать такое перемещение, и если да, то как модель должна представить это перемещение с помощью наших действий. Если перемещение не следовало разрешать, мне нужно было сообщить об этом модели.

    For example, the model tried to merge a parent with a child. This was a problem. Suppose opportunity A has children B, C, and D. If the model tries to merge A with B, what should happen?

    Например, модель пыталась слить родителя с дочерним элементом. Это была проблема. Предположим, у возможности A есть дочерние элементы B, C и D. Если модель пытается слить A с B, что должно произойти?

    A merge is directional. The model has to define which of the two to keep and which to delete.

    Слияние направленное. Модель должна определить, какую из двух возможностей сохранить, а какую удалить.

    If it defines the merge as keep A and delete B, the merge is allowed.

    Если она определяет слияние как «сохранить A и удалить B», то слияние допустимо.

    But if it defines the merge as keep B and delete A, what happens to B, C, and D? They no longer have a parent and are now orphaned.

    Но если она определяет слияние как «сохранить B и удалить A», то что произойдёт с B, C и D? У них больше нет родителя, и теперь они осиротели.

    These were fun errors to resolve. It was like solving little puzzles.

    Эти ошибки было приятно разрешать. Это было похоже на решение маленьких головоломок.

    Category 2 was much harder. I had to go through several iterations of prompt design to nudge the model toward better output.

    Категория 2 была гораздо сложнее. Мне пришлось пройти через несколько итераций дизайна промптов, чтобы подтолкнуть модель к лучшему результату.

    I was able to reduce these errors to about 1 in 40 instances. But each service run has 10–20 LLM calls, so that meant I was hitting this error in about 50% of service runs. Clearly, not good enough for a production product.

    Мне удалось снизить эти ошибки примерно до 1 случая из 40. Но в каждом запуске сервиса 10–20 вызовов LLM, поэтому это означало, что я сталкивалась с этой ошибкой примерно в 50% запусков сервиса. Очевидно, недостаточно хорошо для продакшен-продукта.

    At some point, I hit a wall and couldn't reduce this error rate any further. I felt stuck. And worried. We had a paying customer waiting for my fix. And several more design partners eager to try it out.

    В какой-то момент я упёрлась в стену и не могла снизить частоту этой ошибки дальше. Я почувствовала, что застряла. И встревожилась. У нас был платящий клиент, ожидавший моего исправления. И ещё несколько дизайн-партнёров, которым не терпелось всё попробовать.

    Trying to Correct the Model's Mistakes in Code

    Попытка исправить ошибки модели в коде

    I initially tried to correct the model's mistakes with deterministic code. I had committed to Vistaly that my change sets would generate the output tree, so I needed code that verified this.

    Сначала я попыталась исправлять ошибки модели детерминированным кодом. Я обязалась перед Vistaly, что мои наборы изменений будут генерировать выходное дерево, поэтому мне нужен был код, который бы это проверял.

    After the model generated a change set, I ran a number of quality checks on it. Were there any conflicts in the change set? A conflict might be the model deleted a tree node in one step and then later tried to use it in a merge in a subsequent step. Was there any potential data loss—were all of the source opportunities and children accounted for? Did the model orphan any tree nodes? And so on.

    После того как модель генерировала набор изменений, я прогоняла по нему ряд проверок качества. Были ли в наборе изменений какие-либо конфликты? Конфликтом могло быть, например, то, что модель удалила узел дерева на одном шаге, а затем на последующем шаге пыталась использовать его в слиянии. Была ли какая-либо потенциальная потеря данных — учтены ли все исходные возможности и дочерние элементы? Не осиротила ли модель какие-либо узлы дерева? И так далее.

    I thought, if I can detect these problems, maybe I can correct them. But to do that, I had to guess at what the model intended.

    Я думала: если я могу обнаружить эти проблемы, может быть, я смогу их исправить. Но чтобы это сделать, мне приходилось гадать, что именно имела в виду модель.

    If the model had the following sequence in its change set—delete A, then merge A with B—what did it mean? Should I delete A or should I merge its source opportunities and children with B?

    Если у модели в наборе изменений была такая последовательность — «удалить A, затем слить A с B» — что это значило? Должна ли я удалить A или слить его исходные возможности и дочерние элементы с B?

    There were dozens of cases like this with no easy answers.

    Таких случаев были десятки, и простых ответов не было.

    At this point, I was 11 days into working on this full time from the moment I woke up to the moment I went to sleep (including weekends). I don't say that as a humble brag. I hate hustle culture and this is not how I designed my life. I share this because I want you to understand the context. I was stuck and I was exhausted. And then I had an insight.

    К этому моменту я уже 11 дней работала над этим полный рабочий день — с момента пробуждения до момента отхода ко сну (включая выходные). Я говорю это не как скромное хвастовство. Я ненавижу культуру хастла, и я не так выстраивала свою жизнь. Я делюсь этим, потому что хочу, чтобы вы понимали контекст. Я застряла и была измотана. А потом меня осенило.

    Let the Model Correct Its Own Mistakes

    Пусть модель сама исправляет свои ошибки

    I was walking my dog with my husband. He is also an engineer and he had been hearing about my ups and downs over the past several days. As I explained my stubborn failure mode to him, I had an insight. I realized I could have the LLM repair its own mistakes.

    Я гуляла с собакой вместе с мужем. Он тоже инженер и все эти несколько дней слышал о моих взлётах и падениях. Когда я объясняла ему свой упрямый сценарий сбоя, меня осенило. Я поняла, что могу заставить LLM саму исправлять свои ошибки.

    My data contract with Vistaly requires that my change set generates the output tree. This was the core bug. This wasn't happening 100% of the time. Over the previous week, I had written a lot of code to validate change sets.

    Мой контракт данных с Vistaly требует, чтобы мой набор изменений генерировал выходное дерево. Это и был ключевой баг. Это происходило не в 100% случаев. За предыдущую неделю я написала много кода для валидации наборов изменений.

    I now knew exactly when a change set generated the wrong tree. But no matter how much prompt engineering I did, I couldn't reduce the error rate.

    Теперь я точно знала, когда набор изменений генерировал неправильное дерево. Но сколько бы prompt engineering я ни делала, я не могла снизить частоту ошибок.

    My core insight was that I could use this same validation code as a tool for the agent. The agent could generate the change set, call the tool, and then get the results. If the change set was invalid, the agent would get instructions on what needed to be fixed. The agent would continue to work until the validation passed.

    Мой ключевой инсайт состоял в том, что я могла использовать этот же код валидации как инструмент для агента. Агент мог генерировать набор изменений, вызывать инструмент, а затем получать результаты. Если набор изменений был недопустимым, агент получал инструкции о том, что нужно исправить. Агент продолжал работать, пока валидация не проходила.

    This was a simple idea. But could I get it to work? And more importantly, could I get it to work without blowing through a ton of compute?

    Это была простая идея. Но смогу ли я заставить её работать? И, что важнее, смогу ли я заставить её работать, не спалив кучу вычислительных ресурсов?

    I started by prototyping a solution. I wrote a simple Node.js script that made one LLM call, got the result, ran it through my verifier, and then sent the feedback to the LLM for corrections.

    Я начала с прототипирования решения. Я написала простой Node.js-скрипт, который делал один вызов LLM, получал результат, прогонял его через мой верификатор, а затем отправлял обратную связь LLM для исправлений.

    Initially, it didn't work at all. The model just kept introducing more mistakes. The loop never converged to a valid solution. It just racked up compute.

    Поначалу это вообще не работало. Модель просто продолжала вносить всё новые ошибки. Цикл так и не сходился к допустимому решению. Он лишь наматывал вычислительные ресурсы.

    But I kept trying. I experimented with how to communicate the errors to the LLM. I experimented with how much context to give it about the larger problem. And eventually, I got it to work. The model was fixing its own mistakes and returning a valid change set after one or two repairs.

    Но я продолжала пробовать. Я экспериментировала с тем, как сообщать LLM об ошибках. Я экспериментировала с тем, сколько контекста о более крупной проблеме ей давать. И в конце концов я заставила это работать. Модель исправляла свои собственные ошибки и возвращала допустимый набор изменений после одной-двух правок.

    I was thrilled. But I still had to turn it into something I could run in production. And that's when I remembered: I had already built an agent loop utility to support my AI interviewer.

    Я была в восторге. Но мне ещё предстояло превратить это во что-то, что я могла бы запускать в продакшене. И тут я вспомнила: я ведь уже построила утилиту агентского цикла для поддержки моего AI-интервьюера.

    Here's what my final production implementation looks like. I have a pretty standard agent loop. It starts with a model call. The model can output a tool request. The loop runs the tool and sends the tool result back to the agent. It iterates until the tool (my validation code) returns a success signal or until we hit the max turns that I set in my code.

    Вот как выглядит моя финальная продакшен-реализация. У меня довольно стандартный агентский цикл. Он начинается с вызова модели. Модель может выдать запрос на вызов инструмента. Цикл запускает инструмент и отправляет результат инструмента обратно агенту. Он итерирует, пока инструмент (мой код валидации) не вернёт сигнал успеха или пока мы не достигнем максимального числа ходов, заданного мной в коде.

    The initial model call is the tree-building task. The tool call runs my validation code. If the change set passes validation, the tool emits a success signal and the loop exits. If the change set fails validation, the tool returns simple instructions for the model to fix the errors. The model attempts to fix the errors and calls the validation tool again. My insight led to a feasible solution.

    Первоначальный вызов модели — это задача построения дерева. Вызов инструмента запускает мой код валидации. Если набор изменений проходит валидацию, инструмент выдаёт сигнал успеха, и цикл завершается. Если набор изменений не проходит валидацию, инструмент возвращает простые инструкции для модели по исправлению ошибок. Модель пытается исправить ошибки и снова вызывает инструмент валидации. Мой инсайт привёл к рабочему решению.

    Errors are still theoretically possible. The repair loop can fail and hit max turns. But I haven't seen it in all of my testing. The agent typically fixes the bad change set in one to two turns.

    Ошибки всё ещё теоретически возможны. Цикл исправления может дать сбой и упереться в максимум ходов. Но за всё моё тестирование я этого не видела. Агент обычно исправляет плохой набор изменений за один-два хода.

    I had that insight last Thursday. I worked through the weekend to prototype it, test it, and validate that it could work. I then built a production-quality version and integrated it into my pipeline. I shipped my completely revamped service to Vistaly on Monday at noon. They are working on integrating it and it will be available to our design partners shortly.

    Этот инсайт меня осенил в прошлый четверг. Я работала все выходные, чтобы прототипировать его, протестировать и убедиться, что он может работать. Затем я построила версию продакшен-качества и интегрировала её в свой конвейер. Я отгрузила полностью переработанный сервис в Vistaly в понедельник в полдень. Они работают над его интеграцией, и вскоре он станет доступен нашим дизайн-партнёрам.

    I was relieved. And ready for a day off.

    Я почувствовала облегчение. И была готова к выходному дню.

    Some Reflections on the Whole Process

    Несколько размышлений обо всём процессе

    The last two weeks were intense. But I learned a lot and I mostly enjoyed the process. I want to share some key reflections with you.

    Последние две недели были напряжёнными. Но я многому научилась и в основном получала удовольствие от процесса. Я хочу поделиться с вами несколькими ключевыми размышлениями.

  • I have a lot of limiting beliefs around being an engineer. I shed a lot of those beliefs over the last two weeks. To make this work well and to make it work reliably, I had to solve some legitimately hard problems. That feels good.
  • This work was genuinely fun. I loved the puzzle part of designing an action set and then seeing how the model pushed that design beyond its intended limits. Models are incredibly creative and it was fun to see what it would do with a fixed toolset.
  • I learned a lot about when I can and can't trust Claude to write code for me. Since Opus 4.6 came out, I gave Claude a much longer leash. After the past two weeks, Claude is back on a short leash. I found a lot of gaps in my implementation in areas where I simply trusted that Claude got it right, when in fact it didn't. I know the foundation labs are claiming that the models write most of their code, but I'm learning if you don't have the right infrastructure in place, this can be disastrous. I'll be investing much more in my planning, testing, and code-review tools over the next couple of weeks. And, of course, I'll share here as I do.
  • If this work was spread out over two months, it would have been thoroughly enjoyable. I'm learning I really enjoy being an AI engineer. I was ready for a new chapter and I am even more confident this is it. It's fun to combine my past work—opportunity solution trees—with my new interests—AI engineering.
  • У меня множество ограничивающих убеждений насчёт того, чтобы быть инженером. За последние две недели я избавилась от многих из них. Чтобы заставить это работать хорошо и надёжно, мне пришлось решить несколько действительно сложных проблем. Это приятное чувство.Эта работа была по-настоящему увлекательной. Мне нравилась головоломная часть — проектировать набор действий, а затем смотреть, как модель раздвигает этот дизайн за пределы его задуманных границ. Модели невероятно изобретательны, и было забавно наблюдать, что она будет делать с фиксированным набором инструментов.Я многое узнала о том, когда я могу, а когда не могу доверять Claude написание кода за меня. С выходом Opus 4.6 я дала Claude гораздо более длинный поводок. После прошедших двух недель Claude снова на коротком поводке. Я нашла множество пробелов в своей реализации в тех местах, где я просто доверилась, что Claude всё сделал правильно, тогда как на самом деле это было не так. Я знаю, что ведущие лаборатории утверждают, что модели пишут большую часть их кода, но я понимаю, что если у вас нет правильной инфраструктуры, это может обернуться катастрофой. В ближайшие пару недель я буду гораздо больше вкладываться в свои инструменты планирования, тестирования и код-ревью. И, конечно, я буду делиться этим здесь по ходу дела.Если бы эта работа была растянута на два месяца, она была бы по-настоящему приятной. Я понимаю, что мне действительно нравится быть AI-инженером. Я была готова к новой главе, и теперь я ещё больше уверена, что это она. Здорово объединять мою прошлую работу — деревья возможностей и решений — с моими новыми интересами — AI-инженерией.

    I'm really excited to share the work Vistaly and I are doing together with more people. We'll be adding more design partners soon. You'll be the first to know when we do. In the meantime, get on the waiting list.

    Я очень рада поделиться работой, которую мы с Vistaly делаем вместе, с большим числом людей. Скоро мы будем добавлять новых дизайн-партнёров. Вы узнаете об этом первыми. А пока записывайтесь в лист ожидания.

    And finally, it's still surprising for me to look back at the last 15 months and see how much I've learned and progressed in a whole new field. I have fully embraced what is just now possible. I hope by sharing my story, I inspire you to take the first small step toward what's just now possible for you.

    И наконец, мне до сих пор удивительно оглядываться на последние 15 месяцев и видеть, как многому я научилась и как далеко продвинулась в совершенно новой области. Я полностью приняла то, что стало возможным прямо сейчас. Надеюсь, что, делясь своей историей, я вдохновлю вас сделать первый маленький шаг к тому, что стало возможным прямо сейчас для вас.

    Audio Version

    Аудиоверсия

    The audio version is only available for paid subscribers.

    Аудиоверсия доступна только платным подписчикам.