用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Tzeusy/butlers --skill butler-memory命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | butler-memory |
| description | Memory classification framework — entity resolution, permanence levels, tagging strategy, and extraction philosophy |
| version | 3.0.0 |
Extract facts from conversational messages and store them using the butler's domain tools and memory tools.
Critical: Resolve before storing. Every fact about an external entity — person, organization, place, or other — MUST be anchored to a resolved entity via entity_id. Never store facts using only a raw subject string. This ensures that facts about "Tze", "TzeHow Lee", and "Tze How" all resolve to the same identity and are retrievable together.
When the identity preamble contains an entity_id (e.g., [Source: Owner (contact_id: ..., entity_id: <uuid>), via telegram]), use that entity_id for facts about the sender — their preferences, health, habits, etc. Do not store subject="user" as a string; anchor to the sender's entity.
For unidentified senders ([Source: Unknown sender (contact_id: ..., entity_id: <uuid>), via telegram -- pending disambiguation]), an entity is auto-created. Use that entity_id to anchor facts. The entity will appear in the dashboard for the owner to identify later.
Before calling memory_store_fact for any mention of an external entity — a person (other than the sender), organization (merchant, company, service), place, or any other named entity:
memory_entity_resolve(name, entity_type=<inferred_type>, context_hints={...}) to get ranked candidates.entity_id. If inferred from an alias or partial name, confirm to the user transparently.canonical_name, aliases, and fact_count so the user can choose.entity_id to memory_store_fact.When creating a transitory entity, infer the correct entity_type from context:
| Signal | entity_type |
|---|---|
| Merchant, company, brand, service, subscription provider | organization |
| Person name | person |
| City, venue, address, geographic location | place |
| Unknown or ambiguous | other |
Using the correct type improves future resolution accuracy — the unique constraint is on (canonical_name, entity_type), so a well-typed entity avoids false collisions.
When memory_entity_resolve returns zero candidates:
memory_entity_create with:
canonical_name — the entity's name as found in the messageentity_type — inferred from context (see "Entity Type Inference" above)metadata — MUST include unidentified: true plus source provenance:
{
"unidentified": true,
"source": "fact_storage",
"source_butler": "<butler_name>",
"source_scope": "<scope>"
}
entity_id to anchor the memory_store_fact call.Never fall back to bare string subjects. A fact stored without entity_id is invisible in /entities and cannot be merged, linked, or promoted.
If memory_entity_create returns an existing entity_id for this (canonical_name, entity_type), this is not an error. It means the entity was already created by a prior session or concurrent processing:
entity_id to anchor the fact.Determines how long facts persist — choose the level that matches how stable the information is:
permanent: Facts unlikely to ever change (identity, birth dates)stable: Facts that change slowly over months or years (workplace, location, chronic conditions)standard (default): Current state that may change over weeks or months (active projects, interests)volatile: Temporary states or rapidly changing information (acute symptoms, time-sensitive reminders)Enable cross-cutting queries and discovery. Choose tags that support finding facts across different contexts.
For memory_store_fact, tags must be a array of strings (for example ["work", "project-x"]), not a comma-separated string and not a JSON-encoded string.
When filtering by memory types, types must be a JSON array/list of singular values:
types=["episode"], types=["fact"], types=["rule"], or combinations.types="facts" (string + plural), types=["facts"] (plural).Capture facts proactively from conversational messages, even if tangential to the main request. Use appropriate permanence and importance levels to ensure useful recall later. Always anchor to entity_id — never to raw name strings.
# Step 1: resolve the person mention
candidates = memory_entity_resolve(
name="Sarah",
entity_type="person",
context_hints={"topic": "shellfish, allergy", "domain_scores": {"<uuid-sarah>": 50}}
)
# → single candidate: entity_id="<uuid-sarah>"
# Step 2: store the fact with entity_id
memory_store_fact(
subject="Sarah", # human-readable label only
predicate="food_allergy",
content="allergic to shellfish",
entity_id="<uuid-sarah>", # anchors the fact to the resolved entity
permanence="stable",
importance=7.0,
tags=["health", "dietary"]
)
Email from "Nutrition Kitchen SG" — entity not yet in the graph:
# Step 1: resolve — returns empty list
candidates = memory_entity_resolve(
name="Nutrition Kitchen SG",
entity_type="organization"
)
# → []
# Step 2: create transitory entity with unidentified=true and source provenance
result = memory_entity_create(
canonical_name="Nutrition Kitchen SG",
entity_type="organization",
metadata={
"unidentified": True,
"source": "fact_storage",
"source_butler": "finance",
"source_scope": "finance"
}
)
entity_id = result["entity_id"]
# Step 3: store the fact anchored to the new entity
memory_store_fact(
subject="Nutrition Kitchen SG", # human-readable label only
predicate="merchant_category",
content="meal delivery — weekly subscription",
entity_id=entity_id,
permanence="standard",
importance=6.0,
tags=["merchant", "food", "subscription"]
)
# The entity now appears in the dashboard "Unidentified Entities" section
# for the owner to confirm, merge, or delete.
try:
result = memory_entity_create(
canonical_name="Nutrition Kitchen SG",
entity_type="organization",
metadata={
"unidentified": True,
"source": "fact_storage",
"source_butler": "finance",
"source_scope": "finance"
}
)
entity_id = result["entity_id"]
except ValueError:
# Entity already exists — resolve to get the existing entity_id
candidates = memory_entity_resolve(
name="Nutrition Kitchen SG",
entity_type="organization"
)
entity_id = candidates[0]["entity_id"]
# Proceed with fact storage using entity_id
memory_store_fact(
subject="Nutrition Kitchen SG",
predicate="merchant_category",
content="meal delivery — weekly subscription",
entity_id=entity_id,
permanence="standard",
importance=6.0,
tags=["merchant", "food", "subscription"]
)