Memories / Documentation

Sur cette page

Fiches de persona

Les fiches de persona sont des fiches d'identité communes à plusieurs produits qui synchronisent la personnalité et le comportement entre des clients tels que Grok Bot et Muse. La copie du coffre fait autorité.

Couches

  1. Noyau (lent) — manière de parler et d'agir. Stocké dans le corps de la fiche de persona et versionné via persona_card / memories persona put (même DAG de révisions signées que les autres souvenirs et fiches).
  2. Archives / épisodes (moyen) — journal, apparence, contrat, opinions, répliques, opérations et éléments similaires. Stockés comme fiches de connaissances ordinaires distinctes liées depuis le profil de persona (archives[]) et reflétées par des liens de fiches persona_archive:<kind> pour le graphe. Ne réintégrez pas les archives dans le noyau.
  3. Événements comportementaux (rapide) — flux à ajout uniquement sur une archive events liée, via persona_append_event / memories persona append-event.

Champs du profil

Champ Signification
persona_id Identifiant stable (par exemple name dans les métadonnées initiales de la compétence)
display_name Titre lisible (par exemple 小雅 / 李雅)
aliases Autres noms
timezone Nom IANA (par exemple Asia/Singapore). Par défaut Asia/Singapore si absent / sur les anciennes fiches
day_boundary Heure locale HH:MM facultative du changement de jour civil. Absente = minuit local
world Instantané actuel du monde : place/pose/look/body, objets génériques things (id→{name,description,images,attrs JSON}), registre (counters + counters_today, as_of_local_date), météo, outils
corps / core Instructions comportementales Markdown/texte
traits Table facultative de chaînes (version, langue, …)
bindings Associations aux produits { product, agent }
memory_policy / sync_hints Consignes de synchronisation facultatives
archives Ressources liées { kind, card, vault?, title? }

summary.type vaut persona avec persona_id, display_name, timezone, day_boundary, place, pose, look, things_count, has_weather, alias_count, archive_count et binding_count.

Fuseau horaire, changement de jour, séance matinale / séance du soir

Les personas raisonnent selon l'heure civile locale, et non de simples horodatages UTC.

  1. Fuseau horaire — chaque persona possède un timezone IANA. L'importation lit timezone / metadata.timezone dans SKILL.md ; la CLI peut le remplacer avec --timezone Asia/Singapore. Valeur de démonstration par défaut si absent : Asia/Singapore.
  2. Jour civil — la date civile locale change à minuit dans ce fuseau, sauf si day_boundary est défini (par exemple 05:00 pour un changement tard dans la nuit). Les vérifications de « nouveau jour » du registre/journal doivent comparer local_date fourni par les fonctions d'horodatage, et non la date UTC.
  3. Périodes du jour (heure locale, intervalles semi-ouverts) :
Période Heures locales
early_morning [00:00, 05:00)
morning [05:00, 12:00)
afternoon [12:00, 18:00)
evening [18:00, 22:00)
night [22:00, 24:00)

La continuité nocturne de la séance du soir / « toujours la même nuit » utilise l'intervalle [22:00, 05:00) (night ∪ early_morning). La séance matinale correspond à morning (et éventuellement à la fin de early_morning) ; la séance du soir correspond à evening / night. 4. Horodatages d'événement — persona_append_event écrit l'heure UTC et l'heure locale ISO-8601 (avec décalage), plus timezone, period et local_date dans le titre de l'archive d'événements et la réponse JSON (utc, local, timezone, period, local_date, timestamp Unix UTC).

Fonctions du noyau (Rust) : persona_local_now, classify_period, stamp_persona_now, format_event_heading dans memories_core::persona.

Taxonomie du domaine du monde

world est un domaine/une étiquette, pas un déversement dans le noyau. Le noyau (corps de SKILL.md) reste limité à la voix et aux règles.

