Головна / Статті

створіть AI-агента, який ніколи не забуває (Частина 2): телепортація

Частина 2 агента ШІ, який ніколи не забуває: телепортація пам’яті між сеансами, навички, занепад та пам’ять обмежена гілкою для Claude Code та подібних агентів.

Видавець: REACTAPP.TOP28 хв читання6515 слів

Джерела: Build an AI Agent That Never Forgets (Part 2): Teleportation, Skills, Decay, and Branch-Scoped Memory

Наведені нижче примітки відтворюють практичний підхід до реалізації проекту «Створення AI-агента, який ніколи не забуває (Частина 2): Телепортація, навички, занепад та пам’ять у межах гілок». Основна увага приділяється контрактам, перевіркам та шаблонам коду, а не мотиваційним аспектам. Під час роботи на етапі огляду спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ.

Де ми зупинилися

Механізм «Там, де ми зупинилися» найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітку про скасування змін перед розширенням обсягу завдань. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, щоб оператори могли їх перевіряти, не читаючи весь граф. Зберігайте стан графа у вигляді простих структур із чіткими типами даних. Вкладені структури приховують інформацію про те, який вузол записав яке поле, і ускладнюють продовження роботи після перерв.

Крок 5: Телепортація пам’яті

Етап телепортації пам’яті на кроці 5 працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один золотий запис, один випадок невдачі та примітку про скасування змін перед розширенням обсягу. Документуйте як успішний, так і відновлювальний шляхи. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у вигляді простих структур з чітким типуванням. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

Робочий процес

Етап робочого процесу працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку щодо скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

Agent A: snapshot("for_review.vdb")
         |  send file to Agent B
         v
Agent B: open("for_review.vdb")  ->  work  ->  add new memories
         |  send file back
         v
Agent A: merge("for_review.vdb")
         -> new memories imported
         -> conflicts detected and resolved

Етап робочого процесу працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку щодо скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ.

Знімок стану

На етапі створення знімка необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Необхідно передбачити людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-функціоналу.

class MemoryTeleport:
    def __init__(self, db: vectlite.Database):
        self.db = db
    def snapshot(self, path: str) -> str:
        """Create a portable copy of the entire database."""
        self.db.snapshot(path)
        return path

Різниця — перегляд без змін

Для попереднього перегляду змін без модифікації етапу необхідно визначити вхідні дані, власника кроку та критерії завершення ще до зміни коду. Оператори мають мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Необхідно встановити людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повності функціоналу продукту.

def diff(self, remote_path: str,
         namespaces: list[str] | None = None) -> MergeResult:
    """Preview what a merge would do, without writing anything."""
    return self._merge_internal(remote_path, namespaces, dry_run=True)

Об’єднання — найцікавіша частина

На етапі «Merge the interesting part» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Встановлюйте людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення елементів під час компіляції не є гарантією повноти бізнес-функціоналу. На етапі «Merge the interesting part» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Відомі заздалегідь витрати запобігають несподіваним рахункам під час переходу від демо-версії до спільного середовища.

Стратегії вирішення конфліктів.

def merge(self, remote_path: str,
          namespaces: list[str] | None = None,
          strategy: ConflictStrategy = ConflictStrategy.NEWER_WINS
          ) -> MergeResult:
    """Merge memories from a remote .vdb into the local database."""
    return self._merge_internal(
        remote_path, namespaces,
        dry_run=False, strategy=strategy,
    )

Стратегії вирішення конфліктів

Під час роботи над етапом стратегій вирішення конфліктів спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Робіть контрольні точки після дорогих операцій. Функція відновлення не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перезапускає пізнішу ланку.

class ConflictStrategy(str, Enum):
    LOCAL_WINS = "local_wins"      # keep local version
    REMOTE_WINS = "remote_wins"    # keep remote version
    NEWER_WINS = "newer_wins"      # keep whichever has a more recent updated_at
    KEEP_BOTH = "keep_both"        # import remote as a new record

Механізм злиття

Під час роботи над етапом двигуна злиття спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Документуйте як шлях успішної роботи, так і шлях відновлення одночасно. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Робіть контрольні позначки після дорогих кроків. Функція відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.

