中文版: README.zh-CN.md
An AI-agent-driven knowledge management system (KMS) built on a tree-shaped index. Personal project · Status: in progress (as of 2026-06)
Agents continuously build, retrieve, and repair structured knowledge on a SQLite knowledge tree, driven by diagnostic feedback. The design goal is to let knowledge extend along the axons of an index tree like neurons, rather than pile up in flat documents.
- Design Philosophy
- Three-Layer Data Model
- Diagnostic System
- Three Agent Roles
- Agent ↔ KMS Loop
- KMS Toolset
- TUI
- Project Layout
- Quickstart
- Configuration
- Extending
- Known Issues & Future Work
The core assumption: how usable a piece of knowledge is depends not on how detailed it's written, but on whether its position in the index tree is correct. So the system has two co-equal design pillars:
- The index tree comes before the knowledge itself. Agents must inspect and repair the index structure before writing knowledge. Any "I can't find a good place for this" situation is treated as a signal that the index tree needs to grow — not a reason to bypass the signal.
- Diagnostic feedback drives self-healing. After every KMS write, diagnostics are re-run automatically, and the delta in issue counts is injected into the agent's context, keeping the agent repairing structure inside the same interaction loop.
The underlying infrastructure (agent runtime, LLM SDK, type system) is vendored as workspace crates under crates/agentik-core and crates/corpus, so domain crates (kms / kms_tui / dendrite-tools / agent-compose / agent-knowledge) stay focused on the knowledge domain. This keeps the KMS itself thin, readable, and independently testable.
Index (index-tree skeleton)
├── Group node (internal, has children)
└── Knowledge node (leaf, holds one knowledge)
└── Knowledge references ≥ 1 Entity
| Layer | Role | Key constraints |
|---|---|---|
| Entity | the subject being discussed | must have ≥1 nomenclature (language + full name + optional abbreviation) |
| Knowledge | a record about an entity | aspect (single-entity facet) or relation (multi-entity relation); the entities field lists all mentioned entities; content uses [[entity-name]] wiki-style links |
| Index | tree-shaped organization | sibling nodes share abstraction level and don't overlap; internal nodes hold no knowledge directly |
Typical organization:
Programming languages
├── Python
│ ├── Python · Definition ─┐
│ ├── Python · Type system │ Knowledge nodes
│ ├── Python · Package management ─┘
│ └── ...
├── JavaScript
│ ├── React
│ │ ├── React · Core concepts
│ │ └── React · Ecosystem
│ └── Vue.js
│ └── ...
Knowledge granularity is strictly enforced: one title = one facet. Connectors that merge multiple facets are forbidden ("Setup and configuration", "Concepts and architecture", etc. all count as violations). This rule is enforced twice: in the system prompt and in diagnostic rules.
Acts like a compiler type-checker — re-runs automatically after every KMS write and feeds structural violations back to the agent.
pub enum Severity { Error, Warning, Information, Hint }
pub struct Diagnostic {
pub code: String, // rule identifier
pub code_description: Option<CodeDescription>, // URI: kms://diagnostics/{domain}/{rule}
pub location: String, // path in the knowledge tree
pub severity: Severity,
pub message: String,
pub suggested_actions: Vec<String>, // fix steps; reference KMS tool names directly
}| Rule | Code | Severity | Detects |
|---|---|---|---|
NoNomenclature |
entity.no_nomenclature |
Error | entity has empty nomenclature vector |
EmptyDefinition |
entity.empty_definition |
Warning | definition field is empty |
MissingZhNomenclature |
entity.missing_zh_nomenclature |
Hint | no Chinese (ZH) nomenclature |
| Rule | Code | Severity | Detects |
|---|---|---|---|
EmptyLeaf |
index.empty_leaf |
Warning | Group node with no children and no attached knowledge (depth > 0) |
ExcessiveChildren |
index.excessive_children |
Warning | more than 6 children |
InconsistentPrefixes |
index.inconsistent_prefixes |
Hint | knowledge children's entity-name prefixes (before ·) are scattered |
| Rule | Code | Severity | Detects |
|---|---|---|---|
NestedKnowledge |
knowledge.internal_nested |
Warning | content contains multiple Markdown headings (should stay flat; structure belongs in the index) |
VagueTitle |
knowledge.vague_title |
Warning | title suffix contains vague keywords ("overview", "definition", "intro") |
OrphanKnowledge |
knowledge.orphan |
(placeholder) | currently disabled |
EmptyContent |
knowledge.empty_content |
Hint | empty content |
NoEntities |
knowledge.no_entities |
Warning | linked entity list is empty |
BoldAsHeading |
knowledge.bold_as_heading |
Error | **bold** on its own line used as a heading |
TitleMissingEntityPrefix |
knowledge.title_missing_entity_prefix |
Warning | title has no · separator (no entity-name prefix) |
Diagnostic results render live in the TUI Diagnostics panel; each one carries actionable suggested_actions telling the agent which kms_* tool to use to fix it.
kms_tui runs three independent agents concurrently, switchable by [Tab]:
| Role | Crate | Tools | Purpose |
|---|---|---|---|
| Compose | agent-compose |
25 kms_* tools incl. writes |
knowledge build specialist — handles ingestion, index adjustment, structural repair |
| Retrieval | agent-knowledge |
8 read-only kms_* tools |
read-only retrieval specialist; forced to use parallel tool calls; ends automatically when no tools are called |
| Parallel | agent-compose::ParallelComposeContext |
Compose tools + kms_parallel_dispatch |
orchestration specialist — splits a big task across N sub-agents in parallel, then reports |
When user input is a large block of text (e.g. uploading an entire technical reference book), serial single-agent processing could take hours. Parallel mode's approach:
User pastes a big text
↓
Parallel agent splits along domain boundaries
("Programming languages", "Frontend frameworks", "Databases"...)
↓
Calls kms_parallel_dispatch(subtasks=[
{ staging_title: "Programming languages", content: "Chapter X..." },
{ staging_title: "Frontend frameworks", content: "Chapter Y..." },
...
])
↓
The tool spawns one independent sub-agent per subtask
- dedicated KmsService (pointer pinned to staging area)
- shared ModelPool
- sub-agents are blind to each other → naturally non-conflicting
↓
Once all sub-agents finish, the staging subtree is merged into the
target parent (or kept under Root for manual review)
↓
Parallel agent reports entity / knowledge counts per subtree to the user
Key design points:
- Sub-agent prompts inherit fully from
KMS_SYSTEM_PROMPT— behavior is identical to a single agent. - Progress is pushed to the right-side TUI sub-panel via a
ParallelProgressTxchannel in real time. - Database write isolation comes from per-sub-agent
KmsServiceinstances + a staging subtree — not SQLite transactions.
The core loop of the entire system:
Agent issues a mutation tool call (any kms_* write tool)
↓
agent-compose intercepts via is_mutation_tool
↓
KMS executes the write
↓
KMS re-runs all diagnostic rules
↓
Diagnostics and "delta vs previous run" are injected into agent context
- new issues → prompt agent to keep fixing
- no change → continue normally
↓
Agent continues
Read-only tools do NOT trigger a re-diagnosis (avoids meaningless overhead). The list:
// crates/agent-compose/src/tools.rs
pub(crate) const READONLY_KMS_TOOLS: &[&str] = &[
"kms_search_entity",
"kms_navigate",
"kms_get_entity_knowledge",
];Diagnostic → fix-suggestion chain: each diagnostic's suggested_actions field writes imperative guidance like "use kms_update_entity to fill the entity's definition", so the agent can act directly without a second reasoning step.
All kms_* tools live under crates/dendrite-tools/src/kms_tools/, one file per tool, exposed to the agentik-core runtime via ToolRegistration. 25+ in total.
kms_create_entity/kms_update_entity/kms_delete_entitykms_get_entity/kms_search_entity/kms_list_entitieskms_add_nomenclature/kms_update_nomenclature/kms_delete_nomenclature
kms_create_knowledge/kms_update_knowledge/kms_delete_knowledgekms_rename_knowledge/kms_get_knowledge/kms_get_entity_knowledgekms_link_orphans— auto-attach unreferenced knowledge to a suitable location
kms_create_index/kms_delete_index/kms_move_index(path-based:index_path,new_parent_path) /kms_navigatekms_move_children— move N children into a new grouping under a target pathkms_merge_subtree— merge a staging subtree into a target parentkms_local— stateless local view; preferred by the retrieval agentkms_subtree_knowledge/kms_search_subtree
kms_parallel_dispatch— only exposed to the Parallel agent
(readonly_registrations, used by the retrieval agent):
kms_search_entity, kms_navigate, kms_get_entity, kms_get_entity_knowledge,
kms_get_knowledge, kms_local, kms_subtree_knowledge, kms_search_subtree
Built on ratatui + crossterm. Multi-panel layout, vim-style shortcuts.
| Panel | Content |
|---|---|
| Tree (top-left) | knowledge-tree navigation; expand / collapse / jump |
| Knowledge / Entity (top-right) | content of the selected node or its linked entities |
| Agent (bottom half) | conversations / tool calls / reasoning of all three agents |
| Diagnostics (bottom-left) | live diagnostic results with fix suggestions |
The Agent panel embeds a ParallelPanel: while the Parallel agent is running, each sub-agent's status, completion percent, and any error is shown live. Sub-agent LLM responses are inlined into the main panel (scroll back to trace them).
The theme system (crates/kms_tui/src/theme/) supports custom palettes — all styles are injected through a Theme trait.
- Manage multiple LLM providers concurrently (minimax / mimo / sensenova / custom OpenAI-compatible)
- Each provider has its own
base_url+api_key - Model pool selection: chain N models from N providers as the agent's fallback chain
- Persisted to
data/settings.json
dendrite/
├── crates/
│ ├── agentik-core/ # vendored agent runtime
│ ├── corpus/ # raw-corpus management; sqlx-backed chunk store
│ │
│ ├── kms/ # KMS core: storage, views, diagnostics, service
│ │ └── src/
│ │ ├── storage/ # SQLite schema + repository
│ │ ├── diagnostics/ # entity / index / knowledge rules
│ │ ├── service.rs # KmsService: write ops + diagnostic orchestration
│ │ ├── view.rs # IndexView / LocalView (read-only views)
│ │ └── language.rs # Language enum (ZH/EN/...)
│ │
│ ├── kms_tui/ # TUI app (bin: kms-tui)
│ │ └── src/
│ │ ├── main.rs # entry: load config, build agent, run ratatui loop
│ │ ├── components/ # tree / agent / diagnostics / settings / help
│ │ ├── input/ # keyboard events, paste handling, keymap
│ │ ├── theme/ # themes / color schemes
│ │ ├── chat.rs # ChatMessage rendering
│ │ └── parallel_panel.rs # Parallel agent sub-panel
│ │
│ ├── dendrite-tools/ # KMS-domain tools beyond agentik-core
│ │ └── src/
│ │ ├── kms_tools/ # 25+ kms_* tools, one file each
│ │ └── parallel_progress.rs # progress-push channel
│ │
│ ├── agent-compose/ # knowledge-build agent context (write mode)
│ │ └── src/
│ │ ├── context.rs # KmsContext (main agent)
│ │ ├── subtree_context.rs # SubTreeComposeContext (sub-agent)
│ │ ├── parallel_context.rs # ParallelComposeContext (orchestrator)
│ │ ├── prompt.rs # KMS_SYSTEM_PROMPT (core prompt)
│ │ ├── subtree_prompt.rs # sub-agent prompt variant
│ │ ├── parallel_prompt.rs # orchestrator prompt
│ │ ├── diagnostics.rs # KMS Diagnostic → runtime Diagnostic
│ │ └── tools.rs # mutation-tool classification
│ │
│ └── agent-knowledge/ # read-only retrieval agent context
│ └── src/
│ ├── context.rs # KnowledgeContext
│ └── prompt.rs # KNOWLEDGE_RETRIEVAL_PROMPT
│
├── data/ # runtime data
│ ├── kms_sqlite.db # SQLite database
│ ├── settings.json # TUI settings (provider / model pool)
│ └── tui.log # TUI runtime log (override via KMS_LOG_PATH)
│
├── docs/
│ └── demo.png # README screenshot
│
├── Cargo.toml # workspace root
└── README.md
The agentik-* runtime crates that used to be referenced via git URLs are now vendored as workspace members (see commits 15e228e and 3e413f1). All paths resolve through [workspace.dependencies] in the root Cargo.toml, so a cargo build no longer touches the network for those crates.
- Rust 1.85+ (Edition 2024; install stable via rustup)
- SQLite — schema is managed automatically by sqlx; no manual setup
- An API key for any one supported LLM provider (minimax / mimo / sensenova / custom OpenAI-compatible)
git clone https://github.com/wjixiang/dendrite.git
cd dendrite
cargo build --releasecargo run --release --bin kms-tuiFirst launch: the TUI enters "needs configuration" mode because data/settings.json doesn't exist yet:
- Press
Sto open the settings modal - Add a provider (fill in
base_url+api_key) - Pick one or more models from that provider into the model pool
- Save — the agent is rebuilt automatically
After that, just chat — the agent will progressively build the knowledge tree.
- Default:
data/kms_sqlite.db - Override: env var
KMS_DB_PATH=/path/to/kms.db
- Default:
data/tui.log - Resolution order:
KMS_LOG_PATH>$XDG_DATA_HOME/kms/tui.log>$HOME/.local/share/kms/tui.log>data/tui.log - Level: controlled by
RUST_LOG(default DEBUG)
{
"providers": [
{
"id": "prov-18b69253390fdd3c",
"display_name": "mimo1",
"provider_type": "mimo",
"api_key": "tp-xxx",
"base_url": "https://token-plan-cn.xiaomimimo.com/anthropic"
},
{
"id": "prov-18b6928a3756c36d",
"display_name": "sensenova1",
"provider_type": "sensenova",
"api_key": "sk-xxx",
"base_url": ""
}
],
"pool": [
{ "provider_id": "prov-18b69253390fdd3c", "model": "mimo-v2.5-pro" }
]
}providers: configure multiple providers side-by-side; they don't interferepool: the actual model chain used by the agent; at least one entry is required to build an agentprovider_type: selects the HTTP protocol path the SDK takes (minimax/mimo/sensenova/custom)base_url: empty → use the provider_type's built-in default- No environment-variable provider config (only
KMS_DB_PATH/KMS_LOG_PATH/RUST_LOGare read) — all provider config goes through the TUI form, avoiding shell-history leaks.
- Create
kms_xxx.rsundercrates/dendrite-tools/src/kms_tools/ - Implement
ToolRegistration::registration(svc: Arc<KmsService>) -> ToolRegistration - Register it in
kms_tools.rsregistrations()/readonly_registrations()
Read-only tools should go into readonly_registrations() — the retrieval agent uses it, and read-only tools skip post-mutation diagnostics.
- Add a struct in
crates/kms/src/diagnostics/{entity,index,knowledge}_rules.rs - Implement the matching trait (
EntityDiagnosticRule/IndexDiagnosticRule/KnowledgeDiagnosticRule) - Push it into the
rulesvector inrunner.rs
pub trait EntityDiagnosticRule: Send + Sync {
fn check(&self, entity: &Entity) -> Option<Diagnostic>;
fn name(&self) -> &str;
}Diagnostic-code URI convention: kms://diagnostics/{domain}/{rule-name}. code_description.href must follow this format — keeps the door open for LSP / docs integrations later.
- Main agent (Compose):
crates/agent-compose/src/prompt.rs→KMS_SYSTEM_PROMPT - Sub-agent:
crates/agent-compose/src/subtree_prompt.rs - Orchestrator:
crates/agent-compose/src/parallel_prompt.rs - Retrieval agent:
crates/agent-knowledge/src/prompt.rs→KNOWLEDGE_RETRIEVAL_PROMPT
Prompts are the most important carrier of domain knowledge. After any change, run a regression: feed known inputs and confirm the agent still obeys the rules.
- Add a new variant in the provider enum inside the SDK module
- Add a matching option to the
ProviderTypedropdown in the TUI settings form - If it's OpenAI-compatible, just fill in
base_url
- Parallel subtree merge timing: currently merges in one shot after all sub-agents finish. For very large inputs (>10 sub-tasks), total latency = max(subtask), and progress can't be streamed to the TUI incrementally.
- Diagnostic-rule precision:
MissingZhNomenclatureshould become "at least one major language" in multilingual settingsOrphanKnowledgeis disabled — design decision pending: must every knowledge be referenced by at least one index?
- Schema migration: the sqlx schema is hard-coded in
storage/database.rswith no migration history; needs proper migrations before production - Observability: TUI diagnostics is the only feedback channel; consider exporting traces to OTLP for offline analysis of agent behavior
- Prompt versioning: prompts are string constants with no version/changelog — regressions can't be precisely traced to a specific prompt change
- Multi-user / authorization: single-process single-user; sharing the knowledge base needs row-level permissions
MIT