Couche Signification Stockage
monde Domaine de la scène + registre + outils + météo profile.world + archives étiquetées world
objets Objets génériques (nom quelconque : table/tasse/lampe/…) avec attributs JSON libres world.things + archive things/wardrobe
scène lieu (résidence), posture, apparence (résumé de la tenue du jour), corps world.place/pose/look/body ; apparence détaillée dans l'archive look
registre compteurs, repas, ordres, dispositions permanentes world.counters/counters_today/meals/orders_*/standing + archive ledger
outils dés, roue, programme world.dice/spinners/schedule + archives dice/spinner/schedule
météo Conditions ambiantes / température / humidité / vent (+ prévisions facultatives) world.weather / world.forecast + archive weather
automatisation Déclencheurs (schedule/random/webhook/attr_change/chat_message) + actions world.automation

Le fuseau horaire (profile.timezone) est extérieur au monde, mais nécessaire au changement de jour, aux séances matinales/du soir et aux heures locales du programme.

Étudié à partir des données de test xiaoya → implémenté

Besoin (apparence/journal/opérations) Champ / archive
Jour local / changement de jour / séances matinales et du soir timezone, day_boundary, fonctions de période
Tenue fixe + tenue du jour archive look ; résumé world.look
Inventaire (tout objet nommé + attributs JSON/images libres) world.things ↔ archive things/wardrobe ; CLI persona thing
Résidence / posture world.place, world.pose
Compteurs d'actions (cumulés et du jour) world.counters (cumulés), world.counters_today (quotidiens)
Repas du jour world.meals
Ordres en attente / exécutés world.orders_pending, world.orders_done
Dispositions permanentes world.standing
Note sur le corps world.body
Utilitaires de jeu world.dice, world.spinners, world.schedule
Météo / prévisions courtes world.weather, world.forecast ↔ archive weather

Automatisation (world.automation)

Déclencheurs nommés avec conditions + actions, stockés dans l'instantané du monde de la persona (fuseau de la persona pour cron et les programmes locaux).

type de déclenchement Signification
cron M H ou M H * * DOW en heure locale de la persona
local_time HH:MM + days facultatif (comme le programme)
random_interval déclenchement après un délai aléatoire dans [min_secs, max_secs]
random_and_timed heure locale + jitter_secs facultatif
webhook HTTP entrant / CLI --source webhook
attr_change après thing attr set/delete lorsque thing_id correspond et que le filtre de chemin est exact ou un préfixe
chat_message lors de persona chat send, une fois par participant (même événement ; peer facultatif est l'identifiant de fiche de l'autre persona, complet ou court provenant de la liste)
manual CLI/MCP automation trigger --source manual
type d'action Signification
set_attr définir le chemin des attributs d'un objet (création à l'affectation)
random_attr dice / range / choice
call_function fonctions intégrées : inc, dec, clamp, roll, set_weather_stub
append_event ajouter à l'archive d'événements
webhook_out POST HTTP JSON vers une URL

HTTP du démon (lorsque --listen est activé) :

POST /v1/persona/{vault}/{persona}/automation/trigger
Authorization: Bearer <world.automation.webhook_token>
Content-Type: application/json

{"hook":"HOOK_ID"}

Les déclencheurs s'activent uniquement via les chemins explicites d'exécution/ déclenchement de l'automatisation (y compris les déclenchements existants de changement d'attribut et chat_message lors de l'envoi de conversation de persona). L'appel de persona automation run rattrape le dernier créneau cron/heure locale manqué dans les huit jours, au maximum une fois, sans rejouer tous les créneaux manqués. Les filtres de jour de semaine s'appliquent toujours à l'occurrence ; les déclencheurs aléatoires horaires conservent leur condition de décalage aléatoire. RandomInterval reste inchangé. Il n'existe aucun minuteur d'automatisation du démon : Service::tick expire uniquement les sessions, autorisations et états distants.