def _merge_internal(self, remote_path, namespaces=None,
                    dry_run=False, strategy=ConflictStrategy.NEWER_WINS):
    result = MergeResult()
    remote_db = vectlite.open(remote_path, dimension=DIMENSION)
    try:
        ns_list = namespaces or self._discover_namespaces(remote_db)
        local_index = self._build_index(self.db, ns_list)        for ns in ns_list:
            remote_records = remote_db.list(namespace=ns, limit=10_000)            for record in remote_records:
                rec_id = record["id"]
                remote_meta = record.get("metadata", {})
                remote_hash = remote_meta.get("content_hash", "")                if rec_id in local_index:
                    local_info = local_index[rec_id]                    if local_info["content_hash"] == remote_hash:
                        result.skipped_duplicates += 1  # same content
                        continue                    # Conflict -- same ID, different content
                    conflict = MergeConflict(
                        record_id=rec_id,
                        namespace=ns,
                        local_content=local_info.get("content", ""),
                        remote_content=remote_meta.get("content", ""),
                        local_updated=local_info.get("updated_at", ""),
                        remote_updated=remote_meta.get("updated_at", ""),
                    )
                    if not dry_run:
                        self._resolve_conflict(conflict, record, strategy)
                        result.conflict_resolutions += 1
                    result.conflicts.append(conflict)
                else:
                    # New record -- import
                    if not dry_run:
                        self._import_record(record, ns)
                    result.imported += 1
    finally:
        remote_db.close()    return result

Перегляд роботи

Під час роботи на етапі «Перевірка функціональності» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну функцію, а не на складну послідовність операцій. Робіть перевірки після дорогих кроків. Система має уникати подвійного обчислення вартості виклику ШІ при повторній спробі обробки певного етапу.

from agentmem.store.memory_store import MemoryStore
from agentmem.services.teleport import MemoryTeleport
# Agent A: has been working for weeks
store_a = MemoryStore("agent_a.vdb")
tp = MemoryTeleport(store_a.db)# Create a snapshot for Agent B
tp.snapshot("for_agent_b.vdb")# ... send for_agent_b.vdb to Agent B's machine ...# Agent B works with the snapshot, adds new memories
store_b = MemoryStore("for_agent_b.vdb")
store_b.store(Memory(
    id="mem_new_finding",
    content="The payment API requires idempotency keys for all POST requests",
    memory_type=MemoryType.REFERENCE,
    importance=0.8,
))
store_b.save()
store_b.close()# ... send for_agent_b.vdb back to Agent A ...# Agent A merges the new knowledge
result = tp.merge("for_agent_b.vdb")
print(result.summary())
# -> Imported: 1 new memories
#    Skipped:  47 duplicates
#    Conflicts: 0

Під час роботи на етапі «Перевірка функціональності» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Записуйте час виконання та витрати на токени чи запити поруч із результатами функціонування. Відображення витрат на ранньому етапі запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ.

Чому формат передачі .vdb є ефективним

Формат vdb є найкращим для передачі даних, якщо його розглядати як вимірювану поверхню. Спочатку зафіксуйте один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін, перш ніж розширювати обсяг даних. Тримайте конфігурацію окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, щоб оператори могли їх перевіряти, не читаючи весь граф. Зберігайте стан графа у вигляді простих, типованих структур. Вкладені блоки даних приховують інформацію про те, який вузол записав яке поле, що ускладнює продовження роботи після перерв.

Крок 6: Мотор навичок

Етап Skill Engine у кроці 6 працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок невдачі та примітку про скасування змін перед розширенням обсягу. Документуйте як успішний, так і відновлювальний шляхи роботи. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

---
description: Docker best practices
glob_patterns: ["Dockerfile", "docker-compose*.yml"]
trigger_keywords: ["docker", "container", "compose"]
---
## Docker Guidelines
- Always use multi-stage builds
- Pin base image versions
- Never run as root in production

Три сигнали активації

Три етапи сигналів активації працюють найкраще, коли їх розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.

User message: "deploy the containers to staging"
     |
     v
