Files
MyAiDesk/nomifun-tauri/docs/guides/companions.md
T
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

254 lines
14 KiB
Markdown

# Companions
Nomi's virtual companion has grown from "a single nomi" into a **multi-companion
family**: you can create several companions, use them side by side, raise
them separately, and give each its own name, character, persona, and
chat model. Each companion can also be bound to its own **dedicated knowledge
bases** (turning it into a finance companion, a literature companion, a coding companion,
…), while every companion **shares one memory hub** — collection and learning
run as a single global pipeline, so whatever one companion learns, the whole
family remembers. Memories, companions, and knowledge bases can each be
packed into a `.zip` bundle for export/import, making machine-to-machine
migration painless.
> The entry point is the **Desktop Companion** page in the sidebar (the `/nomi`
> route); the right-click menu of any desktop companion window ("Open chat")
> deep-links there too.
## Page layout: companion switcher + two tab domains
The top of the Desktop Companion page is the **companion switcher bar**: one card per companion
(character thumbnail + name + level) plus a **New companion** button. The
selected companion drives the **companion-domain** tabs below; everything that is
global lives in the **shared-domain** tabs:
| Domain | Tab | Contents |
| --- | --- | --- |
| Companion domain (follows the switcher) | Overview | **Desktop-companion toggle** + that companion's level / XP / mood + shared stats |
| | Chat | That companion's own companion threads |
| | Model & Knowledge | Chat model picker / **knowledge bindings** |
| | Remote | That companion's IM bots (bound per companion — see the [channels guide](./channels.md)) |
| | Settings | Name / character / persona / quiet hours / delete companion |
| Shared domain (one per install) | Memories · Collect · Learn · Suggestions | The shared memory hub (one copy for all companions) |
| | Migrate | Export / import migration bundles (see below) |
## Creating and managing companions
1. Click **New companion** on the switcher bar, pick a name and one of the
six characters (mochi / ink / roux / pixel / bolt / boo).
2. **The first companion automatically becomes the default companion** (its card
carries a "default" badge). The default companion is the fallback whenever
a channel has no explicit binding (see the channels section below).
3. In a companion's **Settings** tab you can rename it at any time (takes
effect immediately), swap the character, tune the persona (preset or
custom), **pick a chat model just for this companion**, and toggle the
desktop companion plus its quiet hours.
4. **Deleting a companion** cascades: its companion conversations, runtime
state (XP, …), and `('companion', companionId)` knowledge bindings are removed
together; if you delete the default companion, the default role moves on
to the next one. Deleting down to zero companions is allowed (the shared
memory hub exists independently of companions — collection and learning
keep running).
On disk each companion is a directory — `{data_dir}/companion/companions/{companion_id}/config.json`,
**the directory is the source of truth** — which is also the unit the
companion bundle exports and imports.
### Multiple desktop companions on screen
Every companion with the desktop-companion switch enabled gets its own desktop
window (transparent, always-on-top, draggable; window label
`companion-{companionId}`). Several can share the screen; keeping it to 5 or fewer
is recommended (each window is an independent WebView instance — the
UI warns about performance beyond that but does not enforce a limit).
Right-click any desktop companion to jump straight to its chat.
## The shared memory hub
All companions share one set of memory facilities under
`{data_dir}/companion/shared/`:
- **Collection** — a single pipeline subscribes to the global event
bus, gathers your working data according to the collect switches,
and writes `shared/events/YYYYMMDD.jsonl`.
- **Learning** — a single learner incrementally distills events into
long-term memories on the configured interval, stored in
`shared/memory.db`. The learning pipeline uses the **learn model
from the shared config** (independent of each companion's chat model — one
pipeline, one budget).
- Memories saved during any companion's chat, and memories produced by
learning, are **visible to every companion** — switch companions mid-stream and
the new one remembers everything that happened before.
### XP and mood attribution
| Source | Credited to |
| --- | --- |
| Learning-run output (scored by events processed + new memories) | **All companions** (the family grows together) |
| Suggestion adopted (+20) | **All companions** |
| Companion chat turn (+2) | Only the companion in that conversation |
| Memory saved during chat (+5) | Only that companion |
**Mood is global**: it is produced by learning runs and stored in
shared state, so all companions share one mood (per-companion mood/personality
divergence is reserved for a later version).
## Binding knowledge bases to a companion
In a companion's **Model & Knowledge tab → Knowledge** section, use the binding
control to mount one or more knowledge bases on that companion (the binding
is `('companion', companionId)`). Scope of effect:
- The companion's **companion chats** and the **channel conversations** it
greets (conversations carrying `extra.companionId`) mount that companion's bound
knowledge bases — searchable during the conversation. Regular
conversations without a companionId keep their conversation-level bindings;
the two are **not merged**.
- **What the agent sees**: bases are mounted at
`{workspace}/.nomi/knowledge/`, and the injected context carries, per
base, the description + an AI digest + "when to consult" hints + a
budgeted table of contents (20 entries per base / 60 global,
directories aggregated beyond that), plus an explicit retrieval
protocol — the agent is told to look things up rather than answer
from memory.
- **Write-back** comes in two modes, briefly:
- **staged** — knowledge produced during a conversation first lands
in the base's `_inbox/` (isolated per conversation) for you to
review on the knowledge page before it is committed;
- **direct** — skips staging and writes straight into the base.
- **AI bootstrap**: the **AI generate** button on the knowledge page
(list edit modal and detail page) calls
`POST /api/knowledge/bases/{id}/autogen` to produce the base's
description and `README.md`; a `.zip` import auto-fills an empty
description. Requires a configured AI provider (`409` otherwise).
- **URL sources**: a base can be created from up to 16 URLs.
*snapshot* mode fetches them at creation, converts each page to
markdown under the base's `snapshots/` (pages over 32 KB are
AI-compressed) and auto-generates the digest — refreshable from the
detail page; *live* mode lets the agent fetch at runtime (engines
without a web tool can call the gateway tool
`nomi_knowledge_fetch_url`). Only public `http/https` URLs are
accepted (SSRF guard).
- The companion can also **grow its own libraries**: the Desktop Gateway
ships seven knowledge tools (list / bindings / create / write /
autogen / fetch-url), and knowledge-deposit tips are built into the
companion's system prompt — a companion or channel chat can create a base
and distill notes into it unprompted. When
`nomi_knowledge_create_base` is called with `urls`, the fetching runs
as a background job — the tool returns immediately, so the agent must
not create the base again just because the snapshots haven't appeared
yet; once the base's description shows up, the fetch + digest
pipeline has finished.
Bind different bases to different companions and you get a "finance companion", a
"literature companion", a "coding companion" — persona, model, and knowledge are
all per-companion, while memory stays shared.
## Binding a companion to a channel
Each IM platform (Telegram / Lark / DingTalk / WeChat) can bind its own
greeter companion for remote messages: open the companion's **Remote**
tab (`/nomi?companion=<id>&tab=remote`) and connect or rebind the bot
there. The binding is still persisted as `assistant.{platform}.companionId`
for legacy platform-level preferences when a channel row has no direct
companion binding. With no binding the **default companion** takes over;
switching the binding resets that channel's active sessions (the next
message is greeted by the new companion); if a bound companion is deleted,
the platform falls back to the default companion and the sessions are
likewise reset. See the "Master Agent mode" section of the
[Channels guide](./channels.md).
> A companionId grants no permissions (memory is shared anyway): it only
> selects persona / model / knowledge mounts — unlike the
> `desktopGateway` marker, which grants gateway tools.
## Export / import: migrating between machines
The shared-domain **Migrate** tab offers three kinds of `.zip` bundles
(the migration UI is desktop-only; paths are picked with the system
dialog):
| Bundle | Contents | Import semantics |
| --- | --- | --- |
| **Memory bundle** | All long-term memories + learning history + mood; **optionally** the raw event data (checkbox) | **Merged with dedup** into local memories (original timestamps and sources preserved) |
| **Companion bundle** | One companion's persona / character / settings / XP + the **name list** of its bound knowledge bases (`knowledge_refs`) | Creates a new companion under a fresh id, name conflicts get a "(2)" suffix; knowledge refs are matched **by name** against local bases to rebuild bindings — unmatched names are listed so you can import those knowledge bundles first and bind manually |
| **Knowledge-base bundle** | Base metadata + the md file tree verbatim | Lands as a new knowledge base, name conflicts get "(2)" |
Migration steps:
1. Old machine: export the **memory bundle** (tick events only if you
want them) → export a **companion bundle** per companion → export a
**knowledge-base bundle** per base.
2. New machine: import the **knowledge-base bundles** first (so companion
bundles can rebuild bindings by name) → then the **companion bundles**
then the **memory bundle**.
3. Check each companion's model setting: model config travels verbatim in
the bundle, but if the new machine has no matching provider it shows
as unconfigured — re-select in settings.
### Privacy boundaries
- `events/*.jsonl` is **raw collected data containing your working
content verbatim** — it is **not** exported by default; it only
enters the memory bundle when you explicitly tick "include raw event
data".
- **Chat history does not travel with the companion bundle**: companion
conversation logs live in the main database; the companion bundle carries
only persona and settings. Chat logs stay on the original machine.
## Automatic migration of legacy data
After upgrading from the single-companion version, the first boot detects the
legacy layout `{data_dir}/companion/nomi/`: if it exists and `companion/shared/`
does not, it is automatically migrated into the shared memory hub plus
a first companion (default name **"Nomi"**, inheriting the existing XP /
persona / character / model / desktop-companion position / companion
threads). The migration is idempotent and re-entrant; on completion a
`.migrated` marker is written into the legacy directory, which is kept
around (to be cleaned up after one release cycle). No manual action is
needed.
## Manual walkthrough checklist
To verify a multi-companion setup end to end, walk through in order:
1. **Create two companions**: create companions A and B, rename them, change
characters; confirm the first one carries the "default" badge.
2. **Bind one base each**: bind knowledge base X to A and Y to B (companion
Model & Knowledge tab → Knowledge).
3. **Retrieval isolation**: in A's and B's chats, ask about content
that only exists in X / Y respectively; confirm A only hits X and B
only hits Y.
4. **Shared memory round-trip**: in A's chat, have it remember
something (save a memory); switch to B's chat and ask — confirm B
knows it.
5. **Export/import roundtrip**: export the memory bundle + A's companion
bundle + base X's bundle; (on a new machine or after a wipe) import
in the order knowledge base → companion → memory; confirm the rebuilt A
has its binding restored automatically and memories merge without
duplicates.
6. **Channel companion switch**: on some channel platform, switch the greeter
companion from A to B; confirm the active sessions are reset and the next
remote message is greeted with B's persona and B's knowledge mounts.
## Routes & API
| What | Where |
| --- | --- |
| List / create companions | `GET/POST /api/companion/companions` |
| Companion detail / update / delete | `GET/PATCH/DELETE /api/companion/companions/{companionId}` |
| Shared config (collect / learn / default companion) | `GET/PATCH /api/companion/config` |
| Per-companion companion threads | `GET /api/companion/companions/{companionId}/companion/threads`, `…/companion/active` |
| Export memory bundle | `POST /api/companion/export/memory` (`{dest_path, include_events}`) |
| Export companion bundle | `POST /api/companion/export/companions/{companionId}` |
| Import memory / companion bundle | `POST /api/companion/import` (dispatched by manifest.kind) |
| Export / import knowledge-base bundle | `POST /api/knowledge/bases/{id}/export`, `POST /api/knowledge/bases/import` |
| Bind a companion to a channel | `POST /api/channel/settings/companion` |
## Related
- [Channels](./channels.md) — channel Master Agent mode and per-platform
companion binding.
- [Data and Storage](../architecture/data-and-storage.md) — the `companion/`
data directory layout.