본문으로 건너뛰기
cccskills
무료GitHub에 공개한국어 소개

add-memory-kind

새 업무 메모리 유형을 영구 저장하도록 개발 작업을 안내합니다. 필요한 Markdown, SQLite, LanceDB 조합과 Markdown 파일 방식을 정한 뒤 해당 계층의 스키마, 저장소, 작성기를 연결합니다.

원문원문 설명 보기

Add a new business memory kind end-to-end. Pick the storage combination (Markdown / SQLite / LanceDB), pick the markdown strategy (daily-log / skill-named / single-file), then wire up the schema(s), repo(s), and writer(s).

EverMind-AI/EverOS스타 13,704(GitHub 13,704 + cccskills 0)

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • 영구 저장할 새 업무 메모리 유형 추가하기
  • 저장 계층 조합 설계하기
  • Markdown 원본과 검색 색인 연결하기

한국어 소개

할 수 있는 일

새 업무 데이터 유형을 영구 저장할 수 있도록 필요한 저장 계층과 구현 위치를 정하는 과정을 안내합니다. 사람이 읽을 수 있는 원본인 Markdown, 구조화 데이터를 담는 SQLite, 벡터 검색 등에 쓰이는 LanceDB를 필요에 맞게 조합합니다. Markdown을 사용한다면 날짜별 항목을 덧붙이는 방식, 이름이 정해진 파일을 덮어쓰는 방식, 단일 파일을 다시 쓰는 방식 중에서 선택합니다. 이어서 스키마와 쓰기 로직을 추가하고, 필요한 경우 테이블과 저장소도 구성합니다. 프로젝트의 다른 부분에서 사용할 수 있도록 새 구성 요소를 다시 내보내는 작업도 다룹니다.

이럴 때 유용합니다

Episode나 Case처럼 새 업무 기록을 영구 저장하려는 개발자에게 유용합니다. 검색, 구조화된 상태 관리, 사람이 편집할 수 있는 원본 가운데 무엇이 필요한지 정리하고 선택한 저장 계층을 일관되게 연결할 수 있습니다.

사용 예시

  • “새 업무 메모리 유형의 저장 구성을 정해 주세요.”
  • “날짜별 Markdown 로그와 SQLite 테이블을 추가해 주세요.”
  • “검색용 LanceDB 색인까지 연결해 주세요.”

주의 사항

날짜별 로그 방식은 구체적인 절차가 제공됩니다. 이름 지정 파일 방식과 단일 파일 방식은 개요만 있으며, 기본 작성기가 마련되기 전까지는 MarkdownWriter를 감싸는 얇은 래퍼가 필요합니다. SQLite와 LanceDB는 필요한 경우에만 추가합니다.

이 소개문은 공개된 SKILL.md를 바탕으로 AI(OpenAI Codex)가 작성했습니다. 정확한 사양은 아래 원문을 확인하세요.

문서 점검

AI가 SKILL.md의 내용에서 주의가 필요한 작업과 도입 전제를 확인했습니다. 점수는 실제로 실행한 결과(실행 검증)만으로 매깁니다.

안전성전송·삭제·명령 실행 등을 명시하고 있는가
A양호파일 덮어쓰기 방식을 명시하고 SQLite와 LanceDB를 선택 사항으로 둡니다.
도입의 용이성필요한 도구나 API 키가 적고 적혀 있는가
B일부 부족Markdown, SQLite, LanceDB 선택지는 제시하지만 설치 절차는 없습니다.

점수에는 넣지 않았습니다. 2026년 10월 29일 기준으로 AI(OpenAI Codex)가 판정했습니다. 참고용이므로 설치 전에 원문도 확인해 주세요.

평가가 다르다고 생각되면 재검토를 요청하기

어느 항목이 왜 다르다고 생각하는지 적어 주세요. 로그인은 필요 없습니다. SKILL.md와 대조해 평가를 다시 만들고 이곳에 반영합니다.

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

포함된 파일(1)

  • SKILL.md11.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

/add-memory-kind — Add a new business memory kind

When to invoke

Adding a new persisted business entity (Episode, Case, Skill, AtomicFact, Foresight, Profile, or something custom). Multiple storage layers may be involved; this skill walks the decision then the wiring.