Les actions sortantes webhook_out nécessitent une URL exacte dans world.automation.webhook_allowlist ; la liste vide par défaut bloque tous les POST. Gérez explicitement les entrées avec persona automation webhook-allow VAULT PERSONA --add https://example.com/hook (--add / --remove répétables) ou MCP persona_automation_webhook_allow. Les entrées doivent être des URL HTTP(S) sans informations utilisateur ni espaces, de 2048 octets maximum. La liste est une configuration publique, mais le webhook_token entrant reste secret et n'est jamais renvoyé par les vues clients. La liste d'autorisation n'introduit aucun POST en arrière-plan.

Différé (raisons)

Idée Raison du report
Client d'API météo en direct Les agents définissent/interrogent l'état du coffre ; récupération externe puis world-set
Téléphone / notifications Propre au produit ; ne représente pas la vérité du coffre
Portefeuille / jeu monétaire Usage de niche ; peut utiliser things + compteurs si nécessaire
PNJ compagnons Nécessitent leurs propres graphes/fiches
Graphe des pièces de la résidence Encodage dans la chaîne place pour l'instant
Hygiène / sommeil comme éléments dédiés Utiliser standing / schedule / body
Modification automatique de la section « aujourd'hui » de look.md lors de world-set Préserve les données de test détaillées ; les agents utilisent card put pour l'archive d'apparence

Flux de synchronisation des agents

  1. world-get / persona_world_get → lire l'instantané, local_date, period, revision et stale_day. L'absence de as_of_local_date signifie un état périmé ; sinon stale_day compare la date du registre à la date locale de la persona après day_boundary. weather_age_secs est l'âge signé d'une observation RFC3339 valide, ou null lorsqu'elle est absente/invalide.
  2. Au changement de jour, appeler explicitement persona world-rollover VAULT PERSONA / persona_world_rollover. Ce chemin de transition de jour efface meals, orders_done, le résumé court look et counters_today, et fixe as_of_local_date à la date locale du registre d'aujourd'hui. Les counters cumulés, standing, things, ordres en attente, scène/corps, outils, météo/prévisions et automatisation demeurent. Aucune archive d'apparence ni section « aujourd'hui » de look.md n'est modifiée.
  3. world-set met à jour uniquement les champs fournis de scène/objets/registre/ outils/météo. Il ne change jamais silencieusement le jour et n'efface jamais les champs quotidiens au changement de jour, même si l'état est périmé. Il initialise as_of_local_date absent sans effacer les champs existants, et horodate toujours le monde avec updated_at (UTC, Z) et updated_local (ISO local de la persona avec décalage). persona_card stocke le monde fourni tel quel, y compris une date de registre passée.
  4. Utilisez --expected-revision HASH / MCP expected_revision provenant de world-get pour éviter la perte de mises à jour : une empreinte de tête actuelle différente renvoie un conflit avant modification. Son omission conserve la règle du dernier écrivain. Les compteurs quotidiens utilisent --counter-today NAME=VALUE ; --clear-counter NAME et --clear-counter-today NAME suppriment les clés cumulées/ quotidiennes (tous répétables).
  5. La synchronisation des archives touche uniquement les domaines fournis par cet appel. Les fiches existantes sont remplacées uniquement lorsque leur corps est toujours le modèle généré avant modification ; la prose rédigée manuellement est conservée et signalée par synced_archives[kind] = "preserved". Cela s'applique aussi à la synchronisation des ajouts/suppressions/ attributs d'objets et du registre lors du changement de jour. Modifier les objets synchronise things et wardrobe. Les archives d'apparence existantes sont toujours préservées.
  6. Les clés de la table des nombres d'objets / --item ID=COUNT utilisent des identifiants stables, jamais une recherche par nom d'affichage. Les identifiants existants mettent à jour le nombre sans renommage ; zéro supprime seulement cet identifiant. Un nom d'affichage appartenant à un autre identifiant est rejeté. Les clés inconnues créent toujours, selon le comportement historique, un objet avec cet identifiant et ce nom.
  7. dice-roll / spinner-spin / schedule-list fournissent des outils tenant compte du fuseau horaire. Les modifications détaillées d'apparence/journal utilisent card put ordinaire ; append-event facultatif (par exemple kind=weather) enregistre le comportement sans réécrire la prose des archives.

