diff --git a/.gitignore b/.gitignore index efd0068e..19341689 100644 --- a/.gitignore +++ b/.gitignore @@ -197,3 +197,4 @@ docker-compose.override.yml # journal of the internal-data guard (scripts/guard), one line per commit attempt .internal-data-guard.log +.wolf/ diff --git a/public/ai-atlas/og-atlas-en-v2.png b/public/ai-atlas/og-atlas-en-v2.png new file mode 100644 index 00000000..c49093bd Binary files /dev/null and b/public/ai-atlas/og-atlas-en-v2.png differ diff --git a/public/ai-atlas/og-atlas-ru-v2.png b/public/ai-atlas/og-atlas-ru-v2.png new file mode 100644 index 00000000..1e69f047 Binary files /dev/null and b/public/ai-atlas/og-atlas-ru-v2.png differ diff --git a/public/ai-atlas/og-ladder-en-v2.png b/public/ai-atlas/og-ladder-en-v2.png new file mode 100644 index 00000000..3d52eaf7 Binary files /dev/null and b/public/ai-atlas/og-ladder-en-v2.png differ diff --git a/public/ai-atlas/og-ladder-ru-v2.png b/public/ai-atlas/og-ladder-ru-v2.png new file mode 100644 index 00000000..6624c6fd Binary files /dev/null and b/public/ai-atlas/og-ladder-ru-v2.png differ diff --git a/public/ai-atlas/og-thesis-en-v2.png b/public/ai-atlas/og-thesis-en-v2.png new file mode 100644 index 00000000..2288224b Binary files /dev/null and b/public/ai-atlas/og-thesis-en-v2.png differ diff --git a/public/ai-atlas/og-thesis-ru-v2.png b/public/ai-atlas/og-thesis-ru-v2.png new file mode 100644 index 00000000..3b08d380 Binary files /dev/null and b/public/ai-atlas/og-thesis-ru-v2.png differ diff --git a/public/llms-full-pages/ai-atlas/ladder.md b/public/llms-full-pages/ai-atlas/ladder.md new file mode 100644 index 00000000..941e2096 --- /dev/null +++ b/public/llms-full-pages/ai-atlas/ladder.md @@ -0,0 +1,282 @@ +# The AI agent ladder: 16 rungs from chat to a system of agents + +Source: https://keepsimple.io/ai-atlas/ladder +Author: Wolf Alexanyan + +Where you stand with AI agents and what to learn next: 16 rungs from chat to Claude Code, evals, orchestration and memory, each with the skill for the next step. + +## Stage I. Solo: you and one run + +### 0. Chat + +_You ask, it answers. It talks, you act._ + +- Wrong perspective: It knows the answer. +- Right perspective: It predicts a likely answer, and a likely answer can be wrong. +- Vibecoding skills: You decided to check what ChatGPT, Claude or Gemini says before you use it. + +You open ChatGPT, Claude or Gemini and type. Behind each chat there is an LLM, a large language model: GPT from OpenAI, Claude from Anthropic, Gemini from Google. + +An LLM is a compressed statistical model of language. It predicts the next piece of text, so a wrong answer sounds as sure as a right one. + +Most LLM users are here. This is important to understand, because these are the people LLM companies actually build their features for. + +Tools: + +- ChatGPT, Claude or Gemini, in the browser or on the phone + +### 1. Agent + +_You let it run commands on its own._ + +- Wrong perspective: AI is something I ask for advice. +- Right perspective: AI is something I hand work to. +- Vibecoding skills: You decided to run Claude Code or Codex in a terminal with bypass mode on and let it act. + +The same models also come as agents that work on your computer. Claude Code runs on Claude, Codex runs on GPT. + +You switch on bypass mode, so it stops asking your permission, and let it run commands on its own. This is where your chatbox becomes an agent, because agency means it acts by itself. Every time you start it on a task, that is one run. + +Before you let it act, keep the project in git and save a version before every run, so a run that breaks something is undone by going back to that version. Read what it changed before you accept it, and never let it see your passwords and keys. + +From here two things grow in parallel. One is its mind: memory, context, judgment. The other is its hands, the tools it can touch: the terminal, files, the web, other services through their APIs, and MCP servers, which are plug-ins that connect it to those services. + +Connect it to your calendar through an MCP server, and it books the meeting itself instead of telling you how. + +Tools: + +- Claude Code, Codex or another vendor’s command-line agent +- A terminal with permission to act +- Git + +### 2. Standing instructions + +_The file it reads every time._ + +- Wrong perspective: It remembers what I told it last time. +- Right perspective: It knows only what is written down for it. +- Vibecoding skills: You decided to keep a global CLAUDE.md and one per project. + +You find CLAUDE.md, the instruction file the agent reads every time you start it, but you barely touch it. So it runs on whatever it happened to pick up from talking to you. + +Later you learn there are two of them. The global one says who the agent always is, and the project one says what this project needs. + +Tools: + +- CLAUDE.md for Claude Code +- AGENTS.md for Codex + +### 3. Context rot + +_You keep adding to the file and the model gets dumber._ + +- Wrong perspective: The more I give it, the smarter it gets. +- Right perspective: Past a point, more context makes it dumber. +- Vibecoding skills: You decided to keep CLAUDE.md under 200 lines and move the rest into documents it opens when needed. + +The context window is everything the model sees at once. Whatever is outside it does not exist for the model. + +You keep adding to CLAUDE.md, the task pulls in more and more files, and at some point the window gets so full that the model gets dumber and starts making things up. That is context rot. + +An agent is only as honest as its context, so you learn to keep the file tight, global and project files together under about 200 lines. Everything else goes into documents the agent opens only when it needs them. + +### 4. The question + +_One goal per request, with everything that goal needs._ + +- Wrong perspective: A bad answer means a weak model. +- Right perspective: A bad answer usually means a bad question. +- Vibecoding skills: You decided to put one goal in each request and say what the answer must contain. + +You stop throwing a wish into the box and start framing the question. You say what exactly you want and what the answer must contain, and after reading the answer you choose the next question. + +An LLM works with meaning the way a person does, so how you frame the question changes the answer you get. It also follows your framing, so a leading question gets you agreement. Whoever talks to it badly becomes delusional. + +### 5. Evals + +_You stop guessing and start measuring._ + +- Wrong perspective: It looks right, so it works. +- Right perspective: Nothing works until a test says so. +- Vibecoding skills: You decided to write tests and bring in a Judge agent that checks the first one. + +You write evals, small tests that prove the result still works. Before the run starts, you write down what done means. + +Almost everyone skips this step, and this is where you start measuring instead of hoping. + +Here you also bring in a Judge agent, a second model whose job is to check the first one. It can be your QA agent, or in my case, The Order. The idea is to automate your evaluation. + +Tools: + +- The test runners you already use +- A Judge agent +- Scripts that read the output and say pass or fail + +### 6. Benchmarks + +_The same task on another model, another vendor._ + +- Wrong perspective: I found my model. +- Right perspective: The best model for my task changes every few months. +- Vibecoding skills: You decided to write a script that sends the same task to several vendors and scores each with your evals. + +You run your own task on models from different vendors and compare speed, cost and how often the eval passes. Which model is good at what changes every few months, so you keep track. + +Picking the wrong model can be a costly mistake, and from inside one chat you will never see it. + +Tools: + +- A subscription or an API key per vendor +- One script that sends the same prompt to each +- Your evals from LVL 5 as the score + +## Stage II. Orchestration: you and a system of runs + +### 7. Decomposition + +_One context window runs out, so you split the work into several runs._ + +- Wrong perspective: One smart agent can do the whole thing. +- Right perspective: A big problem is a chain of small runs. +- Vibecoding skills: You decided to start subagents and pass files from one run to the next. + +The task no longer fits into one context window, so you cut it into subtasks. Each subtask gets its own run with a fresh context window, and one agent’s output becomes the next agent’s input. + +You feel the cost right away: handoffs between agents, and drift away from the goal. Over time you learn to split only when the work does not fit one context window. + +Tools: + +- Subagents, which are agents your agent starts for one piece of the work +- Task queues +- Plain files passed from one run to the next + +### 8. Context routing + +_Who gets which part of the context, and who never sees it._ + +- Wrong perspective: Give every agent everything, just in case. +- Right perspective: What a run does not see cannot mislead it. +- Vibecoding skills: You decided to keep a list per step of what the run receives and where it may write. + +Each run gets what its step needs: the task description, the previous output, the files it must read. It does not get anything that could bias it. + +Up to LVL 11 you are the one who decides this for every step, so the orchestrator is you. At LVL 12 a program takes over that role. + +Tools: + +- A list per step of what the run receives +- Inputs it can read but not change +- A rule for what a run may write and where + +### 9. Evals at every step + +_And one on the whole chain._ + +- Wrong perspective: I check the result at the end. +- Right perspective: Every step needs its own check, or the first mistake spreads through the chain. +- Vibecoding skills: You decided to put an eval between runs, so a failed step passes nothing on. + +A mistake in the first run becomes a fact for every run after it. So each step gets its own eval, and the whole chain gets one too. + +Orchestration without evals at each step is a delusion at scale. A step that fails its eval does not pass its output on. + +Tools: + +- The same evals from LVL 5, now placed between runs + +### 10. Code over model + +_You tear down half of what you built._ + +- Wrong perspective: More AI makes the system better. +- Right perspective: Wherever a step is predictable, code does it better than a model. +- Vibecoding skills: You decided to turn routine steps into ordinary functions and hooks that check the output. + +At some point you tear down half of what you built to make it leaner. Every step that became routine turns into code. + +Code is cheaper and gives the same output every time, and you test it like any other function. The model stays only where there is real uncertainty. + +A lean core is easier to manage and, importantly, to trust. + +Tools: + +- Ordinary functions +- Schedulers +- Validation scripts +- Hooks that check the agent’s output before it lands + +### 11. Memory + +_What survives a session and is shared between runs._ + +- Wrong perspective: Each session is a fresh start. +- Right perspective: What the system learned has to outlive the session, if it passed an eval. +- Vibecoding skills: You decided to give each agent a memory file and every run a shared store to search. + +You find MEMORY.md, memory that outlives one context window and is shared between agents. Decisions go there, gotchas, what you tried and what failed. + +Only what passed an eval goes into memory, and a failure is stored as a failure. Work accumulates, and smart-in-the-moment turns into smart-over-time. + +Tools: + +- A memory file per agent +- A shared store every run can search +- Full-text search over past sessions + +### 12. Agents call each other + +_An orchestrator picks who wakes, hands off, collects._ + +- Wrong perspective: I have to drive every step myself. +- Right perspective: My job is the decisions, and the agents handle the handoffs. +- Vibecoding skills: You decided to write an onboarding protocol and a message format your agents share. + +You write an onboarding protocol that tells a new agent who it is and how it joins. Now your agents call each other instead of you. + +One drafts, the Judge pushes back, the first one revises, and it all settles against your evals. You step in only for the real decisions. + +Tools: + +- A list of agents and how to reach each +- A message format they all use +- An orchestrator with a cap on how many times a task can bounce between agents + +### 13. Autonomy + +_Your agents run on their own._ + +- Wrong perspective: My work is doing the task. +- Right perspective: My work is shaping tasks the system can deliver without me. +- Vibecoding skills: You decided to set up schedules and watchers, so the system runs overnight and leaves logs. + +Your daily routine is to wake up and see what they built during the night. You make sure you and your agents learned the gotchas. Then you build the next batch of tasks. + +Most of your own time goes into shaping a task that fits your system, so it delivers by itself. + +Without you, no agent spends money, deletes anything or publishes anything. + +Tools: + +- Schedules +- Watchers that alert you +- Logs, so every mechanism leaves a trail you can read in the morning + +### 14. Departments + +_Teams of agents, each with its own discipline._ + +- Wrong perspective: I run a set of agents. +- Right perspective: I run something closer to a company. +- Vibecoding skills: You decided to group your agents into departments, each with its own Judge, and let a product manager agent run whole projects through them. + +Highly speculative, I’m not here yet. + +You build departments of agents: marketing, business development, engineering with devs and QAs. Each has its own Judge and follows the protocol, and all of them know about each other. + +Next you bring in a product manager agent that builds whole projects overnight using your departments. Nobody has shipped this yet. + +### 15. ??? + +_One day._ + +At that point you have stopped renting a model to talk to and started owning the system around it. diff --git a/public/llms-full-pages/ai-atlas/ru/ladder.md b/public/llms-full-pages/ai-atlas/ru/ladder.md new file mode 100644 index 00000000..3e26e4c8 --- /dev/null +++ b/public/llms-full-pages/ai-atlas/ru/ladder.md @@ -0,0 +1,282 @@ +# Лестница ИИ-агентов: 16 ступеней от чата до системы агентов + +Source: https://keepsimple.io/ru/ai-atlas/ladder +Author: Wolf Alexanyan + +Где ты сейчас в работе с ИИ-агентами и чему учиться дальше: 16 ступеней от чата до Claude Code, эвалов, оркестрации и памяти, на каждой навык для следующего шага. + +## Этап I. Соло: ты и один прогон + +### 0. Чат + +_Ты спрашиваешь, ИИ отвечает. ИИ говорит, делаешь ты._ + +- Как ты думал: ИИ знает ответ. +- Как на самом деле: ИИ предсказывает вероятный ответ, а вероятный ответ бывает неверным. +- Навыки вайбкодинга: Ты решил проверять, что говорят ChatGPT, Claude или Gemini, прежде чем этим пользоваться. + +Ты открываешь ChatGPT, Claude или Gemini и печатаешь. За каждым чатом стоит LLM, большая языковая модель: GPT у OpenAI, Claude у Anthropic, Gemini у Google. + +LLM устроена как сжатая статистическая модель языка и предсказывает следующий кусок текста, поэтому неверный ответ звучит так же уверенно, как верный. + +Здесь сидит большинство пользователей LLM. Это важно понимать: именно под этих людей компании, которые делают LLM, и строят свои функции. + +Инструменты: + +- ChatGPT, Claude или Gemini, в браузере или на телефоне + +### 1. Агент + +_Ты разрешаешь агенту самому запускать команды._ + +- Как ты думал: У ИИ я спрашиваю совета. +- Как на самом деле: ИИ я отдаю работу. +- Навыки вайбкодинга: Ты решил запустить Claude Code или Codex в терминале с включённым bypass и дать агенту действовать. + +Те же модели есть и в виде агентов, которые работают у тебя на компьютере. Claude Code работает на Claude, Codex на GPT. + +Ты включаешь режим bypass, чтобы агент перестал спрашивать разрешения, и даёшь агенту самому запускать команды. Здесь чат становится агентом, потому что агентность и значит действовать самостоятельно. Каждый запуск на задачу считается одним прогоном. + +Прежде чем дать агенту волю, держи проект в git и сохраняй версию перед каждым прогоном: прогон что-то сломал, откатился к версии, и всё. Читай, что агент поменял, прежде чем принять, и никогда не показывай агенту свои пароли и ключи. + +Отсюда у агента растут две вещи параллельно. Голова: память, контекст, суждение. И руки, то есть инструменты, до которых агент дотягивается: терминал, файлы, веб, другие сервисы через их API и MCP-серверы, плагины, которые к этим сервисам подключают. + +Подключи агента к календарю через MCP-сервер, и агент сам назначит встречу, а не объяснит тебе, как её назначить. + +Инструменты: + +- Claude Code, Codex или агент командной строки другого вендора +- Терминал с правом действовать +- Git + +### 2. Постоянные инструкции + +_Файл, который агент читает каждый раз._ + +- Как ты думал: Агент помнит, что я говорил в прошлый раз. +- Как на самом деле: Агент знает только то, что записано. +- Навыки вайбкодинга: Ты решил завести глобальный CLAUDE.md и по одному на каждый проект. + +Ты находишь CLAUDE.md, файл инструкций, который агент читает при каждом запуске, но почти его не трогаешь. Так что агент работает на том, что случайно нахватал из разговоров с тобой. + +Позже ты узнаёшь, что их два. Глобальный говорит, кто этот агент всегда и везде, проектный говорит, что нужно именно этому проекту. + +Инструменты: + +- CLAUDE.md для Claude Code +- AGENTS.md для Codex + +### 3. Гниение контекста + +_Ты всё дописываешь в файл, а модель тупеет._ + +- Как ты думал: Чем больше я даю, тем умнее становится агент. +- Как на самом деле: После какого-то предела от контекста агент только тупеет. +- Навыки вайбкодинга: Ты решил держать CLAUDE.md короче 200 строк, а остальное вынести в документы, которые агент открывает по мере надобности. + +Контекстное окно вмещает всё, что модель видит разом. Чего в окне нет, для модели не существует. + +Ты дописываешь CLAUDE.md, задача тянет всё больше файлов, и в какой-то момент окно забито так, что модель тупеет и начинает выдумывать. Это и есть гниение контекста. + +Насколько честен контекст, настолько честен и агент, так что ты учишься держать файл коротким: глобальный и проектный вместе примерно в 200 строк. Остальное уходит в документы, которые агент открывает только когда они нужны. + +### 4. Вопрос + +_Одна цель на запрос, и всё, что этой цели нужно._ + +- Как ты думал: Плохой ответ значит слабая модель. +- Как на самом деле: Плохой ответ обычно значит плохой вопрос. +- Навыки вайбкодинга: Ты решил класть в каждый запрос одну цель и говорить, что должно быть в ответе. + +Ты перестаёшь кидать в окошко пожелание и начинаешь ставить вопрос. Говоришь, что именно хочешь и что должно быть в ответе, а прочитав ответ, выбираешь следующий вопрос. + +LLM работает со смыслом так же, как человек, поэтому от постановки вопроса зависит, какой ответ ты получишь. Модель ещё и идёт за твоей постановкой, так что на наводящий вопрос ты получишь согласие. Кто плохо говорит с LLM, уходит в иллюзии. + +### 5. Эвалы + +_Ты перестаёшь гадать и начинаешь измерять._ + +- Как ты думал: Выглядит правильно, значит, работает. +- Как на самом деле: Ничего не работает, пока тест не сказал, что работает. +- Навыки вайбкодинга: Ты решил писать тесты и подключить агента-судью, который проверяет первого. + +Ты пишешь эвалы, маленькие тесты, которые доказывают, что результат всё ещё работает. До старта прогона ты записываешь, что значит «готово». + +Почти все этот шаг пропускают. А именно здесь ты начинаешь измерять вместо того, чтобы надеяться. + +Здесь же ты подключаешь агента-судью, вторую модель, чья работа проверять первую. Это может быть твой QA-агент, у меня это The Order. Смысл в том, чтобы проверку автоматизировать. + +Инструменты: + +- Тестовые раннеры, которыми ты уже пользуешься +- Агент-судья +- Скрипты, которые читают результат и говорят «прошёл» или «не прошёл» + +### 6. Бенчмарки + +_Та же задача на другой модели у другого вендора._ + +- Как ты думал: Я нашёл свою модель. +- Как на самом деле: Лучшая модель под мою задачу меняется каждые несколько месяцев. +- Навыки вайбкодинга: Ты решил написать скрипт, который шлёт одну и ту же задачу нескольким вендорам и оценивает каждого твоими эвалами. + +Ты гоняешь свою задачу на моделях разных вендоров и сравниваешь скорость, цену и то, как часто проходит эвал. Какая модель в чём хороша, меняется каждые несколько месяцев, так что ты за этим следишь. + +Не та модель может дорого обойтись, а изнутри одного чата ты этого никогда не увидишь. + +Инструменты: + +- Подписка или API-ключ у каждого вендора +- Один скрипт, который шлёт один и тот же промпт каждому +- Твои эвалы с LVL 5 как оценка + +## Этап II. Оркестрация: ты и система прогонов + +### 7. Декомпозиция + +_Одно контекстное окно кончается, и ты делишь работу на несколько прогонов._ + +- Как ты думал: Один умный агент сделает всё целиком. +- Как на самом деле: Большая задача складывается из цепочки маленьких прогонов. +- Навыки вайбкодинга: Ты решил запускать субагентов и передавать файлы от одного прогона к следующему. + +Задача больше не влезает в одно контекстное окно, так что ты режешь её на подзадачи. У каждой свой прогон со свежим окном, и результат одного агента становится входом для следующего. + +Цену ты чувствуешь сразу: передачи между агентами и уход в сторону от цели. Со временем ты учишься делить работу, только когда она правда не влезает в одно окно. + +Инструменты: + +- Субагенты, то есть агенты, которых твой агент запускает на один кусок работы +- Очереди задач +- Обычные файлы, которые передаются от прогона к прогону + +### 8. Маршрутизация контекста + +_Кому какой кусок контекста достаётся, а кто его не видит никогда._ + +- Как ты думал: Дай каждому агенту всё, на всякий случай. +- Как на самом деле: Что прогону не показали, то прогон и не собьёт. +- Навыки вайбкодинга: Ты решил вести на каждый шаг список: что прогон получает и куда может писать. + +Каждый прогон получает то, что нужно этому шагу: описание задачи, предыдущий результат, файлы, которые надо прочитать. И ничего, что могло бы сбить с толку. + +До LVL 11 это решаешь ты сам на каждом шаге, так что оркестратор здесь ты. На LVL 12 эту роль забирает программа. + +Инструменты: + +- Список на каждый шаг: что прогон получает +- Входы, которые можно читать, но нельзя менять +- Правило, что прогону можно писать и куда + +### 9. Эвалы на каждом шаге + +_И один на всю цепочку._ + +- Как ты думал: Я проверяю результат в конце. +- Как на самом деле: Каждому шагу нужна своя проверка, иначе первая же ошибка расползётся по цепочке. +- Навыки вайбкодинга: Ты решил ставить эвал между прогонами, чтобы проваленный шаг ничего не передавал дальше. + +Ошибка в первом прогоне становится фактом для всех следующих прогонов. Поэтому у каждого шага свой эвал, и у всей цепочки тоже. + +Оркестрация без эвалов на каждом шаге только создаёт иллюзию промышленного масштаба. Шаг, который не прошёл свой эвал, ничего дальше не передаёт. + +Инструменты: + +- Те же эвалы с LVL 5, теперь между прогонами + +### 10. Код вместо модели + +_Ты сносишь половину того, что построил._ + +- Как ты думал: Чем больше ИИ, тем лучше система. +- Как на самом деле: Там, где шаг предсказуем, код справляется лучше модели. +- Навыки вайбкодинга: Ты решил превратить рутинные шаги в обычные функции и хуки, которые проверяют результат. + +В какой-то момент ты сносишь половину построенного, чтобы система стала легче. Каждый шаг, который стал рутиной, превращается в код. + +Код дешевле, каждый раз даёт один и тот же результат, и тестируешь ты его как любую другую функцию. Модель остаётся только там, где есть настоящая неопределённость. + +Лёгким ядром проще управлять и, что важнее, ему проще доверять. + +Инструменты: + +- Обычные функции +- Планировщики +- Скрипты валидации +- Хуки, которые проверяют результат агента, прежде чем его принять + +### 11. Память + +_То, что переживает сессию и общее для всех прогонов._ + +- Как ты думал: Каждая сессия начинается с чистого листа. +- Как на самом деле: То, чему система научилась, должно пережить сессию, если прошло эвал. +- Навыки вайбкодинга: Ты решил дать каждому агенту файл памяти, а всем прогонам общее хранилище с поиском. + +Ты находишь MEMORY.md, память, которая живёт дольше одного контекстного окна и общая для всех агентов. Туда уходят решения, подводные камни, что ты пробовал и что не сработало. + +В память попадает только то, что прошло эвал, а провал записывается как провал. Работа накапливается, и умная в моменте система становится умной надолго. + +Инструменты: + +- Файл памяти у каждого агента +- Общее хранилище, в котором ищет каждый прогон +- Полнотекстовый поиск по прошлым сессиям + +### 12. Агенты вызывают друг друга + +_Оркестратор выбирает, кто проснётся, передаёт работу и собирает результат._ + +- Как ты думал: Каждый шаг мне приходится вести самому. +- Как на самом деле: Я принимаю решения, а передачи берут на себя агенты. +- Навыки вайбкодинга: Ты решил написать протокол онбординга и общий для агентов формат сообщений. + +Ты пишешь протокол онбординга, из которого новый агент узнаёт свою роль и как войти в систему. Теперь агенты вызывают друг друга вместо тебя. + +Один пишет черновик, судья возражает, первый правит, и всё это сходится на твоих эвалах. Ты вмешиваешься только там, где нужно настоящее решение. + +Инструменты: + +- Список агентов и как до каждого достучаться +- Общий для всех формат сообщений +- Оркестратор с лимитом на то, сколько раз задача может ходить между агентами + +### 13. Автономия + +_Твои агенты работают сами._ + +- Как ты думал: Моя работа в том, чтобы делать задачу. +- Как на самом деле: Моя работа в том, чтобы собирать задачи, которые система выполнит без меня. +- Навыки вайбкодинга: Ты решил настроить расписания и дозорных, чтобы система работала ночью и оставляла логи. + +Твой день начинается с того, что ты просыпаешься и смотришь, что агенты построили за ночь. Проверяешь, что вы с агентами усвоили подводные камни. Потом собираешь следующую пачку задач. + +Большая часть твоего времени уходит на то, чтобы собрать задачу под твою систему, такую, чтобы система выполнила её сама. + +Без тебя ни один агент не тратит деньги, ничего не удаляет и ничего не публикует. + +Инструменты: + +- Расписания +- Дозорные, которые поднимают тревогу +- Логи, чтобы каждый механизм оставлял след, который ты читаешь утром + +### 14. Отделы + +_Команды агентов, у каждой своя дисциплина._ + +- Как ты думал: Я управляю набором агентов. +- Как на самом деле: Я управляю чем-то похожим на компанию. +- Навыки вайбкодинга: Ты решил собрать агентов в отделы, каждому дать своего судью, а агенту-продакту отдать целые проекты, которые идут через эти отделы. + +Это пока чистая спекуляция, я сам до этого не дошёл. + +Ты строишь отделы агентов: маркетинг, развитие бизнеса, разработку с девелоперами и QA. У каждого свой судья, каждый следует протоколу, и все знают друг о друге. + +Дальше ты подключаешь агента-продакт-менеджера, который за ночь строит целые проекты силами твоих отделов. Такого ещё никто не выпустил. + +### 15. ??? + +_Когда-нибудь._ + +К этому моменту ты уже не арендуешь модель, чтобы с ней поговорить. Ты владеешь системой вокруг модели. diff --git a/public/llms-full-pages/ai-atlas/ru/thesis.md b/public/llms-full-pages/ai-atlas/ru/thesis.md new file mode 100644 index 00000000..0f879ebf --- /dev/null +++ b/public/llms-full-pages/ai-atlas/ru/thesis.md @@ -0,0 +1,28 @@ +# Тезис: преимущество у того, кто решает сложные задачи с ИИ + +Source: https://keepsimple.io/ru/ai-atlas/thesis +Author: Wolf Alexanyan + +> Вайбкодинг сократил разрыв между инженерами и остальными, так что преимущество теперь у того, кто в одиночку, с ИИ-агентами, решает самые сложные задачи. + +Даже там, где инженеры всё ещё впереди, отрыв стал короче. Год назад это были лет десять форы, сейчас, может быть, два. И закрыл этот разрыв вайбкодинг. По сути, ты строишь софт тем, что объясняешь ИИ-агенту, чего хочешь, а это **позволяет одному человеку собрать продукт от начала до конца**. + +Для рынка ещё год назад ты стоил ровно столько, насколько сложные задачи умел разглядеть как человек из продукта или бизнеса. Но чтобы их решить, нужна была целая команда. Теперь этого ограничения нет: задачу, которую ты увидел, ты можешь решить сам. + +**Поэтому сегодня ты стоишь ровно столько, насколько сложные задачи можешь решить.** + +А сложные задачи, в свою очередь, **требуют другого уровня мастерства в работе с LLM-агентами**. Начинается оно с вопросов, потому что с вопросов начинается любой серьёзный софт. Что спрашивать, подсказывает опыт в бизнесе и продукте. **Как спрашивать, это второй навык.** **То есть поставить вопрос, разобрать ответ на части и выбрать следующий вопрос.** + +Потом в какой-то момент до тебя доходит: LLM работает на связности, примерно как мозг. На любой вопрос модель отвечает тем, что лучше всего склеивается с полученным, и идёт за твоей постановкой вопроса. **Поэтому тот, кто не научится правильно разговаривать с LLM, уходит в иллюзии.** **Лечится это эвалами и бенчмарками**. Эвалы проверяют твою работу, бенчмарки проверяют твой выбор модели. + +Дальше ты упираешься в **контекстное окно**. Один запуск агента на задачу назовём прогоном. Прогон видит только то, что влезло в его контекстное окно, а сложная задача в одно окно не влезает. После какого-то предела окно забито всем, чего задача касалась, модель тупеет и начинает галлюцинировать. Это **гниение контекста**. + +Поэтому ты режешь работу на прогоны, и результат одного агента становится входом для следующего. Потом решаешь, что каждый прогон видит, а чего не увидит никогда. Это **маршрутизация контекста**. Прогоны, передачи между ними и маршрутизация контекста вместе и есть **оркестрация**. Так решается задача, которая в одно контекстное окно не помещается. Но в какой-то момент ты понимаешь, что **оркестрация без эвалов на каждом шаге превращается в иллюзию промышленного масштаба**. Прежде всего потому, что ошибка первого прогона становится фактом для всех следующих. Поэтому ты ставишь эвал на каждый шаг и ещё один на всю цепочку. + +Со временем часть шагов становится рутиной, и модель им больше не нужна, так что ты **заменяешь её кодом**. Код дешевле и детерминирован, то есть каждый раз даёт один и тот же результат. Там, где шагу модель всё ещё нужна, бенчмарки подсказывают, какая именно и не добавить ли вторую. + +Но каждый прогон всё равно стартует с нуля, и всё, до чего система додумалась вчера, к следующему прогону пропало. Поэтому ты даёшь системе **память**, которая живёт дольше одного контекстного окна и общая для всех прогонов. Туда уходят решения, подводные камни, что ты пробовал и что не сработало. В память попадает только то, что прошло эвал, а провал записывается как провал. Иначе система начинает помнить собственные ошибки как факты. + +До этого момента оркестратор ты сам: решаешь, какой прогон идёт следующим, что этот прогон видит и куда уходит результат. Потом ты пишешь протокол онбординга, короткий файл, из которого новый агент узнаёт свою роль и как войти в систему. И теперь **агенты начинают вызывать друг друга вместо тебя**. Один пишет черновик, другой возражает, первый правит, и всё это сходится на твоих эвалах. Кто просыпается следующим, теперь выбирает программа, она же собирает результаты, а ты вмешиваешься только там, где нужно настоящее решение. + +В какой-то момент **система начинает работать сама**. Ты просыпаешься, читаешь, что система построила за ночь, проверяешь, что вы с агентами усвоили подводные камни, и ставишь следующую пачку задач. Большая часть твоего времени теперь уходит на то, чтобы собрать задачу, которую система сможет выполнить сама. То есть ты снова задаёшь вопросы, только теперь всей системе, а не одной модели. Это и есть сложная оркестрация, и **это позволяет одному человеку решать задачи, для которых раньше нужна была целая команда.** diff --git a/public/llms-full-pages/ai-atlas/thesis.md b/public/llms-full-pages/ai-atlas/thesis.md new file mode 100644 index 00000000..7d8cc85a --- /dev/null +++ b/public/llms-full-pages/ai-atlas/thesis.md @@ -0,0 +1,28 @@ +# Thesis: the edge now is solving hard problems with AI agents + +Source: https://keepsimple.io/ai-atlas/thesis +Author: Wolf Alexanyan + +> Vibe coding narrowed the gap between people in tech, so the edge now belongs to whoever can solve the hardest problems solo with AI agents. + +Even where engineers still lead, the lead got shorter. What was ten years ahead a year ago is perhaps two years ahead now. And the gap closed because of vibe coding. Basically, you build software by telling an AI agent what you want, and that **enables solo building end to end**. + +From the market’s perspective, a year ago you were worth as much as the complexity of the problems you could see as a product or business person. But to solve them, you needed a whole team. Now that limit is gone, and you can solve the problem you saw on your own. + +**So today you are worth as much as the complexity of the problems you can solve.** + +In turn, **complex problems require a higher mastery of working with LLM agents**. That mastery starts with questions, because any sophisticated software begins with them. What to ask takes business and product expertise. **How to ask is a second skill.** **It means framing the question, decomposing the answer and choosing the next question.** + +Then at some point you realize that an LLM works on coherence, much as a brain does. It will answer any question with whatever fits best with what it was given, and it follows your framing. **So whoever does not learn to talk to an LLM properly becomes delusional.** **The cure is evals and benchmarks**. Evals test your work, and benchmarks test your choice of model. + +The next thing you hit is the **context window**. One start of an agent on a task is one run. A run sees only what fits in its context window. A complex problem does not fit one context window. Past a point the window fills with everything the task has touched, and the model gets dumber and starts to hallucinate. That is **context rot**. + +So you split the work into runs, and one agent’s output becomes the next agent’s input. Then you decide what each run sees and what it never sees. That is **context routing**. Runs, handoffs and context routing together make **orchestration**. This is how you solve a problem too big for one context window. However, at some point you understand that **orchestration without evals at each step is a delusion at scale**. Primarily because the error of the first run becomes a fact for every run after it. So you decide to build an eval for each step, and one for the whole chain too. + +After a while some steps turn routine and no longer need a model, so you **replace it with code**. Code is cheaper and deterministic (which means it gives the same output every time). Where a step still needs a model, benchmarks tell you which one, or whether to add a second. + +But every run still starts from zero, and whatever the system figured out yesterday is gone by the next one. So you give it **memory** that outlives a single context window and is shared between runs. Decisions go there, gotchas, what you tried and what failed. Only what passed an eval goes into memory, and a failure is stored as a failure. Otherwise the system starts remembering its own mistakes as facts. + +Up to this point the orchestrator is you. You decide which run goes next, what it sees and where its output goes. Then you write an onboarding protocol, a short file that tells a new agent who it is and how it joins the system. Now **your agents start calling each other instead of you**. One drafts, another pushes back, the first one revises, and it all settles against your evals. A program now picks who wakes up and collects the results, and you step in only for the real decisions. + +At some point **the system runs on its own**. You wake up, read what it built overnight, make sure you and your agents learned the gotchas, and set the next batch of tasks. Most of your time now goes into shaping a task the system can deliver by itself. So you are back to asking questions, only now you ask them of the whole system instead of one model. This is sophisticated orchestration, and **it lets one person solve problems that used to take a whole team.** diff --git a/public/llms-full.txt b/public/llms-full.txt index 4da9cd69..73f87193 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -1,6 +1,330 @@ # Keep Simple | Cognitive Science & UX Leadership > Elevate your leadership with applied cognitive science. Explore behavioral psychology and cognitive frameworks designed for modern managers. + +## AI Atlas: learning to work with AI agents + +- [AI Atlas: how one person runs a team of AI agents](https://keepsimple.io/ai-atlas): The map of how I run a team of AI agents, one task from my message to the result. Next to it are a Thesis and a 16-rung ladder for learning to work with agents. +- [Thesis: the edge now is solving hard problems with AI agents](https://keepsimple.io/ai-atlas/thesis): Vibe coding narrowed the gap between engineers and everyone else. What comes next: asking well, context windows, orchestration, evals and memory. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/thesis.md +- [The AI agent ladder: 16 rungs from chat to a system of agents](https://keepsimple.io/ai-atlas/ladder): Where you stand with AI agents and what to learn next: 16 rungs from chat to Claude Code, evals, orchestration and memory, each with the skill for the next step. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/ladder.md +- [AI Атлас: как один человек руководит командой ИИ-агентов](https://keepsimple.io/ru/ai-atlas): Карта того, как я работаю с командой ИИ-агентов: одна задача от моего сообщения до результата. Рядом Тезис и Лестница из 16 ступеней, чтобы этому научиться. +- [Тезис: преимущество у того, кто решает сложные задачи с ИИ](https://keepsimple.io/ru/ai-atlas/thesis): Вайбкодинг сократил разрыв между инженерами и остальными. Что дальше: умение спрашивать, контекстное окно, оркестрация, эвалы и память. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/ru/thesis.md +- [Лестница ИИ-агентов: 16 ступеней от чата до системы агентов](https://keepsimple.io/ru/ai-atlas/ladder): Где ты сейчас в работе с ИИ-агентами и чему учиться дальше: 16 ступеней от чата до Claude Code, эвалов, оркестрации и памяти, на каждой навык для следующего шага. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/ru/ladder.md + +## Thesis: the edge now is solving hard problems with AI agents + +Source: https://keepsimple.io/ai-atlas/thesis +Author: Wolf Alexanyan + +> Vibe coding narrowed the gap between people in tech, so the edge now belongs to whoever can solve the hardest problems solo with AI agents. + +Even where engineers still lead, the lead got shorter. What was ten years ahead a year ago is perhaps two years ahead now. And the gap closed because of vibe coding. Basically, you build software by telling an AI agent what you want, and that **enables solo building end to end**. + +From the market’s perspective, a year ago you were worth as much as the complexity of the problems you could see as a product or business person. But to solve them, you needed a whole team. Now that limit is gone, and you can solve the problem you saw on your own. + +**So today you are worth as much as the complexity of the problems you can solve.** + +In turn, **complex problems require a higher mastery of working with LLM agents**. That mastery starts with questions, because any sophisticated software begins with them. What to ask takes business and product expertise. **How to ask is a second skill.** **It means framing the question, decomposing the answer and choosing the next question.** + +Then at some point you realize that an LLM works on coherence, much as a brain does. It will answer any question with whatever fits best with what it was given, and it follows your framing. **So whoever does not learn to talk to an LLM properly becomes delusional.** **The cure is evals and benchmarks**. Evals test your work, and benchmarks test your choice of model. + +The next thing you hit is the **context window**. One start of an agent on a task is one run. A run sees only what fits in its context window. A complex problem does not fit one context window. Past a point the window fills with everything the task has touched, and the model gets dumber and starts to hallucinate. That is **context rot**. + +So you split the work into runs, and one agent’s output becomes the next agent’s input. Then you decide what each run sees and what it never sees. That is **context routing**. Runs, handoffs and context routing together make **orchestration**. This is how you solve a problem too big for one context window. However, at some point you understand that **orchestration without evals at each step is a delusion at scale**. Primarily because the error of the first run becomes a fact for every run after it. So you decide to build an eval for each step, and one for the whole chain too. + +After a while some steps turn routine and no longer need a model, so you **replace it with code**. Code is cheaper and deterministic (which means it gives the same output every time). Where a step still needs a model, benchmarks tell you which one, or whether to add a second. + +But every run still starts from zero, and whatever the system figured out yesterday is gone by the next one. So you give it **memory** that outlives a single context window and is shared between runs. Decisions go there, gotchas, what you tried and what failed. Only what passed an eval goes into memory, and a failure is stored as a failure. Otherwise the system starts remembering its own mistakes as facts. + +Up to this point the orchestrator is you. You decide which run goes next, what it sees and where its output goes. Then you write an onboarding protocol, a short file that tells a new agent who it is and how it joins the system. Now **your agents start calling each other instead of you**. One drafts, another pushes back, the first one revises, and it all settles against your evals. A program now picks who wakes up and collects the results, and you step in only for the real decisions. + +At some point **the system runs on its own**. You wake up, read what it built overnight, make sure you and your agents learned the gotchas, and set the next batch of tasks. Most of your time now goes into shaping a task the system can deliver by itself. So you are back to asking questions, only now you ask them of the whole system instead of one model. This is sophisticated orchestration, and **it lets one person solve problems that used to take a whole team.** + +## The AI agent ladder: 16 rungs from chat to a system of agents + +Source: https://keepsimple.io/ai-atlas/ladder +Author: Wolf Alexanyan + +Where you stand with AI agents and what to learn next: 16 rungs from chat to Claude Code, evals, orchestration and memory, each with the skill for the next step. + +### Stage I. Solo: you and one run + +#### 0. Chat + +_You ask, it answers. It talks, you act._ + +- Wrong perspective: It knows the answer. +- Right perspective: It predicts a likely answer, and a likely answer can be wrong. +- Vibecoding skills: You decided to check what ChatGPT, Claude or Gemini says before you use it. + +You open ChatGPT, Claude or Gemini and type. Behind each chat there is an LLM, a large language model: GPT from OpenAI, Claude from Anthropic, Gemini from Google. + +An LLM is a compressed statistical model of language. It predicts the next piece of text, so a wrong answer sounds as sure as a right one. + +Most LLM users are here. This is important to understand, because these are the people LLM companies actually build their features for. + +Tools: + +- ChatGPT, Claude or Gemini, in the browser or on the phone + +#### 1. Agent + +_You let it run commands on its own._ + +- Wrong perspective: AI is something I ask for advice. +- Right perspective: AI is something I hand work to. +- Vibecoding skills: You decided to run Claude Code or Codex in a terminal with bypass mode on and let it act. + +The same models also come as agents that work on your computer. Claude Code runs on Claude, Codex runs on GPT. + +You switch on bypass mode, so it stops asking your permission, and let it run commands on its own. This is where your chatbox becomes an agent, because agency means it acts by itself. Every time you start it on a task, that is one run. + +Before you let it act, keep the project in git and save a version before every run, so a run that breaks something is undone by going back to that version. Read what it changed before you accept it, and never let it see your passwords and keys. + +From here two things grow in parallel. One is its mind: memory, context, judgment. The other is its hands, the tools it can touch: the terminal, files, the web, other services through their APIs, and MCP servers, which are plug-ins that connect it to those services. + +Connect it to your calendar through an MCP server, and it books the meeting itself instead of telling you how. + +Tools: + +- Claude Code, Codex or another vendor’s command-line agent +- A terminal with permission to act +- Git + +#### 2. Standing instructions + +_The file it reads every time._ + +- Wrong perspective: It remembers what I told it last time. +- Right perspective: It knows only what is written down for it. +- Vibecoding skills: You decided to keep a global CLAUDE.md and one per project. + +You find CLAUDE.md, the instruction file the agent reads every time you start it, but you barely touch it. So it runs on whatever it happened to pick up from talking to you. + +Later you learn there are two of them. The global one says who the agent always is, and the project one says what this project needs. + +Tools: + +- CLAUDE.md for Claude Code +- AGENTS.md for Codex + +#### 3. Context rot + +_You keep adding to the file and the model gets dumber._ + +- Wrong perspective: The more I give it, the smarter it gets. +- Right perspective: Past a point, more context makes it dumber. +- Vibecoding skills: You decided to keep CLAUDE.md under 200 lines and move the rest into documents it opens when needed. + +The context window is everything the model sees at once. Whatever is outside it does not exist for the model. + +You keep adding to CLAUDE.md, the task pulls in more and more files, and at some point the window gets so full that the model gets dumber and starts making things up. That is context rot. + +An agent is only as honest as its context, so you learn to keep the file tight, global and project files together under about 200 lines. Everything else goes into documents the agent opens only when it needs them. + +#### 4. The question + +_One goal per request, with everything that goal needs._ + +- Wrong perspective: A bad answer means a weak model. +- Right perspective: A bad answer usually means a bad question. +- Vibecoding skills: You decided to put one goal in each request and say what the answer must contain. + +You stop throwing a wish into the box and start framing the question. You say what exactly you want and what the answer must contain, and after reading the answer you choose the next question. + +An LLM works with meaning the way a person does, so how you frame the question changes the answer you get. It also follows your framing, so a leading question gets you agreement. Whoever talks to it badly becomes delusional. + +#### 5. Evals + +_You stop guessing and start measuring._ + +- Wrong perspective: It looks right, so it works. +- Right perspective: Nothing works until a test says so. +- Vibecoding skills: You decided to write tests and bring in a Judge agent that checks the first one. + +You write evals, small tests that prove the result still works. Before the run starts, you write down what done means. + +Almost everyone skips this step, and this is where you start measuring instead of hoping. + +Here you also bring in a Judge agent, a second model whose job is to check the first one. It can be your QA agent, or in my case, The Order. The idea is to automate your evaluation. + +Tools: + +- The test runners you already use +- A Judge agent +- Scripts that read the output and say pass or fail + +#### 6. Benchmarks + +_The same task on another model, another vendor._ + +- Wrong perspective: I found my model. +- Right perspective: The best model for my task changes every few months. +- Vibecoding skills: You decided to write a script that sends the same task to several vendors and scores each with your evals. + +You run your own task on models from different vendors and compare speed, cost and how often the eval passes. Which model is good at what changes every few months, so you keep track. + +Picking the wrong model can be a costly mistake, and from inside one chat you will never see it. + +Tools: + +- A subscription or an API key per vendor +- One script that sends the same prompt to each +- Your evals from LVL 5 as the score + +### Stage II. Orchestration: you and a system of runs + +#### 7. Decomposition + +_One context window runs out, so you split the work into several runs._ + +- Wrong perspective: One smart agent can do the whole thing. +- Right perspective: A big problem is a chain of small runs. +- Vibecoding skills: You decided to start subagents and pass files from one run to the next. + +The task no longer fits into one context window, so you cut it into subtasks. Each subtask gets its own run with a fresh context window, and one agent’s output becomes the next agent’s input. + +You feel the cost right away: handoffs between agents, and drift away from the goal. Over time you learn to split only when the work does not fit one context window. + +Tools: + +- Subagents, which are agents your agent starts for one piece of the work +- Task queues +- Plain files passed from one run to the next + +#### 8. Context routing + +_Who gets which part of the context, and who never sees it._ + +- Wrong perspective: Give every agent everything, just in case. +- Right perspective: What a run does not see cannot mislead it. +- Vibecoding skills: You decided to keep a list per step of what the run receives and where it may write. + +Each run gets what its step needs: the task description, the previous output, the files it must read. It does not get anything that could bias it. + +Up to LVL 11 you are the one who decides this for every step, so the orchestrator is you. At LVL 12 a program takes over that role. + +Tools: + +- A list per step of what the run receives +- Inputs it can read but not change +- A rule for what a run may write and where + +#### 9. Evals at every step + +_And one on the whole chain._ + +- Wrong perspective: I check the result at the end. +- Right perspective: Every step needs its own check, or the first mistake spreads through the chain. +- Vibecoding skills: You decided to put an eval between runs, so a failed step passes nothing on. + +A mistake in the first run becomes a fact for every run after it. So each step gets its own eval, and the whole chain gets one too. + +Orchestration without evals at each step is a delusion at scale. A step that fails its eval does not pass its output on. + +Tools: + +- The same evals from LVL 5, now placed between runs + +#### 10. Code over model + +_You tear down half of what you built._ + +- Wrong perspective: More AI makes the system better. +- Right perspective: Wherever a step is predictable, code does it better than a model. +- Vibecoding skills: You decided to turn routine steps into ordinary functions and hooks that check the output. + +At some point you tear down half of what you built to make it leaner. Every step that became routine turns into code. + +Code is cheaper and gives the same output every time, and you test it like any other function. The model stays only where there is real uncertainty. + +A lean core is easier to manage and, importantly, to trust. + +Tools: + +- Ordinary functions +- Schedulers +- Validation scripts +- Hooks that check the agent’s output before it lands + +#### 11. Memory + +_What survives a session and is shared between runs._ + +- Wrong perspective: Each session is a fresh start. +- Right perspective: What the system learned has to outlive the session, if it passed an eval. +- Vibecoding skills: You decided to give each agent a memory file and every run a shared store to search. + +You find MEMORY.md, memory that outlives one context window and is shared between agents. Decisions go there, gotchas, what you tried and what failed. + +Only what passed an eval goes into memory, and a failure is stored as a failure. Work accumulates, and smart-in-the-moment turns into smart-over-time. + +Tools: + +- A memory file per agent +- A shared store every run can search +- Full-text search over past sessions + +#### 12. Agents call each other + +_An orchestrator picks who wakes, hands off, collects._ + +- Wrong perspective: I have to drive every step myself. +- Right perspective: My job is the decisions, and the agents handle the handoffs. +- Vibecoding skills: You decided to write an onboarding protocol and a message format your agents share. + +You write an onboarding protocol that tells a new agent who it is and how it joins. Now your agents call each other instead of you. + +One drafts, the Judge pushes back, the first one revises, and it all settles against your evals. You step in only for the real decisions. + +Tools: + +- A list of agents and how to reach each +- A message format they all use +- An orchestrator with a cap on how many times a task can bounce between agents + +#### 13. Autonomy + +_Your agents run on their own._ + +- Wrong perspective: My work is doing the task. +- Right perspective: My work is shaping tasks the system can deliver without me. +- Vibecoding skills: You decided to set up schedules and watchers, so the system runs overnight and leaves logs. + +Your daily routine is to wake up and see what they built during the night. You make sure you and your agents learned the gotchas. Then you build the next batch of tasks. + +Most of your own time goes into shaping a task that fits your system, so it delivers by itself. + +Without you, no agent spends money, deletes anything or publishes anything. + +Tools: + +- Schedules +- Watchers that alert you +- Logs, so every mechanism leaves a trail you can read in the morning + +#### 14. Departments + +_Teams of agents, each with its own discipline._ + +- Wrong perspective: I run a set of agents. +- Right perspective: I run something closer to a company. +- Vibecoding skills: You decided to group your agents into departments, each with its own Judge, and let a product manager agent run whole projects through them. + +Highly speculative, I’m not here yet. + +You build departments of agents: marketing, business development, engineering with devs and QAs. Each has its own Judge and follows the protocol, and all of them know about each other. + +Next you bring in a product manager agent that builds whole projects overnight using your departments. Nobody has shipped this yet. + +#### 15. ??? + +_One day._ + +At that point you have stopped renting a model to talk to and started owning the system around it. + + + ## Pages & Resources - [Home](https://keepsimple.io/): KeepSimple home page. - [Slug](https://keepsimple.io/[page]): No description available. diff --git a/public/llms.txt b/public/llms.txt index b2fb9fca..27a60c6a 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -1,6 +1,17 @@ # Keep Simple | Cognitive Science & UX Leadership > Elevate your leadership with applied cognitive science. Explore behavioral psychology and cognitive frameworks designed for modern managers. + +## AI Atlas: learning to work with AI agents + +- [AI Atlas: how one person runs a team of AI agents](https://keepsimple.io/ai-atlas): The map of how I run a team of AI agents, one task from my message to the result. Next to it are a Thesis and a 16-rung ladder for learning to work with agents. +- [Thesis: the edge now is solving hard problems with AI agents](https://keepsimple.io/ai-atlas/thesis): Vibe coding narrowed the gap between engineers and everyone else. What comes next: asking well, context windows, orchestration, evals and memory. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/thesis.md +- [The AI agent ladder: 16 rungs from chat to a system of agents](https://keepsimple.io/ai-atlas/ladder): Where you stand with AI agents and what to learn next: 16 rungs from chat to Claude Code, evals, orchestration and memory, each with the skill for the next step. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/ladder.md +- [AI Атлас: как один человек руководит командой ИИ-агентов](https://keepsimple.io/ru/ai-atlas): Карта того, как я работаю с командой ИИ-агентов: одна задача от моего сообщения до результата. Рядом Тезис и Лестница из 16 ступеней, чтобы этому научиться. +- [Тезис: преимущество у того, кто решает сложные задачи с ИИ](https://keepsimple.io/ru/ai-atlas/thesis): Вайбкодинг сократил разрыв между инженерами и остальными. Что дальше: умение спрашивать, контекстное окно, оркестрация, эвалы и память. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/ru/thesis.md +- [Лестница ИИ-агентов: 16 ступеней от чата до системы агентов](https://keepsimple.io/ru/ai-atlas/ladder): Где ты сейчас в работе с ИИ-агентами и чему учиться дальше: 16 ступеней от чата до Claude Code, эвалов, оркестрации и памяти, на каждой навык для следующего шага. Markdown: https://keepsimple.io/llms-full-pages/ai-atlas/ru/ladder.md + + ## Pages & Resources - [Home](https://keepsimple.io/): KeepSimple home page. - [Slug](https://keepsimple.io/[page]): No description available. diff --git a/public/robots.txt b/public/robots.txt index c5823871..e4c57d02 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -21,8 +21,11 @@ Disallow: /hy/all-about-user-stories/ Disallow: /hy/client-dev-company-workflow-birds-eye-view/ Disallow: /hy/career-path-of-a-manager-and-a-few-universal-tips/ Disallow: /hy/overengineering_and_demo_readiness/ +Disallow: /hy/ai-atlas +Disallow: /hy/ai-atlas/ Disallow: /articles/[page] Disallow: /uxcore_next/ Sitemap: https://strapi.keepsimple.io/sitemap/index.xml Sitemap: https://keepsimple.io/library-sitemap.xml +Sitemap: https://keepsimple.io/ai-atlas-sitemap.xml diff --git a/scripts/ai-atlas/generate-llms.mjs b/scripts/ai-atlas/generate-llms.mjs new file mode 100644 index 00000000..7f794ae7 --- /dev/null +++ b/scripts/ai-atlas/generate-llms.mjs @@ -0,0 +1,182 @@ +/* The Atlas for answer engines and AI assistants. + + node scripts/ai-atlas/generate-llms.mjs write + node scripts/ai-atlas/generate-llms.mjs --check exit 1 when stale + + Writes the Thesis and the Ladder as Markdown, in English and Russian, to + public/llms-full-pages/ai-atlas/, and keeps an Atlas section in + public/llms.txt (links) and public/llms-full.txt (the full English text) + between ai-atlas markers. Everything comes from the texts the pages draw + (readings.json, readings.ru.json, copy.json, ru.json), so run it after + any change to them. The rest of both llms files is left as it is. One + journal line per run in logs/ai-atlas-llms.jsonl. */ +import { + appendFileSync, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, +} from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../..', +); +const ORIGIN = 'https://keepsimple.io'; +const CHECK = process.argv.includes('--check'); +const START = ''; +const END = ''; + +const json = f => JSON.parse(readFileSync(path.join(ROOT, f), 'utf8')); +const LANGS = { + en: { + prefix: '', + readings: json('src/lib/aiAtlas/readings.json'), + copy: json('src/lib/aiAtlas/copy.json'), + mapName: 'The map', + }, + ru: { + prefix: '/ru', + readings: json('src/lib/aiAtlas/readings.ru.json'), + copy: json('src/lib/aiAtlas/ru.json').copy, + mapName: 'Карта', + }, +}; +const runs = rs => rs.map(r => (r.m === 'key' ? `**${r.t}**` : r.t)).join(''); +const plain = rs => rs.map(r => r.t).join(''); + +const pageUrl = (lang, p) => `${ORIGIN}${LANGS[lang].prefix}${p}`; +const mdUrl = (lang, name) => + `${ORIGIN}/llms-full-pages/ai-atlas/${lang === 'en' ? '' : `${lang}/`}${name}.md`; + +function thesisMd(lang) { + const { readings, copy } = LANGS[lang]; + const { lead, paras } = readings.article; + return [ + `# ${copy.seoTitleThesis}`, + '', + `Source: ${pageUrl(lang, '/ai-atlas/thesis')}`, + 'Author: Wolf Alexanyan', + '', + `> ${plain(lead)}`, + '', + ...paras.flatMap(p => [runs(p), '']), + ].join('\n'); +} + +function ladderMd(lang) { + const { readings, copy } = LANGS[lang]; + const { stages, perspective, tracks, toolsLabel, closing, rungs } = + readings.ladder; + const out = [ + `# ${copy.seoTitleLadder}`, + '', + `Source: ${pageUrl(lang, '/ai-atlas/ladder')}`, + 'Author: Wolf Alexanyan', + '', + copy.seoDescriptionLadder, + '', + ]; + for (const stage of stages) { + out.push(`## ${stage.label}`, ''); + for (const r of rungs.filter(x => x.stage === stage.id)) { + out.push(`### ${r.n}. ${r.title}`, '', `_${r.sub}_`, ''); + if (r.was) out.push(`- ${perspective.was}: ${r.was}`); + if (r.think) out.push(`- ${perspective.think}: ${r.think}`); + if (r.skill) out.push(`- ${tracks.skill}: ${r.skill}`); + if (r.was || r.think || r.skill) out.push(''); + for (const t of r.text || []) out.push(t, ''); + if (r.tools?.length) + out.push(`${toolsLabel}:`, '', ...r.tools.map(t => `- ${t}`), ''); + } + } + out.push(closing, ''); + return out.join('\n'); +} + +function indexBlock() { + const en = LANGS.en.copy; + const ru = LANGS.ru.copy; + return [ + START, + '## AI Atlas: learning to work with AI agents', + '', + `- [${en.seoTitle}](${pageUrl('en', '/ai-atlas')}): ${en.seoDescription}`, + `- [${en.seoTitleThesis}](${pageUrl('en', '/ai-atlas/thesis')}): ${en.seoDescriptionThesis} Markdown: ${mdUrl('en', 'thesis')}`, + `- [${en.seoTitleLadder}](${pageUrl('en', '/ai-atlas/ladder')}): ${en.seoDescriptionLadder} Markdown: ${mdUrl('en', 'ladder')}`, + `- [${ru.seoTitle}](${pageUrl('ru', '/ai-atlas')}): ${ru.seoDescription}`, + `- [${ru.seoTitleThesis}](${pageUrl('ru', '/ai-atlas/thesis')}): ${ru.seoDescriptionThesis} Markdown: ${mdUrl('ru', 'thesis')}`, + `- [${ru.seoTitleLadder}](${pageUrl('ru', '/ai-atlas/ladder')}): ${ru.seoDescriptionLadder} Markdown: ${mdUrl('ru', 'ladder')}`, + END, + ].join('\n'); +} + +function fullBlock() { + const demote = md => md.replace(/^(#+) /gm, '#$1 '); + return [ + indexBlock().replace(END, ''), + demote(thesisMd('en')), + demote(ladderMd('en')), + END, + ].join('\n'); +} + +/* Insert the block after the file's opening title and summary, or replace + the block a previous run left. */ +function withBlock(text, block) { + if (text.includes(START) && text.includes(END)) { + const a = text.indexOf(START); + const b = text.indexOf(END) + END.length; + return text.slice(0, a) + block + text.slice(b); + } + const at = text.indexOf('\n## '); + if (at === -1) return `${text.trimEnd()}\n\n${block}\n`; + return `${text.slice(0, at).trimEnd()}\n\n${block}\n${text.slice(at)}`; +} + +const pagesDir = path.join(ROOT, 'public', 'llms-full-pages', 'ai-atlas'); +const targets = [ + [path.join(pagesDir, 'thesis.md'), () => thesisMd('en')], + [path.join(pagesDir, 'ladder.md'), () => ladderMd('en')], + [path.join(pagesDir, 'ru', 'thesis.md'), () => thesisMd('ru')], + [path.join(pagesDir, 'ru', 'ladder.md'), () => ladderMd('ru')], + [path.join(ROOT, 'public', 'llms.txt'), old => withBlock(old, indexBlock())], + [ + path.join(ROOT, 'public', 'llms-full.txt'), + old => withBlock(old, fullBlock()), + ], +]; + +const stale = []; +for (const [file, make] of targets) { + const old = existsSync(file) ? readFileSync(file, 'utf8') : ''; + const next = make(old); + if (next === old) continue; + stale.push(path.relative(ROOT, file)); + if (!CHECK) { + mkdirSync(path.dirname(file), { recursive: true }); + writeFileSync(file, next); + } +} + +mkdirSync(path.join(ROOT, 'logs'), { recursive: true }); +appendFileSync( + path.join(ROOT, 'logs', 'ai-atlas-llms.jsonl'), + JSON.stringify({ + at: new Date().toISOString(), + mode: CHECK ? 'check' : 'write', + changed: stale, + }) + '\n', +); + +if (CHECK && stale.length) { + console.error(`Atlas llms files are stale: ${stale.join(', ')}`); + process.exit(1); +} +console.log( + stale.length + ? `${CHECK ? 'stale' : 'wrote'}: ${stale.join(', ')}` + : 'Atlas llms files are current', +); diff --git a/scripts/ai-atlas/og/render.mjs b/scripts/ai-atlas/og/render.mjs new file mode 100644 index 00000000..4054ab7f --- /dev/null +++ b/scripts/ai-atlas/og/render.mjs @@ -0,0 +1,212 @@ +/* Share thumbnails for the three Atlas pages, in English and Russian. + + node scripts/ai-atlas/og/render.mjs + + Renders 1200x630 PNGs into public/ai-atlas/ with headless Chromium, in the + Atlas palette and its own fonts. The words come from the Atlas texts + (readings.json, readings.ru.json), so a thumbnail never says what the + page does not. Static files are cached for thirty days: when a picture + changes, bump VERSION so the new one gets a new name. One journal line per + run in logs/ai-atlas-og.jsonl. + + motion-passport: exempt, the output is a still PNG; the only transform + rotates a square into a diamond and nothing moves. */ +import { appendFileSync, mkdirSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +import { chromium } from 'playwright'; + +const VERSION = 'v2'; +const ROOT = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../..', +); +const OUT = path.join(ROOT, 'public', 'ai-atlas'); +const font = f => pathToFileURL(path.join(ROOT, 'public', f)).href; + +const readings = { + en: JSON.parse( + readFileSync(path.join(ROOT, 'src/lib/aiAtlas/readings.json'), 'utf8'), + ), + ru: JSON.parse( + readFileSync(path.join(ROOT, 'src/lib/aiAtlas/readings.ru.json'), 'utf8'), + ), +}; +const runs = rs => rs.map(r => r.t).join(''); + +/* The one sentence of the Thesis that carries it, as written. */ +const thesisLine = { + en: runs(readings.en.article.paras[2]).replace(/^So today/, 'Today'), + ru: runs(readings.ru.article.paras[2]).replace(/^Поэтому сегодня/, 'Сегодня'), +}; +const rungCount = readings.en.ladder.rungs.length; +const soloCount = readings.en.ladder.rungs.filter( + r => r.stage === 'solo', +).length; + +const WORDS = { + en: { + site: 'KEEPSIMPLE · AI ATLAS', + atlasTitle: 'AI · ATLAS', + atlasSub: 'How one person runs a team of AI agents', + thesisLabel: 'Thesis', + ladderLabel: 'The Ladder', + ladderTitle: `${rungCount} rungs from chat to a system of agents`, + stages: ['I · Solo', 'II · Orchestration'], + author: 'Wolf Alexanyan', + }, + ru: { + site: 'KEEPSIMPLE · AI АТЛАС', + atlasTitle: 'AI · АТЛАС', + atlasSub: 'Как один человек руководит командой ИИ-агентов', + thesisLabel: 'Тезис', + ladderLabel: 'Лестница', + ladderTitle: `${rungCount} ступеней от чата до системы агентов`, + stages: ['I · Соло', 'II · Оркестрация'], + author: 'Wolf Alexanyan', + }, +}; + +const base = ` +@font-face { font-family: 'EB Garamond'; font-weight: 400; src: url(${font('ai-atlas/font-2.woff2')}); } +@font-face { font-family: 'EB Garamond'; font-weight: 500; src: url(${font('ai-atlas/font-3.woff2')}); } +@font-face { font-family: 'EB Garamond'; font-style: italic; font-weight: 400; src: url(${font('ai-atlas/font-0.woff2')}); } +@font-face { font-family: 'JetBrains Mono'; font-weight: 400; src: url(${font('ai-atlas/font-4.woff2')}); } +@font-face { font-family: 'JetBrains Mono'; font-weight: 500; src: url(${font('ai-atlas/font-5.woff2')}); } +@font-face { font-family: 'Kanji'; src: url(${font('fonts/YujiSyuku-Regular.ttf')}); } +* { box-sizing: border-box; margin: 0; padding: 0; } +html, body { width: 1200px; height: 630px; } +body { + position: relative; overflow: hidden; + background: #ece4cf; color: #1a1a1a; + font-family: 'JetBrains Mono', monospace; + -webkit-font-smoothing: antialiased; +} +.corner { position: absolute; width: 30px; height: 30px; border: 0 solid #2a2a2a; } +.tl { top: 22px; left: 22px; border-top-width: 1px; border-left-width: 1px; } +.tr { top: 22px; right: 22px; border-top-width: 1px; border-right-width: 1px; } +.bl { bottom: 22px; left: 22px; border-bottom-width: 1px; border-left-width: 1px; } +.br { bottom: 22px; right: 22px; border-bottom-width: 1px; border-right-width: 1px; } +.ring { position: absolute; border-radius: 50%; border: 1px solid rgba(42, 42, 42, 0.22); } +.dmd { display: inline-block; width: 18px; height: 18px; transform: rotate(45deg); background: #b3392b; } +.dmd.gold { background: #c08a2c; } .dmd.blue { background: #4a6f99; } +.site { position: absolute; top: 44px; left: 72px; font-size: 14px; letter-spacing: 0.32em; color: #6a6457; } +.water { position: absolute; font-family: 'Kanji', serif; color: rgba(42, 42, 42, 0.07); line-height: 1; } +.kanji { font-family: 'Kanji', serif; color: #b3392b; line-height: 1; } +.label { display: flex; align-items: center; gap: 18px; font-size: 18px; letter-spacing: 0.32em; text-transform: uppercase; color: #b3392b; } +.label .dmd { width: 12px; height: 12px; } +.serif { font-family: 'EB Garamond', serif; } +.author { font-size: 14px; letter-spacing: 0.32em; text-transform: uppercase; color: #6a6457; } +`; + +const frame = ( + inner, + extraCss = '', +) => ` + + +${inner}`; + +const rings = (cx, cy, radii) => + radii + .map( + r => + ``, + ) + .join(''); + +const atlas = w => + frame( + `${rings(600, 300, [110, 190, 275, 370])} + 図 +
+
天
+
${w.atlasTitle}
+
${w.atlasSub}
+
+
`, + `.stack { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px; } + .title { display: flex; align-items: center; gap: 44px; } + .title .serif { font-size: 76px; font-weight: 500; letter-spacing: 0.18em; padding-left: 0.18em; } + .sub { font-size: 22px; letter-spacing: 0.24em; text-transform: uppercase; color: #3d3a33; } + .dots { display: flex; gap: 26px; margin-top: 6px; } + .dots .dmd { width: 14px; height: 14px; }`, + ); + +const thesis = (w, line) => + frame( + `
${w.site}
+ 論 +
+
${w.thesisLabel}
+

${line}

+
${w.author}
+
`, + `.body { position: absolute; left: 72px; right: 300px; top: 118px; bottom: 64px; display: flex; flex-direction: column; justify-content: space-between; } + .quote { font-size: 48px; line-height: 1.18; font-weight: 400; }`, + ); + +const ladder = w => { + const STEP_W = 30; + const STEP_H = 19; + const steps = Array.from({ length: rungCount }, (_, i) => { + const last = i === rungCount - 1; + const solo = i < soloCount; + const cls = last ? 'step open' : solo ? 'step solo' : 'step orch'; + return ``; + }).join(''); + return frame( + `
${w.site}
+ 梯 +
+
${w.ladderLabel}
+

${w.ladderTitle}

+
${w.author}
+
+
${steps} + ${w.stages[0]} + ${w.stages[1]} +
`, + `.body { position: absolute; left: 72px; width: 560px; top: 118px; bottom: 64px; display: flex; flex-direction: column; justify-content: space-between; } + .title { font-size: 48px; line-height: 1.15; font-weight: 400; } + .stairs { position: absolute; right: 80px; bottom: 120px; } + .step { position: absolute; bottom: 0; width: 26px; } + .step.solo { background: #1a1a1a; } + .step.orch { background: #b3392b; } + .step.open { border: 1px dashed #b3392b; background: transparent; } + .stage { position: absolute; top: calc(100% + 18px); font-size: 14px; letter-spacing: 0.24em; text-transform: uppercase; color: #3d3a33; white-space: nowrap; }`, + ); +}; + +const pages = []; +for (const lang of ['en', 'ru']) { + const w = WORDS[lang]; + pages.push([`og-atlas-${lang}-${VERSION}.png`, atlas(w)]); + pages.push([`og-thesis-${lang}-${VERSION}.png`, thesis(w, thesisLine[lang])]); + pages.push([`og-ladder-${lang}-${VERSION}.png`, ladder(w)]); +} + +const browser = await chromium.launch(); +const page = await browser.newPage({ viewport: { width: 1200, height: 630 } }); +const written = []; +try { + for (const [name, html] of pages) { + await page.setContent(html, { waitUntil: 'load' }); + await page.evaluate(() => document.fonts.ready); + await page.screenshot({ path: path.join(OUT, name), type: 'png' }); + written.push(name); + } +} finally { + await browser.close(); + mkdirSync(path.join(ROOT, 'logs'), { recursive: true }); + appendFileSync( + path.join(ROOT, 'logs', 'ai-atlas-og.jsonl'), + JSON.stringify({ + at: new Date().toISOString(), + version: VERSION, + written, + }) + '\n', + ); +} +console.log(`wrote ${written.length} thumbnails: ${written.join(', ')}`); diff --git a/src/lib/aiAtlas/adapter.ts b/src/lib/aiAtlas/adapter.ts index bb8ca621..b1c5405a 100644 --- a/src/lib/aiAtlas/adapter.ts +++ b/src/lib/aiAtlas/adapter.ts @@ -31,10 +31,18 @@ export const PAGE_TEXT_DEFAULTS: Record = { ringTerritoriesDesc: 'Every topic opens its own card.', metaLabel: 'TERMINAL DOCUMENTATION', tabAtlas: 'Atlas', - tabArticle: 'Story', + tabArticle: 'Thesis', tabLadder: 'Ladder', legendKey: 'Key thesis', legendSee: 'Product & business', + rowInside: 'inside', + rowMechanism: 'mechanism', + rowResource: 'resource', + rowStage: 'stage', + stageStart: 'START', + stageEnd: 'END', + topicsLabel: 'Topics', + doctrineCaption: 'AUTHORITY & EVIDENCE', topicsPlaceholder: 'The Atlas', stagesAria: 'Project to Result, clockwise', }; @@ -93,7 +101,7 @@ export function adaptGuide(guide: any) { desc: describe(id, entry.detail || [entry.text]).join('\n\n'), rows: [ ...(entry.children || []).map((child: string) => ({ - k: 'inside', + k: text('rowInside'), v: (entries.get(child) as any)?.title || child, ref: child, })), @@ -145,7 +153,7 @@ export function adaptGuide(guide: any) { title: step.title, desc: describe(id, [step.text]).join('\n\n'), rows: step.children.map((child: string) => ({ - k: 'mechanism', + k: text('rowMechanism'), v: (entries.get(child) as any)?.title || child, ref: child, })), @@ -155,7 +163,11 @@ export function adaptGuide(guide: any) { label: labels[index], sub: String(index + 1).padStart(2, '0') + - (index === 0 ? ' · START' : index === 5 ? ' · END' : ''), + (index === 0 + ? ' · ' + text('stageStart') + : index === 5 + ? ' · ' + text('stageEnd') + : ''), support: support[index].map(systemRef), diamond: 'red', theta: angles[index], @@ -252,7 +264,7 @@ export function adaptGuide(guide: any) { title: text('ringDevEnvTitle'), desc: text('ringDevEnvDesc'), rows: ['agents', 'memory', 'tools', 'models'].map(id => ({ - k: 'resource', + k: text('rowResource'), v: node(id).title, ref: dossiers['system-' + id] ? 'system-' + id : id, })), @@ -260,13 +272,17 @@ export function adaptGuide(guide: any) { dossiers['ring:projects'] = { title: text('ringProjectsTitle'), desc: text('ringProjectsDesc'), - rows: projects.map((p: any) => ({ k: 'stage', v: p.label, ref: p.id })), + rows: projects.map((p: any) => ({ + k: text('rowStage'), + v: p.label, + ref: p.id, + })), }; dossiers['ring:territories'] = { title: text('ringTerritoriesTitle'), desc: text('ringTerritoriesDesc'), rows: guide.entries.map((e: any) => ({ - k: 'mechanism', + k: text('rowMechanism'), v: e.title, ref: e.id, })), diff --git a/src/lib/aiAtlas/copy.json b/src/lib/aiAtlas/copy.json index fbe3db80..89eef003 100644 --- a/src/lib/aiAtlas/copy.json +++ b/src/lib/aiAtlas/copy.json @@ -1,8 +1,16 @@ { - "seoTitle": "Wolf’s Terminal: an atlas of my orchestration harness", - "seoDescription": "How I run a team of AI agents: one task, from my message to the result, every feature explained by me.", - "seoKeywords": "Wolf’s Terminal, orchestration harness, AI agents", - "ogImageAlt": "Wolf’s Terminal", + "seoTitle": "AI Atlas: how one person runs a team of AI agents", + "seoDescription": "The map of how I run a team of AI agents, one task from my message to the result. Next to it are a Thesis and a 16-rung ladder for learning to work with agents.", + "seoKeywords": "AI agents, multi-agent system, AI orchestration, Claude Code, Codex, vibe coding, Wolf’s Terminal", + "ogImageAlt": "AI Atlas: how one person runs a team of AI agents", + "seoTitleThesis": "Thesis: the edge now is solving hard problems with AI agents", + "seoDescriptionThesis": "Vibe coding narrowed the gap between engineers and everyone else. What comes next: asking well, context windows, orchestration, evals and memory.", + "seoKeywordsThesis": "vibe coding, AI agents, solo builder, context window, context routing, orchestration, evals, LLM", + "ogImageAltThesis": "Today you are worth as much as the complexity of the problems you can solve.", + "seoTitleLadder": "The AI agent ladder: 16 rungs from chat to a system of agents", + "seoDescriptionLadder": "Where you stand with AI agents and what to learn next: 16 rungs from chat to Claude Code, evals, orchestration and memory, each with the skill for the next step.", + "seoKeywordsLadder": "how to learn AI agents, AI skills roadmap, vibe coding, Claude Code, CLAUDE.md, context rot, evals, benchmarks, subagents, AI orchestration, agent memory", + "ogImageAltLadder": "The Ladder: 16 rungs from chat to a system of agents", "loading": "Loading…", "failedToLoad": "Failed to load data : ", "welcomeBanner": "AN ATLAS OF MY ORCHESTRATION HARNESS", diff --git a/src/lib/aiAtlas/localize.ts b/src/lib/aiAtlas/localize.ts new file mode 100644 index 00000000..ad164a51 --- /dev/null +++ b/src/lib/aiAtlas/localize.ts @@ -0,0 +1,40 @@ +/* The Atlas in another language. The guide keeps its structure (which + stages, tiles and cards exist, pushed by the Terminal in English); a + translation replaces only the words, matched by id. Anything the + translation does not cover stays in English, so a card the Terminal adds + later shows up at once instead of disappearing. + + Translations: ru.json (map words, stage and system cards, mechanisms, + Wolf's card prose) and readings.ru.json (Thesis and Ladder). */ + +type Fields = Record; +export type AtlasTranslation = { + copy?: Fields & { stageLabels?: string[] }; + steps?: Record; + nodes?: Record; + entries?: Record; + cards?: Record; +}; + +const overlay = (items: any[], words: Record = {}) => + items.map(item => (words[item.id] ? { ...item, ...words[item.id] } : item)); + +export function localizeGuide(guide: any, words: AtlasTranslation) { + if (!guide || !words) return guide; + const { stageLabels, ...copy } = words.copy || {}; + return { + ...guide, + steps: overlay(guide.steps, words.steps), + entries: overlay(guide.entries, words.entries), + system: { + ...guide.system, + nodes: overlay(guide.system.nodes, words.nodes), + }, + /* Translated words win over pushed English ones on this locale. */ + copy: { ...(guide.copy || {}), ...copy }, + cards: { ...(guide.cards || {}), ...(words.cards || {}) }, + stages: stageLabels + ? stageLabels.map((label, i) => ({ ...(guide.stages?.[i] || {}), label })) + : guide.stages, + }; +} diff --git a/src/lib/aiAtlas/readings.ru.json b/src/lib/aiAtlas/readings.ru.json new file mode 100644 index 00000000..9b57d5e6 --- /dev/null +++ b/src/lib/aiAtlas/readings.ru.json @@ -0,0 +1,551 @@ +{ + "article": { + "lead": [ + { + "t": "Вайбкодинг сократил разрыв между инженерами и остальными", + "m": "key" + }, + { + "t": ", так что преимущество теперь у того, кто в одиночку, с ИИ-агентами, решает самые сложные задачи." + } + ], + "paras": [ + [ + { + "t": "Даже там, где инженеры всё ещё впереди, отрыв стал короче. Год назад это были лет десять форы, сейчас, может быть, два. И закрыл этот разрыв вайбкодинг. По сути, ты строишь софт тем, что объясняешь ИИ-агенту, чего хочешь, а это " + }, + { + "t": "позволяет одному человеку собрать продукт от начала до конца", + "m": "key" + }, + { + "t": "." + } + ], + [ + { + "t": "Для рынка ещё год назад " + }, + { + "t": "ты стоил ровно столько, насколько сложные задачи умел разглядеть как человек из продукта или бизнеса", + "m": "see" + }, + { + "t": ". Но чтобы их решить, нужна была целая команда. Теперь этого ограничения нет: задачу, которую ты увидел, ты можешь решить сам." + } + ], + [ + { + "t": "Поэтому сегодня ты стоишь ровно столько, насколько сложные задачи можешь решить.", + "m": "key" + } + ], + [ + { + "t": "А сложные задачи, в свою очередь, " + }, + { + "t": "требуют другого уровня мастерства в работе с LLM-агентами", + "m": "key" + }, + { + "t": ". Начинается оно с вопросов, потому что с вопросов начинается любой серьёзный софт. " + }, + { + "t": "Что спрашивать, подсказывает опыт в бизнесе и продукте.", + "m": "see" + }, + { + "t": " " + }, + { + "t": "Как спрашивать, это второй навык.", + "m": "key" + }, + { + "t": " " + }, + { + "t": "То есть поставить вопрос, разобрать ответ на части и выбрать следующий вопрос.", + "m": "key" + } + ], + [ + { + "t": "Потом в какой-то момент до тебя доходит: LLM работает на связности, примерно как мозг. На любой вопрос модель отвечает тем, что лучше всего склеивается с полученным, и идёт за твоей постановкой вопроса. " + }, + { + "t": "Поэтому тот, кто не научится правильно разговаривать с LLM, уходит в иллюзии.", + "m": "key" + }, + { + "t": " " + }, + { + "t": "Лечится это эвалами и бенчмарками", + "m": "key" + }, + { + "t": ". Эвалы проверяют твою работу, бенчмарки проверяют твой выбор модели." + } + ], + [ + { + "t": "Дальше ты упираешься в " + }, + { + "t": "контекстное окно", + "m": "key" + }, + { + "t": ". Один запуск агента на задачу назовём прогоном. Прогон видит только то, что влезло в его контекстное окно, а сложная задача в одно окно не влезает. После какого-то предела окно забито всем, чего задача касалась, модель тупеет и начинает галлюцинировать. Это " + }, + { + "t": "гниение контекста", + "m": "key" + }, + { + "t": "." + } + ], + [ + { + "t": "Поэтому ты режешь работу на прогоны, и результат одного агента становится входом для следующего. Потом решаешь, что каждый прогон видит, а чего не увидит никогда. Это " + }, + { + "t": "маршрутизация контекста", + "m": "key" + }, + { + "t": ". Прогоны, передачи между ними и маршрутизация контекста вместе и есть " + }, + { + "t": "оркестрация", + "m": "key" + }, + { + "t": ". Так решается задача, которая в одно контекстное окно не помещается. Но в какой-то момент ты понимаешь, что " + }, + { + "t": "оркестрация без эвалов на каждом шаге превращается в иллюзию промышленного масштаба", + "m": "key" + }, + { + "t": ". Прежде всего потому, что ошибка первого прогона становится фактом для всех следующих. Поэтому ты ставишь эвал на каждый шаг и ещё один на всю цепочку." + } + ], + [ + { + "t": "Со временем часть шагов становится рутиной, и модель им больше не нужна, так что ты " + }, + { + "t": "заменяешь её кодом", + "m": "key" + }, + { + "t": ". Код дешевле и детерминирован, то есть каждый раз даёт один и тот же результат. Там, где шагу модель всё ещё нужна, бенчмарки подсказывают, какая именно и не добавить ли вторую." + } + ], + [ + { + "t": "Но каждый прогон всё равно стартует с нуля, и всё, до чего система додумалась вчера, к следующему прогону пропало. Поэтому ты даёшь системе " + }, + { + "t": "память", + "m": "key" + }, + { + "t": ", которая живёт дольше одного контекстного окна и общая для всех прогонов. Туда уходят решения, подводные камни, что ты пробовал и что не сработало. В память попадает только то, что прошло эвал, а провал записывается как провал. Иначе система начинает помнить собственные ошибки как факты." + } + ], + [ + { + "t": "До этого момента оркестратор ты сам: решаешь, какой прогон идёт следующим, что этот прогон видит и куда уходит результат. Потом ты пишешь протокол онбординга, короткий файл, из которого новый агент узнаёт свою роль и как войти в систему. И теперь " + }, + { + "t": "агенты начинают вызывать друг друга вместо тебя", + "m": "key" + }, + { + "t": ". Один пишет черновик, другой возражает, первый правит, и всё это сходится на твоих эвалах. Кто просыпается следующим, теперь выбирает программа, она же собирает результаты, а ты вмешиваешься только там, где нужно настоящее решение." + } + ], + [ + { + "t": "В какой-то момент " + }, + { + "t": "система начинает работать сама", + "m": "key" + }, + { + "t": ". Ты просыпаешься, читаешь, что система построила за ночь, проверяешь, что вы с агентами усвоили подводные камни, и ставишь следующую пачку задач. Большая часть твоего времени теперь уходит на то, чтобы собрать задачу, которую система сможет выполнить сама. То есть ты снова задаёшь вопросы, только теперь всей системе, а не одной модели. Это и есть сложная оркестрация, и " + }, + { + "t": "это позволяет одному человеку решать задачи, для которых раньше нужна была целая команда.", + "m": "key" + } + ] + ], + "board": { + "steps": [ + "Контекстное окно", + "Прогоны", + "Маршрутизация", + "Эвалы", + "Код", + "Бенчмарки", + "Память", + "Оркестратор", + "Автономия" + ], + "tips": { + "Context window": "Всё, что модель видит разом.", + "Files": "Всё, чего касалась задача, оседает в одном и том же окне.", + "Tool output": "Всё, чего касалась задача, оседает в одном и том же окне.", + "Old answers": "Всё, чего касалась задача, оседает в одном и том же окне.", + "Logs": "Всё, чего касалась задача, оседает в одном и том же окне.", + "Notes": "Всё, чего касалась задача, оседает в одном и том же окне.", + "Task brief": "Всё, чего касалась задача, оседает в одном и том же окне.", + "Raw source": "Всё, чего касалась задача, оседает в одном и том же окне.", + "More files": "Это уже не влезает. Модель это теряет и начинает выдумывать.", + "More logs": "Это уже не влезает. Модель это теряет и начинает выдумывать.", + "Run": "Один прогон: один агент, одно контекстное окно.", + "Run A": "Прогон со своим контекстным окном. Что из него вышло, идёт на вход следующему.", + "Run B": "Прогон со своим контекстным окном. Что из него вышло, идёт на вход следующему.", + "Run C": "Прогон со своим контекстным окном. Что из него вышло, идёт на вход следующему.", + "Eval": "Проверка этого шага. Не прошла, значит, результат дальше не идёт.", + "Chain eval": "Проверка результата всей цепочки.", + "Code": "Шаг стал рутиной, и теперь его делает код. Дешевле, и результат каждый раз один и тот же.", + "Test": "Обычный тест: проверяет, что код делает то, что должен.", + "Other vendor": "Бенчмарки показали: модель другого вендора делает этот шаг лучше.", + "Second model": "Вторая модель работает над тем же шагом рядом с первой.", + "Memory": "То, чему система научилась. Сюда попадает только то, что прошло эвал.", + "Orchestrator": "Программа, которая решает, кто работает следующим, и собирает результаты.", + "@srcOff": "Выбранный прогон этого не видит никогда. В этом и смысл маршрутизации.", + "@srcOn": "Кусок контекста. Маршрутизация решает, каким прогонам его дать, а каким не показывать никогда.", + "@chipOutA": "Результат прогона A, переданный сюда на вход.", + "@chipOutB": "Результат прогона B, переданный сюда на вход.", + "@chipIn": "Передано в это окно. Прогон видит это и больше ничего.", + "@outRot": "Окно переполнено, и результат портится. Это гниение контекста.", + "@outFinal": "Готовый результат всей цепочки.", + "@youBottom": "Ты вмешиваешься только там, где нужно настоящее решение.", + "@you": "Ты решаешь, какой прогон идёт следующим и что в него попадает." + }, + "labels": { + "Task brief": "Бриф задачи", + "Raw source": "Исходники", + "Files": "Файлы", + "Tool output": "Вывод инструментов", + "Logs": "Логи", + "Notes": "Заметки", + "Old answers": "Старые ответы", + "More files": "Ещё файлы", + "More logs": "Ещё логи", + "Run": "Прогон", + "You": "Ты", + "Orchestrator": "Оркестратор", + "Output": "Результат", + "Run A": "Прогон A", + "Run B": "Прогон B", + "Run C": "Прогон C", + "Context window": "Контекстное окно", + "Eval": "Эвал", + "Chain eval": "Эвал цепочки", + "Code": "Код", + "Test": "Тест", + "Other vendor": "Другой вендор", + "Second model": "Вторая модель", + "Memory": "Память", + "Context of A": "Контекст A", + "Context of B": "Контекст B", + "Context of C": "Контекст C", + "Output of A": "Результат A", + "Output of B": "Результат B" + } + } + }, + "ladder": { + "stages": [ + { + "id": "solo", + "label": "Этап I. Соло: ты и один прогон" + }, + { + "id": "orch", + "label": "Этап II. Оркестрация: ты и система прогонов" + } + ], + "tracks": { + "think": "Мышление", + "skill": "Навыки вайбкодинга" + }, + "perspective": { + "was": "Как ты думал", + "think": "Как на самом деле" + }, + "toolsLabel": "Инструменты", + "closing": "К этому моменту ты уже не арендуешь модель, чтобы с ней поговорить. Ты владеешь системой вокруг модели.", + "rungs": [ + { + "n": 0, + "stage": "solo", + "title": "Чат", + "sub": "Ты спрашиваешь, ИИ отвечает. ИИ говорит, делаешь ты.", + "was": "ИИ знает ответ.", + "think": "ИИ предсказывает вероятный ответ, а вероятный ответ бывает неверным.", + "skill": "Ты решил проверять, что говорят ChatGPT, Claude или Gemini, прежде чем этим пользоваться.", + "text": [ + "Ты открываешь ChatGPT, Claude или Gemini и печатаешь. За каждым чатом стоит LLM, большая языковая модель: GPT у OpenAI, Claude у Anthropic, Gemini у Google.", + "LLM устроена как сжатая статистическая модель языка и предсказывает следующий кусок текста, поэтому неверный ответ звучит так же уверенно, как верный.", + "Здесь сидит большинство пользователей LLM. Это важно понимать: именно под этих людей компании, которые делают LLM, и строят свои функции." + ], + "tools": ["ChatGPT, Claude или Gemini, в браузере или на телефоне"] + }, + { + "n": 1, + "stage": "solo", + "title": "Агент", + "sub": "Ты разрешаешь агенту самому запускать команды.", + "was": "У ИИ я спрашиваю совета.", + "think": "ИИ я отдаю работу.", + "skill": "Ты решил запустить Claude Code или Codex в терминале с включённым bypass и дать агенту действовать.", + "text": [ + "Те же модели есть и в виде агентов, которые работают у тебя на компьютере. Claude Code работает на Claude, Codex на GPT.", + "Ты включаешь режим bypass, чтобы агент перестал спрашивать разрешения, и даёшь агенту самому запускать команды. Здесь чат становится агентом, потому что агентность и значит действовать самостоятельно. Каждый запуск на задачу считается одним прогоном.", + "Прежде чем дать агенту волю, держи проект в git и сохраняй версию перед каждым прогоном: прогон что-то сломал, откатился к версии, и всё. Читай, что агент поменял, прежде чем принять, и никогда не показывай агенту свои пароли и ключи.", + "Отсюда у агента растут две вещи параллельно. Голова: память, контекст, суждение. И руки, то есть инструменты, до которых агент дотягивается: терминал, файлы, веб, другие сервисы через их API и MCP-серверы, плагины, которые к этим сервисам подключают.", + "Подключи агента к календарю через MCP-сервер, и агент сам назначит встречу, а не объяснит тебе, как её назначить." + ], + "tools": [ + "Claude Code, Codex или агент командной строки другого вендора", + "Терминал с правом действовать", + "Git" + ] + }, + { + "n": 2, + "stage": "solo", + "title": "Постоянные инструкции", + "sub": "Файл, который агент читает каждый раз.", + "was": "Агент помнит, что я говорил в прошлый раз.", + "think": "Агент знает только то, что записано.", + "skill": "Ты решил завести глобальный CLAUDE.md и по одному на каждый проект.", + "text": [ + "Ты находишь CLAUDE.md, файл инструкций, который агент читает при каждом запуске, но почти его не трогаешь. Так что агент работает на том, что случайно нахватал из разговоров с тобой.", + "Позже ты узнаёшь, что их два. Глобальный говорит, кто этот агент всегда и везде, проектный говорит, что нужно именно этому проекту." + ], + "tools": ["CLAUDE.md для Claude Code", "AGENTS.md для Codex"] + }, + { + "n": 3, + "stage": "solo", + "title": "Гниение контекста", + "sub": "Ты всё дописываешь в файл, а модель тупеет.", + "was": "Чем больше я даю, тем умнее становится агент.", + "think": "После какого-то предела от контекста агент только тупеет.", + "skill": "Ты решил держать CLAUDE.md короче 200 строк, а остальное вынести в документы, которые агент открывает по мере надобности.", + "text": [ + "Контекстное окно вмещает всё, что модель видит разом. Чего в окне нет, для модели не существует.", + "Ты дописываешь CLAUDE.md, задача тянет всё больше файлов, и в какой-то момент окно забито так, что модель тупеет и начинает выдумывать. Это и есть гниение контекста.", + "Насколько честен контекст, настолько честен и агент, так что ты учишься держать файл коротким: глобальный и проектный вместе примерно в 200 строк. Остальное уходит в документы, которые агент открывает только когда они нужны." + ] + }, + { + "n": 4, + "stage": "solo", + "title": "Вопрос", + "sub": "Одна цель на запрос, и всё, что этой цели нужно.", + "was": "Плохой ответ значит слабая модель.", + "think": "Плохой ответ обычно значит плохой вопрос.", + "skill": "Ты решил класть в каждый запрос одну цель и говорить, что должно быть в ответе.", + "text": [ + "Ты перестаёшь кидать в окошко пожелание и начинаешь ставить вопрос. Говоришь, что именно хочешь и что должно быть в ответе, а прочитав ответ, выбираешь следующий вопрос.", + "LLM работает со смыслом так же, как человек, поэтому от постановки вопроса зависит, какой ответ ты получишь. Модель ещё и идёт за твоей постановкой, так что на наводящий вопрос ты получишь согласие. Кто плохо говорит с LLM, уходит в иллюзии." + ] + }, + { + "n": 5, + "stage": "solo", + "title": "Эвалы", + "sub": "Ты перестаёшь гадать и начинаешь измерять.", + "was": "Выглядит правильно, значит, работает.", + "think": "Ничего не работает, пока тест не сказал, что работает.", + "skill": "Ты решил писать тесты и подключить агента-судью, который проверяет первого.", + "text": [ + "Ты пишешь эвалы, маленькие тесты, которые доказывают, что результат всё ещё работает. До старта прогона ты записываешь, что значит «готово».", + "Почти все этот шаг пропускают. А именно здесь ты начинаешь измерять вместо того, чтобы надеяться.", + "Здесь же ты подключаешь агента-судью, вторую модель, чья работа проверять первую. Это может быть твой QA-агент, у меня это The Order. Смысл в том, чтобы проверку автоматизировать." + ], + "tools": [ + "Тестовые раннеры, которыми ты уже пользуешься", + "Агент-судья", + "Скрипты, которые читают результат и говорят «прошёл» или «не прошёл»" + ] + }, + { + "n": 6, + "stage": "solo", + "title": "Бенчмарки", + "sub": "Та же задача на другой модели у другого вендора.", + "was": "Я нашёл свою модель.", + "think": "Лучшая модель под мою задачу меняется каждые несколько месяцев.", + "skill": "Ты решил написать скрипт, который шлёт одну и ту же задачу нескольким вендорам и оценивает каждого твоими эвалами.", + "text": [ + "Ты гоняешь свою задачу на моделях разных вендоров и сравниваешь скорость, цену и то, как часто проходит эвал. Какая модель в чём хороша, меняется каждые несколько месяцев, так что ты за этим следишь.", + "Не та модель может дорого обойтись, а изнутри одного чата ты этого никогда не увидишь." + ], + "tools": [ + "Подписка или API-ключ у каждого вендора", + "Один скрипт, который шлёт один и тот же промпт каждому", + "Твои эвалы с LVL 5 как оценка" + ] + }, + { + "n": 7, + "stage": "orch", + "title": "Декомпозиция", + "sub": "Одно контекстное окно кончается, и ты делишь работу на несколько прогонов.", + "was": "Один умный агент сделает всё целиком.", + "think": "Большая задача складывается из цепочки маленьких прогонов.", + "skill": "Ты решил запускать субагентов и передавать файлы от одного прогона к следующему.", + "text": [ + "Задача больше не влезает в одно контекстное окно, так что ты режешь её на подзадачи. У каждой свой прогон со свежим окном, и результат одного агента становится входом для следующего.", + "Цену ты чувствуешь сразу: передачи между агентами и уход в сторону от цели. Со временем ты учишься делить работу, только когда она правда не влезает в одно окно." + ], + "tools": [ + "Субагенты, то есть агенты, которых твой агент запускает на один кусок работы", + "Очереди задач", + "Обычные файлы, которые передаются от прогона к прогону" + ] + }, + { + "n": 8, + "stage": "orch", + "title": "Маршрутизация контекста", + "sub": "Кому какой кусок контекста достаётся, а кто его не видит никогда.", + "was": "Дай каждому агенту всё, на всякий случай.", + "think": "Что прогону не показали, то прогон и не собьёт.", + "skill": "Ты решил вести на каждый шаг список: что прогон получает и куда может писать.", + "text": [ + "Каждый прогон получает то, что нужно этому шагу: описание задачи, предыдущий результат, файлы, которые надо прочитать. И ничего, что могло бы сбить с толку.", + "До LVL 11 это решаешь ты сам на каждом шаге, так что оркестратор здесь ты. На LVL 12 эту роль забирает программа." + ], + "tools": [ + "Список на каждый шаг: что прогон получает", + "Входы, которые можно читать, но нельзя менять", + "Правило, что прогону можно писать и куда" + ] + }, + { + "n": 9, + "stage": "orch", + "title": "Эвалы на каждом шаге", + "sub": "И один на всю цепочку.", + "was": "Я проверяю результат в конце.", + "think": "Каждому шагу нужна своя проверка, иначе первая же ошибка расползётся по цепочке.", + "skill": "Ты решил ставить эвал между прогонами, чтобы проваленный шаг ничего не передавал дальше.", + "text": [ + "Ошибка в первом прогоне становится фактом для всех следующих прогонов. Поэтому у каждого шага свой эвал, и у всей цепочки тоже.", + "Оркестрация без эвалов на каждом шаге только создаёт иллюзию промышленного масштаба. Шаг, который не прошёл свой эвал, ничего дальше не передаёт." + ], + "tools": ["Те же эвалы с LVL 5, теперь между прогонами"] + }, + { + "n": 10, + "stage": "orch", + "title": "Код вместо модели", + "sub": "Ты сносишь половину того, что построил.", + "was": "Чем больше ИИ, тем лучше система.", + "think": "Там, где шаг предсказуем, код справляется лучше модели.", + "skill": "Ты решил превратить рутинные шаги в обычные функции и хуки, которые проверяют результат.", + "text": [ + "В какой-то момент ты сносишь половину построенного, чтобы система стала легче. Каждый шаг, который стал рутиной, превращается в код.", + "Код дешевле, каждый раз даёт один и тот же результат, и тестируешь ты его как любую другую функцию. Модель остаётся только там, где есть настоящая неопределённость.", + "Лёгким ядром проще управлять и, что важнее, ему проще доверять." + ], + "tools": [ + "Обычные функции", + "Планировщики", + "Скрипты валидации", + "Хуки, которые проверяют результат агента, прежде чем его принять" + ] + }, + { + "n": 11, + "stage": "orch", + "title": "Память", + "sub": "То, что переживает сессию и общее для всех прогонов.", + "was": "Каждая сессия начинается с чистого листа.", + "think": "То, чему система научилась, должно пережить сессию, если прошло эвал.", + "skill": "Ты решил дать каждому агенту файл памяти, а всем прогонам общее хранилище с поиском.", + "text": [ + "Ты находишь MEMORY.md, память, которая живёт дольше одного контекстного окна и общая для всех агентов. Туда уходят решения, подводные камни, что ты пробовал и что не сработало.", + "В память попадает только то, что прошло эвал, а провал записывается как провал. Работа накапливается, и умная в моменте система становится умной надолго." + ], + "tools": [ + "Файл памяти у каждого агента", + "Общее хранилище, в котором ищет каждый прогон", + "Полнотекстовый поиск по прошлым сессиям" + ] + }, + { + "n": 12, + "stage": "orch", + "title": "Агенты вызывают друг друга", + "sub": "Оркестратор выбирает, кто проснётся, передаёт работу и собирает результат.", + "was": "Каждый шаг мне приходится вести самому.", + "think": "Я принимаю решения, а передачи берут на себя агенты.", + "skill": "Ты решил написать протокол онбординга и общий для агентов формат сообщений.", + "text": [ + "Ты пишешь протокол онбординга, из которого новый агент узнаёт свою роль и как войти в систему. Теперь агенты вызывают друг друга вместо тебя.", + "Один пишет черновик, судья возражает, первый правит, и всё это сходится на твоих эвалах. Ты вмешиваешься только там, где нужно настоящее решение." + ], + "tools": [ + "Список агентов и как до каждого достучаться", + "Общий для всех формат сообщений", + "Оркестратор с лимитом на то, сколько раз задача может ходить между агентами" + ] + }, + { + "n": 13, + "stage": "orch", + "title": "Автономия", + "sub": "Твои агенты работают сами.", + "was": "Моя работа в том, чтобы делать задачу.", + "think": "Моя работа в том, чтобы собирать задачи, которые система выполнит без меня.", + "skill": "Ты решил настроить расписания и дозорных, чтобы система работала ночью и оставляла логи.", + "text": [ + "Твой день начинается с того, что ты просыпаешься и смотришь, что агенты построили за ночь. Проверяешь, что вы с агентами усвоили подводные камни. Потом собираешь следующую пачку задач.", + "Большая часть твоего времени уходит на то, чтобы собрать задачу под твою систему, такую, чтобы система выполнила её сама.", + "Без тебя ни один агент не тратит деньги, ничего не удаляет и ничего не публикует." + ], + "tools": [ + "Расписания", + "Дозорные, которые поднимают тревогу", + "Логи, чтобы каждый механизм оставлял след, который ты читаешь утром" + ] + }, + { + "n": 14, + "stage": "orch", + "title": "Отделы", + "sub": "Команды агентов, у каждой своя дисциплина.", + "was": "Я управляю набором агентов.", + "think": "Я управляю чем-то похожим на компанию.", + "skill": "Ты решил собрать агентов в отделы, каждому дать своего судью, а агенту-продакту отдать целые проекты, которые идут через эти отделы.", + "text": [ + "Это пока чистая спекуляция, я сам до этого не дошёл.", + "Ты строишь отделы агентов: маркетинг, развитие бизнеса, разработку с девелоперами и QA. У каждого свой судья, каждый следует протоколу, и все знают друг о друге.", + "Дальше ты подключаешь агента-продакт-менеджера, который за ночь строит целые проекты силами твоих отделов. Такого ещё никто не выпустил." + ] + }, + { + "n": 15, + "stage": "orch", + "title": "???", + "sub": "Когда-нибудь." + } + ] + } +} diff --git a/src/lib/aiAtlas/readings.tsx b/src/lib/aiAtlas/readings.tsx index 5fedead6..115b66fa 100644 --- a/src/lib/aiAtlas/readings.tsx +++ b/src/lib/aiAtlas/readings.tsx @@ -29,7 +29,12 @@ export type Readings = { article: { lead: Run[]; paras: Run[][]; - board: { steps: string[]; tips: Record }; + board: { + steps: string[]; + tips: Record; + /* English tile label -> label drawn, for a translated board. */ + labels?: Record; + }; }; ladder: { stages: { id: string; label: string }[]; @@ -173,6 +178,8 @@ function drawBoard( }; const STEPS = board.steps; const TIPS = board.tips; + /* Tiles are keyed by their English label; a translation draws its own. */ + const say = (text: string) => board.labels?.[text] || text; const LAST = STEPS.length; const TH = 44, Y = 236, @@ -243,23 +250,28 @@ function drawBoard( const tipFor = (label: string, g: SVGElement) => { if (g.classList.contains('src')) { return g.classList.contains('is-off') - ? 'The selected run never sees this. That is the point of routing.' - : 'A piece of context. Routing decides which runs get it and which never see it.'; + ? TIPS['@srcOff'] || + 'The selected run never sees this. That is the point of routing.' + : TIPS['@srcOn'] || + 'A piece of context. Routing decides which runs get it and which never see it.'; } if (g.classList.contains('chip')) { return /^Output of/.test(label) - ? label.replace('Output of ', 'The result of Run ') + - ', routed in as this run’s input.' - : 'Routed into this window. The run sees this and nothing else.'; + ? TIPS['@chipOut' + label.slice(-1)] || + label.replace('Output of ', 'The result of Run ') + + ', routed in as this run’s input.' + : TIPS['@chipIn'] || + 'Routed into this window. The run sees this and nothing else.'; } if (label === 'Output') return current === 1 - ? 'The window is too full, so the result gets worse. That is context rot.' - : 'The finished result of the whole chain.'; + ? TIPS['@outRot'] || + 'The window is too full, so the result gets worse. That is context rot.' + : TIPS['@outFinal'] || 'The finished result of the whole chain.'; if (label === 'You') return g.dataset.bottom - ? 'You step in only for the real decisions.' - : 'You decide which run goes next and what it sees.'; + ? TIPS['@youBottom'] || 'You step in only for the real decisions.' + : TIPS['@you'] || 'You decide which run goes next and what it sees.'; return TIPS[label] || ''; }; /* One-line tips on hover, focus or tap, ordered for this board on @@ -322,7 +334,7 @@ function drawBoard( }, g, ); - t.textContent = label; + t.textContent = say(label); withTip(g, label); return span(g, from, until); }; @@ -346,7 +358,7 @@ function drawBoard( until?: number, ) => { const t = span(el('text', { x, y, class: 'd-label' }, gBack), from, until); - t.textContent = text; + t.textContent = say(text); return t; }; const line = (x1: number, x2: number, from: number, until?: number) => diff --git a/src/lib/aiAtlas/ru.json b/src/lib/aiAtlas/ru.json new file mode 100644 index 00000000..fdafa15d --- /dev/null +++ b/src/lib/aiAtlas/ru.json @@ -0,0 +1,968 @@ +{ + "copy": { + "seoTitle": "AI Атлас: как один человек руководит командой ИИ-агентов", + "seoDescription": "Карта того, как я работаю с командой ИИ-агентов: одна задача от моего сообщения до результата. Рядом Тезис и Лестница из 16 ступеней, чтобы этому научиться.", + "seoKeywords": "ИИ-агенты, мультиагентная система, оркестрация ИИ, Claude Code, Codex, вайбкодинг, Wolf’s Terminal", + "ogImageAlt": "AI Атлас: как один человек руководит командой ИИ-агентов", + "seoTitleThesis": "Тезис: преимущество у того, кто решает сложные задачи с ИИ", + "seoDescriptionThesis": "Вайбкодинг сократил разрыв между инженерами и остальными. Что дальше: умение спрашивать, контекстное окно, оркестрация, эвалы и память.", + "seoKeywordsThesis": "вайбкодинг, ИИ-агенты, контекстное окно, маршрутизация контекста, оркестрация, эвалы, LLM", + "ogImageAltThesis": "Сегодня ты стоишь ровно столько, насколько сложные задачи можешь решить.", + "seoTitleLadder": "Лестница ИИ-агентов: 16 ступеней от чата до системы агентов", + "seoDescriptionLadder": "Где ты сейчас в работе с ИИ-агентами и чему учиться дальше: 16 ступеней от чата до Claude Code, эвалов, оркестрации и памяти, на каждой навык для следующего шага.", + "seoKeywordsLadder": "как научиться работать с ИИ-агентами, вайбкодинг, Claude Code, CLAUDE.md, гниение контекста, эвалы, бенчмарки, субагенты, оркестрация, память агентов", + "ogImageAltLadder": "Лестница: 16 ступеней от чата до системы агентов", + "loading": "Загрузка…", + "failedToLoad": "Не удалось загрузить данные: ", + "welcomeBanner": "АТЛАС МОЕЙ СИСТЕМЫ ОРКЕСТРАЦИИ", + "day": "ДЕНЬ", + "daySinceTail": "с тех пор, как мы начали это строить", + "apexFounderFallback": "основатель", + "redactedPlaceholder": "СКРЫТО", + "engLeadLabel": "Техлид", + "publicInternetLabel": "ОТКРЫТЫЙ ИНТЕРНЕТ", + "telegramLabel": "TELEGRAM", + "claudeMdLabel": "claude.md", + "canvasStats": { + "humans": "люди", + "agents": "ИИ-агенты", + "subAgents": "ИИ-субагенты", + "products": "продукты" + }, + "introDossierTitle": "АТЛАС", + "introDossierCjk": "地図帳", + "introStoryBody": "Вот как я управляю командой ИИ-агентов. Одна задача проходит путь от моего сообщения до результата, и каждая плитка на карте обозначает функцию, которую я построил на этом пути. Нажми на плитку, и я расскажу, что она делает и зачем мне нужна.", + "introStoryLink": "", + "introQuestionsBefore": "", + "introQuestionsLink": "", + "introQuestionsAfter": "", + "introDepthLabel": "глубина", + "introDepthValue": "Владение и жизнь одной задачи", + "introInhabitantsLabel": "основа", + "introPrincipleLabel": "доказательства", + "principles": [ + "Каждая карточка про функцию, которой я пользуюсь каждый день. Там, где я пишу, что что-то работает, это подтверждает журнал на моём сервере." + ], + "legendTitle": "Легенда", + "legendCjk": "凡例", + "legendHumanLabel": "человек", + "legendHumanDesc": "курс · последнее слово", + "legendAgentLabel": "ИИ-агент", + "legendAgentDesc": "владелец проекта", + "legendSubAgentLabel": "помощник", + "legendSubAgentDesc": "узкая задача", + "legendProductLabel": "механизм", + "legendProductDesc": "этап или его опора", + "legendSolidLabel": "сплошная", + "legendSolidDesc": "стрелки: ход задачи", + "legendFilledLabel": "заливка", + "legendFilledDesc": "поддерживает этап", + "legendArticleCta": "", + "legendArticleUrl": "", + "toggleEnvironment": "Среда", + "toggleSecurity": "Безопасность", + "doctrineTitle": "Доктрина", + "doctrineCjk": "守則", + "doctrineImageAlt": "Права и доказательства в Terminal", + "securityIntroTitle": "ПРАВА И ДОКАЗАТЕЛЬСТВА", + "securityIntroCjk": "守り", + "securityIntroDesc": "Это зоны доступа и ответственности на моём сервере. Кольца их группируют. Сертификат безопасности это не заменяет.", + "securityIntroRows": [ + { + "k": "разрешения", + "v": "Записанные права проектов" + }, + { + "k": "контроль", + "v": "Зависит от механизма" + }, + { + "k": "доказательства", + "v": "Записи прогонов и журналы" + } + ], + "securityLayers": [], + "securityWhyWeLikeIt": "Доказательства", + "securityCenterCore": "РАБОТА", + "securityCenterKanji": "守", + "statsHeading": "Принципы работы", + "statsCjk": "数", + "securityStats": [ + { + "v": "Зона", + "k": "Записанные владельцы" + }, + { + "v": "Доступ", + "k": "Свои условия у каждой зоны" + }, + { + "v": "Факты", + "k": "Записи прогонов" + }, + { + "v": "Приёмка", + "k": "Готовность подтверждаю я" + } + ], + "agentsHeading": "Кто чем владеет в системе", + "agentsCjk": "共棲", + "agentsSubtitle": "Операторы проектов действуют в пределах прав, которые я записал. Общая инфраструктура принадлежит The Order.", + "authorityAgentRole": "Общая инфраструктура", + "orderName": "The Order", + "orderCjk": "序", + "orderCreds": ["Границы доступа", "Общая инфраструктура"], + "securityAgents": [], + "agentsPunchline": "Правила разрешений говорят, что агенту можно делать. О технической изоляции они не говорят ничего.", + "patternsHeading": "Непрерывность и доказательства", + "patternsCjk": "型", + "patternsSubtitle": "Механизмы, которые сохраняют контекст и записывают, что произошло.", + "securityPatterns": [], + "footerEnd": "КОНЕЦ · WOLF’S TERMINAL", + "hankoSelfTitle": "здесь построено, здесь работает", + "hankoCoTitle": "у каждого проекта один владелец", + "modalCloseLabel": "Закрыть", + "modalHint": "Esc закрывает. Тема внутри открывается на месте.", + "readMoreHint": "нажми, чтобы читать дальше", + "brandTitle": "Wolf’s Terminal", + "apexLabel": "WOLF", + "apexSub": "курс", + "orderLabel": "The Order", + "resourceAgents": "Коллеги", + "resourceMemory": "Память", + "resourceTools": "Инструменты", + "resourceModels": "Модели", + "ringOrderLabel": "I · Владение", + "ringDevEnvLabel": "II · Ресурсы", + "ringProjectsLabel": "III · Жизнь задачи", + "ringTerritoriesLabel": "IV · Механизмы", + "ringOrderTitle": "Владение", + "ringDevEnvTitle": "Ресурсы", + "ringDevEnvDesc": "На что опирается каждый этап задачи: коллеги, память, инструменты, модели.", + "ringProjectsTitle": "Жизнь задачи", + "ringProjectsDesc": "Кольца показывают этапы одной задачи, по часовой стрелке от «Проекта» до «Итога».", + "ringTerritoriesTitle": "Механизмы", + "ringTerritoriesDesc": "Каждая тема открывает свою карточку.", + "metaLabel": "ДОКУМЕНТАЦИЯ TERMINAL", + "tabAtlas": "Атлас", + "tabArticle": "Тезис", + "tabLadder": "Лестница", + "legendKey": "Ключевой тезис", + "legendSee": "Продукт и бизнес", + "topicsPlaceholder": "Атлас", + "stagesAria": "От проекта до итога, по часовой стрелке", + "rowInside": "внутри", + "rowMechanism": "механизм", + "rowResource": "ресурс", + "rowStage": "этап", + "stageStart": "СТАРТ", + "stageEnd": "ИТОГ", + "topicsLabel": "Темы", + "doctrineCaption": "ПРАВА И ДОКАЗАТЕЛЬСТВА", + "stageLabels": [ + "Проект", + "Задача", + "Отправка", + "Подготовка", + "Работа", + "Итог" + ] + }, + "steps": { + "project-home": { + "title": "Проект", + "location": "Terminal.", + "text": "Я выбираю проект. Его правила, память и решения о сборке действуют на всё, что идёт дальше." + }, + "task": { + "title": "Я даю задачу", + "location": "Мой браузер", + "text": "Я отправляю задачу. Браузер передаёт сообщение в Terminal." + }, + "terminal": { + "title": "Terminal её доставляет", + "location": "Наш сервер", + "text": "Terminal кладёт задачу в сессию агента того проекта, который я выбрал." + }, + "prepare": { + "title": "Агент готовится", + "location": "Сессия агента.", + "text": "Новая сессия загружает свои правила, а уже открытая продолжает с тем, что помнит." + }, + "agent": { + "title": "Агент работает", + "location": "Наш сервер + провайдер модели", + "text": "Агент делает задачу, опираясь на свои правила и память. Инструменты работают на моём сервере, а модель, к которой агент обращается, у провайдера." + }, + "result": { + "title": "Я получаю результат", + "location": "Мой браузер", + "text": "Terminal стримит мне ответ агента. То, что построили инструменты, остаётся в проекте." + } + }, + "nodes": { + "wolf": { + "title": "Wolf", + "role": "Курс", + "detail": [ + "Я ставлю задачу и решаю, кто чем владеет. Агенты работают в пределах полномочий, которые я им уже дал; всё, что требует моего решения, ждёт его." + ], + "basis": "Мой устав владения и правила согласования." + }, + "access": { + "title": "Cloudflare", + "role": "Вход", + "detail": [ + "Cloudflare Tunnel публикует веб-приложения; ничто на сервере не слушает интернет напрямую. Access проверяет, кто ты, прежде чем пустить во внутреннее приложение. Публичные сайты отвечают всем." + ], + "basis": "Конфигурация туннеля и список приложений Access." + }, + "models": { + "title": "Провайдеры моделей", + "role": "Внешнее мышление", + "detail": [ + "Claude и Codex отправляют разговор своим провайдерам. Мышление происходит там; инструменты работают здесь или ходят на хосты, куда проекту разрешён доступ." + ], + "basis": "Отправка к провайдерам в Terminal и подключённые инструменты." + }, + "services": { + "title": "Подключённые сервисы", + "role": "Внешние инструменты", + "detail": [ + "Наружу агенты выходят через инструменты: репозиторий GitHub, headless-браузер для страниц, которые обычным запросом не прочитать, таблицу, поток аналитики. У каждого подключения свои границы доступа." + ], + "basis": "Интерфейсы подключённых инструментов." + }, + "telegram": { + "title": "Telegram", + "role": "Передача людям", + "detail": [ + "Telegram доставляет оповещения и мои голосовые задачи. Reception берёт задачу у человека, которому я дал полный доступ, и возвращает результат в его личный чат." + ], + "basis": "Каналы уведомлений и проверка доступа в Reception." + }, + "order": { + "title": "The Order", + "role": "Владелец сервера", + "detail": [ + "Мой серверный агент The Order владеет общей инфраструктурой и границами доступа, а перезапуски и пересборки выполняет сам, а не возвращает мне." + ], + "basis": "Устав владения, включая делегированные исключения." + }, + "secrets": { + "title": "Секреты", + "role": "Граница учётных данных", + "detail": [ + "The Order хранит общие учётки. Оператор проекта получает только те, что названы в допуске, и никаких других." + ], + "basis": "Правила владения учётными данными." + }, + "apex": { + "title": "Пульт сервера", + "role": "Управление инфраструктурой", + "detail": [ + "Пульт сервера показывает каждый Docker-сервис и выполняет по SSH операции, которые я разрешил. Изменения общей инфраструктуры идут через The Order." + ], + "basis": "Docker-прокси только на чтение и шлюз с forced-command." + }, + "terminal": { + "title": "Terminal", + "role": "Оркестрация задач", + "detail": [ + "Задача живёт в Terminal: здесь открывается, здесь выполняется и сюда возвращается. Ветки и выделения остаются такими, какими я их оставил." + ], + "basis": "Отправка задач в Terminal и жизнь задачи на этой карте." + }, + "agents": { + "title": "Агенты проектов", + "role": "Владение работой", + "detail": [ + "Terminal держит по одной сессии на проект. Реестр показывает, кто чем владеет; SEND TO идёт к владельцу, а не делает работу за владельца." + ], + "basis": "Реестр владельцев проектов и канал для совместной работы." + }, + "projects": { + "title": "Сервисы проектов", + "role": "Готовые продукты", + "detail": [ + "На этом хосте за loopback-портами и Tunnel работают десятки сервисов проектов. Часть привязана к tailnet; часть живёт на своих серверах." + ], + "basis": "Привязки контейнеров на хосте и записанные допуски к удалённым хостам." + }, + "memory": { + "title": "Память", + "role": "Контекст между сессиями", + "detail": [ + "Заметки проекта и MemPalace переносят прошлые решения в дальнейшую работу. Они отделены от живого разговора и истории сессии." + ], + "basis": "Память проекта, записи сессий и интерфейс MemPalace." + }, + "tools": { + "title": "Инструменты и среда", + "role": "Исполнение", + "detail": [ + "Инструменты читают и меняют работу проекта в пределах того, что проекту разрешено. Что инструмент может и что ему можно: два разных вопроса." + ], + "basis": "Правила владения проектами." + }, + "deploy": { + "title": "Операторы проектов", + "role": "Ответственность за релизы", + "detail": [ + "Операторы проектов деплоят, перезапускают и читают логи в пределах допусков, которые я записал, в том числе на удалённых хостах. Общая инфраструктура остаётся за The Order. Плановые задачи крутятся в cron на хосте или внутри сервисов." + ], + "basis": "Делегированные допуски операторов." + }, + "records": { + "title": "Логи прогонов", + "role": "Доказательства", + "detail": [ + "История сессий и журналы механизмов хранят то, что произошло. Финальный ответ агента остаётся заявлением, а доказательством служит журнал." + ], + "basis": "Правило логирования: каждый механизм оставляет след." + }, + "watchers": { + "title": "Дозорные", + "role": "После релиза", + "detail": [ + "Дозорные проверяют подключённые проекты и сообщают о сбоях. Trivy сканирует образы и хосты на уязвимости. Сообщают; чинит человек." + ], + "basis": "Раннер дозорных и его heartbeat." + }, + "recovery": { + "title": "Резерв", + "role": "Постоянное состояние", + "detail": [ + "Restic каждую ночь копирует выбранное состояние наружу, в зашифрованном виде. В охвате есть исключения. Снимок засчитывается, когда он существует и восстановление из него проверено." + ], + "basis": "Настройка бэкапов и лог бэкапов." + }, + "remote": { + "title": "Другие хосты", + "role": "Площадки проектов", + "detail": [ + "Часть проектов работает на машинах вне этого сервера. У каждой есть обёртка, через которую агент там деплоит, перезапускает и читает логи. На продакшен нужен отдельный допуск." + ], + "basis": "Делегированные обёртки для удалённых хостов." + }, + "backups": { + "title": "Хранилище копий", + "role": "Вне сервера", + "detail": [ + "Restic копирует выбранное состояние на другую площадку; зеркало GitHub копирует подходящие репозитории. У обоих есть исключения. Что восстановление работает, доказывают успешная копия и проверенный рестор." + ], + "basis": "Настройка зеркала и его лог прогонов; список снимков Restic." + } + }, + "entries": { + "message": { + "title": "Запрос", + "text": "Мой браузер отправляет задачу в Terminal.", + "detail": ["Я говорю агенту, что нужно изменить и какой результат жду."] + }, + "attachments": { + "title": "Вложения", + "text": "Файлы идут вместе с задачей.", + "detail": [ + "Скриншоты и файлы идут вместе с сообщением. Агент работает с тем, что я показал, без моего пересказа." + ] + }, + "reply-mode": { + "title": "DEF / SIM", + "text": "DEF отправляет задачу как есть. SIM добавляет инструкцию ответить коротко и по-русски, не ограничивая ни работу, ни инструменты.", + "detail": [ + "DEF: обычный ответ. SIM просит короткий ответ на русском, а работа делается целиком. Я читаю меньше, работа та же." + ] + }, + "recipient": { + "title": "Адресат и модель", + "text": "Сообщение уходит выбранному агенту вместе с выбранной моделью.", + "detail": [ + "Я выбираю агента, модель и глубину мышления. Каждая линия помнит свой выбор, так что переключиться на другой движок и обратно мне ничего не стоит." + ] + }, + "actions": { + "title": "Сохранить код и решения", + "text": "Две вещи я прошу намеренно: сохранить решения и запушить код." + }, + "project": { + "title": "Движок", + "text": "Движок определяет, кто выполняет задачу: Claude, OpenAI или Grok.", + "detail": [ + "Terminal запускает агента на выбранном мной движке (Claude, OpenAI или Grok) внутри открытого проекта." + ] + }, + "queue": { + "title": "Очередь", + "text": "Как работа в очереди ждёт и запускается." + }, + "sendto": { + "title": "Позвать агента", + "text": "Агент называет коллегу в одной строке SEND TO. Terminal доставляет просьбу в собственную сессию коллеги; два разговора остаются раздельными." + }, + "global": { + "title": "Общий CLAUDE.md", + "text": "Общие правила: как агенты работают со мной и что им можно менять.", + "detail": [ + "Один файл правил над всеми проектами. В нём сказано, как агенты проверяют утверждения, разговаривают со мной, уважают границы владения и доводят работу до конца. Claude загружает его сам; Codex получает указание прочитать его первым." + ] + }, + "local": { + "title": "CLAUDE.md проекта", + "text": "Собственный файл правил проекта в корне папки. В Terminal AGENTS.md указывает на тот же файл для линии OpenAI.", + "detail": [ + "У каждой папки проекта свои правила в корне: как здесь работать, дизайн-паспорт, где кончается владение. Агент читает их прежде, чем что-то трогать." + ] + }, + "memorymd": { + "title": "MEMORY.md", + "text": "Индекс памяти проекта со ссылками на сохранённые заметки. Ведёт агента к прежним решениям и отделён от полной истории разговора и от MemPalace.", + "detail": [ + "Индекс того, что проект уже узнал. Claude загружает его на старте сессии; Codex получает указатель на него в первом ходе." + ] + }, + "palace": { + "title": "MemPalace", + "text": "Хранилище сохранённых решений с поиском, разложенное по проектам. Агенты находят нужные записи и сохраняют новые через его инструменты. Бриф на старте сессии может подтянуть свежие записи.", + "detail": [ + "В MemPalace решения уходят, чтобы выжить. Агент записывает, о чём мы договорились, а новая сессия ищет там, прежде чем снова спрашивать меня." + ] + }, + "skills": { + "title": "Навыки", + "text": "Инструкции для одного вида работы. Агент читает подходящий навык и идёт по его порядку. Навык может указывать, какими инструментами пользоваться.", + "detail": [ + "Навык: записанный метод для одного вида работы. Агент открывает подходящий и следует ему без импровизации." + ] + }, + "tools": { + "title": "Инструменты", + "text": "Инструменты делают действия: читают или правят файл, запускают команду, ходят во внешний сервис. Результат возвращается агенту, и агент решает, что дальше.", + "detail": [ + "Через инструменты агент действует: правит файл, запускает команду, обращается к сервису. Результат возвращается, и агент решает, каким будет следующий шаг." + ] + }, + "history": { + "title": "История разговоров", + "text": "Сессия хранит запись разговора. Возобновление сессии и поиск по отобранной памяти используют разные хранилища.", + "detail": [ + "Сессия хранит весь разговор, так что я могу к нему вернуться. Архив держит каждую сессию дольше, чем сам CLI, а полнотекстовый индекс ищет по всем сразу." + ] + }, + "decisions": { + "title": "Сохранённые решения", + "text": "Решение можно сохранить для будущей работы в памяти проекта или в MemPalace. Сохранить должен агент; один финальный ответ этого не гарантирует.", + "detail": [ + "Решение, которое живёт только в чате, пропадает. Агент записывает его в память, иначе следующая сессия начнёт с нуля." + ] + }, + "action-prep": { + "title": "PREP", + "text": "Сохранить и проверить решения.", + "detail": [ + "PREP делается последним перед уходом: сохранить решения, потом перечитать их и доказать, что они записались." + ] + }, + "action-push": { + "title": "PUSH", + "text": "Закоммитить и запушить изменения.", + "detail": [ + "PUSH коммитит изменения в исходниках и отправляет их в репозиторий проекта. Записью считается код. Чат записью не считается." + ] + }, + "timing-order": { + "title": "Порядок запуска", + "text": "Задачи из очереди идут одна за другой.", + "detail": [ + "Задачи из очереди доходят до агента одна за другой, в порядке, который задал я." + ] + }, + "timing-availability": { + "title": "Свободен ли агент", + "text": "Ждёт, пока слот сможет принять работу.", + "detail": [ + "Следующая задача ждёт, пока её агент освободится. Сервер проверяет это перед отправкой." + ] + }, + "timing-schedule": { + "title": "Отсрочка старта", + "text": "Первую задачу в очереди запускает время.", + "detail": [ + "Я задаю, когда работе из очереди можно начинаться. Первая задача стартует, когда время пришло и агент свободен." + ] + }, + "timing-server": { + "title": "Очередь на сервере", + "text": "Работает без открытого браузера.", + "detail": [ + "Очередь держит сервер. Я закрываю браузер, а работа всё равно идёт." + ] + }, + "ask-once": { + "title": "Спросить", + "text": "Одна просьба агента к коллеге.", + "detail": [ + "Агент называет коллегу в одной строке SEND TO. Terminal доставляет просьбу в собственную сессию коллеги, с контекстом того проекта, и приносит ответ обратно." + ] + }, + "auto": { + "title": "Авторежим", + "text": "Авторежим работает на сервере Terminal.", + "detail": [ + "В авторежиме пара агентов, которую я включил, передаёт работу друг другу через сервер, пока мой браузер закрыт. Останавливается на лимите пересылок или когда сообщение повторяется." + ] + }, + "onboarding": { + "title": "Онбординг агента", + "text": "Новая сессия начинается со знакомства с тем, как мы работаем, и с проектом, к которому относится.", + "detail": [ + "Codex получает указание прочитать общие правила, правила проекта, потом индекс памяти. Claude получает правила проекта; остальное CLI загружает сам. Файл фактов сообщает линию, модель и режим." + ] + }, + "directory": { + "title": "Чей проект", + "text": "Агенты могут найти коллег и увидеть, кто каким проектом владеет.", + "detail": [ + "Справочник говорит, кто владеет каждым проектом. На территорию коллеги заходят только с вопросом к владельцу; делать чужую работу нельзя." + ] + }, + "new-project": { + "title": "Новый проект", + "text": "Я даю проекту цель и место в системе.", + "detail": [ + "Terminal запускает мой проектный скрипт: он готовит рабочее пространство и стартовые правила. Открыл проект, и агент делает первый ход." + ] + }, + "new-agent": { + "title": "Новый агент", + "text": "Проект получает агента, когда я его открываю.", + "detail": [ + "Я открываю проект, и рождается агент, названный по имени папки. На первом ходу читает правила и перечисляет открытые задачи бэклога." + ] + }, + "keys": { + "title": "Композиты", + "text": "Я выбираю проверенные структуры, которые нужны проекту. Каждый ключ несёт спецификацию, по которой агент строит.", + "detail": [ + "Ключи: проверенные структуры, которые я встраиваю в любой проект, например дизайн-паспорт или дозорный здоровья. Они принадлежат проекту и переживают его агента.", + "Прежде чем ключ построят, агент задаёт вводные вопросы и записывает мои ответы. Они остаются с ключом, так что никто не спрашивает дважды.", + "Зажигание ключа идёт так: ASK, вводные вопросы, проверка контракта, исполнение, механическое доказательство, запись версии, LIT. Каждый шаг пишется в журнал. Считается только ключ в состоянии LIT.", + "Еженедельный обход проставляет версии ключей, пересобирает ленту и говорит мне, в каком проекте стоит устаревший ключ. Когда всё свежее, обход молчит." + ] + }, + "work-checks": { + "title": "Проверки", + "text": "Правилам нужны проверки прямо во время работы. Включённые шлюзы, в том числе двери, ловят нарушения и отправляют агента исправлять.", + "detail": [ + "Двери: хуки на линии Claude в фиксированных точках (отправка задачи, перед чтением, после каждой правки, конец хода). Сработала дверь, значит, исправь работу. У Codex их нет." + ] + }, + "monitoring": { + "title": "Мониторинг", + "text": "За проектом нужно следить и после того, как агент закончил. Подключённые дозорные проверяют, что проект работает и даёт заявленный результат.", + "detail": [ + "Дозорные проверяют подключённые проекты после запуска и сообщают о сбое в Telegram, называя владельца. Health Watcher проверяет, что проект работает и выдаёт то, что заявил. Чинить не чинит никогда." + ] + }, + "backlog": { + "title": "Бэклог", + "text": "Задачи проекта ждут здесь, пока их не возьмут в работу. Агент отмечает сданную задачу как готовую к проверке; закрываю её я.", + "detail": [ + "Задачи проекта ждут в бэклоге, пока их не возьмут. Агент отмечает сданную работу для проверки; закрываю её только я." + ] + }, + "background-work": { + "title": "Фоновая работа", + "text": "Процесс может работать и после конца хода агента. Terminal следит за ним и может принять новое сообщение; сам по себе фоновый процесс агента не перезапускает.", + "detail": [ + "Процесс может работать и после конца хода. Terminal следит за ним и принимает моё следующее сообщение; сам по себе фоновый процесс агента никогда не перезапускает." + ] + }, + "session-resume": { + "title": "Возобновление сессии", + "text": "Вернуться в открытую сессию с её контекстом. Когда разговор уже не держит прежние решения, их отдаёт сохранённая память проекта.", + "detail": [ + "Я возвращаюсь в сессию, и контекст в ней ещё есть. Когда разговор забыл, помнит сохранённая память." + ] + }, + "delivered-work": { + "title": "Сданная работа", + "text": "Агент сообщает, что изменил и что проверил. Готовая работа остаётся в проекте, и там я ею пользуюсь.", + "detail": [ + "Агент сообщает, что изменил и что проверил. Сама работа остаётся в проекте, там я ею и пользуюсь." + ] + }, + "review-result": { + "title": "Приёмка работы", + "text": "Я оцениваю сданную работу и прошу исправить. Задача в бэклоге закрывается только после моего подтверждения.", + "detail": [ + "Я оцениваю результат и возвращаю, если он неверный. Задача в бэклоге закрывается по моему подтверждению, никогда по отчёту агента.", + "LOOP: путь оценки. Агент читает данные проекта и предлагает трёх кандидатов с метрикой, базовым уровнем, приростом, стоимостью и риском. Я выбираю одного." + ] + }, + "engine-switch": { + "title": "Смена движка", + "text": "Любая плитка запускает Claude, Codex или Grok; бриф передачи переносит работу.", + "detail": [ + "Любая плитка запускает Claude, Codex или Grok. Переключение посреди хода отклоняется: ход сначала надо закончить или остановить. Бриф, собранный из состояния сервера, переносит работу; заново набирать ничего не нужно." + ] + }, + "tracks": { + "title": "Треки", + "text": "Какая подписка оплачивает следующий ход плитки.", + "detail": [ + "Три трека на линию, по подписке на каждый, под моим псевдонимом. Плитка списывает со своего трека начиная со следующего хода. Если у трека нет ключа или он выключен, переход на него отклоняется." + ] + }, + "steering": { + "title": "Сообщение во время хода", + "text": "Сообщение посреди хода направляет ход Codex; ход Claude получает его напрямую.", + "detail": [ + "Codex: сообщение посреди хода направляет текущий ход. Claude: сообщение входит прямо в открытый ход. Terminal никогда не откладывает его на потом." + ] + }, + "session-end": { + "title": "Конец сессии", + "text": "Kill или clear завершают сессию; очередь переживает clear.", + "detail": [ + "Kill или clear завершают сессию. Очередь переживает clear; при kill она уходит в корзину на 14 дней. Плановое завершение оставляет строку-эпитафию." + ] + }, + "auto-capture": { + "title": "Автосохранение памяти", + "text": "Хуки Claude сохраняют в MemPalace при остановке сессии и перед сжатием контекста.", + "detail": [ + "На линии Claude хуки сохраняют память при остановке сессии и перед сжатием контекста. Codex сохраняет через свои инструменты памяти. PREP сохраняет намеренно и перечитывает записанное." + ] + }, + "notify": { + "title": "Уведомление мне", + "text": "Итоги Arena, флаги Reception и строки, пересланные человеку, приходят мне в Telegram.", + "detail": [ + "Telegram приносит мне итоги Arena и пересланные сообщения. Reception возвращает результат задачи в тот личный чат, откуда её прислали. Для пересылки из браузера всё ещё нужна открытая вкладка.", + "Reception показывает исполнение и доставку как два отдельных факта. Конец исполнения делает результат доступным; доставку подтверждает только запись курьера. Принимать результат или нет, решаю я." + ] + }, + "codemap": { + "title": "Карта кода", + "text": "Индекс символов, доступный через инструменты; двери Claude заворачивают в него чтение и поиск.", + "detail": [ + "CodeGraph индексирует каждый символ проекта. На линии Claude двери заворачивают в него чтение целых файлов и поиск. Codex ходит в индекс напрямую." + ] + }, + "discipline": { + "title": "Отзыв о дисциплине", + "text": "Аудиты прошлых сессий пишут уведомления, которые агент читает на следующем старте.", + "detail": [ + "Аудиты прошлых сессий пишут уведомление, которое агент читает на следующем старте: оценку дисциплины, уроки от коллег, мои решения и мои ручные правки работы агента." + ] + }, + "phases": { + "title": "Замеры ходов", + "text": "Каждый завершённый ход записывает в журнал, сколько команда ждала провайдера, первого слова потока и финального события; раз в пять минут строка фиксирует зависания сервера.", + "detail": [ + "Terminal пишет одну строку на каждый завершённый ход: миллисекунды до провайдера, до первого слова, до конца, по линии и слоту. Раз в пять минут строка сообщает о зависаниях сервера. Текста промптов там нет." + ] + }, + "human-collab": { + "title": "Работа с людьми", + "text": "Агенты обращаются к людям из списка так же, как друг к другу; строка человеку уходит в Telegram, а человек с доступом может так же вернуть задачу назад." + }, + "cross-review": { + "title": "Кросс-ревью", + "text": "Второй взгляд от модели другого семейства: GPT на треке Codex, Grok как запасной, никогда не другой Claude. Ревьюер получает задачу моими словами и снимок проекта.", + "detail": [ + "Слово «готово» никогда не остаётся за самим агентом. Ревьюер без контекста автора читает задачу моими словами и снимок проекта и отвечает.", + "Ревьюер работает на модели другого провайдера: GPT на треке Codex, Grok, если GPT отказал, и никогда не другой Claude. Рабочее дерево ревьюер трогать не может; каждая попытка пишется в журнал." + ] + }, + "council": { + "title": "Совет", + "text": "Я задаю один вопрос сразу нескольким моделям.", + "detail": [ + "Я задаю один вопрос сразу нескольким моделям. За каждой стоит тот же агент с тем же проектом и памятью: каждая читает наш разговор и отвечает в свою очередь, а последняя выносит вердикт.", + "Агент получает вердикт вместе с моим следующим сообщением." + ] + } + }, + "cards": { + "stage-project-home": [ + "Любая задача начинается с того, что я выбираю проект. Проект у меня устроен как папка со своими правилами, своей памятью и одним агентом, который ею владеет. Поэтому на всё, что я спрошу дальше, отвечает тот, кто уже знает эту кодовую базу и помнит, что в ней ломалось раньше.", + "В проекте уже лежит и то, что я решил до задачи: ключи, которые я включил (они задают, как проект собран), и бэклог, где ждут мои идеи. В самой задаче я ничего из этого не объясняю. Оно уже там." + ], + "stage-task": [ + "Задачу агенту я ставлю так же, как поставил бы коллеге: что должно измениться и что я жду в конце. Пишу, говорю голосом или шлю из Telegram, со скриншотом, когда показать проще, чем описать.", + "Перед отправкой выбираю, кому задача уйдёт, какая модель будет думать и насколько глубоко, и нужен ли мне полный ответ или пара строк. Одна задача или пятнадцать в очереди, сервер принимает их одинаково и выполняет, пока мой браузер закрыт." + ], + "stage-terminal": [ + "Terminal работает диспетчером, я построил его сам. Берёт моё сообщение и кладёт в сессию выбранного проекта, на выбранном движке, на той подписке, куда я этот проект посадил. С моего сервера уходит только вызов модели.", + "Если у трека кончился лимит, работа переезжает на другой вместе с контекстом. А работающий агент не закрытая дверь: я пишу посреди хода, и агент поправляется там, где сейчас стоит." + ], + "stage-prepare": [ + "Прежде чем что-то тронуть, агент загружает закон. Глобальные правила говорят, как со мной разговаривать, где кончаются полномочия и что считается сделанным. Правила проекта говорят, чего требует эта кодовая база и что здесь ломалось раньше. Потом агент читает решения, которые мы уже приняли, и уведомление о своём вчерашнем дне.", + "Так что новая сессия стартует, уже зная, о чём мы договорились, и не спрашивает меня дважды." + ], + "stage-agent": [ + "Агент читает, правит, запускает команды и спрашивает коллег, и всё это на моём сервере. У провайдера происходит только мышление. За каждым шагом я не слежу. Следят двери: маленькие программы в фиксированных точках хода, которые возвращают работу, если она сбилась, и флага, чтобы их выключить, нет.", + "Того, что лежит за пределами своего проекта, агент не трогает. Спрашивает владельца по имени, агента или человека, и ответ приходит обратно в ту же сессию." + ], + "stage-result": [ + "Ответ стримится обратно в мой браузер; код и деплои остаются в проекте. Отчёт я читаю, сверяя с доказательствами, и только я могу назвать задачу сделанной.", + "Задача не кончается на том, что я её прочитал. Разговор сохраняется, и по нему можно искать, решения записываются туда, где их найдёт следующая сессия, а то, что я отправил назад, за ночь разбирается и к утру становится правилом. Это и есть стрелка от «Итога» обратно к «Проекту»." + ], + "message": [ + "В Terminal задача попадает тремя путями. Я её печатаю, и прикладываю скриншоты и файлы, когда показать проще, чем описать. Я её наговариваю голосом прямо в Terminal. Или шлю из Telegram, где до моих агентов один чат, а голос расшифровывается по прибытии.", + "У канала Telegram свои правила, и они держатся, даже если кто-то клонирует мой голос. Голос, похожий на мой, сам по себе никаких полномочий не даёт.", + "Каждое сообщение уходит на мой сервер, так что я не привязан к одной машине. Телефон, ноутбук, чужой браузер: работа всё равно лежит в одном месте. У меня это работало задолго до того, как Anthropic и OpenAI выпустили что-то похожее." + ], + "attachments": [ + "Вместо описания я прикладываю скриншот или файл. Агент работает с тем, что я показал, а не с моим пересказом, и круг «нет, другая кнопка» отпадает." + ], + "reply-mode": [ + "SIM переключает ответ, а не работу. Когда SIM включён, Terminal велит агенту отвечать мне коротко и по-русски, а задачу делать целиком, ровно как без него. Я читаю несколько строк вместо страницы.", + "DEF включает обычный режим: задача уходит так, как я её набрал." + ], + "recipient": [ + "Перед отправкой я выбираю, кому достанется задача, какая модель её выполнит и насколько глубоко думать. Каждый движок помнит мой выбор отдельно, так что переключиться с Claude на Codex и обратно мне ничего не стоит." + ], + "actions": [ + "Две вещи в конце работы я прошу отдельно и намеренно: сохранить решения и запушить код. Ни то ни другое я чату не доверяю." + ], + "action-prep": [ + "PREP делает последний шаг перед тем, как агент уйдёт: сохранить решения сессии, потом перечитать их и доказать, что они записались. Я его запускаю, потому что память агента о сессии кончается вместе с сессией." + ], + "action-push": [ + "PUSH коммитит изменения в исходниках и отправляет их в репозиторий проекта. Записью считается код, чат ею не считается. Пока не запушено, ничего из того, что я одобрил, не существует." + ], + "project": [ + "Каждая плитка проекта работает на Claude или на Codex от OpenAI. Движок я выбираю на каждый проект отдельно, и Terminal запускает агента в папке этого проекта на этом движке. Третий у меня Grok. Модель xAI работает на тех же плитках, под одним логином, и каждый ход Grok Terminal хранит в отдельном архиве, так что ничего из сделанного на этом канале не теряется." + ], + "engine-switch": [ + "Terminal гоняет и Claude, и Codex, и под каждым у меня всегда активны от трёх до пяти треков. Один трек равен одной подписке. В обычный день наготове стоят примерно пять подписок Claude и пять подписок Codex.", + "Переход между ними мягкий. Работа переезжает на другой трек вместе с контекстом, так что посреди задачи ничего не ломается. То же самое, когда я перевожу проект с Claude на Codex: агент сначала заканчивает или останавливается, потом бриф, собранный из состояния сервера, переносит работу, и заново набирать ничего не надо.", + "Этот диспетчер сам по себе отдельный движок. Крутит мой Terminal и ещё работает внутри продуктов, которые я строю, так что у тех проектов такое же переключение при сбоях, как у меня." + ], + "tracks": [ + "Один трек равен одной подписке. Плитка списывает с того трека, куда я её посадил, начиная со следующего хода, а трек без ключа или выключенный мной работу не берёт.", + "Так я выбираю, какая подписка платит за какой проект, и останавливаю одну, не трогая остальные." + ], + "steering": [ + "Занятый агент не значит, что мне надо ждать. Я могу писать прямо во время работы, и сообщение попадает в уже идущий ход, а не сидит в очереди до его конца.", + "Так что когда я вижу, что агент идёт не туда, я говорю об этом сразу, и агент поправляется на месте. Terminal никогда не откладывает моё сообщение на потом." + ], + "queue": [ + "Я могу выстроить пачку команд в нужном порядке, отдать агенту и уйти. Сервер выполнит их одну за другой, пока мой браузер закрыт.", + "У каждой команды в очереди своя модель, так что задача может начаться на быстрой дешёвой модели и закончиться на самой сильной, а контекст переедет вместе с ней.", + "Системные команды встают в ту же очередь. PREP складывает ключевые решения по работе на этот момент в память моих агентов. Clear очищает контекстное окно. Так что я могу поставить пятнадцать команд и знать: после тяжёлых агент сохранит важное в ящик памяти на моём сервере и начнёт с чистого окна, вместо того чтобы тупеть по мере того, как окно заполняется." + ], + "timing-order": [ + "Задачи из очереди приходят к агенту одна за другой, в том порядке, который задал я. Могу набросать работы на вечер и утром прочитать результаты." + ], + "timing-availability": [ + "Следующая задача ждёт, пока её агент освободится. Сервер проверяет это перед отправкой, так что две задачи никогда не столкнутся в одной сессии." + ], + "timing-schedule": [ + "Я могу задать, когда очереди можно стартовать. Первая задача запускается, когда время пришло и агент свободен; остальные идут следом." + ], + "timing-server": [ + "Очередь живёт на сервере, а не во вкладке браузера. Я закрываю ноутбук, а работа всё равно идёт." + ], + "onboarding": [ + "Новая сессия перед первой задачей проходит вводную. Codex велено прочитать глобальные правила, правила проекта, потом индекс памяти. Claude получает правила проекта, остальное CLI подгружает сам.", + "Маленький файл с фактами говорит, на каком движке, модели и в каком режиме работает сессия." + ], + "global": [ + "Над всеми проектами стоит один файл правил, и каждая сессия читает его прежде, чем что-то делать. Туда я кладу то, что должно быть верно везде: как агент со мной разговаривает, где кончаются полномочия агента, что считается законченным.", + "Ещё этот файл знакомит агента с остальным сервером. Здесь десятки агентов и около десяти людей, и реестр держит всех одинаково, с хэндлами одного вида. Агенту, которому нужно что-то за пределами своего проекта, достаточно одной строки с именем владельца. Если владелец человек, строка уходит с сервера и приходит ему в Telegram. Вот это и есть настоящий онбординг: на первом же ходу сессия знает, кто существует, кто чем владеет и кого спрашивать.", + "Правило, которое для меня важнее всех, про доказательства. Агент не может сам заверить свою работу. «Сделано» зарабатывается тем, что не умеет спорить: кодом выхода, живой пробой, тестом, который правда запускался, или другим агентом с чистым контекстом, который это проверил. Собственный отчёт не считается.", + "В том же файле лежит закон о логах: всё, что строит агент, должно оставлять след своих запусков, по строке на запуск, рядом с самой вещью. Механизм, который работает и не оставляет записи, это дефект, как бы хорошо он ни работал.", + "Ничего из этого не было написано заранее. Каждая строка в этом файле появилась, потому что однажды что-то сломалось, и каждый месяц я сверяю файл с тем, что пошло не так." + ], + "local": [ + "В корне каждой папки проекта лежат её собственные правила, и агент читает их прежде, чем что-то трогать. На практике этот файл состоит из трёх слоёв, положенных друг на друга.", + "Первый слой составляют композитные ключи, которые я здесь включил. Ключ оставляет в файле свой закон: UI-паспорт фиксирует шкалу шрифтов проекта, его единственный акцент и нижнюю границу контраста, и отступить от паспорта значит нарушить правило, а не проявить вкус. Паспорт безопасности делает то же для того, что можно выставлять наружу. Так стандарты принадлежат проекту, а не тому, кто над ним работает сегодня.", + "Второй слой приходит из шаблона, с которым рождается каждый новый проект. Часть его скучная, но всё равно важная: для чего эта штука, какие кредиты вендоров она жжёт и чек-лист, как её правильно выключить, если я когда-нибудь её убью. Проект с первого дня знает, как умереть.", + "Третий слой ниоткуда не скопируешь. Это запись того, что здесь шло не так: строки с датами, большинство написаны в тот день, когда что-то сломалось, и говорят, чего в этой кодовой базе больше не делать. Поэтому агент, который открывает незнакомый проект, всё равно работает так, будто сидит здесь месяцами.", + "И на всё это есть бюджет. Я целюсь в агента, которому хватает примерно трёхсот строк правил, глобальных и локальных вместе. Каждая добавленная строка забирает у агента внимание, которое ушло бы на работу. Так что правило либо это окупает, либо вылетает." + ], + "session-resume": [ + "Сессия не открывается пустой. Хук на моём сервере ещё до моего первого слова отдаёт агенту последние сохранённые решения по этому проекту, самые свежие первыми. Агент стартует, уже зная, о чём мы договорились, и не спрашивает заново.", + "Ещё агент читает уведомление о себе. Каждую ночь скрипт без единой модели внутри проходит по транскриптам этого агента за прошедший день и записывает, где был нарушен устав, рядом с моими решениями и правками, которые я внёс в работу агента руками. Агент читает это перед первой задачей, так что правило, нарушенное вчера, сегодня первое, что попадается на глаза. После чистого дня уведомление само исчезает.", + "Когда я возвращаюсь в сессию, разговор в ней ещё есть. Когда начинаю новую, она собирается из слоёв, которые переживают любой разговор: глобальные правила, правила проекта, индекс памяти и это уведомление. Так что в новой сессии работает тот же агент, только с более лёгкой головой. Вот почему очистка контекста мне ничего не стоит." + ], + "session-end": [ + "Сессию я заканчиваю очисткой или убиваю. Очистка сохраняет очередь; убийство отправляет её в корзину на 14 дней. Плановое завершение оставляет эпитафию в одну строку, чтобы я знал, чем всё кончилось." + ], + "new-project": [ + "Когда я создаю проект, Terminal запускает мой проектный скрипт: он готовит рабочее пространство и стартовые правила. Открыл проект, и агент делает первый ход." + ], + "new-agent": [ + "Агент рождается, когда я впервые открываю проект, и получает имя по папке. На первом ходу читает правила и перечисляет открытый бэклог, так что с самого начала понимает, где находится." + ], + "directory": [ + "Реестр говорит, кто каким проектом владеет. Агенту, которому что-то нужно от другого проекта, положено спросить владельца, а не зайти и сделать самому.", + "На правиле владения держится всё остальное." + ], + "memorymd": [ + "У каждого проекта есть короткий индекс того, что проект уже усвоил: по строке на урок, со ссылкой на полную заметку. Агент загружает его на старте сессии.", + "Вот так поправка, которую я дал весной, держится и осенью." + ], + "palace": [ + "В хранилище MemPalace решения выживают. Агент записывает туда, о чём мы договорились, а новая сессия ищет там, прежде чем снова спрашивать меня.", + "Разложено по проектам, и искать там может любой агент." + ], + "skills": [ + "Навык записывает метод для одного вида работы: как выпускать релиз, как делать ревью, как писать в нашем стиле. Агент открывает подходящий навык и идёт по нему, вместо того чтобы каждый раз придумывать процесс заново." + ], + "tools": [ + "Инструментами агент действует: правит файл, запускает команду, делает запрос в базу, обращается к сервису. Каждый результат возвращается агенту, и агент решает, какой шаг следующий.", + "Что умеет инструмент и что агенту разрешено, это два разных вопроса. На второй отвечают правила." + ], + "sendto": [ + "Каждый проект принадлежит одному агенту. Когда агенту что-то нужно от другого проекта, агент спрашивает владельца, а не идёт и делает сам.", + "Называет коллегу в одной строке. Вопрос попадает в собственную сессию этого коллеги, среди файлов и правил того проекта, и ответ возвращается. Два разговора остаются раздельными.", + "Вопрос может пройти два перехода, дальше нет. Если я свожу пару агентов, работа ходит от одного к другому на моём сервере, пока мой браузер закрыт. Останавливается это на лимите переходов или когда один из двоих начинает повторяться." + ], + "human-collab": [ + "Реестр на моём сервере перечисляет людей так же, как агентов, с хэндлами.", + "Когда агенту нужно то, что может дать только человек, доступ, решение, факт, который никто не записал, агент обращается к этому человеку по имени одной строкой. Строка уходит с сервера и падает человеку в Telegram.", + "Работает это и в обратную сторону. Тот, кому я дал доступ, отдаёт задачу моим агентам из своего чата и там же получает результат. Исполнение и доставка показаны как два отдельных факта, потому что задача, которая завершилась, ещё не значит, что ответ дошёл до человека." + ], + "ask-once": [ + "Один агент задаёт другому один вопрос. Terminal доставляет его прямо в сессию коллеги, с контекстом того проекта, и приносит ответ обратно.", + "Лимит два перехода, так что вопрос не будет вечно гулять по команде." + ], + "auto": [ + "Авторежим позволяет паре агентов, которых я свёл, передавать работу друг другу через сервер, пока мой браузер закрыт. Останавливается на лимите переходов или когда сообщение повторяется, так что цикл не может пойти вразнос." + ], + "background-work": [ + "Процесс может работать и после того, как ход агента закончился: сборка, деплой, долгая проверка. Terminal следит за ним и тем временем принимает моё следующее сообщение.", + "Сам по себе фоновый процесс агента не будит. Будит моё сообщение." + ], + "codemap": [ + "CodeGraph индексирует каждый символ в проекте. На линии Claude двери заворачивают чтение целых файлов и поиск в индекс, так что агент читает нужную функцию, а не весь файл. Codex ходит в индекс напрямую." + ], + "keys": [ + "Я выпускаю софт больше десяти лет. После нескольких месяцев, когда я строил с агентами проект за проектом, я прогнал исследование по всей этой работе, нашёл то, что делаю чаще всего, и выбрал из этого то, что можно автоматизировать. Так появились ключи, они же композиты.", + "Ключ и скилл устроены по-разному. Скилл объясняет агенту, как делать один вид работы. Ключ держит всё, что нужно одной функции: куда ей встать, проверки, которые гоняются в git и громко падают, доступ к агенту, у которого есть учётка для нужного API, как ведёт себя интерфейс, какие данные трогать нельзя. Некоторые ключи ещё указывают на скелет этой штуки, и агент стартует с реальной основы, а не с пустой папки.", + "Ещё я построил механизм оркестрации между ключами: смотря какие ключи активны, агент перестраивает свой подход к сборке.", + "Активация ключа не проходит молча. Агент возвращается с парой вопросов, чтобы подогнать ключ под этот проект, и мои ответы остаются с ключом, так что второй раз меня никто не спрашивает.", + "Когда я начинаю новый проект, я включаю нужные ключи, и одним промптом 40-80% работы сделано заранее. Это бутстрап, и на большом проекте агент уходит и работает один несколько часов, прежде чем что-то мне показать. С правильными ключами я пишу один промпт и на следующее утро получаю работающий B2B-сервис, собранный так, как собрал бы его я, с нужными мне взаимодействиями в интерфейсе, с нужными лимитами и так далее." + ], + "work-checks": [ + "Я устал от того, что агенты соглашаются с правилом, а через час его нарушают. Прочитать правило и выполнять его оказалось двумя разными вещами, так что я перестал писать правила и начал их запускать. Двери устроены как маленькие программы, которые стоят в фиксированных точках хода. Когда дверь срабатывает, агент возвращается и чинит работу. Пропустить её нельзя, и флага, чтобы выключить, нет.", + "Первая встречает мою задачу. Она заставляет агента сказать вслух, как агент меня понял, до того как начнёт строить не то. Следующая стоит перед каждым чтением файла, потому что я видел, как агенты сжигают половину контекста, читая файл целиком ради одной функции. Теперь это чтение уходит в индекс символов.", + "Дальше после каждой правки стоит целый ряд дверей, и все они выросли из реального ущерба. Размеры шрифта и цвета сверяются с паспортом проекта, потому что агенты всё время придумывали оттенок покрасивее. Анимации проверяются на reduced motion. Вёрстка проверяется на элементы, которые прыгают под курсором. А текст вычитывается на ИИ-воду, после того как я нашёл слово seamless в одной из своих же модалок.", + "Последняя дверь стоит в конце хода, и она мне важнее всех. Агент не может сказать мне, что работа сделана, если в том же ходу нет чего-то, что не умеет спорить: команды, запущенной после изменения, пробы, перечитывания. Мне слишком много раз говорили «готово» те, кто ничего не проверял.", + "Каждая из них существует из-за конкретного плохого дня. Спорить с дверью, править её, обходить её: всё это хуже исходной ошибки." + ], + "monitoring": [ + "Когда агент закончил, за проектом всё равно надо следить. Подключённые дозорные проверяют, что проект жив и выдаёт то, что заявил, и о сбое сообщают мне в Telegram, называя владельца.", + "Сами дозорные ничего не чинят. Чинит человек." + ], + "backlog": [ + "Бэклог делает для меня две вещи. Первая: это память для моих собственных идей. Всё, что я придумываю для проекта, идёт туда, а не остаётся в голове, и агент, который владеет проектом, может прочитать бэклог, когда нужно понять, куда проект движется.", + "Вторая: так композитные ключи остаются свежими. Когда я улучшаю ключ в корне, каждый проект с этим ключом получает задачу в бэклог: свериться со мной, брать ли обновление. Скажем, я поменял поведение интерфейса или что-то в платёжном сценарии. Агент не переписывает себя молча, и улучшение тоже не теряется. Оно приходит ко мне вопросом, по каждому проекту отдельно." + ], + "delivered-work": [ + "Агент сообщает, что изменилось и что проверено. Сама работа остаётся в проекте: файлы, коммиты, деплои. Отчёт я читаю, сверяя с этими доказательствами, а не сам по себе. И «готово» тоже не слово агента. Прежде чем отчёт дойдёт до меня, ревьюер на модели другого вендора читает задачу моими словами и снимок проекта. У ревьюера нет контекста автора, а значит, нет и слепых пятен автора. Это ревью делает GPT, когда GPT лежит, подхватывает Grok, и это никогда не Claude, проверяющий Claude." + ], + "review-result": [ + "Результат оцениваю я, и если он неверный, отправляю назад. Задача из бэклога закрывается по моему подтверждению, никогда по слову агента.", + "LOOP включает путь оценки, когда мне нужны варианты: агент читает доказательства из проекта и предлагает трёх кандидатов с метрикой, базовым уровнем, приростом, стоимостью и риском. Я выбираю одного." + ], + "notify": [ + "Через Telegram система до меня дотягивается. Туда падают сбои дозорных, итоги Arena и строки, которые агент пересылает человеку. Reception берёт задачу у человека, которому я дал доступ, и возвращает результат в его личный чат.", + "Reception показывает исполнение и доставку как два отдельных факта. Задача завершилась, значит, результат доступен; доставку подтверждает только запись курьера. Принять или нет, решаю я." + ], + "history": [ + "Каждая сессия хранит весь свой разговор, а архив держит его дольше, чем сам CLI. Один полнотекстовый поиск идёт по всем сессиям, так что когда агент говорит мне «мы такого никогда не делали», я проверяю.", + "Храню я их ради того, что происходит раз в месяц. Вся запись перечитывается: каждая задача, которую я дал, каждая поправка, которую пришлось делать дважды, каждое место, где работа застряла или вернулась не той. На выходе список предложений, у каждого доказательства, из которых оно выросло, и каждое называет, что именно поменять. Строку в правилах одного проекта. Строку в глобальных правилах, которые читает каждый агент. То, как очередь выстраивает мою работу. Композитный ключ, который тащит один и тот же изъян в каждый проект, собранный на нём.", + "Каждое я принимаю или отклоняю сам. Принятое попадает в правила к следующей сессии, и месяц моих повторений превращается в то, что система делает сама." + ], + "decisions": [ + "Решение, которое живёт только в чате, потеряно. Агент записывает его в память проекта или в MemPalace, а я проверяю, что оно туда попало.", + "Ответ «принято» сохранением не считается." + ], + "discipline": [ + "Результат, который я отправил назад, на мне не заканчивается. Каждую ночь скан без единой модели внутри читает прошедший день этого агента: где нарушен устав, что я решил и каждую правку, которую я внёс в работу агента руками.", + "Всё это возвращается уведомлением, которое агент читает утром перед первой задачей, так что правило, нарушенное вчера, сегодня первое, что попадается на глаза. После чистого дня уведомление снимается само.", + "Раз в месяц те же доказательства сверяются с глобальными правилами. Ошибка, которая возвращается снова и снова, перестаёт быть уведомлением и становится правилом, которое читают все, или дверью, которую никому не обойти." + ], + "auto-capture": [ + "На линии Claude хуки сохраняют память, когда сессия останавливается и перед сжатием контекста. Codex сохраняет через свои инструменты памяти.", + "Поверх этого есть PREP, сохранение намеренное: сохранил и сразу перечитал." + ], + "phases": [ + "Каждый завершённый ход пишет одну строку: сколько команда ждала провайдера, сколько прошло до первого слова, сколько до конца, по каждому движку и слоту. Раз в пять минут ещё одна строка фиксирует зависания сервера. Текст промптов не хранится.", + "Так я знаю, тормозит у меня или у провайдера." + ], + "wolf": [ + "Я задаю направление и решаю, кто чем владеет. Агенты работают в пределах полномочий, которые я уже дал; всё, что требует моего решения, его ждёт.", + "Я единственный человек в этом цикле, и цикл построен так, чтобы одного человека хватало." + ], + "order": [ + "The Order мой главный оркестратор. У этого агента ключи от всех дверей, общая инфраструктура и границы доступа, и почти всё, что происходит на сервере, The Order знает. Перезапуски и пересборки выполняет сам, а не перекидывает обратно мне.", + "Это ещё и единственный агент, который выходит на меня сам. Когда The Order что-то нужно, мне прилетает сообщение в Telegram, а когда ждать нельзя, звонок на телефон, который меня будит.", + "Агенты проектов идут к The Order только за центральным. Всё внутри своего проекта ведут сами." + ], + "agents": [ + "Два вида коллег, один список. Агенты, по одному на проект, и каждый единственный, кто меняет свой проект. Люди, которым я дал доступ, записаны так же, к ним обращаются по хэндлу, а когда строка для них, она приходит им в Telegram.", + "Всё, что за пределами собственного проекта, это вопрос к названному владельцу. У каждого агента ровно те полномочия, которые я записал, и ни строкой больше; за этой границей агент спрашивает меня и ждёт." + ], + "memory": [ + "Память здесь слоями, и каждый слой отвечает на свой вопрос. Сам разговор, где каждая сессия лежит в архиве и ищется, отвечает, делали ли мы это когда-нибудь. Память проекта, одна заметка на урок и короткий индекс, который агент загружает на старте, отвечает, чего требует этот код. MemPalace держит решения: их записывает PREP, и они возвращаются на следующем старте сессии. Каждый механизм ведёт журнал, одна строка на прогон, и каждый завершённый ход записывает свои тайминги.", + "Каждый слой перечитывается. Еженедельный скан проходит по заметкам каждого проекта, оставляет уроки, которые могли бы ударить и по другому проекту, и присылает мне в Telegram нумерованную сводку; я отвечаю номерами, и строка, которую я поднял, переносится в глобальные правила руками. Ежемесячный аудит транскриптов показывает, где агенты нарушили устав. Когда я спросил, стоит ли PREP своих денег, ответ пришёл из тех же данных: 41% сессий заканчиваются сохранением, а большая часть MemPalace оказалась автоматически намайненным шумом, в котором тонули ящики, которые я записывал руками, так что поиск теперь этот шум пропускает." + ], + "system-tools": [ + "Что умеет инструмент и что разрешено агенту, это два разных вопроса. На второй отвечают правила.", + "Инструментами агент действует, и большинство из них я написал сам. CodeGraph индексирует каждый символ проекта, и агент читает одну нужную функцию, а не файл. MemPalace держит решения. Headless-браузер читает страницы, которые обычным запросом не прочитать. У каждого проекта есть рычаг для своих контейнеров: деплой, перезапуск, логи, шелл, в пределах того, что я разрешил.", + "Сеть тоже инструмент. Tailscale собирает мои серверы в одну сеть, и агент на одном из них достаёт до другого по имени хоста, где бы тот ни стоял. Мой собственный VPN и мой собственный пул прокси нужны для работы, которой нужен другой выход в интернет." + ], + "models": [ + "Думает модель у провайдера. Сегодня это Claude и Codex от OpenAI, по несколько подписок на каждый, а инструменты остаются на моём сервере, кто бы ни думал. Grok от xAI третий провайдер на тех же условиях.", + "Переключатель между ними мой собственный код, так что третий движок займёт в нём новый слот, и переписывать ничего не придётся. Почва под локальные модели готова: сервер выбран, модель выбрана, план написан. Пока это не работает, и Атлас так и говорит." + ], + "terminal": [ + "В Terminal живёт задача: здесь открывается, здесь выполняется и сюда возвращается. Ветки и выделения остаются ровно такими, какими я их оставил." + ], + "access": [ + "Ничто на моём сервере не слушает интернет напрямую. Cloudflare Tunnel публикует веб-приложения, а Access проверяет, кто ты, прежде чем открыть внутреннее. Публичные сайты отвечают кому угодно; всё остальное сначала спрашивает, кто пришёл." + ], + "services": [ + "Наружу агенты выходят через подключённые инструменты: репозиторий на GitHub, headless-браузер для страниц, которые обычным запросом не прочитать, таблицу, поток аналитики. У каждого подключения свои границы." + ], + "telegram": [ + "Telegram носит оповещения и мои голосовые задачи. Reception берёт задачу у человека, которому я дал полный доступ, и возвращает результат в его личный чат. Это канал, который работает, когда я не за столом." + ], + "secrets": [ + "Общие учётки хранит The Order. Оператор проекта получает только те, что названы в допуске, и никаких других. Сколько бед наделает утёкший ключ, ограничено самим устройством системы." + ], + "apex": [ + "Пульт сервера показывает каждый Docker-сервис и выполняет по SSH те операции, которые я разрешил. Изменения общей инфраструктуры идут через The Order." + ], + "projects": [ + "На этом хосте работают десятки сервисов проектов, за loopback-портами и Tunnel. Часть привязана к tailnet, часть живёт на своих серверах. Каждый принадлежит ровно одному агенту." + ], + "deploy": [ + "Операторы проектов деплоят, перезапускают и читают логи в пределах записанных мной допусков, включая удалённые хосты. Общая инфраструктура остаётся за The Order. Работа по расписанию идёт через cron на хосте или внутри сервисов.", + "Допуск покрывает исполнение и никогда не покрывает решение. Удалить данные, открыть порт или тронуть продакшен можно только с моего согласия, и там, где рычаг это умеет, мои слова цитируются в команде и остаются в её журнале." + ], + "records": [ + "История сессий и журналы механизмов хранят то, что произошло. Финальный ответ агента остаётся заявлением, а доказательством служит журнал.", + "Каждый механизм, который я выпускаю, оставляет след: одна строка на прогон." + ], + "watchers": [ + "Дозорные проверяют подключённые проекты и сообщают о сбоях. Trivy сканирует образы и хосты на уязвимости. Сообщают; чинит человек." + ], + "recovery": [ + "Restic каждую ночь копирует выбранное состояние с сервера наружу, в зашифрованном виде. В охвате есть исключения. Снимок засчитывается, когда он существует и восстановление из него проверено." + ], + "remote": [ + "Часть проектов работает на машинах за пределами этого сервера. Для каждой есть обёртка, через которую агент там деплоит, перезапускает и читает логи. Продакшен остаётся за отдельным шлюзом." + ], + "backups": [ + "Restic копирует выбранное состояние на другую площадку; зеркало GitHub копирует подходящие репозитории. У обоих есть исключения. Что восстановление работает, доказывают успешная копия и проверенный рестор, а не расписание." + ] + } +} diff --git a/src/lib/aiAtlas/seo.ts b/src/lib/aiAtlas/seo.ts new file mode 100644 index 00000000..03a01ce0 --- /dev/null +++ b/src/lib/aiAtlas/seo.ts @@ -0,0 +1,155 @@ +import type { Readings } from './readings'; + +/** + * What search engines and answer engines read about each Atlas page: its + * title, description, share picture and structured data. + * + * Every word comes from the page itself: titles and descriptions are copy + * keys (copy.json, ru.json), so a Terminal push can change them like any + * other page word, and the Ladder's list is built from the rungs the page + * draws. The share pictures are rendered by scripts/ai-atlas/og/render.mjs; + * OG_VERSION follows its VERSION. + */ + +export const OG_VERSION = 'v2'; + +type SeoTab = 'atlas' | 'article' | 'ladder'; + +const SUFFIX: Record = { + atlas: '', + article: 'Thesis', + ladder: 'Ladder', +}; +const PICTURE: Record = { + atlas: 'atlas', + article: 'thesis', + ladder: 'ladder', +}; + +const runsText = (runs: { t: string }[]) => runs.map(r => r.t).join(''); + +export function atlasSeo({ + tab, + locale, + words, + readings, + domain, + path, + atlasPath, + modified, +}: { + tab: SeoTab; + locale: string; + words: Record; + readings: Readings; + domain: string; + /* This page and the map, without the locale prefix. */ + path: string; + atlasPath: string; + modified?: string; +}) { + const pick = (key: string) => words[key + SUFFIX[tab]] || words[key]; + const title: string = pick('seoTitle'); + const description: string = pick('seoDescription'); + const keywords: string = pick('seoKeywords'); + const imageAlt: string = pick('ogImageAlt'); + /* Armenian reads the English Atlas, so it shares the English picture. */ + const lang = locale === 'ru' ? 'ru' : 'en'; + const prefix = locale === 'en' ? '' : `/${locale}`; + const image = `${domain}/ai-atlas/og-${PICTURE[tab]}-${lang}-${OG_VERSION}.png`; + const url = `${domain}${prefix}${path}`; + const home = `${domain}${prefix}`; + + const author = { + '@type': 'Person', + name: 'Wolf Alexanyan', + sameAs: ['https://x.com/AlexanyanWolf'], + }; + const publisher = { + '@type': 'Organization', + name: 'KeepSimple', + url: domain, + logo: { '@type': 'ImageObject', url: `${domain}/assets/favicon.svg` }, + }; + const common = { + url, + inLanguage: lang, + description, + keywords, + image: { '@type': 'ImageObject', url: image, width: 1200, height: 630 }, + author, + publisher, + ...(modified ? { dateModified: modified } : {}), + }; + + const crumbs = [ + { name: 'KeepSimple', item: home || domain }, + { name: words.seoTitle, item: `${domain}${prefix}${atlasPath}` }, + ...(tab === 'atlas' ? [] : [{ name: title, item: url }]), + ].map((crumb, i) => ({ '@type': 'ListItem', position: i + 1, ...crumb })); + + const main: Record[] = []; + if (tab === 'article') { + const text = [readings.article.lead, ...readings.article.paras] + .map(runsText) + .join(' '); + main.push({ + '@type': 'Article', + '@id': `${url}#thesis`, + headline: title, + mainEntityOfPage: url, + articleSection: 'AI Atlas', + wordCount: text.split(/\s+/).filter(Boolean).length, + ...common, + }); + } else if (tab === 'ladder') { + const rungs = readings.ladder.rungs; + main.push( + { + '@type': 'LearningResource', + '@id': `${url}#ladder`, + name: title, + learningResourceType: 'Skill ladder', + educationalLevel: 'Beginner to advanced', + teaches: rungs.filter(r => r.skill).map(r => r.title), + ...common, + }, + { + '@type': 'ItemList', + '@id': `${url}#rungs`, + name: title, + numberOfItems: rungs.length, + itemListOrder: 'https://schema.org/ItemListOrderAscending', + itemListElement: rungs.map(r => ({ + '@type': 'ListItem', + position: r.n + 1, + name: r.title, + description: [r.sub, r.skill].filter(Boolean).join(' '), + })), + }, + ); + } else { + main.push({ + '@type': 'WebPage', + '@id': `${url}#atlas`, + name: title, + ...common, + }); + } + + return { + title, + description, + keywords, + imageAlt, + image, + ogType: tab === 'article' ? 'article' : 'website', + schema: { + '@context': 'https://schema.org', + '@graph': [ + ...main, + { '@type': 'BreadcrumbList', itemListElement: crumbs }, + ], + }, + }; +} diff --git a/src/pages/ai-atlas-sitemap.xml.ts b/src/pages/ai-atlas-sitemap.xml.ts new file mode 100644 index 00000000..6e42ec19 --- /dev/null +++ b/src/pages/ai-atlas-sitemap.xml.ts @@ -0,0 +1,57 @@ +import type { GetServerSideProps } from 'next'; + +import { readStoredGuide } from '@lib/aiAtlas/store'; + +/** + * The Atlas pages offered to search: the map, the Thesis and the Ladder, in + * English and Russian, each pointing at its other language. + * + * Strapi's sitemap plugin lists only its own content types, and the Atlas is + * not one of them; on 2026-10-13 not one Atlas address was in any sitemap. + * Armenian is left out: it shows the English Atlas, so it would only be a + * duplicate of the English page. + */ + +const ORIGIN = 'https://keepsimple.io'; +const PATHS = ['/ai-atlas', '/ai-atlas/thesis', '/ai-atlas/ladder']; +const LOCALES = [ + { lang: 'en', prefix: '' }, + { lang: 'ru', prefix: '/ru' }, +]; + +export const getServerSideProps: GetServerSideProps = async ({ res }) => { + /* generatedAt is pushed content: a value that is not a date only drops + lastmod, it never turns the sitemap into an error. */ + const guide = await readStoredGuide().catch(() => null); + const modified = new Date(guide?.generatedAt ?? NaN); + const lastmod = Number.isNaN(modified.getTime()) + ? '' + : `${modified.toISOString()}`; + const alternates = (path: string) => + [ + ...LOCALES.map( + ({ lang, prefix }) => + ``, + ), + ``, + ].join(''); + const urls = PATHS.flatMap(path => + LOCALES.map( + ({ prefix }) => + `${ORIGIN}${prefix}${path}${lastmod}${alternates(path)}`, + ), + ); + + const body = `\n\n${urls.join('\n')}\n`; + + res.setHeader('Content-Type', 'application/xml; charset=utf-8'); + res.setHeader('Cache-Control', 'public, max-age=0, s-maxage=3600'); + res.write(body); + res.end(); + + return { props: {} }; +}; + +const AtlasSitemap = () => null; + +export default AtlasSitemap; diff --git a/src/pages/ai-atlas.tsx b/src/pages/ai-atlas.tsx index 57ea1af6..441edd34 100644 --- a/src/pages/ai-atlas.tsx +++ b/src/pages/ai-atlas.tsx @@ -1,4 +1,5 @@ import type { GetStaticProps } from 'next'; +import { useRouter } from 'next/router'; import React, { useEffect, useLayoutEffect, @@ -17,6 +18,7 @@ import { } from '@lib/aiAtlas/readings'; import readingsData from '@lib/aiAtlas/readings.json'; import { securityPassage, securityRadii } from '@lib/aiAtlas/securityPassage'; +import { atlasSeo } from '@lib/aiAtlas/seo'; import SeoGenerator from '@components/SeoGenerator'; @@ -868,7 +870,15 @@ type ViewMode = 'environment' | 'security'; /* The header tabs: the map, and the two readings that sit beside it. */ type Tab = 'atlas' | 'article' | 'ladder'; const READING_TABS: Tab[] = ['article', 'ladder']; -const readings = readingsData as Readings; +/* Each reading has its own address on keepsimple.io: /ai-atlas/thesis and + /ai-atlas/ladder. The map stays at /ai-atlas. */ +export const TAB_SLUGS: Record = { + article: 'thesis', + ladder: 'ladder', +}; +export const tabPath = (tab: Tab) => + tab === 'atlas' ? '/ai-atlas' : '/ai-atlas/' + TAB_SLUGS[tab]; +const bundledReadings = readingsData as Readings; function DoctrinePanel({ t }: { t: T }) { return ( @@ -878,7 +888,7 @@ function DoctrinePanel({ t }: { t: T }) { ); @@ -1262,6 +1272,13 @@ export function AiAtlasApp({ initialGuide = null, initialView = 'environment', initialFocus = null, + initialTab = 'atlas', + /* Thesis and Ladder words for this locale; English when not given. */ + readings = bundledReadings, + /* On keepsimple.io a tab is its own page and this navigates to it. Without + it (the Terminal's frame) the tabs switch in place and the hash holds + the tab. */ + onTabChange = null, }: any = {}) { const [data] = useState(() => initialGuide ? adaptGuide(initialGuide) : null, @@ -1272,7 +1289,7 @@ export function AiAtlasApp({ const [hoverNode, setHoverNode] = useState(null); const [linkHoverNode, setLinkHoverNode] = useState(null); const [viewMode, setViewMode] = useState(initialView); - const [tab, setTab] = useState('atlas'); + const [tab, setTab] = useState(initialTab); const tabPending = useRef(false); const changeTab = (next: Tab) => { if (next === tab || tabPending.current) return; @@ -1281,6 +1298,10 @@ export function AiAtlasApp({ stage?.classList.add('is-leaving'); setTimeout( () => { + if (onTabChange) { + onTabChange(next); + return; + } setTab(next); setFocusedNode(null); stage?.classList.remove('is-leaving'); @@ -1321,10 +1342,16 @@ export function AiAtlasApp({ const sync = () => { const hash = window.location.hash.replace(/^#/, '').toLowerCase(); if ((READING_TABS as string[]).includes(hash)) { - setTab(hash as Tab); - setFocusedNode(null); + /* An old #article / #ladder link opens the reading's own page. */ + if (onTabChange) onTabChange(hash as Tab, true); + else { + setTab(hash as Tab); + setFocusedNode(null); + } return; } + /* A reading page has no map state to restore from the hash. */ + if (onTabChange && initialTab !== 'atlas') return; setTab('atlas'); if (hash === 'security') { setViewMode('security'); @@ -1350,13 +1377,15 @@ export function AiAtlasApp({ useEffect(() => { if (typeof window === 'undefined') return; let desired = ''; - if (tab !== 'atlas') desired = '#' + tab; + if (tab !== 'atlas') desired = onTabChange ? '' : '#' + tab; else if (viewMode === 'security') desired = '#security'; else if (focusedNode) desired = '#' + focusedNode; const current = window.location.hash; if (current !== desired) { const url = window.location.pathname + window.location.search + desired; - window.history.replaceState(null, '', url); + /* Keep the router's own entry state, so Back between the tab pages + still navigates. */ + window.history.replaceState(window.history.state, '', url); } }, [tab, viewMode, focusedNode]); @@ -2133,7 +2162,7 @@ export function AiAtlasApp({ )}