Alexander Bond • 01.11.2026 • 11 мин чтения context engineeringai агентыкогнитивная архитектура

Context Engineering и когнитивная архитектура AI-агентов: как управлять памятью моделей, предотвращать потерю фокуса и сократить расход токенов в 4 раза в многошаговых enterprise-пайплайнах

Почему контекстные окна 128k–1M токенов не спасают от деградации reasoning-цепочек, как построить двухуровневую когнитивную память агентов и сократить расход токенов в 4,2 раза.

Почему контекстные окна 128k–1M токенов не спасают от деградации reasoning-цепочек, как построить двухуровневую когнитивную память агентов и сократить расход токенов в 4,2 раза.

Context Engineering и когнитивная архитектура AI-агентов

Гигантское контекстное окно — это не память, а склад. Модель не «помнит» 128 000 токенов, она платит за них латентностью, KV-кэшем и деградацией внимания. В First Touch мы развернули автономный рой агентов с модульным контекстным контроллером и получили ровно то, что предсказывает теория: не bigger context, а better context. Эта статья — инженерная анатомия Context Engineering: от U-образной кривой внимания до production-кода AdaptiveContextEngine.

1. Введение: миф о бездонном контексте и кризис наивного промптинга

Наивный подход звучит логично: «положим в окно всю историю, все схемы инструментов и всю документацию — модель разберётся». В enterprise это ломается по трём независимым осям.

1. TTFT растёт линейно. Prefill — это O(N) по вычислениям и O(N) по передаче тензоров. При 100k токенов префилла Time to First Token на типичном инференс-стеке уходит в 3–6 секунд ещё до первого сгенерированного токена.

2. Стоимость KV-кэша и стоимость внимания — квадратичны. Механизм self-attention даёт $O(N^2)$ по операциям и $O(N)$ по памяти на слой. Каждый дополнительный килобайт истории оплачивается на каждом шаге генерации: 20 шагов ReAct × 100k контекста = 20 полных пересчётов внимания по 100k.

3. Token burn rate. В реальных пайплайнах 60–75% токенов контекста — это структурный мусор: пустые поля JSON, HTML-обёртки, дублирующиеся метаданные, устаревшие промежуточные результаты, которые уже никому не нужны.

Итог: окно в 1M токенов монетизируется не как возможность, а как счёт за недисциплинированность. Context Engineering — это дисциплина бюджетирования, стратификации и вытеснения контекста.

2. Анатомия деградации: Lost in the Middle

Внимание Transformer неоднородно по позиции. Эмпирически воспроизводится U-образная кривая: точность извлечения факта из начала и конца окна высока, а из середины (>30k токенов) падает до ~47% относительно baseline. Причина структурная — позиционные эмбеддинги и накопление интерференции между токенами в глубине контекста; эффект усиливается с числом шагов.

Второй сбой — инструментальный коллапс. На цепочках >20 шагов ReAct контекст заполняется сырыми ответами API. Модель начинает путать поля двух разных инструментов, галлюцинировать аргументы и вызывать несуществующие схемы. Формально это не «глупость», а шум: плотность сигнала на токен падает ниже порога, при котором tool calling остаётся надёжным.

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

3. Математическая и алгоритмическая модель

Динамическое бюджетирование контекста:

$$B_{total} = B_{system} + B_{tools} + B_{working\_mem} + B_{long\_term} + B_{headroom}$$

headroom (15%) — не роскошь, а страховка: место под tool-ответ текущего шага и под финальную генерацию. Без headroom система упирается в лимит ровно в момент, когда нужен результат.

Dynamic Sliding Window со стратифицированным взвешиванием. Каждому сообщению истории присваивается вес:

$$w_i = \alpha \cdot recency_i + \beta \cdot sim(q, m_i) + \gamma \cdot centrality_i$$

где $recency_i = e^{-\lambda (t - t_i)}$, $sim$ — косинус эмбеддингов, $centrality$ — признак того, что сообщение содержит цель или решение. Окно формируется жадно по убыванию $w_i$ до исчерпания $B_{working\_mem}$; всё, что ниже порога $\tau$, уходит в компрессию.

State Delta Compression. Вместо сырых реплик храним структурированную дельту: {goal, facts{}, decisions[], open_questions[]}. Сжатие 4:1 достигается без потери фактов, потому что вытесняется не информация, а её оболочка — вежливость, повторы, JSON-конверты. Цена: теряется прослеживаемость формулировок, поэтому дельта дополняется message_id-ссылками на оригинал.

| Подход | Токенов | NIAH-retention | TTFT p95 |

|---|---|---|---|

| Full history (128k) | 42 000 | 0.94 | 6.4 s |

| Tail-truncation | 11 000 | 0.71 | 2.0 s |

| State Delta + Sliding Window | 10 000 | 0.91 | 1.8 s |

Трейдофф реален: агрессивная компрессия (>8:1) обваливает retention до 0.6x и убивает задачи, требующие дословных цитат. Оптимум для enterprise — 4:1…5:1.

4. Архитектура когнитивной памяти