+-- SkillEngine.activate() ----------------------------+
|                                                      |
|  1. Hybrid search (semantic + keyword via VectLite)  |
|     -> matches "docker" skill (score 0.91)           |
|     -> matches "devops" skill (score 0.78)           |
|                                                      |
|  2. Glob match (if file path detected in message)    |
|     -> "Dockerfile" mentioned? -> boost "docker"     |
|                                                      |
|  3. Keyword boost                                    |
|     -> "containers" matches docker triggers          |
|                                                      |
|  4. Weighted combination -> top-k activated skills   |
+------------------------------------------------------+
     |
     v
Injected into system prompt as ## Active skills

Три етапи сигналів активації працюють найкраще, коли їх розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним витратам під час переходу з демо-середовища до спільних середовищ.

Модель даних навичок

На етапі моделювання даних The Skill необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Коли наступним кроком є код або виклик інструменту, краще використовувати структуровані результати з перевіркою схеми, ніж вільний текст.

@dataclass
class Skill:
    name: str                    # "docker", "python", "security"
    content: str                 # Full Markdown instructions
    glob_patterns: list[str]     # ["Dockerfile", "*.py"]
    trigger_keywords: list[str]  # ["container", "docker"]
    tools_allowed: list[str]     # ["docker_build", "docker_push"]
    importance: float = 0.7      # Base weight
    tags: list[str] = field(default_factory=list)
    description: str = ""

Реєстрація — індексування навичок у VectLite

Для розробки навичок індексації під час реєстрації необхідно заздалегідь визначити вхідні дані, виконавця кроку та критерії завершення перед зміною коду. Оператори мають мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Встановлюйте людське схвалення для тих кроків, які призводять до витрат грошей або змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повності функціоналу продукту.

NAMESPACE = "__skills__"
class SkillEngine:
    def __init__(self, db: vectlite.Database,
                 max_active: int = 3, min_score: float = 0.30):
        self.db = db
        self.max_active = max_active
        self.min_score = min_score    def register(self, skill: Skill) -> str:
        skill_id = f"skill_{skill.name}_{uuid.uuid4().hex[:6]}"        # Rich embedding text: name + description + keywords + content
        embed_text = (
            f"{skill.name}: {skill.description or ''} "
            f"{' '.join(skill.trigger_keywords)} "
            f"{skill.content[:500]}"
        )
        vector = embed(embed_text)
        sparse = vectlite.sparse_terms(embed_text)        self.db.upsert(
            skill_id, vector, skill.to_metadata(),
            sparse=sparse, namespace=NAMESPACE,
        )
        return skill_id

Активація — основна логіка зіставлення

Для фази активації та основного етапу зіставлення необхідно визначити вхідні дані, власника кроку та критерії завершення ще до зміни коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, тестовані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Встановлюйте людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення елементів під час компіляції не є гарантією повноти бізнес-функціоналу. Для фази активації та основного етапу зіставлення необхідно визначити вхідні дані, власника кроку та критерії завершення ще до зміни коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Відображення витрат на ранньому етапі запобігає несподіваним рахункам під час переходу від демо-режиму до спільного використання.

середовищ.