1. Decide the storage combination

A memory kind does not have to use all three layers. Pick by what the kind actually needs:

NeedMarkdownSQLiteLanceDB
Human-readable / agent-editable source-of-truth text✅
Structured state, ACID transactions, joins, predicates✅
Vector / BM25 / hybrid retrieval✅

Common combinations seen in EverOS:

ComboExampleRationale
md onlyscratch notes / dump binstext-of-truth, no index needed
md + lancedbepisode / memcell / casetext-of-truth + semantic retrieval
md + sqliteprofile / playbook / soul.md statetext-of-truth + structured state to query
md + sqlite + lancedbfull-blown business recordswhen you need both transactional state AND retrieval
sqlite onlyaudit log / task queue / LSN watermarksystem state, never user-facing
lancedb onlyrare; usually you still want mdderived embeddings without text-of-truth

Rule of thumb: markdown is the truth; sqlite and lancedb are derived indexes that can be rebuilt from md. Drop md only when the kind has no human-readable form (pure system state).

2. Pick the markdown storage strategy (if md is in your combo)

Three strategies — declared in the EverOS Markdown First spec:

StrategyFilenameMutationExamples
Daily-log append<prefix>-YYYY-MM-DD.mdappend entriesmemcell / episode / case / atomic_fact / foresight
Skill-named in-placeskill_<name>.mdoverwrite the fileskills (procedural memory)
Single-file rewriteuser.md / agent.md / soul.md / behaviors.md / tools.mdoverwrite the fileprofiles / playbooks

This skill currently has a complete recipe for daily-log append. Skill-named and single-file recipes are sketched at the bottom — their base writers (BaseSkillWriter / BaseProfileWriter) land later in the project; until then build a thin wrapper over MarkdownWriter directly.


3. Markdown daily-log: 4 steps

3.1 Frontmatter schema — infra/persistence/markdown/mds/<name>.py

"""Episode daily-log frontmatter."""

from __future__ import annotations

import datetime as _dt
from typing import ClassVar, Literal

from everos.core.persistence.markdown import UserScopedFrontmatter


class UserEpisodeDailyFrontmatter(UserScopedFrontmatter):
    """``users/<u>/episodes/episode-<YYYY-MM-DD>.md``."""

    ENTRY_ID_PREFIX: ClassVar[str] = "ep"
    DIR_NAME: ClassVar[str] = "episodes"
    FILE_PREFIX: ClassVar[str] = "episode"

    type: Literal["user_episode_daily"] = "user_episode_daily"
    date: _dt.date
    entry_count: int = 0
    last_appended_at: _dt.datetime | None = None

For agent-track kinds subclass AgentScopedFrontmatter instead. If user-track and agent-track share a kind name (e.g. memcell), give each a distinct ENTRY_ID_PREFIX (e.g. umc vs amc) so reverse lookup is unambiguous.

3.2 Re-export — mds/__init__.py

from .episode import UserEpisodeDailyFrontmatter as UserEpisodeDailyFrontmatter

3.3 Business writer — infra/persistence/markdown/writers/<name>.py

"""Episode appender."""

from __future__ import annotations

from pathlib import Path

from everos.core.persistence import MarkdownReader

from ..mds import UserEpisodeDailyFrontmatter
from .base import BaseDailyWriter


class UserEpisodeAppender(BaseDailyWriter):
    schema = UserEpisodeDailyFrontmatter

    # OPTIONAL: override the count strategy. Default is len(entries);
    # override to trust the frontmatter field instead.
    def _current_count(self, path: Path) -> int:
        if not path.exists():
            return 0
        return MarkdownReader.read(path).frontmatter.get("entry_count", 0)

3.4 Re-export — writers/__init__.py

from .episode import UserEpisodeAppender as UserEpisodeAppender

Done — usage

from everos.infra.persistence.markdown.writers import UserEpisodeAppender

appender = UserEpisodeAppender(memory_root)
eid = appender.append("u_jason", "I went to the doctor today.")
# → users/u_jason/episodes/episode-<today>.md
# → entry markers carry an auto-generated EntryId (e.g. ep_20260507_001)

