Knowledge Graph

Browse, search, and manage Owlat's typed organizational knowledge — the 7 entry types, source attribution, confidence decay, relations, and how entries are extracted from mail.

The Knowledge Graph is Owlat's typed store of organizational knowledge — customer preferences, decisions, commitments, and relationships, captured as searchable entries that the AI agent can draw on. Instead of a write-only log, it's a living set of facts that each carry a confidence score, decay over time, and can be linked to contacts, conversation threads, and one another. You browse and search it from the dashboard, and you can add entries by hand.

What the Knowledge Graph is (and the ai.knowledge flag)

Every entry is a small, typed piece of knowledge: a fact, a decision, an event, a preference, a goal, a relationship, or an action item. Each one records what the knowledge is, where it came from, how confident Owlat is in it, and who or what it relates to.

The graph is both a browsing and authoring surface and a live input to drafting: the Knowledge Graph page in the dashboard is where you and your team search and review what Owlat knows, and where you add entries by hand. When the AI agent drafts a reply, its context-retrieval step also searches the graph (contact-scoped) and injects the matching entries into the drafting prompt — so knowledge you capture is fed back into the agent's replies today.

The whole feature sits behind the Knowledge graph feature flag (ai.knowledge), which is off by default and depends on the master AI features flag (ai). Until an operator enables both, the knowledge search query returns nothing. See Feature flags for how to turn them on (you'll need an LLM provider configured, since the backfill extraction and embeddings call your model).

One graph per workspace

Owlat runs one organization per deployment, so there is a single knowledge graph for the whole workspace. There's no per-team or per-org partitioning — every entry belongs to this deployment.

Entry types, sources, confidence and tags

The seven entry types

Each entry has exactly one type. The type controls its icon, its colour in the UI, and — importantly — how fast its confidence decays (see Confidence decay and expiry).

TypeWhat it capturesExample
FactVerifiable information about a person, company, or thing"Acme Corp is on the Enterprise plan"
DecisionA choice that was made, with reasoning"Extended Acme's trial by two weeks (approved by Sarah)"
EventSomething that happened at a point in time"Met Acme's CTO at the conference on March 5"
PreferenceHow someone likes things done"Acme prefers email over phone for support"
GoalAn objective someone is working toward"Acme wants to launch their email program by September"
RelationshipA connection between people"Alice at Acme reports to Bob"
Action ItemA commitment or task mentioned in conversation"Send Acme the updated proposal by Friday"

Source attribution

Every entry records where it came from. The source appears in the entry's detail sidebar, and when the source is a conversation thread you get a View source thread link straight into the inbox.

SourceMeaning
EmailCaptured from an email message
ChatCaptured from a chat message
ManualCreated by hand in the dashboard
FileCaptured from a file
AI ExtractedPulled out of a message by the extraction pipeline

Confidence and tags

  • Confidence is a 0–100% score of how reliable the knowledge is. It's shown as a coloured bar on the entry — green at 70%+, amber from 40%, red below — and it drifts down over time unless re-validated.
  • Tags are free-form labels (for example important, customer, q1-2026) for grouping and skimming. They're shown as chips on the entry.
  • Entries can also be linked to contacts and to a conversation thread. Linked contacts appear in the detail sidebar with links to each contact profile.

Browsing and searching entries

Open Knowledge Graph from the dashboard. The page gives you a search box, a row of type tabs, and the entry list.

  • Type tabsAll plus one tab per entry type. Selecting a type filters the list to that type (newest first). The sidebar shows a legend of every type with its icon.
  • Search — type into the search box and Owlat runs a full-text search over entry titles and content. The search respects the active type tab, so you can search within just facts, just decisions, and so on.
Ask-anything: cross-source synthesized answers

The ask-anything box (QuickQueryPanel, on the knowledge dashboard) is a step up from the plain search box: it retrieves across both the Knowledge Graph and the file store and asks the LLM to synthesize a grounded answer that cites each source it used. Backed by quickQuery.ask and gated on the ai.knowledge flag plus the knowledge:read permission.

Click any entry to open its detail page, which shows the full content, tags, the confidence bar, source, created/updated/expiry dates, linked contacts, and any relations.

Contact-scoped knowledge

Knowledge entries linked to a contact also surface against that contact elsewhere in the product. That lookup goes through an indexed contact→entry junction, so it returns all entries for that contact — the complete history, not just the most recent — and stays efficient even on a very large graph.

Creating entries manually

You don't have to wait for extraction — you can write entries yourself. Click Create Entry (top-right of the Knowledge Graph page, or Create First Entry from the empty state) and fill in the form:

Pick a type

Choose one of the seven entry types from the Type dropdown.

Write a title and content

Give the entry a short, descriptive Title and the detailed knowledge in Content. Both are required.

Set the source

The Source defaults to Manual for hand-authored entries; you can change it if you're recording where the knowledge actually originated.

Set confidence

Drag the Confidence slider (0–100%, default 80%) to reflect how sure you are.

Add tags and an expiry (optional)

Enter comma-separated Tags, and optionally set Expires in a number of days — after which the entry is cleaned up automatically (see expiry).

Saving takes you straight to the new entry's detail page. Any signed-in member of the workspace can create entries.

Relations between entries (typed edges)

Entries can be connected by typed edges, and the entry detail page renders an entry's relations in both directions — incoming and outgoing. The supported relation types are:

RelationMeaning
supportsOne entry provides evidence for another
contradictsOne entry conflicts with another
supersedesOne entry replaces another with newer information
relates_toA general association
causesA causal link
blocksOne entry prevents another

To link two entries, open an entry's detail page, click Add relation in the Relations card, pick the edge type, search for the target entry, and save. The new edge appears immediately on both entries — outgoing on this one, incoming on the other. Hover any relation and click the × to remove it. Any signed-in member of the workspace can curate relations; a relation is automatically removed when either of its two entries is deleted or expires.

Automatic relation authoring — deterministic plus optional LLM inference

Two linkers run automatically after extraction, and you can still author relations by hand. The deterministic structural linker (always on with ai.knowledge) creates generic relates_to edges between entries from the same message or conversation thread. On top of that, the LLM edge inference pass (ai.knowledge.autoLink, off by default) reads the content of newly stored entries and infers the semantic supports / contradicts / supersedes edges the deterministic pass can't. Turn ai.knowledge.autoLink off and only the deterministic relates_to edges are created automatically; the semantic edges then remain yours to add by hand.

How knowledge gets extracted

Extraction happens in two places: live, as inbound mail is processed, and a one-time backfill that seeds the graph from your existing history.

Live per-message extraction

As each inbound message moves through the inbox processing lifecycle, once it's been classified Owlat mines it for organizational knowledge. The extraction fires exactly once per message, is idempotent, and runs best-effort (a failure on one message never blocks the message's own processing). New mail therefore grows the graph as it arrives.

