Галоўная / Артыкулы

AI-агент, які нічога не забывае, частка 2: тэлепартацыя памяці

Частка 2 пра AI-агента, які нічога не забывае: перанос памяці паміж сесіямі, навыкі, згасанне і памяць у межах галіны.

Выдавец: REACTAPP.TOP28 хв чытання6515 слоў

Крыніцы: Build an AI Agent That Never Forgets (Part 2): Teleportation, Skills, Decay, and Branch-Scoped Memory

Наступныя прыміткі паказваюць практычны шлях для рэалізацыі праекту «Ствароўчы АІ-агента, які ніколі не забывае (Частка 2): Тэлепортацыя, навыкі, занэхаўванне і памяць у межах галузей». Акцэнт ставіцца на контракты, перакананняя і мескі для коду, які можна легка адразу вставіць, а не на мотывацыйныя аспекты. Калі працуеце над стадзіяй агледжэння, спачатку запісайце контракт: неабяжныя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі спіс контроля дапамагае залішыцца чыстаснам у пазнейшых змянах коду. Запісвайце час выканання і вартасць токеноў або запытак праза функцыйнае рэзультат. Відразувыя даны пра вартасць запобегаюць неспакойным рахункам, калі праект пераходзіць з дэмаверсіі ў спяльныя асяроддзя.

Дзе мы застаўіліся

Функцыя «Where We Left Off» працюе наўсёх лепшая, калі яе розглядаць як вімерную паверхню. Запісаўце адна «золатая» копія дадзеных, адзін прыклад неудачы і запіс пра вярнэнне да поперадньага стану, прычым розшыруючы сферу дзеяння.

Шаг 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)

Спаўненне — цікавая частка

У стадыі «Злучэнне — цікавая частка» неабяжна практыкуваць задаць вхідныя даны, адміністратара крока і крытэрыя завершэння пры перадзеіснаванні коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю без неабяжнага вычыслення захаванога стану. Валіць маленькія, тэставальныя елементы працэй над велікімі скрыптамі. Калі крок не выйшаў, прычына неудачы павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач. Заставіць людзкую апраўду на тых этапах, дзе відбываецца выдатак грошэй або зміняюцыся даны для працы. Компіляцыйныя налашчэння не ўзначаюць павнае выпанаванне бізнес-процэсаў. У стадыі «Злучэнне — цікавая частка» неабяжна практыкуваць задаць вхідныя даны, адміністратара крока і крытэрыя завершэння пры перадзеіснаванні коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю без неабяжнага вычыслення захаванога стану. Запісваць час выканання і кост токенаў або запытаў разам з функцыйнальнымі рэзультатамі. Відразувая візуабілізацыя костоў запобегае неспакойным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльную срэду.

ронменты.

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

Адаптаванне дарабочага раштату

Калі працюеце над стадзіяй адаптавання дарабочага раштату, спачатку запісайце умовы працы: неабяжлівыя даны, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі спіс контроля дапамагае заліцвачыць змяны ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, неудача павінна вказваць на адзін конкрэтны элемент, а не на заплутаны ланцюг задач. Зрабіце пераконтроль пасля дорогіх крокаў. Програма не павінна зноў ставіць плату за той самы вызов LLM, калі аператар прабуе зноў выконаць пазнейшы элемент.

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: Апаратна частка навыкоў

Этап Апаратна частка навыкоў у шагу 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

Этап трохі знакаў актывацыі працюе найэфектывнейша, калі яго розглядаць як вімерную паверхню. Запісаўце адна «золатая» транскрыпцыю, адзін прыклад неудачы і прыметку па адвярненню перад расшырэнням масштаба. Запісвайце часы выканання і кост токенаў або запытаў праза функцыйнае рэзультат. Відразлівае паказанне костаў з’являецца перашкоду неспакою, калі працэс пераходзіць з дэмаверсіі ў спяльныя асяроддзя.

Модэл дадзеных Skill

На этапе модэля дадзеных 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)

Адаптаванне дарабочага раштату

Калі працюеце над стадзіяй адаптавання дарабочага раштату, спачатку запісайце умовы працы: неабяжлівыя даны, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі спіс контроля дапамагае заліцвачыць змяны ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, неудача павінна вказваць на адзін конкрэтны элемент, а не на заплутаны ланцюг задач. Зрабіце пераконтроль пасля дорогіх крокаў. Програма не павінна зноў ставіць плату за той самы вызов LLM, калі аператар прабуе зноў выконаць пазнейшы элемент.

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

Этап The 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"]

Аўтаматычнае выявленне гіт-галузі

У этапы аўтаматычнага выявлення гіт-галузі неабходна пазначыць вхідныя даны, адпаведальнага за крок і крэтыяры завершэння пры зміне коду. Аперацыйныя працавнікі должны магчымае перайсці на выкананне кроку з вядомага пункта контролю, не спрабоўваючы здогадвацца пра схованы стан. Валічыце маленькія, тэставальныя елементы працэсу над велікімі скрыптамі. Калі крок не выйшоў, прычына неудачы должна вказываць на адну конкрэтную прычыну, а не на заплутаны цэп выканання задач. Заставляйце людзей апраўдваць тыя крокі, якія ведуць да вітрачання грошаў або змены дадзэнняў у прыемным рэжыме. Компіляцыйныя налашчэння не ёсць прамаравамым паказатэлем завершання бізнес-процэсу.

@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

Для аўтаматычнага выявлення этапы гіт-галоўкі неабходна пазначыць параметры, власніка крока і крэтыяры завершэння пры змены коду. Аперацыйныя працавнікі должны магчымае перзапускаць крок з вядомай точкі контролю, не прабуючы спадарожваць скрытые станы. Запісвайце час выканання і вартась токенаў або запытак паляглі разам з функцыйнальнымі рэзультатамі. Відразлівае паказанне вартасей запобегае неспадзяваным рашчыткам, калі працэс пераходзіць з дэмовай асяроддзя ў спяльнаваныя сераўеры.