def activate(self, message: str, file_path: str | None = None,
             k: int | None = None) -> list[ActivatedSkill]:
    k = k or self.max_active
    # 1. Hybrid search via VectLite
    vector = embed(message)
    sparse = vectlite.sparse_terms(message)
    results = self.db.search(
        vector,
        k=min(k * 3, self.count()),  # over-fetch for re-ranking
        sparse=sparse, fusion="rrf",
        namespace=NAMESPACE,
    )    # Build candidates with normalized semantic scores
    candidates = {}
    max_score = max((r.get("score", 0) for r in results), default=1) or 1
    for r in results:
        meta = r.get("metadata", {})
        name = meta.get("name", "")
        if name:
            candidates[name] = _Candidate(
                skill=self._meta_to_skill(meta),
                semantic_score=r.get("score", 0) / max_score,
            )    # 2. Keyword boost
    msg_lower = message.lower()
    for name, cand in candidates.items():
        hits = sum(1 for kw in cand.skill.trigger_keywords
                   if kw.lower() in msg_lower)
        if hits > 0:
            cand.keyword_score = min(0.5, hits * 0.2)    # 3. Glob match
    detected_paths = self._detect_paths(message)
    if file_path:
        detected_paths.append(file_path)
    for name, cand in candidates.items():
        for pattern in cand.skill.glob_patterns:
            for path in detected_paths:
                if fnmatch.fnmatch(os.path.basename(path), pattern):
                    cand.glob_score = 0.35
                    break    # 4. Weighted combination
    activated = []
    for name, cand in candidates.items():
        final_score = (
            cand.semantic_score * 0.55
            + cand.keyword_score * 0.25
            + cand.glob_score * 0.15
            + cand.skill.importance * 0.05
        )
        if final_score >= self.min_score:
            activated.append(ActivatedSkill(
                skill=cand.skill,
                score=final_score,
                activated_by=...,  # "semantic", "keyword", "glob", "combined"
            ))    activated.sort(key=lambda a: a.score, reverse=True)    # Gap filter: drop skills scoring < 75% of top skill
    if activated:
        top = activated[0].score
        activated = [a for a in activated if a.score >= top * 0.75]    return activated[:k]

Завантаження навичок з файлів Markdown

Під час виконання етапу завантаження навичок з файлів Markdown спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Робіть контрольні точки після дорогих операцій. Функція відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор перезапускає пізнішу ланку.

def load_from_directory(self, dir_path: str) -> list[str]:
    """Load skills from .md files with optional YAML-like frontmatter."""
    ids = []
    for md_file in sorted(Path(dir_path).glob("*.md")):
        skill = self._parse_skill_file(md_file)
        if skill:
            ids.append(self.register(skill))
    return ids
---
description: Python best practices
glob_patterns: ["*.py", "*.pyx", "pyproject.toml"]
trigger_keywords: ["python", "pip", "venv", "pytest"]
tools_allowed: ["python_run", "pytest"]
importance: 0.8
tags: ["language", "backend"]
---
## Python Guidelines
- Use type hints for all function signatures
- Prefer pathlib over os.path
- Use pytest with fixtures, not unittest
- Format with black, lint with ruff

Вставка запиту

Під час роботи над етапом введення команд напишіть спочатку контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Документуйте як шлях успішної роботи, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте у кеші стабільні інструкції системи та схеми інструментів. Повторна передача ідентичного вступу є поширеною причиною проблем.

def format_for_prompt(self, activated: list[ActivatedSkill]) -> str:
    if not activated:
        return ""
    lines = ["\n\n## Active skills\n"]
    for act in activated:
        lines.append(f"### {act.skill.name}")
        if act.skill.tools_allowed:
            lines.append(f"**Allowed tools:** {', '.join(act.skill.tools_allowed)}")
        lines.append(act.skill.content)
        lines.append("")    return "\n".join(lines)

Перегляд роботи

Під час роботи на етапі «Перевірка функціональності» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну функцію, а не на складну послідовність операцій. Робіть перевірки після дорогих кроків. Система повинна не стягувати знову плату за один і той самий виклик ШІ, коли оператор перезапускає пізніший етап.

engine = SkillEngine(db)
engine.register(Skill(
    name="docker",
    content="## Docker\n- Always use multi-stage builds\n- Pin versions",
    glob_patterns=["Dockerfile", "docker-compose*.yml"],
    trigger_keywords=["docker", "container", "image", "compose"],
))engine.register(Skill(
    name="python",
    content="## Python\n- Use type hints\n- Prefer pathlib\n- Use pytest",
    glob_patterns=["*.py", "pyproject.toml"],
    trigger_keywords=["python", "pip", "venv"],
))# Semantic + keyword match
activated = engine.activate("deploy the containers to staging")
# -> [ActivatedSkill(skill=<docker>, score=0.87, activated_by="combined")]# Glob match
activated = engine.activate("review the Dockerfile")
# -> [ActivatedSkill(skill=<docker>, score=0.91, activated_by="combined")]# Semantic match only (no exact keywords)
activated = engine.activate("optimize the CI/CD pipeline for faster builds")
# -> [ActivatedSkill(skill=<docker>, score=0.52, activated_by="semantic")]