4. (Optional) SQLite table — 4 steps

Skip this section if the kind doesn't need structured state beyond markdown.

4.1 Schema — infra/persistence/sqlite/tables/<name>.py

from everos.core.persistence.sqlite import BaseTable, Field


class EpisodeState(BaseTable, table=True):
    __tablename__ = "episode_state"  # type: ignore[assignment]

    id: int | None = Field(default=None, primary_key=True)
    entry_id: str = Field(index=True, unique=True)
    cluster_id: str | None = Field(default=None, index=True)
    status: str = Field(default="active")

BaseTable already provides created_at / updated_at (auto-bumped).

4.2 Re-export — tables/__init__.py

from .episode import EpisodeState as EpisodeState

4.3 Repo — infra/persistence/sqlite/repos/<name>.py

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

from everos.core.persistence.sqlite import RepoBase

from ..sqlite_manager import get_session_factory
from ..tables import EpisodeState


class _EpisodeStateRepo(RepoBase[EpisodeState]):
    model = EpisodeState

    def _factory_lookup(self) -> async_sessionmaker[AsyncSession]:
        return get_session_factory()


episode_state_repo = _EpisodeStateRepo()

4.4 Re-export — repos/__init__.py

from .episode import episode_state_repo as episode_state_repo

5. (Optional) Derived index — 4 steps

Skip this section if the kind doesn't need vector / BM25 / hybrid retrieval.

5.1 Record declaration — infra/persistence/records/<name>.py

from typing import ClassVar

from everos.core.persistence.lancedb import BaseLanceTable, Vector


class EpisodeIndex(BaseLanceTable):
    TABLE_NAME: ClassVar[str] = "episode_index"
    PRIMARY_KEY: ClassVar[tuple[str, ...]] = ("app_id", "project_id", "id")

    id: str
    app_id: str = "default"
    project_id: str = "default"
    entry_id: str
    text: str
    tags: list[str]
    vector: Vector(1024) | None = None  # type: ignore[valid-type]

TABLE_NAME / BM25_FIELDS / PRIMARY_KEY are the backend-neutral declarations every index backend reads (IndexRecordMeta). PRIMARY_KEY has no default: check_primary_keys refuses to start, and LanceDB upsert refuses to write, a table that does not declare one. id is unique only within (app_id, project_id), so every business table declares the three-column key — LanceDB merges on it, Milvus keys the collection on a composed __pk and rejects key components containing /. Vector(N) must match your embedding dimension.

5.2 Re-export — records/__init__.py

from .episode import EpisodeIndex as EpisodeIndex

5.3 Repo — infra/persistence/lancedb/repos/<name>.py

from lancedb import AsyncTable

from everos.core.persistence.lancedb import LanceRepoBase
from everos.infra.persistence.records import EpisodeIndex

from ..lancedb_manager import get_table


class _EpisodeIndexRepo(LanceRepoBase[EpisodeIndex]):
    schema = EpisodeIndex

    async def _table_lookup(self) -> AsyncTable:
        return await get_table(self.schema.TABLE_NAME, self.schema)


episode_index_repo = _EpisodeIndexRepo()

Business code writes through the everos.infra.persistence.index facade: upsert(records) merges on the declared key and is the only write; look a row up with find_one_where(primary_key(app_id, project_id, row_id)).

5.4 Re-export — repos/__init__.py

from .episode import episode_index_repo as episode_index_repo

6. (Future) Skill-named & single-file markdown strategies

When the new memory kind needs:

  • skill-named files (one file per named skill, in-place rewrite) — wait for BaseSkillWriter, or use MarkdownWriter.write_markdown directly with a thin wrapper.
  • single-file rewrite (one fixed file like user.md) — wait for BaseProfileWriter, same fallback.

These strategies do not use entry markers; their frontmatter schema does not need ENTRY_ID_PREFIX (only id / type / schema_version plus the scope mixin fields).