The backfill is a first-run job that seeds the graph from your existing mail so the agent has historical context from day one:

  • It is triggered once, on the first time the AI agent is switched on (the false → true edge of the AI agent toggle). Turning the agent off and on again does not re-run it.
  • It walks the deployment's existing inbound messages in batches and runs each through the LLM extractor, which produces typed entries with AI Extracted as their source.
  • It is idempotent: a message that's already been extracted is skipped, so the job is safe to resume.
  • Very short messages and greetings are skipped, and extraction failures on individual messages are tolerated rather than aborting the whole run.

You watch and control the backfill from the AI Agent settings page (apps/web/app/pages/dashboard/admin/instance/agent.vue), which shows live progress — scanned, extracted, skipped, and error counts — and a Cancel action while it's running. Cancelling is admin-only and stops the run cleanly at the next batch.

What extraction does and doesn't do

The extractor makes a single structured LLM call per message, embeds each resulting entry, and stores it. It does not itself deduplicate near-identical entries (a separate daily job handles that — see Confidence decay and expiry) or check for contradictions. It does hand the freshly-stored batch to a deterministic structural linker that auto-creates generic relates_to edges (as noted under Relations), and — when ai.knowledge.autoLink is on — an LLM pass that infers the semantic supports / contradicts / supersedes edges. Semantic ("vector") search over knowledge is live: the agent's contact-scoped knowledge retrieval runs hybrid vector + full-text search fused with reciprocal-rank fusion, returning true nearest-neighbour matches. Full-text search and browse-by-type remain the live retrieval paths in the dashboard UI.

Graph-augmented retrieval (seed-then-expand)

By default, knowledge retrieval is flat: the agent's contact-scoped search returns the hybrid vector + full-text nearest neighbours and stops there. Turn on the ai.knowledge.graphRetrieval flag and retrieval switches to seed-then-expand — it uses those nearest neighbours as seeds, then walks one or two hops along the typed relationship edges to pull in the connected subgraph before grounding a draft. This is how a directly-relevant entry drags in the fact that supports it, or the newer entry that supersedes it, even when the second entry didn't match the query text on its own.

The expanded set is re-ranked (edge strength, hop distance, and the original search score all count), and each surfaced entry can carry a few annotations:

  • _via — the typed relationships that connected this entry to a seed, so you can see why it was pulled in.
  • _stale — the entry is the target of a supersedes edge, i.e. a newer entry replaces it; the agent is told to treat it cautiously.
  • _caveat — the entry is an endpoint of a contradicts edge, so it's surfaced but flagged as disputed rather than trusted outright.
Every hop is contact-scope-gated

Edges have no contact scope of their own, and a dedup-merge can union a node's linked contacts — so following an edge is a potential data-isolation leak. Graph expansion re-checks every neighbour against the caller's contact scope at each hop before it can enter the results (only an explicit org-wide scope skips the check). A neighbour that fails is dropped entirely — neither its content, its existence, nor any edge to it is emitted, and it never becomes a frontier for the next hop. ai.knowledge.graphRetrieval is a kill switch: turn it off and retrieval reverts to the flat nearest-neighbour path with no _via / _stale / _caveat annotations.

Confidence decay and expiry

The graph is deliberately not append-only. A scheduled maintenance job runs every 24 hours and does two things to every batch of entries it visits:

  1. Expiry cleanup — entries past their expiresAt date are deleted (along with their relations). This is how the optional Expires in value you set on an entry takes effect.
  2. Confidence decay — each entry's confidence is reduced based on how long it's been since it was last validated and a per-type decay rate. Frequently-recalled entries decay slightly slower (a usage-recency boost). Confidence never drops below a floor of 10%.

A second daily job dedup-merges near-identical entries for the same contact, keeping the graph from accumulating duplicates as new mail is mined.

Different knowledge ages at different speeds — a customer's plan changes far less often than a quarterly goal — so each type decays at its own rate:

TypeDecay per dayWhy
Fact0.5%Facts change infrequently (~90-day half-life)
Decision0.2%Decisions persist unless explicitly reversed
EventNoneHistorical events don't become less true
Preference1.5%Preferences evolve over time (~30-day half-life)
Goal3%Goals have deadlines and shift (~14-day half-life)
Relationship1%Org structures change
Action Item5%Commitments resolve quickly (~7-day half-life)
Planned: validation boost

The intent is for confidence to be raised when knowledge is validated — for example when the agent uses an entry and a human approves the resulting reply. That boost isn't implemented yet, so today confidence only decays (or holds steady, for events). See the Knowledge Graph vision for the full picture.