知識圖譜如何應用於 AI 記憶,以及 Palace 如何運用
Knowledge graphs, explained for AI memory (and how Palace uses one)
作者説明知識圖譜如何與向量搜尋互補,並介紹 Palace 以時間知識圖譜記錄事實的有效期,區分目前狀態和歷史。Palace 預設在同一個本地 SQLite 檔案保存以混合搜尋取回的原始記憶片段,以及含 valid_from、valid_to 的知識圖譜;前者提供證據,後者記錄狀態。圖譜不會自動從文字抽取三元組,Agent 需主動新增或作廢事實,並在回答過往決策前同時使用搜尋和圖譜查詢。
Most AI memory systems today come down to the same recipe. Split text into chunks, turn each chunk into an embedding, store the vectors, and when a question comes in, return the chunks closest to it. That recipe works well, and it's the backbone of Palace, the memory server I build for coding agents.
Some questions, though, it answers badly. "What database does the billing service use now?" "Who owns the deploy pipeline?" "Which services depend on this queue?" Those are questions about facts: things with one current answer, a history of past answers, and connections to other things.
That's what knowledge graphs are for. This post covers what a knowledge graph is, why it complements vector search instead of replacing it, what adding time to it changes, and what it costs. It ends with how Palace uses one, including where Palace keeps it deliberately simple.
What a knowledge graph is
A knowledge graph stores knowledge as entities and the relationships between them.
- An entity is a thing you can name: a person, a service, a library, a team, a decision.
- A relationship is a typed, directed link between two entities:
depends_on,owned_by,uses_queue,replaced_by.
The basic unit is a triple: subject, predicate, object.
billing_service → uses_database → Postgres
billing_service → owned_by → payments_team
payments_team → reports_to → Dana
Put enough triples together and you get a graph: entities are the nodes, relationships are the edges. Since every edge has a type, you can ask precise questions, like "follow owned_by out of billing_service" or "find everything with a uses_database edge pointing at Postgres."
The idea isn't new. The W3C's RDF standard has modeled data as triples for over two decades, and Wikidata stores its facts that way. Google introduced its Knowledge Graph in 2012 under the slogan "things, not strings." That slogan is the core idea: a knowledge graph stores things and how they relate, not text that happens to mention them.
There are two common styles:
- RDF / triple stores: everything is a triple, queried with SPARQL.
- Property graphs (Neo4j and similar): nodes and edges can carry key/value properties, queried with languages like Cypher.
For agent memory, the distinction matters less than the core idea: knowledge as explicit, typed, queryable links.
What vector search is good at, and what it isn't
To see why a graph helps, be clear about what embeddings do.
An embedding model maps text to a point in a high-dimensional space, so that text with similar meaning lands close together. Vector search finds the stored chunks nearest to your question. It's very good at:
- Fuzzy recall. You can ask "that time the build broke because of the lockfile" without knowing any exact words.
- Evidence. It returns the original text, with all its nuance and reasoning.
- Zero structure up front. Nobody has to decide a schema before storing anything.
Its weaknesses all follow from one property: similarity isn't truth.
- It doesn't know what's current. Suppose your team moved a queue from Redis to SQLite in October. Memory now holds a long March thread about tuning Redis and a short October note saying "switched to SQLite." Ask "which queue do we use?" and both match well. The detailed Redis thread may well rank higher. Nothing in the vectors records that one superseded the other.
- Facts get scattered. "Billing uses Postgres" may never appear as one sentence. It's spread across a config discussion, a migration log, and a code review. Each chunk holds part of the fact.
- It can't enumerate. "Which services use Postgres?" is a question about all matches. Top-k search returns the k most similar chunks, not a complete list.
- It can't detect contradictions. Two chunks that disagree are just two nearby points. Nothing marks them as a conflict.
A knowledge graph covers exactly these weak spots. It doesn't replace search, because a triple keeps no nuance and no reasoning. Search gives you the evidence. The graph gives you the state.
Adding time: temporal knowledge graphs
A plain knowledge graph has a problem of its own: facts change. If you just overwrite uses_queue → Redis with uses_queue → SQLite, you lose the history. Worse, you lose the context that makes old evidence readable. A future reader who finds the March Redis thread can't tell it describes the past.
A temporal knowledge graph gives each fact a validity interval:
my_api → uses_queue → Redis valid 2026-03-02 … 2026-10-12
my_api → uses_queue → SQLite valid 2026-10-12 … (still true)
Three things follow:
- Facts are ended, not deleted. When something stops being true, you close its interval. The record stays.
- "Current" is a query, not a guess. A fact with no end date is the current one.
- You can ask about the past. "What did we use in June?" filters to facts valid on that date.
The database world calls this valid time: when a fact was true in reality. It's different from transaction time: when the system recorded it. Systems that track both are called bitemporal. Valid time is usually what an agent needs, because the question is "what's true?", not "when did someone write it down?"
What a knowledge graph adds to retrieval
Put vector search and a temporal graph side by side, and an agent gets several things it didn't have before:
A direct answer for state questions. "Which queue does my_api use?" turns into an exact lookup that returns one current fact. There's no similarity ranking to second-guess.
Context for interpreting search results. The graph also says Redis was in use until October 12. When search turns up the March thread, the agent knows it's history, not instructions.
Reverse and complete lookups. Following an edge backwards ("what points at SQLite?") returns every match, not the top k.
Contradiction detection. Two current facts with the same subject and predicate but different objects are a structural signal. Something changed and nobody closed out the old fact, or two sources disagree.
Compactness. A triple is a handful of tokens. Stating a fact as a triple is far cheaper to put in a model's context than the paragraphs that originally established it.
Multi-hop questions. Graphs make it natural to chain relationships: billing → owned_by → payments team → reports_to → Dana. Each hop is a precise step, not another fuzzy search.
What knowledge graphs cost
Graphs aren't free, and pretending otherwise is how graph projects fail.
- Entity resolution. Are "Postgres", "PostgreSQL" and "pg" one thing or three? If you get it wrong, the graph splits one fact into several pieces, or merges facts that should stay separate. This is one of the hardest problems in the field.
-
Schema drift. Without discipline you end up with
uses_db,uses_databaseanddatabase_is, all meaning the same thing. - Getting facts in. Someone has to write the triples. Systems such as Microsoft's GraphRAG use an LLM to extract them from text automatically. That scales, but extraction makes mistakes, and those mistakes become "facts." Writing triples explicitly is accurate but needs a writer who reliably does it.
- Maintenance. A temporal graph only stays correct if old facts get closed when new ones arrive.
How Palace uses a knowledge graph
Palace stores memory in two complementary forms. By default, both live in the same local SQLite file:
- Drawers: verbatim chunks of code, conversations, notes and diaries, retrieved by hybrid search (BM25 keyword scoring plus embedding similarity). This is the evidence.
-
A temporal knowledge graph: entities and triples with
valid_from/valid_todates. This is the state.
The two don't feed into each other. Search doesn't read the graph, and the graph doesn't rank drawers. Instead, the memory protocol Palace gives agents asks for both before answering anything about past decisions:
→ palace_search + palace_kg_query — never answer from training data alone.
Here's how the queue example plays out in practice.
Writing a fact. After the agent learns that my_api uses Redis, it records that:
palace_kg_add my_api → uses queue → Redis (valid_from 2026-03-02)
Palace normalizes names on the way in. My API, my api and my_api all resolve to the same entity, and uses queue is the same relationship as uses_queue. Adding a fact that's already current returns the existing one instead of a duplicate. It's a lightweight form of entity resolution: it covers case and spacing, not synonyms.
Changing a fact. In October the agent closes out the old fact and adds the new one:
palace_kg_invalidate my_api → uses_queue → Redis (ended 2026-10-12)
palace_kg_add my_api → uses_queue → SQLite (valid_from 2026-10-12)
Nothing is deleted. The Redis fact gets an end date and stays as history.
Confidence is binary on purpose. Graphs built by automatic extraction often attach a probability to each fact, and then every reader has to decide what a 0.6 means. Palace stores every fact at full confidence (1.0). When an agent decides a fact is no longer true, it doesn't lower a score. It invalidates the fact. So the graph never holds half-believed facts: a fact is either current or ended, and the dates record when it changed.
Reading it back. palace_kg_query on my_api returns both facts, each flagged:
my_api → uses_queue → Redis 2026-03-02 … 2026-10-12 current: false
my_api → uses_queue → SQLite 2026-10-12 … current: true
The agent now has the current answer and the context to read older search results correctly. The same tool takes:
- a
directionto follow edges backwards ("what uses SQLite?") - an
as_ofdate for point-in-time questions. Both ends of the interval count, so on the switch day itself, both facts come back.
Catching contradictions. palace_conflicts lists every subject and predicate pair with more than one distinct object. It includes properly closed-out history like the example above, so it's a review list, not a verdict. The case to act on is a group where more than one fact is still marked current.
Linking state to evidence. A fact can store an optional source_closet pointing at the stored memory that justifies it, so the short triple can lead back to the full discussion.
Solving "someone has to write the facts"
This is the cost that sinks most graph efforts, so it's worth being specific. Palace doesn't extract triples from text automatically. Agents add them on purpose, and Palace works to make sure they do:
- The protocol that ships in the MCP server's instructions says to use
palace_kg_addfor stable facts, andpalace_kg_invalidatepluspalace_kg_addwhen facts change. -
palace_kg_queryandpalace_kg_addare pinned, so clients that defer MCP tools behind a search still keep them loaded. - Session hooks remind the agent to save durable facts, including invalidating changed ones first, before it finishes.
The agent writing the fact is already an LLM, and it has the full task context, so there's no need for a second model reading raw messages after the fact. The graph contains what agents decided was worth stating as a fact, not whatever an extractor guessed.
Where Palace keeps it simple
The graph is deliberately small. These are its current limits:
- One hop per query. Multi-hop questions mean the agent chains calls itself.
-
Exact names. After normalizing case and spacing, the graph matches names exactly, so
PostgresandPostgreSQLare two entities. Search is the fuzzy layer. It finds the memories that say "PostgreSQL" when the question says "Postgres", and the agent then queries the graph under that name. If two agents record the same fact with different object names,palace_conflictsflags the pair. - Valid time only. Each triple keeps a creation timestamp, but queries filter on validity dates only.
Takeaway
Vector search answers "what have we said about this?" A temporal knowledge graph answers "what is true about this, and since when?" Agent memory needs both. Without the graph, an agent can't tell current facts from history. Without search, it has facts but no reasoning behind them.
Palace pairs the two in one local file and gets agents to keep the graph up to date. Every connected agent reads and writes the same palace, so a fact Claude Code records today is there for Cursor or Codex in their next session, with the same history of what changed and when.
Palace is open source (MIT) at github.com/AncientiCe/palace-rs. palace install connects 11 agents over MCP: Claude Code, Codex, Cursor, Gemini CLI, VS Code, OpenCode, Cline, Zed, Goose, Windsurf and Claude Desktop.
If you want one shared memory across a whole team, that's what we're building at palacememory.com.
來源:DEV Community · dev.to