7. Verification checklist

  • make lint — ruff + import-linter clean
  • make test — existing manager / lifespan / writer tests still pass
  • When the kind crosses multiple layers:
    • markdown entry id is the join key for the sqlite / lancedb rows
    • business code reads only via the repo singleton (no raw engine access in service / memory)
    • cascade daemon (when it lands) can rebuild sqlite / lancedb from md alone — keep md as the truth

Tests by layer:

Tests forLocation
Markdown frontmatter schematests/unit/test_infra/test_markdown/test_mds/
Markdown business appendertests/unit/test_infra/test_markdown/test_writers/
SQLite RepoBase logictests/unit/test_core/test_persistence/test_sqlite/
SQLite manager / lifespantests/unit/test_infra/test_sqlite/
LanceDB LanceRepoBase logictests/unit/test_core/test_persistence/test_lancedb/
LanceDB manager / lifespantests/unit/test_infra/test_lancedb/

8. Common pitfalls

MistakeSymptomFix
Forgot ENTRY_ID_PREFIX / DIR_NAME / FILE_PREFIX on a daily-log schemaBaseDailyWriter.__init__ raises TypeErrorAdd all three ClassVars
Same ENTRY_ID_PREFIX on user + agent variantsMemoryLayout.locate_for_entry collision errorUse distinct prefixes (e.g. umc vs amc)
Imported RepoBase from infra.persistence.sqliteImportErrorLives in core.persistence.sqlite (moved earlier)
Skipped one of the four files (schema / writer / table / repo)One side silently absentRe-export both/all from each __init__.py
Vector(N) mismatched with embedding dimLanceDB raises on insertMake N exactly match the model output
Imported MemoryLayout from a writer (infra)import-linter fails (infra → memory reverse dep)Use MemoryRoot (in core) and let the schema's ClassVars drive paths
Hand-rolling datetime.now() instead of today_with_timezone()Day-boundary drift across timezonesAlways go through everos.component.utils.datetime

Background

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

commit

무료요약 있음

현재 Git 변경 사항과 최근 기록을 살펴본 뒤 Conventional Commits 형식으로 범위가 분명한 커밋을 만듭니다. 관련 파일만 선택해 스테이징하고 커밋 훅 오류에 대응하도록 안내합니다.

  • 변경 사항을 검토하고 커밋하기
  • 차이에서 관련 파일만 선택하기
  • 규칙에 맞는 커밋 메시지 작성하기
EverMind-AI/EverOS1.4만2026년 10월 29일 업데이트

new-branch

무료요약 있음

업데이트된 `main`에서 용도에 맞는 Git 브랜치를 만들고, 변경 유형에 따라 기능·수정·문서·CI·유지 관리·리팩터링 접두사를 선택합니다. 뒤에는 짧은 소문자 하이픈 이름을 붙입니다.

  • 새 기능용 브랜치 만들기
  • 버그 수정용 브랜치 만들기
  • 문서 업데이트 브랜치 시작
EverMind-AI/EverOS1.4만2026년 10월 29일 업데이트

pr

무료요약 있음

GitHub CLI `gh`를 사용해 현재 작업 브랜치에서 `main`을 대상으로 저장소 템플릿에 맞춘 풀 리퀘스트를 엽니다. 먼저 `make ci`를 실행하고, 통과하면 브랜치를 푸시한 뒤 변경 요약, 영역, 검증 결과, 체크리스트와 리뷰어 메모를 작성합니다.

  • 완료한 변경 사항의 풀 리퀘스트 만들기
  • 풀 리퀘스트 템플릿 각 항목 작성하기
  • CI와 수동 확인 결과 기록하기
EverMind-AI/EverOS1.4만2026년 10월 29일 업데이트

release

무료요약 있음

everos의 새 버전을 PyPI에 배포하도록 버전과 변경 로그를 정리하고, 태그로 시작되는 자동 작업과 승인 절차를 안내합니다. 업로드 후에는 GitHub Release 초안을 검토하고 게시하는 단계까지 진행합니다.

  • 패치 또는 마이너 버전 배포하기
  • 프리릴리스 배포하기
  • 변경 로그로 릴리스 페이지 준비하기
EverMind-AI/EverOS1.4만2026년 10월 29일 업데이트

EverMind-AI의 스킬 모두 보기

이 스킬의 문제 신고