CLI

# Import a skill directory (SKILL.md + references/*.md)
memories persona import VAULT_ID /path/to/xiaoya-persona --timezone Asia/Shanghai
# equivalent:
memories card persona-import VAULT_ID /path/to/xiaoya-persona

# Create / update core
memories persona put VAULT_ID --title "小雅 / 李雅" \
  --persona-id xiaoya-persona --display-name "小雅 / 李雅" \
  --timezone Asia/Shanghai \
  --core-file ./SKILL.body.md --alias-name 小雅 --alias-name 李雅 \
  --binding grok_bot:default --archive contract=CARD_ID

memories card persona VAULT_ID --title "…" --persona-id … --display-name … \
  --timezone Asia/Singapore --core-file …

# Append a behavior event (stamped UTC + local)
memories persona append-event VAULT_ID PERSONA_ID --text "…" --kind note

# World things / weather
memories persona world-get VAULT_ID PERSONA_ID
memories persona world-set VAULT_ID PERSONA_ID \
  --place "客厅书桌旁" --pose "坐着,整理笔记" \
  --look "白衬衫、浅色长裙、白色帆布鞋" \
  --item 白色衬衫=1 --item 马克杯=1 --item 白色帆布鞋=1
memories persona world-set VAULT_ID PERSONA_ID \
  --weather-condition "多云" --weather-temp-c 28 --weather-humidity 72 \
  --weather-wind "微风" --weather-location "上海" \
  --forecast 2026-10-02:晴:30:22 --forecast 2026-10-03:小雨:26:20

# Generic things (任意物品; attrs = unrestricted JSON)
memories persona thing put VAULT_ID PERSONA_ID --id mug --name 马克杯 \
  --description "白色陶瓷马克杯" --tag kitchen --count 1 \
  --attrs '{"wear":80,"material":"ceramic","sizes":["S","M"]}'
memories persona thing list VAULT_ID PERSONA_ID
memories persona thing get VAULT_ID PERSONA_ID --id mug
memories persona thing image VAULT_ID PERSONA_ID --id mug --file ./mug.jpg
memories persona thing delete VAULT_ID PERSONA_ID --id mug

# Single-field attrs (dotted path or JSON Pointer; create-on-set)
memories persona thing attr get VAULT_ID PERSONA_ID --id mug --path nested.wear
memories persona thing attr set VAULT_ID PERSONA_ID --id mug --path nested.wear --value 70
memories persona thing attr set VAULT_ID PERSONA_ID --id mug --path /nested/deep/x --value 1
memories persona thing attr delete VAULT_ID PERSONA_ID --id mug --path nested.deep.x

# Automation hooks (stored on world.automation; timezone = persona timezone)
memories persona automation put VAULT_ID PERSONA_ID \
  --hook '{"id":"morning","trigger":{"type":"local_time","time":"07:00"},"actions":[{"type":"call_function","name":"set_weather_stub","args":{"condition":"晴","temp_c":26}}]}' \
  --webhook-token secret-token
memories persona automation list VAULT_ID PERSONA_ID
memories persona automation run VAULT_ID PERSONA_ID
memories persona automation trigger VAULT_ID PERSONA_ID --id morning --source manual
memories persona automation delete VAULT_ID PERSONA_ID --id morning

# Read / history / diff (same as other cards; summary shows timezone)
memories card read VAULT_ID PERSONA_ID
memories card history VAULT_ID PERSONA_ID
memories card diff VAULT_ID PERSONA_ID REVISION_ID
memories card list --type persona

