Design: Doso Architecture
Applied architecture combining workflow-graph engineering and the knowledge-graph pipeline into one instance (API + database) with thin surfaces.
Goals#
- Ingest personal sources (notes, bookmarks, transcripts, emails exports, articles).
- Build a durable, provenance-rich knowledge graph.
- Answer multi-hop questions with cited edges.
- Run optional multi-agent “diamonds” for research, audit, and discovery over that graph.
- Start simple; scale storage without redesigning the pipeline.
Non-goals#
- Replacing a general chat product UI.
- Fully autonomous unsupervised write-back to production facts.
- Training custom NER models.
- Real-time collaborative multi-user editing.
Logical components#
1. Source connectors#
Normalize everything to:
1Document { id, title, text, uri?, collected_at, mime?, checksum, meta }Implemented:
| Connector | How | Notes |
|---|---|---|
| Library upload | UI / POST /v1/content/documents | text, md, pdf, email shorthand, messages[] |
| Host CLI ingest | paired doso-runtime or doso-sync POSTing documents | See Connections — CLIs stay on the host |
| Live ask | Firecrawl + xAI search | Ingested mid-ask with stable URIs; see Ask, chat, and live sources |
URI dedupe + GET …/documents/by-uri keep sync and live ingest idempotent.
2. Staging store#
- Append-only document blobs + metadata.
- Candidate extractions (pre-canonical) keyed by
doc_id+extractor_version. - Run ledger: workflow id, status, token/cost counters, caps.
3. Graph store#
The graph is the canonical knowledge model used by Ask. Library documents remain provenance; Memories and entity wiki pages are projections over verified relations rather than independent fact stores. The entity wiki is compiled at read time from an entity’s verified one-hop neighborhood and may reuse a cached summary only while its relation fingerprint still matches. See Entity wiki and statements.
Minimum viable schema:
Nodes entities(id, canonical_name, type, description, aliases jsonb, mention_count, props jsonb)
Edges relations(id, src_id, predicate, dst_id, source_doc_id, chunk_id?, confidence?, extracted_at, status)
status: candidate | verified | rejected
When an embedding provider is configured, entity and document-chunk vectors support two jobs only: retrieve library passages for Ask, and bind an extracted name to an existing same-type entity when cosine similarity and token overlap agree they are the same referent. Embeddings are not a second memory.
v1 recommendation: SQLite (or Postgres) adjacency tables. Optional NetworkX projection for local algorithms/visualization. Neo4j later if Cypher/path UX becomes primary.
4. Workflow runners (implemented: crates/doso-core wgraph + workflows + skills)#
Rust workflow-graph runtime — coordination is code, not another chat turn:
run_agent/run_codenodes withNodeRole+ contractsparallelbarrier with null-on-failure + agent semaphorepipeline(streaming map),diamond,loop_until_dryroutefor deterministic branches; structuredtopology_dagon traces- Caps: max agents/nodes/duration/rounds
- Named catalog in
workflows(POST /v1/workflows/run)
Persisted, user-authored definitions use the declarative scheduler in
skills/engine.rs through skills/interpreter.rs. It adds dependency-ready
parallel execution, all/any/quorum joins, runtime contracts, bounded dynamic
maps and loops, nested subgraphs, verifier voting, safe-result caching,
resource locks, and policy-derived approval/isolation. These database-backed
DAGs are distinct from the fixed Rust catalog below.
Named topologies:
| Workflow | Shape |
|---|---|
| Ingest | chunk → parallel(extract) → barrier merge → resolve → assemble(candidates) |
| Ask | fast: seed → 1-hop → passages → one draft. expert: seed → draft(graph) ∥ live → merge → skeptics (skeptics default on in config and Settings) |
| research_desk | angles → parallel research → reduce → verify → report |
| discovery | loop-until-dry → verify → candidate write-back |
| promote_review | sample candidates → skeptics → promote/reject |
| memory_groom | dirty subjects → bounded grooming → refreshed projections |
Ask UX: prose facts (not Cypher in the bubble), citations in a drawer, live progress in the workflow trace, refined answer collapsed. See Ask, chat, and live sources.
The implemented HTTP surface is summarized in the API reference.
5. Query API / agent tools#
Tools (MCP or local functions):
| Tool | Behavior |
|---|---|
kg_search_entities(q, type?) | Lexical / simple embed over names+descriptions |
kg_neighbors(entity_id, hops=1..3) | Bounded expansion |
kg_ask(question) | POST /v1/chat/completions effort=fast — one retrieve + one draft |
kg_ingest(path_or_text) | Enqueue extract workflow |
kg_promote(candidate_ids) | Human/verifier promotion |
Answers must include citations: [{ src, predicate, dst, source_doc }].
End-to-end flows#
Ingest flow#
- Connector writes
Documentto staging. - Workflow fans out
extractper doc/chunk (cheap model). - Barrier: gather candidates.
resolveper type (strong model).- Deterministic
assembleintocandidateedges/nodes. - Optional verifier sample or human review →
verified.
Ask flow#
Two HTTP efforts share the same retrieve path (see the API reference):
- fast (
POST /v1/chat/completionsdefault,POST /v1/search) — seed → 1-hop → passages → one draft. No rewrite, live research, or skeptics. - evidence (
POST /v1/ask/evidence) — the same retrieve pack, no draft. - expert (product UI via
POST /v1/ui-chat, oreffort=expert) —
- Entity search seeds from question.
- Expand ≤ N hops.
- Serialize triples (+ provenance).
- Strong model answers with mandatory citations.
- Skeptic panel filters citations vs graph; it defaults on and can be changed in Settings.
- Return answer; new live ingest lands as candidates (not auto-verified).
Discovery flow (optional)#
Unknown-size sweep over corpus or open web notes: loop-until-dry with seen set persisted in run ledger; verify before confirm; hard caps.
Minimal stack (implemented)#
| Layer | Choice |
|---|---|
| Language | Rust (doso-core) + Next.js UI |
| LLM | Provider-agnostic OpenAI-compatible client (roles: extract/resolve/chat/skeptic) |
| Graph | SQLite or Postgres adjacency tables |
| Workflows | wgraph + named workflows registry |
| Secrets | Settings DB (+ optional .env seed) |
No LangGraph/Neo4j required; interfaces stay swappable.
Evaluation#
- Gold entities/relations on a small personal fixture set → P/R/F1.
- Grounded Q&A set: citation present + factually supported.
- Cost ledger per workflow run.
- Spot-check resolver merges weekly.
Open questions#
- Entity type system for “personal” domains (health, finance) — privacy boundaries?
Hybrid vector index for seed retrieval — when does it pay off?Paid off as Ask’s document-chunk arm (not as memory). Graph edges stay extracted and typed.- Write-back from web research: default deny vs staging-only?
- Multi-device sync / encryption at rest for personal corpora?
Security & privacy#
- Treat the graph as sensitive personal data.
- Never commit API keys, exports, or
media/. - Rotate any key pasted into chat (xAI/OpenAI/Anthropic).
- The graph lives with the instance. Cloud LLM calls send document text, and the product must make that tradeoff clear.
System guarantees#
- Ingest a folder of markdown → connected graph with provenance.
- Answer a multi-hop question with edge citations.
- Run a capped research diamond that fact-checks claims against the graph.
- Survive process restart without losing confirmed triples.