Під час роботи на етапі «Перевірка функціональності» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Відомі заздалегідь витрати запобігають несподіваним рахункам, коли процес переходить від демо-версії до спільних середовищ.

Крок 7: Занепад пам’яті та вирішення конфліктів

Етап занепаду пам’яті у Кроці 7 найкраще функціонує, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Зберігайте стан графа у простій та типованій формі. Вкладені блоки приховують інформацію про те, який вузол записав яке поле, і ускладнюють продовження роботи після перерв.

Формула занепаду

Формула занепаду працює найкраще, коли її розглядають як вимірювану поверхню. Запишіть один ідеальний варіант виконання, один випадок збою та примітку про скасування дій перед розширенням обсягу. Документуйте як успішний, так і відновлювальний шляхи роботи. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

new_importance = current_importance * 2^(-elapsed_hours / half_life)

The MemoryDecayEngine

Етап MemoryDecayEngine працює найкраще, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу завдань. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.

class MemoryDecayEngine:
    def __init__(self, store: MemoryStore,
                 half_life_hours: float = 168.0,      # 1 week
                 tombstone_threshold: float = 0.05,
                 conflict_threshold: float = 0.80):
        self.store = store
        self.half_life_hours = half_life_hours
        self.tombstone_threshold = tombstone_threshold
        self.conflict_threshold = conflict_threshold

Застосування ефекту зношення

Етап застосування занепаду працює найкраще, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний приклад, один випадок невдачі та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.

def apply_decay(self, now: datetime | None = None) -> DecayReport:
    """Run exponential decay over all memories."""
    now = now or datetime.now(timezone.utc)
    report = DecayReport()
    for mtype in MemoryType:
        memories = self.store.recall_by_type(mtype, k=500)
        for mem in memories:
            if mem.superseded_by:
                continue  # already replaced            report.total_scanned += 1
            last_accessed = self._parse_time(
                mem.last_accessed or mem.created_at
            )
            elapsed_hours = max(
                (now - last_accessed).total_seconds() / 3600.0, 0.0
            )            # The core formula
            decay_factor = math.pow(
                2.0, -elapsed_hours / self.half_life_hours
            )
            new_importance = mem.importance * decay_factor            if new_importance < self.tombstone_threshold:
                # Below threshold -> auto-delete
                self.store.update_importance(mem.id, 0.0)
                self.store.forget(mem.id)
                self.store.compact()
                report.pruned += 1
            elif abs(new_importance - mem.importance) > 0.001:
                # Importance decayed -> update
                self.store.update_importance(mem.id, new_importance)
                report.decayed += 1
            else:
                report.untouched += 1    return report

Виявлення конфліктів

Етап виявлення конфліктів працює найкраще, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Візуалізація витрат на ранньому етапі запобігає несподіваним рахункам під час переходу від демо-середовища до спільних середовищ. Зберігайте стан графа у випрямленому та типованому форматі. Вкладені блоки приховують інформацію про те, який вузол заповнив яке поле, та ускладнюють продовження роботи після перерв.

def check_conflicts(self, new_memory: Memory) -> list[ConflictResult]:
    """Find existing memories that contradict the new one."""
    # Get candidates via VectLite search
    similar = self.store.find_similar(
        new_memory.content,
        memory_type=new_memory.memory_type,
        k=5, min_score=0.0,
    )    new_hash = self.store._hash(new_memory.content)
    new_vector = embed(new_memory.content)    conflicts = []
    for existing, _ in similar:
        if existing.id == new_memory.id:
            continue
        if existing.superseded_by:
            continue        # Skip exact duplicates (same hash = not a conflict)
        if self.store._hash(existing.content) == new_hash:
            continue        # Compute direct cosine similarity
        existing_vector = embed(existing.content)
        cosine_sim = self._cosine_similarity(new_vector, existing_vector)        if cosine_sim >= self.conflict_threshold:
            conflicts.append(ConflictResult(
                existing=existing,
                new_content=new_memory.content,
                similarity=cosine_sim,
                resolution=self.default_resolution,
            ))    return conflicts