Змена галоўак

Калі працуеце над стадзіяй «Перэключэнне галузей», спачатку запісайце умовы кантракту: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі спіс контроля дапамагае заліцвачыць змяны ў кодзе пазнейша. Зберагайце настройкі за межамі коду прыемліка. Файлы сераўнавання, хранальнікі секрэтных даных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаюць адбавіць аўдыт без неабяжлівага чытання всей структуры. Ставьце контрольныя пункты пасля дорогіх крокаў. Програма для продакцыі не должна занова ставіць плату за той самы вызов 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

Разлік і выдалення

Калі працюеце на стадыі Разлік і выдалення, спачатку запісайце умовы вікорыстання: неабяжлівыя даны, сігнал працэздольнасці і тое, што вядзецца пад частковай нявыходзі. Такі спіс контролю дапамагае заставіць пазнейшыя змены коду быць чыстымі. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, прычына нявыходзу павінна вказываць на адзін конкрэтны элемент, а не на заплутаны ланцюг задач. Зробіце перапактаванне пасля дорогіх крокаў. Сістэма вярнення праблем не павінна зноў стаўіць плату за той самы вызов LLM, калі аператар праканае выконанне наступнага элемента. Калі працюеце на стадыі Разлік і выдалення, спачатку запісайце умовы вікорыстання: неабяжлівыя даны, сігнал працэздольнасці і тое, што вядзецца пад частковай нявыходзі. Такі спіс контролю дапамагае заставіць пазнейшыя змены коду быць чыстымі. Запісвайце час выканання і кост токеноў або запытаў разам з функцыйнальнымі рэзультатамі. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмавайнага рэжыма ў спакульнаныя сераўы.

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)

Паўная карціна

Этап «Паўная карціна» працюе наякшэй, калі яго спрыяваць як мерыемую паверхню. Зберагуце адны ідэальны прыклад працы, адзін прыклад неудачы і запіс паўранення раней, чым расшыруваце сферу дзеяння. Валідзіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісьць кроку не выходзіце, неудача должна адносіцца да аднай відпаведальнасці, а не да заплутанага ланца задач. Рэгулюйце стан графа так, каб ён быў простым і з адзіным тыпам дадзеных. Вярнутыя структуры дадзеных маскуюць інфармацыю пра тое, калькі вузел запісаў калькі поле, і спакшваюць продажчыку працу пасля перэрываў. Этап «Паўная карціна» працюе наякшэй, калі яго спрыяваць як мерыемую паверхню. Зберагуце адны ідэальны прыклад працы, адзін прыклад неудачы і запіс паўранення раней, чым расшыруваце сферу дзеяння. Запісвайце часы выканання і косты токенавання або запыткаў па боку функцыйнальных рэзультатаў. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльныя сераўы.

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

На стадыі «Што дазволяе VectLite» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння пры перадзеі коду. Аперацыйныя працавнікі павінны магчымае перазапускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Конфігурацыю трэба залічыць пазначкай ад коду прыемліка. Файлы сераўнавання, храненні секрэтных даных і флагі функцыйяў павінны знаходзіцца ў аднам месцы, якое працавнікі можуць пераглядаць, не чытаючы весь граф. Пры выконанні дзеянь, якія коштуюць грошы або зменяюць даны у працэйнай сістэме, неабяжна ўключыць людскую апраўду. Підключэння пад час компіляцыі не ўзначае повнасці бізнес-процэсу.

Як ён паўстанае у порэвананні з Claude Code

Ёнколі хочаце парабяліць, як гэта працуе на практычным етапе, неабяжна з’явіць вакульныя даныя, адпаведальнага за крок і крэтыяры завершэння пры перадзеіснаванні коду. Аператары павінны магчымаць перапрацоўваць крок па вядомай точцы контролю, не спрабоўваючы здагадвацца пра схованы стан. Неабяжна задокументаваць як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрацоўкі, людзкія пераказы і обработка некорэктных паведамленняў ёсць часткай продукту, а не дадатковымі паследнімі правкамі. Неабяжна атрыбутаваць людзкую затверджэнняе тым крокам, якія вітрачаюць грошы або зменяюць даныя ў працэсе вырабоцтва. Компіляцыйныя налашчэння не ёсць адпаведнікамі пачынковай готовасці продукту.

Спробаваць

Для этапы «Практыкуйце» неабяжна ўзначыць даныя, адпаведальнага за крок і крэтыры завершэння пры перадзеі коду. Аперацыйныя працавнікі павінны магчымае перадзеі крок з вядомай точкі контролю, не падозрываючы схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выйшае, прычына нехасабносты павінна вказваць на адзіну адпаведальнасць, а не на заплутаны працэс. Неабяжна ўключыць людзкія апраўленні для тых крокоў, якія выкарыстоўваюць грошы або зменяюць даныя ў працэсе. Компіляцыйныя налашчэнні не ўзначаюць павнае адпрацоўванне бізнес-процэса.

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

Для этапы «Практыкуйце» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтыяры завершэння пры перадзеі коду. Аперацыйныя працавнікі должны магчымае запускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан.

Чэк-ліст для аперацый

Для этапы чэк-ліста для аперацый неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтыяры завершэння пры перадзеі коду. Аперацыйныя працавнікі должны магчымае запускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан.

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

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

Напісце кароткі путаводзік: як роцыяваць клучы, як апустошыць чэргу, як вярнуць стан да пярэдніх настройкаў.