This guide shows how to replace facts and upsert facts into the knowledge base using atomic update operations.

Update a Single Fact

Use update_fact to atomically replace an existing fact with a new one. Both the old and new facts must be valid Prolog terms.

json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"remember_fact","arguments":{"fact":"role(jean, manager)"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"update_fact","arguments":{"old_fact":"role(jean, manager)","new_fact":"role(jean, director)"}}}

After the update, querying role(jean, manager) returns no results, and role(jean, director) returns the new fact.

Atomic Semantics

update_fact is atomic: either both the retraction and assertion succeed, or neither does. If the old fact is not found in the knowledge base, the operation fails with an error and no changes are made:

json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"update_fact","arguments":{"old_fact":"role(nonexistent, manager)","new_fact":"role(nonexistent, director)"}}}

This returns an error because role(nonexistent, manager) does not exist.

Use Cases

  • Correct outdated facts: role(alice, junior) → role(alice, senior)
  • Evolve state: deploy(prod, v1) → deploy(prod, v2)
  • Migrate data: user(id=123, name="John") → user(id=123, name="Jonathan")

Upsert a Fact

Use upsert_fact to replace a fact if it exists, or insert it if it doesn’t. Upsert matches on the functor and first argument only.

json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"upsert_fact","arguments":{"fact":"deploy(prod, v1)"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"upsert_fact","arguments":{"fact":"deploy(prod, v2)"}}}

After the first upsert_fact, deploy(prod, v1) is asserted. After the second, the old version is removed and deploy(prod, v2) is inserted.

Pattern Matching

Upsert removes all facts that match the functor and first argument of the provided fact:

json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"upsert_fact","arguments":{"fact":"project(beta, active)"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"upsert_fact","arguments":{"fact":"project(beta, archived, 2025-04-13)"}}}

The second call removes project(beta, active) and asserts the new project(beta, archived, 2025-04-13), even though the arity differs.

Idempotent Behavior

upsert_fact always succeeds, even when the fact is new. This makes it safe for initialization or repeated operations:

json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"upsert_fact","arguments":{"fact":"status(ready)"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"upsert_fact","arguments":{"fact":"status(ready)"}}}

Both calls succeed. The first inserts the fact, the second finds it already exists and does nothing.

Use Cases

  • Maintain a single “current” value: current_version(prod, v2)
  • Track state without duplicates: session(user_id, token_hash)
  • Enforce at-most-one semantics: primary_database(prod_db_1)

When to Use Which Tool

ScenarioToolBehavior
Correct a fact that must existupdate_factFails if old fact is missing
Replace or insert a factupsert_factAlways succeeds
Evolve state (same functor)EitherBoth work; choose based on error handling needs
Initialize with defaultsupsert_factSafe for repeated initialization
Enforce exactly-one factupsert_factRemoves duplicates automatically

Differences from Deletion

OperationUpdate/UpsertDeletion
ScopeSingle factSingle fact or pattern
EffectReplaceRemove
AtomicityAtomic (both-or-nothing)Single operation
Idempotenceupsert_fact is idempotentforget_fact errors if not found

See Also