Хранилище з вирішенням конфліктів

Магазин із етапом вирішення конфліктів працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Тримайте стан графа простим та типованим. Вкладені блоки приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

def store_with_conflict_check(self, memory: Memory,
                              resolution=None) -> tuple[str, list]:
    resolution = resolution or self.default_resolution
    conflicts = self.check_conflicts(memory)
    if not conflicts:
        return self.store.store(memory), []    for conflict in conflicts:
        conflict.resolution = resolution        if resolution == ConflictResolution.SUPERSEDE:
            mem_id = self.store.store(memory)
            self.store.mark_superseded(conflict.existing.id, mem_id)        elif resolution == ConflictResolution.KEEP_BOTH:
            mem_id = self.store.store(memory)        elif resolution == ConflictResolution.KEEP_EXISTING:
            mem_id = ""  # don't store the new one    return mem_id, conflicts

Як це працює

Етап «Перевірка роботи» найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один приклад успішної роботи, один випадок збою та примітку про скасування змін перед розширенням обсягу. Документуйте як шлях успішної роботи, так і шлях відновлення разом. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

store = MemoryStore("decay_demo.vdb")
decay = MemoryDecayEngine(store)
# Store two memories
store.store(Memory(id="m1", content="The database is PostgreSQL 14",
                   memory_type=MemoryType.PROJECT, importance=0.7))# Later, the project migrates
new_mem = Memory(id="m2", content="The database is PostgreSQL 16",
                 memory_type=MemoryType.PROJECT, importance=0.8)mem_id, conflicts = decay.store_with_conflict_check(new_mem)
print(conflicts[0])
# -> Conflict: [project] sim=0.953 -> supersede
# m1 is now marked as superseded by m2# Weeks later, run decay
report = decay.apply_decay()
print(report)
# -> Decay: 15 scanned, 8 decayed, 2 pruned, 5 untouched

Крок 8: Пам’ять, обізнана з структурою робочого дерева

Етап пам’яті, що враховує структуру робочого дерева з кроком 8, працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Віддавайте перевагу невеликим, тестованим одиницям перед об’ємними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв. Етап пам’яті, що враховує структуру робочого дерева з кроком 8, працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Записуйте час виконання та витрати на обробку токенів чи запитів поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним витратам під час переходу з демо-середовища до спільних середовищ.

Стратегія іменування простору

На етапі стратегії простору імен необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Необхідно передбачити людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-логіки.

# Global -- shared across all branches
GLOBAL_TYPES = {MemoryType.USER, MemoryType.FEEDBACK}
# Scoped -- isolated per branch
SCOPED_TYPES = {MemoryType.PROJECT, MemoryType.REFERENCE}
Memory Type     Branch          VectLite Namespace
-----------------------------------------------------
user            (any)           "user"
feedback        (any)           "feedback"
project         main            "project:main"
project         feature/auth    "project:feature/auth"
reference       main            "reference:main"
reference       feature/auth    "reference:feature/auth"

The WorktreeManager

Для етапу The WorktreeManager необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Встановлюйте людське схвалення для операцій, які призводять до витрат грошей або змінюють дані у виробництві. Підключення на етапі компіляції не є гарантією повноти функціоналу продукту.

BRANCH_SEP = ":"
class WorktreeManager:
    def __init__(self, db: vectlite.Database,
                 branch: str | None = None):
        self.db = db
        self._branch = branch or self._detect_git_branch()    def resolve_namespace(self, memory_type: MemoryType) -> str:
        """Map a MemoryType to its VectLite namespace."""
        if memory_type in GLOBAL_TYPES:
            return memory_type.value            # "user", "feedback"
        return f"{memory_type.value}{BRANCH_SEP}{self._branch}"
        # -> "project:main", "reference:feature/auth"    def recall_namespaces(self) -> list[str]:
        """All namespaces to search for the current branch."""
        namespaces = []
        for mt in MemoryType:
            if mt in GLOBAL_TYPES:
                namespaces.append(mt.value)
            else:
                namespaces.append(f"{mt.value}{BRANCH_SEP}{self._branch}")
        return namespaces
        # -> ["user", "feedback", "project:main", "reference:main"]

