Logic Tools
Store, query, and reason over structured facts without a database or LLM call.
Logic cells
Every operation below runs inside a logic cell β a per-user symbolic fact space. Each cell has a persistent layer for durability and a symbolic layer for sub-millisecond query execution. When you call any logic.* tool, the system ensures your cell is ready: facts are loaded into the symbolic engine automatically so queries execute instantly with no LLM calls and no external round-trips.
Facts inside a logic cell are typed (entity, attribute, relation, metric, tag, context, constraint) and can be stored from natural language or passed directly as structured maps. Once stored, they persist across sessions β an agent that stores a fact today can query it tomorrow without re-learning it.
All Logic tools are available at data-grout@1/logic.*@1.
Namespaces
Logic cells support namespaces β fully isolated fact spaces per user. Each namespace has its own facts and constraints; facts in "varenholm" never appear in "default" and vice versa.
-
namespaceparameter β All logic tools (logic.remember,logic.query,logic.assert,logic.forget,logic.constrain,logic.reflect,logic.hydrate,logic.export,logic.import,logic.tabulate) accept an optionalnamespacestring parameter. -
Default is
"default"β Omittingnamespaceuses"default"; existing behavior is unchanged. -
Isolation β Prolog uses a composite scope key (
user_id::namespace). - Worker pool β One Prolog worker per user; facts are scoped by namespace within it.
-
logic.worldsβ Lists all available namespaces for the user with fact/constraint counts per namespace.
logic.remember@1
Store one or more facts. Facts are natural language statements or structured key-value pairs that you want to recall later. Each fact gets a unique handle you can use to retract it.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
statement |
string | conditionally | β |
A single fact as a natural language statement. Provide either statement or facts, not both |
facts |
array | conditionally | β | An array of structured facts to store in batch |
tag |
string | no | β |
A label to group related facts (e.g. "project-alpha", "user-preferences") |
namespace |
string | no |
"default" |
Isolated fact space. Facts in one namespace never appear in another |
Example: single fact
{
"name": "data-grout@1/logic.remember@1",
"arguments": {
"statement": "The billing contact for Acme Corp is jane@acme.com",
"tag": "contacts"
}
}
Example: batch facts
{
"name": "data-grout@1/logic.remember@1",
"arguments": {
"facts": [
{ "entity": "Acme Corp", "attribute": "billing_contact", "value": "jane@acme.com" },
{ "entity": "Acme Corp", "attribute": "plan", "value": "enterprise" },
{ "entity": "Globex", "attribute": "billing_contact", "value": "bob@globex.com" }
],
"tag": "contacts"
}
}
Response:
{
"stored": 3,
"handles": ["fact_a1", "fact_a2", "fact_a3"]
}
logic.assert@1
Store typed facts directly β zero LLM cost. Unlike logic.remember, Assert skips natural language translation entirely. Pass structured fact maps and they are stored exactly as specified. Facts are immediately queryable and participate in constraint inference.
Use Assert when you know the exact fact structure. Use Remember when you want natural language input.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
facts |
array | yes | β |
Array of typed fact maps. Each must have a type field: entity, attribute, relation, metric, tag, or context. Any fact may include probability (0.0-1.0) for ProbLog-style weighted assertions |
tag |
string | no |
"default" |
Grouping tag for the asserted facts |
namespace |
string | no |
"default" |
Isolated fact space |
skip_embedding |
boolean | no | false | Skip embedding generation (cheaper for structural/navigation data) |
Example
{
"name": "data-grout@1/logic.assert@1",
"arguments": {
"facts": [
{ "type": "entity", "name": "Acme Corp" },
{ "type": "attribute", "entity": "Acme Corp", "attribute": "industry", "value": "saas" },
{ "type": "relation", "subject": "Acme Corp", "relation": "acquired", "object": "Globex", "probability": 0.85 },
{ "type": "metric", "entity": "Acme Corp", "metric": "arr", "value": 2500000 }
],
"tag": "companies"
}
}
Response:
{
"handles": ["a1b2c3", "d4e5f6", "g7h8i9", "j0k1l2"],
"count": 4,
"llm_credits": 0,
"message": "Asserted 4 facts directly."
}
logic.query@1
Retrieve facts. Three modes: ask a question in natural language, provide structured patterns, or write a raw Prolog goal for maximum precision. All modes execute against the symbolic layer at sub-millisecond latency.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
question |
string | conditionally | β |
A natural language question. Provide one of question, patterns, or prolog |
patterns |
array | conditionally | β |
Structured match patterns (e.g. [{ "entity": "Acme Corp" }]) |
prolog |
string | conditionally | β |
Raw Prolog goal string β zero LLM cost, full precision. Variables are returned as structured JSON bindings. Base predicates: entity/1, relation/3, attribute/3, metric/3, tag/2. Handle-aware variants for probability joins: entity_h/2, relation_h/4, attribute_h/4, metric_h/4, tag_h/3. Probability: probability/2. Graph: path/5, path_prob/6, reachable/4, reachable_prob/5, neighbors/4. Plus user-defined constraint predicates |
limit |
integer | no | 50 | Maximum number of results to return |
namespace |
string | no |
"default" |
Isolated fact space. Queries only return facts from this namespace |
Example: natural language
{
"name": "data-grout@1/logic.query@1",
"arguments": {
"question": "Who is the billing contact for Acme Corp?"
}
}
Response:
{
"results": [
{
"handle": "fact_a1",
"statement": "The billing contact for Acme Corp is jane@acme.com",
"tag": "contacts"
}
]
}
Example: pattern match
{
"name": "data-grout@1/logic.query@1",
"arguments": {
"patterns": [{ "attribute": "billing_contact" }],
"limit": 10
}
}
Returns all facts with a billing_contact attribute across all entities.
Example: raw Prolog query
{
"name": "data-grout@1/logic.query@1",
"arguments": {
"prolog": "metric(E, \"arr\", V), V > 1000000"
}
}
Response:
{
"results": [
{ "E": "Acme Corp", "V": 2500000 }
],
"total": 1,
"description": "Direct Prolog query"
}
The prolog mode costs 1 credit (vs 3 for natural language) and gives you full control over the query. Available predicates include entity/1, relation/3, attribute/3, metric/3, tag/2, and the graph traversal predicates described below.
Graph traversal via patterns
Query patterns support three built-in graph predicates for navigating relation edges:
| Predicate | Description | Pattern fields |
|---|---|---|
path |
Find paths between two nodes |
from, to, relation, max_depth |
reachable |
All nodes reachable from a start |
from, relation, max_depth |
neighbors |
One-hop adjacency |
node, relation, direction |
{
"name": "data-grout@1/logic.query@1",
"arguments": {
"patterns": [{ "predicate": "reachable", "from": "Acme Corp", "relation": "acquired" }]
}
}
Returns all entities reachable from βAcme Corpβ via acquired relation edges. These predicates are also available in raw Prolog queries as short-form wrappers that auto-scope to the current namespace: path(From, To, RelType, MaxDepth, Path), reachable(From, To, RelType, MaxDepth), and neighbors(Node, RelType, Direction, Neighbors). User-defined constraint predicates (created via logic.constrain) are also callable in direct Prolog queries.
logic.constrain@1
Store a rule or policy that applies to future queries and workflows. Constraints are persistent and affect how the system reasons about your facts. If a constraint with the same name already exists, it is automatically overwritten (the old version is retracted and replaced). Constraints are eagerly compiled into callable Prolog predicates at storage time, so they are immediately available for queries in the same namespace.
The rule parameter accepts both natural language and raw Prolog syntax. If the input looks like a Prolog rule (e.g. connected(A, B) :- relation(A, "links_to", B).), it bypasses NL translation entirely for zero LLM cost. The constraint name is always derived from the rule head predicate β not from an LLM-generated descriptive name. Once stored, the constraint predicate is callable directly via logic.query(prolog: "connected(A, B)") β no special syntax needed.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
rule |
string | yes | β | The rule to store, as a natural language statement |
tag |
string | no | β | A label to group related constraints |
namespace |
string | no |
"default" |
Isolated fact space. Constraints apply only within this namespace |
Example
{
"name": "data-grout@1/logic.constrain@1",
"arguments": {
"rule": "Enterprise customers must have a billing contact before creating invoices",
"tag": "billing-policy"
}
}
Subsequent plans and queries will respect this constraint. For example, a workflow that creates an invoice for an enterprise customer without a billing contact on file would flag a policy violation.
logic.forget@1
Retract facts from your fact space. Provide specific fact handles or a pattern to match against.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
handles |
array | conditionally | β |
Array of fact handles to retract (e.g. ["fact_a1", "fact_a2"]). Provide either handles or pattern, not both |
pattern |
string | conditionally | β |
A match pattern to retract all matching facts (e.g. "entity:Acme Corp") |
namespace |
string | no |
"default" |
Isolated fact space. Retracts only from this namespace |
Example: by handle
{
"name": "data-grout@1/logic.forget@1",
"arguments": {
"handles": ["fact_a1"]
}
}
Example: by pattern
{
"name": "data-grout@1/logic.forget@1",
"arguments": {
"pattern": "entity:Globex"
}
}
Retracts all facts about the entity βGlobexβ.
logic.reflect@1
Get a summary of what the system knows about an entity or across your entire fact space. Useful for auditing stored knowledge or debugging unexpected behavior.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
entity |
string | no | β | Focus reflection on a specific entity. Omit to reflect on the full fact space |
summary_only |
boolean | no | false | Return only a high-level summary instead of listing individual facts |
namespace |
string | no |
"default" |
Isolated fact space. Reflects only facts in this namespace |
Example
{
"name": "data-grout@1/logic.reflect@1",
"arguments": {
"entity": "Acme Corp"
}
}
Response:
{
"entity": "Acme Corp",
"fact_count": 2,
"facts": [
{ "handle": "fact_a1", "attribute": "billing_contact", "value": "jane@acme.com" },
{ "handle": "fact_a2", "attribute": "plan", "value": "enterprise" }
],
"constraints_applied": 1,
"tags": ["contacts"]
}
logic.hydrate@1
Hydrate your context from symbolic memory. Given a goal in natural language, the tool decomposes it into subgoals, queries your logic cell for relevant facts, scores them by relevance, and returns a curated context payload within a token budget. Uses one LLM call for decomposition; all fact retrieval is symbolic and zero-cost.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
goal |
string | yes | β | The goal or current task in natural language |
max_tokens |
integer | no | 4000 | Token budget for returned context. Set to -1 for unlimited |
include_constraints |
boolean | no | true | Always include active constraints regardless of scoring |
format |
string | no |
"text" |
"text" for a prompt-ready string, "structured" for raw fact maps |
namespace |
string | no |
"default" |
Isolated fact space. Hydrates only from this namespace |
Example
{
"name": "data-grout@1/logic.hydrate@1",
"arguments": {
"goal": "Help the user migrate their payment system from Stripe to Adyen",
"max_tokens": 2000
}
}
Response:
{
"context": "Entity: Stripe\nStripe: type = payment_provider\nConstraint: PCI compliance required\nAcme uses Stripe",
"facts_included": 4,
"facts_available": 12,
"subgoals": ["payment_system", "stripe_integration", "migration_constraints"],
"token_count": 85,
"budget": 2000,
"gaps": ["No facts found for 'adyen_knowledge' (Adyen integration details) β consider using discovery tools"]
}
The gaps field tells you what you donβt know β subgoals that returned no matching facts. Use this to decide whether to call discovery tools before proceeding.
logic.worlds@1
List all available namespaces for the user with fact and constraint counts per namespace. Use this to discover which worlds exist and how much data each contains.
Parameters
This tool has no required parameters.
Example
{
"name": "data-grout@1/logic.worlds@1",
"arguments": {}
}
Response:
{
"worlds": [
{ "namespace": "default", "fact_count": 42, "constraint_count": 3 },
{ "namespace": "varenholm", "fact_count": 12, "constraint_count": 1 }
]
}
Typical usage
Logic tools are useful for maintaining state across conversations and workflows:
-
Worlds β List namespaces with
logic.worldsto see available fact spaces -
Hydrate context at the start of a task β get all relevant facts in one call (optionally scoped by
namespace) - Remember context an agent learns during a session (customer preferences, previous decisions)
- Assert structured facts directly when you know the exact shape (zero LLM cost)
- Constrain what future workflows can do based on business rules
- Query stored knowledge instantly when making decisions β use natural language for simplicity, patterns for structure, or raw Prolog for precision
- Forget outdated facts when information changes
- Reflect to audit what the system knows before taking action
Use the namespace parameter to keep separate worlds (e.g. "project-alpha", "varenholm") fully isolated from each other.
Power user: raw Prolog
For maximum control, use logic.query(prolog: "...") to write Prolog goals directly. This costs 1 credit (vs 3 for NL), has zero LLM latency, and supports the full fact-space predicate set including graph traversal. Use logic.assert to build up fact graphs, then query them with path/5, reachable/4, and neighbors/4 for native graph operations β these auto-scope to the current namespace so you never need to specify internal scope keys. User-defined constraint predicates from logic.constrain are also callable directly in Prolog queries, enabling a workflow of: define rules once, query them with full Prolog precision.