ReMe/docs4/expert.yaml
jinli.yl c8e96b5ae8 up
2026-05-15 23:17:49 +08:00

378 lines
13 KiB
YAML

app_name: reme-expert
enable_logo: false
log_to_console: true
log_to_file: false
# Expert-exposure profile: every memory_* read + write primitive plus
# `sync` (event log) are surfaced as MCP tools. The agent picks whatever
# it needs — finest granularity, no opinion enforced beyond the
# wikilink-uniqueness gate.
#
# **No `ingest` tool.** Cold-path R-M-W in expert mode is owned by the
# host agent (Claude Code, with the `reme-expert` SKILL loaded), not by
# reme2's internal ReActAgent. The reme-expert plugin spawns a
# `reme-distiller` subagent at SessionEnd to handle distillation in its
# own context window, using the same memory_* primitives.
#
# Use when you want the agent to manage memory directly with full
# control. For an opinionated minimal surface (where reme2's internal
# Ingestor owns R-M-W), see ./service.yaml.
service:
backend: mcp
transport: stdio
sidecar_http: true
sidecar_http_host: "127.0.0.1"
sidecar_http_port: 8765
sidecar_info_path: "./vault/.reme/sidecar.json"
jobs:
# -- Write entry points ------------------------------------------------
- backend: base
name: sync
description: |
Hot-path event sync: idempotent upsert of an event FOLDER under
`events/{date}/{name}/`. The folder contains the index `{name}.md`
(Event schema, frontmatter + narrative + Materials footer) plus
any raw materials you pass — conversation snippets, tool outputs,
data dumps. The watcher indexes everything inside.
CONTINUITY MODEL: pick a stable `name` per logical thread and
call `sync` repeatedly through the task. Each call extends the
same folder:
* new `content` → appended under a `## Update — {iso}` section
* new `materials` → siblings (auto-suffix on filename collision)
* `topics` + `tags` merged (union) into frontmatter
* Materials footer regenerated to list every artifact in the folder
First call (folder doesn't exist) → CREATE; subsequent calls with
the same `name` while the event is `status: active` → APPEND.
If the event is `status: distilled` / `archived`, `sync` REFUSES
and returns a `suggested_name` so you start a fresh thread
instead of mutating prior cognition.
Zero LLM cost. Call CONTINUOUSLY through a task as facts land,
and especially at PreCompact to dump verbose raw text into
`materials` before context truncation loses it. The folder is
the unit `ingest` later reads from.
parameters:
type: object
required: [name]
properties:
name:
type: string
description: "kebab-case event identifier (folder + index stem). Reuse the same name across calls in one thread to keep extending the same folder."
description: { type: string, description: "one-line summary for index frontmatter (set on initial create only)" }
content: { type: string, description: "markdown body. Initial create: the body. Subsequent calls: appended as a `## Update — {iso}` section." }
topics:
type: array
items: { type: string }
description: "related topic wikilinks ('[[X]]' or '[[topics/X/X]]'). Unioned into frontmatter on append."
tags: { type: array, items: { type: string }, description: "free-form tags; unioned on append." }
materials:
type: array
description: "Raw artifacts written as siblings of the index inside the event folder. Filenames must be safe (letters/digits/dot/underscore/dash). On filename collision with an existing artifact, auto-suffixes (foo.txt → foo-2.txt)."
items:
type: object
required: [filename, content]
properties:
filename: { type: string, description: "e.g. 'raw-prompt.md', 'tool-output.txt', 'snapshot.json'" }
content: { type: string }
on_date: { type: string, description: "ISO date for events/{date}/ bucket; defaults to today" }
origin_session_id: { type: string, description: "set on initial create only" }
steps:
- backend: sync
# -- Read tools --------------------------------------------------------
- backend: base
name: memory_search
description: "Hybrid (vector + keyword) search over chunks."
parameters:
type: object
required: [query]
properties:
query: { type: string }
max_results: { type: integer, default: 5 }
min_score: { type: number, default: 0.1 }
paths: { type: array, items: { type: string } }
tags: { type: array, items: { type: string } }
exclude_paths: { type: array, items: { type: string } }
steps:
- backend: memory_search
- backend: base
name: memory_graph_search
description: |
Three-way fusion search: vector + keyword + graph (BFS over wikilinks).
Pulls in chunks reachable through linked topics/events that pure
relevance search would miss.
parameters:
type: object
properties:
query: { type: string }
max_results: { type: integer, default: 5 }
min_score: { type: number, default: 0.0 }
graph_depth: { type: integer, default: 1 }
seeds: { type: array, items: { type: string } }
paths: { type: array, items: { type: string } }
tags: { type: array, items: { type: string } }
exclude_paths: { type: array, items: { type: string } }
steps:
- backend: memory_graph_search
- backend: base
name: memory_get
description: "Read frontmatter + body of a single file."
parameters:
type: object
required: [path]
properties:
path: { type: string }
include_chunks: { type: boolean, default: false }
steps:
- backend: memory_get
- backend: base
name: memory_list
description: |
List indexed files filtered by frontmatter exact-match, tags, or
path prefix. Returns {items: [{path, metadata}], count}.
parameters:
type: object
properties:
metadata: { type: object }
tags: { type: array, items: { type: string } }
path_prefix: { type: string }
limit: { type: integer, default: 100 }
steps:
- backend: memory_list
- backend: base
name: memory_backlinks
description: "Files linking TO the given path (with edge predicates)."
parameters:
type: object
required: [path]
properties:
path: { type: string }
steps:
- backend: memory_backlinks
- backend: base
name: memory_links
description: "Files the given path links to (resolved, with edge predicates)."
parameters:
type: object
required: [path]
properties:
path: { type: string }
steps:
- backend: memory_links
- backend: base
name: memory_resolve_wikilink
description: |
Resolve a `[[target]]` wikilink to a vault path. Stem-form (`X`)
consults the file_store's stem index; path-form (`a/b` or `a/b.md`)
is anchored at the vault root.
parameters:
type: object
required: [wikilink]
properties:
wikilink: { type: string }
steps:
- backend: memory_resolve_wikilink
- backend: base
name: memory_count_tokens
description: "Estimate token count for a file body or raw text."
parameters:
type: object
properties:
path: { type: string }
text: { type: string }
steps:
- backend: memory_count_tokens
- backend: base
name: memory_lint
description: |
Read-only projection of the Maintainer's lint findings. Walks the
indexed files (optionally restricted to a path prefix) and returns
structural issues — broken wikilinks, schema violations, stem
collisions. Never mutates. The agent decides what to do with each
finding using existing memory_* primitives (rename, update,
property_update, archive, delete).
parameters:
type: object
properties:
target_prefix:
type: string
description: "restrict scan to relpaths starting with this prefix (e.g. 'events/2026-05-09/'). Empty string scans the whole vault."
steps:
- backend: memory_lint
# -- Write primitives (raw building blocks) ----------------------------
- backend: base
name: memory_create
description: |
Create a new file (raw primitive — no LLM reasoning). Prefer
`ingest` when you want an LLM curator. Two gates run unless
`force=true`: (1) path template — must be `topics/{folder}/{name}.md`,
`events/{date}/{name}/...`, or `Archive/...`; (2) wikilink uniqueness
— `[[stem]]` must resolve to ≤1 path post-create.
parameters:
type: object
required: [path]
properties:
path: { type: string }
metadata: { type: object }
content: { type: string }
overwrite: { type: boolean, default: false }
force:
type: boolean
default: false
description: "bypass BOTH the path-template gate and the wikilink-uniqueness gate. Use only when you intentionally need a non-template path or accept the ambiguity."
steps:
- backend: memory_create
- backend: base
name: memory_update
description: |
Edit-style content update: replace `old_string` with `new_string`
in the file body. For frontmatter changes use `memory_property_update`.
parameters:
type: object
required: [path, old_string, new_string]
properties:
path: { type: string }
old_string: { type: string }
new_string: { type: string }
replace_all: { type: boolean, default: false }
steps:
- backend: memory_update
- backend: base
name: memory_property_update
description: |
Update a single YAML frontmatter key. value=null deletes the key.
When key='status', enforces the active → distilled → archived
single-direction state machine (skip / reverse refused). Pass
`force=true` to bypass.
parameters:
type: object
required: [path, key]
properties:
path: { type: string }
key: { type: string }
value: {}
force:
type: boolean
default: false
description: "bypass the status state machine (only meaningful when key='status')."
steps:
- backend: memory_property_update
- backend: base
name: memory_rename
description: |
Rename a file and rewrite all incoming `[[wikilink]]` references
across the vault. Refuses on destination conflict or stem ambiguity.
parameters:
type: object
required: [old_path, new_path]
properties:
old_path: { type: string }
new_path: { type: string }
steps:
- backend: memory_rename
- backend: base
name: memory_delete
description: "Delete a file."
parameters:
type: object
required: [path]
properties:
path: { type: string }
steps:
- backend: memory_delete
- backend: base
name: memory_archive
description: |
Archive a file: flip `status: archived` then move under
`<vault>/<archive_dir>/<original_relative_path>`.
parameters:
type: object
required: [path]
properties:
path: { type: string }
archive_dir: { type: string, default: "Archive" }
steps:
- backend: memory_archive
components:
# Ingestor LLM (opt-in). Without this the Ingestor degrades to a
# direct create from explicit `target_path`; edits/renames/deletes
# require the LLM. Uncomment + provide LLM_API_KEY to enable.
#
# as_llm:
# default:
# backend: openai
# model_name: ${LLM_MODEL_NAME:-gpt-4o-mini}
# api_key: ${LLM_API_KEY}
# client_kwargs:
# base_url: ${LLM_BASE_URL:-https://api.openai.com/v1}
# stream: false
#
# as_llm_formatter:
# default:
# backend: openai
as_token_counter:
default:
backend: estimated
# Embedding is opt-in: leave embedding_model="" on file_store to run
# keyword-only; uncomment + flip to "default" to enable hybrid search.
#
# embedding_model:
# default:
# backend: openai
# model_name: ${EMBEDDING_MODEL_NAME:-text-embedding-3-small}
# dimensions: 1536
# pass_dimensions: false
# enable_cache: true
# max_batch_size: 10
# max_cache_size: 2000
# max_input_length: 8192
file_parser:
md:
backend: md
default:
backend: text
file_store:
default:
backend: local
embedding_model: ""
store_name: "reme"
db_path: "./vault/.reme"
working_dir: "./vault"
file_watcher:
default:
backend: full
file_store: default
default_parser: md
recursive: true
# Retriever (`hybrid`) is a Step, not a pre-instantiated component —
# the `memory_search` / `memory_graph_search` shells build it on
# demand. To tune defaults, pass knobs (`vector_weight`, `graph_*`,
# …) on the step config under each job below.