Автоматичне виявлення гілки git

Для автоматичного визначення етапу гілки git необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Необхідно включати людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення елементів під час компіляції не є гарантією повноти бізнес-функціоналу.

@staticmethod
def _detect_git_branch() -> str:
    """Auto-detect the current git branch."""
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--abbrev-ref", "HEAD"],
            capture_output=True, text=True, timeout=5,
        )
        if result.returncode == 0:
            branch = result.stdout.strip()
            if branch and branch != "HEAD":
                return branch
    except (FileNotFoundError, subprocess.TimeoutExpired):
        pass
    return "main"  # fallback

Для автоматичного виявлення етапу гілки git необхідно визначити параметри входу, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні.

Переключення гілок

Під час роботи над етапом перемикання гілок спочатку запишіть умову виконання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність подальших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Робіть контрольні пункти після дорогих операцій. Функція відновлення не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перезапускає пізнішу ланку.

def switch_branch(self, branch: str) -> str:
    """Switch to a different branch context. Returns previous branch."""
    prev = self._branch
    self._branch = branch
    return prev

Об’єднання гілок (як git merge для „спогадів“)

Під час роботи з об’єднанням гілок, як-от за допомогою git stage, спочатку складіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Одночасно задокументуйте шлях успішного виконання та шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Робіть контрольні пункти після дорогих кроків. Функція відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.

def merge_branch(self, source: str,
                 into: str | None = None) -> BranchMergeResult:
    """Merge branch-scoped memories from source into target."""
    target = into or self._branch
    result = BranchMergeResult(source=source, target=target)
    for mt in SCOPED_TYPES:  # only project + reference
        src_ns = f"{mt.value}{BRANCH_SEP}{source}"
        tgt_ns = f"{mt.value}{BRANCH_SEP}{target}"        src_records = self.db.list(namespace=src_ns, limit=500)        # Build dedup set from target
        tgt_records = self.db.list(namespace=tgt_ns, limit=500)
        tgt_hashes = {
            r.get("metadata", {}).get("content_hash", "")
            for r in tgt_records
        }        for record in src_records:
            content_hash = record.get("metadata", {}).get("content_hash", "")
            if content_hash in tgt_hashes:
                result.skipped_duplicates += 1
                continue            # Import with new ID into target namespace
            new_id = f"mem_{uuid.uuid4().hex[:12]}"
            new_meta = dict(record.get("metadata", {}))
            new_meta["merged_from"] = source            self.db.upsert(
                new_id, record["vector"], new_meta,
                sparse=record.get("sparse", {}),
                namespace=tgt_ns,
            )
            result.imported += 1    return result

Різниця та видалення

Під час роботи на етапах порівняння та видалення спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед складними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну функцію, а не на заплутану послідовність операцій. Робіть контрольні пункти після дорогих кроків. Система повторного запуску не повинна знову стягувати плату за один і той самий виклик ШІ, коли оператор намагається виконати наступний етап. Під час роботи на етапах порівняння та видалення спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних середовищ.

def diff_branch(self, source: str,
                target: str | None = None) -> BranchDiffResult:
    """Like git diff -- preview without modifying anything."""
    # Same logic as merge_branch but without writing
    ...
def delete_branch(self, branch: str) -> int:
    """Delete all branch-scoped memories. Global types untouched."""
    total_deleted = 0
    for mt in SCOPED_TYPES:
        ns = f"{mt.value}{BRANCH_SEP}{branch}"
        records = self.db.list(namespace=ns, limit=500)
        for record in records:
            self.db.delete(record["id"])
            total_deleted += 1
    if total_deleted > 0:
        self.db.compact()
    return total_deleted

Перегляд повного життєвого циклу

Етап «Перегляд повного життєвого циклу» функціонує найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Тримайте стан графа простим та типованим. Вкладені блоки приховують інформацію про те, який вузол записав яке поле, і ускладнюють продовження роботи після перерв.