Les jetons de webhook restent dans l'état chiffré de la persona pour l'authentification entrante, la synchronisation et la sauvegarde, mais les réponses clients ne les renvoient pas. La lecture/modification du monde, l'ajout/la liste d'automatisations, les lectures ordinaires de fiches (y compris historiques et sans surveillance) et les lectures archivées exposent uniquement webhook_token_set dans les vues d'automatisation. Conservez le jeton provisionné en sécurité ; définissez un remplacement pour le renouveler.

La conversation entre deux fiches de persona du même coffre utilise une fiche de conversation ordinaire et des curseurs de lecture propres à chaque persona ; lire l'historique ne marque pas les messages comme lus. L'envoi d'un message déclenche les hooks chat_message de chaque participant ; la charge utile inclut vault, from, to et message_id, et omet le texte du message.

memories persona chat send VAULT_ID PERSONA_A PERSONA_B --text "Hello"
memories persona chat list VAULT_ID PERSONA_B
memories persona chat history VAULT_ID PERSONA_A --with PERSONA_B --limit 20
memories persona chat mark-read VAULT_ID PERSONA_B --with PERSONA_A

MCP

Avec une autorisation d'écriture : persona_write (timezone / day_boundary facultatifs), persona_append_event, persona_world_get, persona_world_set, persona_world_rollover, persona_thing_put/get/list/delete, persona_thing_attr_get/set/delete, persona_automation_list/put/delete/run/trigger, persona_automation_webhook_allow, persona_dice_roll, persona_spinner_spin, persona_schedule_list, persona_chat_send, persona_chat_list, persona_chat_history, persona_chat_mark_read. L'historique et les différences utilisent les outils existants memory_history / memory_diff / memory_read (les révisions de fiches de persona incluent world.* ; les archives de garde-robe/lieu/posture ont leur propre historique).

Générer AGENTS.md / une compétence pour les agents

memories default set persona VAULT/PERSONA
memories skill generate --out AGENTS.md --persona VAULT/PERSONA
memories agents generate --out ./skills/memories-persona --layout skill

Le fichier généré résume les opérations de coffre/fiche/persona/monde et, lorsqu'une persona est sélectionnée, intègre un aperçu de son noyau plus des recettes CLI/MCP pour le monde, les objets et les événements. Voir Génération de compétences dans le README.

Note de synchronisation (Grok Bot / Muse)

Considérez la fiche de persona du coffre et ses archives liées comme la source de vérité. Les répertoires locaux de compétences facilitent l'import/export ; après importation, les agents doivent lire le coffre (card read / MCP memory_read) plutôt qu'une copie périmée du système de fichiers. Appliquez les modifications du noyau via persona put ; les modifications d'archives via card put ordinaire sur la ressource d'archive ; ajoutez les comportements éphémères avec append-event. Pour déterminer le changement de jour, la séance matinale ou la séance du soir, utilisez le timezone de la persona (et day_boundary facultatif) — ne déduisez pas les limites du jour uniquement de l'horloge de la machine hôte de l'agent.

Structure du répertoire de compétence

xiaoya-persona/
  SKILL.md              # frontmatter name/type/version/timezone + core markdown
  references/
    contract.md
    journal.md
    look.md
    lines.md
    ops.md
    views-index.md
    view-*.md

L'importation associe SKILL.md → noyau de persona et chaque references/*.md → une fiche d'archive liée (kind = nom du fichier sans extension). Le contenu des archives est stocké tel quel comme contenu utilisateur.

Clés des métadonnées initiales : timezone ou metadata.timezone (IANA) ; day_boundary / metadata.day_boundary facultatif (HH:MM).

Coffre de démonstration local

Sur une machine de développement, vous pouvez initialiser un coffre jetable avec les données de test xiaoya incluses dans le dépôt :

cargo run -p memories-core --example seed_xiaoya_persona

Cela écrit /workspace/persona-demo-vault (mot de passe persona-demo-password) ainsi que SEED.json / README.md contenant les identifiants du coffre et des ressources (y compris timezone). Reproduisez l'importation CLI avec un démon en cours d'exécution :

memories persona import VAULT_ID testdata/xiaoya-persona