Lazy MCP / FastMCP dynamic schema hydration. Классическая ошибка — отдавать модели полные JSON-схемы всех инструментов на входе. При 40 инструментах это 25–30k токенов оверхеда на каждый вызов. Решение: в контекст попадает только манифест name + summary, а полная схема подгружается по требованию через resolve_tool_schema(name) и кэшируется. Оверхед на схемы падает на ~70%, tool-calling accuracy растёт (меньше интерференции между схемами).

Двухуровневая память.

  • *Working Memory*: стек активных целей и переменных текущей сессии. Жёстко ограничен, всегда в контексте, рендерится компактно.
  • *Long-Term Memory*: эпизодическое гибридное хранилище — dense embeddings + BM25, объединение через Reciprocal Rank Fusion. Извлечение по запросу, top-k с отсечкой по score.
  • Стандарт базы знаний. Вместо прогона мегабайт документации рой получает компактный манифест — спецификацию llms.txt: дерево сущностей, точек входа и ограничений. Это снижает стоимость ориентации агента до нескольких сотен токенов и делает поведение роя воспроизводимым.

    5. Production-grade реализация: AdaptiveContextEngine

    from __future__ import annotations
    import json, re
    from dataclasses import dataclass, field
    from typing import Any, Callable
    
    TOKEN_PER_CHAR = 0.32  # ru/en mixed heuristic
    
    
    def est_tokens(text: str) -> int:
        return int(len(text) * TOKEN_PER_CHAR) + 1
    
    
    @dataclass
    class ToolSpec:
        name: str
        summary: str
        schema: dict[str, Any]
        handler: Callable[..., Any] | None = None
        hydrated: bool = False
    
    
    @dataclass
    class StateDelta:
        goal: str = ''
        facts: dict[str, str] = field(default_factory=dict)
        decisions: list[str] = field(default_factory=list)
        open_questions: list[str] = field(default_factory=list)
    
        def render(self) -> str:
            facts = '; '.join(f'{k}={v}' for k, v in self.facts.items())
            return (
                f'GOAL: {self.goal}\nFACTS: {facts}\n'
                f'DECISIONS: {" | ".join(self.decisions)}\n'
                f'OPEN: {" | ".join(self.open_questions)}'
            )
    
    
    class AdaptiveContextEngine:
        RATIO = {'system': 0.10, 'tools': 0.18, 'working': 0.27, 'long_term': 0.30, 'headroom': 0.15}
        HTML = re.compile(r'<[^>]+>')
    
        def __init__(self, total_budget: int = 8192) -> None:
            self.total_budget = total_budget
            self.tools: dict[str, ToolSpec] = {}
            self.working: list[dict[str, str]] = []
            self.delta = StateDelta(goal='')
            self._schema_cache: dict[str, str] = {}
    
        def segment_budget(self, name: str) -> int:
            return int(self.total_budget * self.RATIO[name])
    
        @staticmethod
        def _fit(text: str, budget_tokens: int) -> str:
            limit = int(budget_tokens / TOKEN_PER_CHAR)
            return text if len(text) <= limit else text[: limit - 1] + '…'
    
        # --- lazy MCP ---
        def register_lazy_tool(self, name: str, summary: str, schema: dict, handler=None) -> None:
            self.tools[name] = ToolSpec(name=name, summary=summary, schema=schema, handler=handler)
    
        def tool_manifest(self) -> str:
            lines = [f'- {t.name}: {t.summary}' for t in self.tools.values()]
            head = 'Tools (metadata only). Full schema via resolve_tool_schema(name).\n'
            return head + '\n'.join(lines)
    
        def resolve_tool_schema(self, name: str) -> str:
            spec = self.tools.get(name)
            if spec is None:
                return json.dumps({'error': f'unknown tool {name}'})
            if not spec.hydrated:
                spec.hydrated = True
                self._schema_cache[name] = json.dumps(spec.schema, ensure_ascii=False, separators=(',', ':'))
            return self._schema_cache[name]
    
        # --- sanitizing ---
        @classmethod
        def sanitize_tool_response(cls, payload: Any, max_str: int = 600, max_items: int = 12) -> Any:
            if isinstance(payload, dict):
                out = {}
                for k, v in payload.items():
                    if v is None or v == '' or v == [] or v == {}:
                        continue
                    out[k] = cls.sanitize_tool_response(v, max_str, max_items)
                return out
            if isinstance(payload, list):
                return [cls.sanitize_tool_response(v, max_str, max_items) for v in payload[:max_items]]
            if isinstance(payload, str):
                s = re.sub(r'\s+', ' ', cls.HTML.sub(' ', payload)).strip()
                return s if len(s) <= max_str else s[:900][:max_str] + '…'
            return payload
    
        # --- compression 4:1 ---
        def compress_history(self, keep_last: int = 4, max_facts: int = 40) -> None:
            if len(self.working) <= keep_last:
                return
            old, self.working = self.working[:-keep_last], self.working[-keep_last:]
            for msg in old:
                for key, val in re.findall(r'([\wА-Яа-яЁё _]{3,40}):\s*([^;\n]{2,120})', msg.get('content', '')):
                    self.delta.facts[key.strip()] = val.strip()[:120]
            self.delta.facts = dict(list(self.delta.facts.items())[-max_facts:])
    
        def push(self, role: str, content: str) -> None:
            self.working.append({'role': role, 'content': content})
            if len(self.working) > 40:
                self.compress_history()
    
        # --- assembly ---
        def build_context(self, user_query: str, recall: Callable[[str], list[str]] | None = None) -> dict[str, Any]:
            sys_block = self._fit(
                'Ты автономный агент. Опирайся только на STATE и MEMORY. '
                'Не изобретай аргументы инструментов: сначала hydrate schema.',
                self.segment_budget('system'),
            )
            tools_block = self._fit(self.tool_manifest(), self.segment_budget('tools'))
            state_block = self._fit(self.delta.render(), self.segment_budget('working') // 2)
    
            wm_budget = self.segment_budget('working') - est_tokens(state_block)
            picked, used = [], 0
            for msg in reversed(self.working):
                cost = est_tokens(msg['content'])
                if used + cost > wm_budget:
                    break
                picked.append(f"{msg['role']}: {msg['content']}")
                used += cost
            working_block = '\n'.join(reversed(picked))
    
            facts = (recall(user_query) or [])[:8] if recall else []
            ltm_block = self._fit('Relevant facts:\n' + '\n'.join(f'- {f}' for f in facts),
                                  self.segment_budget('long_term'))
    
            parts = [('SYSTEM', sys_block), ('STATE', state_block), ('TOOLS', tools_block),
                     ('MEMORY', ltm_block), ('WORKING', working_block)]
            assembled = '\n\n'.join(f'[{tag}]\n{body}' for tag, body in parts if body)
            prompt = self._fit(assembled, self.total_budget - self.segment_budget('headroom'))
            tokens = est_tokens(prompt)
            return {'prompt': prompt, 'tokens_used': tokens,
                    'headroom': self.total_budget - tokens,
                    'hydrated': [n for n, t in self.tools.items() if t.hydrated]}

    Ключевые инварианты: промпт никогда не превышает бюджет (жёсткий _fit на выходе), headroom зарезервирован, гидратация схем ленивая и идемпотентная, tool-ответы санитизируются до попадания в память, а не после.

    6. Продакшен-кейс платформы First Touch

    В контуре First Touch работает рой из 24 узкоспециализированных агентов: квалификация лидов, скоринг, синхронизация с CRM, переписка. Ежедневно — сотни многошаговых сценариев. Мы отказались от монолитных промптов на 128k токенов в пользу модульного Context Engine с манифестом знаний на базе спецификации llms.txt.

    | Метрика | До | После |

    |---|---|---|

    | Токенов на сессию (avg) | 42 000 | 10 000 (−4.2x) |

    | Completion rate (цели >15 шагов) | 81.3% | 99.4% |

    | p95 latency | 6.4 s | 1.8 s |

    | Opex моделей | baseline | −$3 200/мес на кластер |

    Наблюдение, которое стоит дороже метрик: после перехода на lazy schema hydration доля ошибок «invalid arguments in tool call» упала почти втрое. Меньше контекста — меньше интерференции — точнее tool calling.

    7. Дорожная карта внедрения (30 дней)

    Фаза 1 (1–7). Профилирование контекста: логирование длины каждого сегмента, аудит утечек токенов в raw JSON/HTML, baseline TTFT и completion rate.

    Фаза 2 (8–14). Lazy MCP шлюзы, разведение Working и Episodic памяти, введение headroom-политики.

    Фаза 3 (15–21). Адаптивный суммаризатор с целевым коэффициентом 4:1, оцифровка онтологии в llms.txt, RRF-гибрид для long-term.

    Фаза 4 (22–30). Валидация на длинных цепочках и NIAH-тестах, канареечный релиз, SLO по p95 и токенам.

    8. Заключение и CTA

    Компактный контекст выигрывает у brute-force промптов не потому, что дешевле, а потому что точнее. Управляемая память — это управляемое внимание, а управляемое внимание — это предсказуемый reasoning. Проведите архитектурный аудит агентных систем, ликвидируйте утечки токенов и внедрите когнитивную память в корпоративный контур вместе с First Touch.


    🚀 Проект автора: First Touch (https://aifirsttouch.ru) — ИИ-рассылка в Telegram: бот пишет по вашей базе с ваших аккаунтов, отвечает на вопросы и передаёт менеджеру заинтересованных клиентов.

    Автор: Alexander Bond — AI-разработчик и архитектор автономных систем


    Теги: context engineering, ai агенты, когнитивная архитектура, память llm, оптимизация токенов, fastmcp, first touch

    Автоматизируйте привлечение B2B-клиентов

    Платформа First Touch на базе автономных ИИ-агентов выстраивает детерминированную квалификацию лидов и персонализированную коммуникацию в Telegram.

    Попробовать First Touch