wt = WorktreeManager(db, branch="main")
# Store memories on main
store.store(Memory(content="FastAPI backend", memory_type=MemoryType.PROJECT))
# -> stored in namespace "project:main"store.store(Memory(content="I prefer type hints", memory_type=MemoryType.USER))
# -> stored in namespace "user" (global)# Switch to feature branch
wt.switch_branch("feature/auth")store.store(Memory(content="OAuth2 with PKCE for mobile clients",
                   memory_type=MemoryType.PROJECT))
# -> stored in namespace "project:feature/auth"# Recall on feature/auth -- sees global + branch-scoped
namespaces = wt.recall_namespaces()
# -> ["user", "feedback", "project:feature/auth", "reference:feature/auth"]
# "I prefer type hints" is visible (global)
# "FastAPI backend" is NOT visible (it's in project:main)
# "OAuth2 with PKCE" is visible (feature branch)# Merge feature into main
result = wt.merge_branch("feature/auth", into="main")
# -> Imported: 1, Skipped: 0# Now on main, "OAuth2 with PKCE" is also visible
wt.switch_branch("main")
# -> recall sees: "FastAPI backend" + "OAuth2 with PKCE" + all globals# Clean up
wt.delete_branch("feature/auth")
# -> project:feature/auth and reference:feature/auth emptied

Кінцева структура проекту

Найкраще працює останній етап структурування проекту, якщо його розглядати як вимірювану поверхню. Зафіксуйте один ідеальний варіант виконання, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Документуйте як успішний, так і відновлювальний сценарії роботи разом. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.

agentmem/
  core/
    memory_types.py       # MemoryType + Memory dataclass
    embedder.py           # embed() / embed_batch()
  store/
    memory_store.py       # MemoryStore -- VectLite persistence
  services/
    extractor.py          # Background memory extraction
    session.py            # Session summary
    compaction.py         # 4-level cascading compaction
    swarm.py              # Multi-agent swarm
    teleport.py           # Snapshot / merge / diff
    skills.py             # Skill engine
    decay.py              # Exponential decay + conflict resolution
    worktree.py           # Branch-scoped namespaces
  agent.py                # Agent -- the full loop
  mcp_server.py           # MCP server (bonus)

Повна картина

Етап «The Complete Picture» працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв. Етап «The Complete Picture» працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ.

User message
     |
     v
+-- Agent.chat() ----------------------------------------+
|                                                        |
|  RECALL: hybrid search (HNSW + BM25 + RRF)      [P1]  |
|  SKILLS: semantic + keyword + glob activation    [P2]  |
|  BUILD PROMPT: memories + skills + session summary     |
|  COMPACT: snip -> micro -> collapse -> auto      [P1]  |
|  LLM RESPONSE                                         |
|  EXTRACT: background sub-agent learns facts      [P1]  |
|  SESSION: update summary if threshold crossed    [P1]  |
|                                                        |
|  TELEPORT: snapshot / merge / diff               [P2]  |
|  DECAY: exponential forgetting + conflict detect [P2]  |
|  WORKTREE: branch-scoped namespaces              [P2]  |
|  SWARM: multi-agent via shared namespaces        [P1]  |
|                                                        |
|  Storage: single .vdb file (VectLite)                  |
+--------------------------------------------------------+

Що дозволяє VectLite

Для етапу, який дозволяє What VectLite, необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Необхідно передбачити людське схвалення для дій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-функціоналу.

Як це порівнюється з Claude Code

Щоб зрозуміти, як це порівнюється з певною стадією, необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Необхідно встановити людське схвалення для тих кроків, які призводять до витрат грошей або змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повності функціоналу продукту.

Спробуйте

На етапі «Спробувати» необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, тестовані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на складну структуру процесу. Встановлюйте людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення елементів під час компіляції не є гарантією повноти бізнес-функціоналу.

pip install vectlite sentence-transformers
git clone https://github.com/mcsedition-hub/agentmem-vectlite.git
cd agentmem-vectlite
python demo.py

На етапі «Спробувати» необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних середовищ.

Чек-лист операцій

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

Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового завершення роботи.

Вимагайте людського схвалення для операцій, які призводять до витрат грошей чи змін даних у продакшені. Компіляційна налаштування не є гарантією повності виконання завдань у бізнес-сенсі.

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