{
  "access": "public",
  "type": "reference",
  "format": "markdown",
  "title": "Logic Tools Reference",
  "chunked": true,
  "url": "https://library.datagrout.ai/logic-tools",
  "summary": "Store, query, and reason over structured facts without a database or LLM call.",
  "content_markdown": "# Logic Tools\n\nStore, query, and reason over structured facts without a database or LLM call.\n\n## Logic cells\n\nEvery 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.\n\nFacts 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.\n\nAll Logic tools are available at `data-grout@1/logic.*@1`.\n\n### Namespaces\n\nLogic 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.\n\n- **`namespace` parameter** — 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 optional `namespace` string parameter.\n- **Default is `\"default\"`** — Omitting `namespace` uses `\"default\"`; existing behavior is unchanged.\n- **Isolation** — Prolog uses a composite scope key (`user_id::namespace`).\n- **Worker pool** — One Prolog worker per user; facts are scoped by namespace within it.\n- **`logic.worlds`** — Lists all available namespaces for the user with fact/constraint counts per namespace.\n\n---\n\n## `logic.remember@1`\n\nStore 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.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `statement` | string | conditionally | -- | A single fact as a natural language statement. Provide either `statement` or `facts`, not both |\n| `facts` | array | conditionally | -- | An array of structured facts to store in batch |\n| `tag` | string | no | -- | A label to group related facts (e.g. `\"project-alpha\"`, `\"user-preferences\"`) |\n| `namespace` | string | no | `\"default\"` | Isolated fact space. Facts in one namespace never appear in another |\n\n### Example: single fact\n\n```json\n{\n  \"name\": \"data-grout@1/logic.remember@1\",\n  \"arguments\": {\n    \"statement\": \"The billing contact for Acme Corp is jane@acme.com\",\n    \"tag\": \"contacts\"\n  }\n}\n```\n\n### Example: batch facts\n\n```json\n{\n  \"name\": \"data-grout@1/logic.remember@1\",\n  \"arguments\": {\n    \"facts\": [\n      { \"entity\": \"Acme Corp\", \"attribute\": \"billing_contact\", \"value\": \"jane@acme.com\" },\n      { \"entity\": \"Acme Corp\", \"attribute\": \"plan\", \"value\": \"enterprise\" },\n      { \"entity\": \"Globex\", \"attribute\": \"billing_contact\", \"value\": \"bob@globex.com\" }\n    ],\n    \"tag\": \"contacts\"\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"stored\": 3,\n  \"handles\": [\"fact_a1\", \"fact_a2\", \"fact_a3\"]\n}\n```\n\n---\n\n## `logic.assert@1`\n\nStore 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.\n\nUse Assert when you know the exact fact structure. Use Remember when you want natural language input.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `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 |\n| `tag` | string | no | `\"default\"` | Grouping tag for the asserted facts |\n| `namespace` | string | no | `\"default\"` | Isolated fact space |\n| `skip_embedding` | boolean | no | false | Skip embedding generation (cheaper for structural/navigation data) |\n\n### Example\n\n```json\n{\n  \"name\": \"data-grout@1/logic.assert@1\",\n  \"arguments\": {\n    \"facts\": [\n      { \"type\": \"entity\", \"name\": \"Acme Corp\" },\n      { \"type\": \"attribute\", \"entity\": \"Acme Corp\", \"attribute\": \"industry\", \"value\": \"saas\" },\n      { \"type\": \"relation\", \"subject\": \"Acme Corp\", \"relation\": \"acquired\", \"object\": \"Globex\", \"probability\": 0.85 },\n      { \"type\": \"metric\", \"entity\": \"Acme Corp\", \"metric\": \"arr\", \"value\": 2500000 }\n    ],\n    \"tag\": \"companies\"\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"handles\": [\"a1b2c3\", \"d4e5f6\", \"g7h8i9\", \"j0k1l2\"],\n  \"count\": 4,\n  \"llm_credits\": 0,\n  \"message\": \"Asserted 4 facts directly.\"\n}\n```\n\n---\n\n## `logic.query@1`\n\nRetrieve 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.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `question` | string | conditionally | -- | A natural language question. Provide one of `question`, `patterns`, or `prolog` |\n| `patterns` | array | conditionally | -- | Structured match patterns (e.g. `[{ \"entity\": \"Acme Corp\" }]`) |\n| `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 |\n| `limit` | integer | no | 50 | Maximum number of results to return |\n| `namespace` | string | no | `\"default\"` | Isolated fact space. Queries only return facts from this namespace |\n\n### Example: natural language\n\n```json\n{\n  \"name\": \"data-grout@1/logic.query@1\",\n  \"arguments\": {\n    \"question\": \"Who is the billing contact for Acme Corp?\"\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"results\": [\n    {\n      \"handle\": \"fact_a1\",\n      \"statement\": \"The billing contact for Acme Corp is jane@acme.com\",\n      \"tag\": \"contacts\"\n    }\n  ]\n}\n```\n\n### Example: pattern match\n\n```json\n{\n  \"name\": \"data-grout@1/logic.query@1\",\n  \"arguments\": {\n    \"patterns\": [{ \"attribute\": \"billing_contact\" }],\n    \"limit\": 10\n  }\n}\n```\n\nReturns all facts with a `billing_contact` attribute across all entities.\n\n### Example: raw Prolog query\n\n```json\n{\n  \"name\": \"data-grout@1/logic.query@1\",\n  \"arguments\": {\n    \"prolog\": \"metric(E, \\\"arr\\\", V), V > 1000000\"\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"results\": [\n    { \"E\": \"Acme Corp\", \"V\": 2500000 }\n  ],\n  \"total\": 1,\n  \"description\": \"Direct Prolog query\"\n}\n```\n\nThe `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.\n\n### Graph traversal via patterns\n\nQuery patterns support three built-in graph predicates for navigating relation edges:\n\n| Predicate | Description | Pattern fields |\n|-----------|-------------|---------------|\n| `path` | Find paths between two nodes | `from`, `to`, `relation`, `max_depth` |\n| `reachable` | All nodes reachable from a start | `from`, `relation`, `max_depth` |\n| `neighbors` | One-hop adjacency | `node`, `relation`, `direction` |\n\n```json\n{\n  \"name\": \"data-grout@1/logic.query@1\",\n  \"arguments\": {\n    \"patterns\": [{ \"predicate\": \"reachable\", \"from\": \"Acme Corp\", \"relation\": \"acquired\" }]\n  }\n}\n```\n\nReturns 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.\n\n---\n\n## `logic.constrain@1`\n\nStore 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.\n\nThe `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.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `rule` | string | yes | -- | The rule to store, as a natural language statement |\n| `tag` | string | no | -- | A label to group related constraints |\n| `namespace` | string | no | `\"default\"` | Isolated fact space. Constraints apply only within this namespace |\n\n### Example\n\n```json\n{\n  \"name\": \"data-grout@1/logic.constrain@1\",\n  \"arguments\": {\n    \"rule\": \"Enterprise customers must have a billing contact before creating invoices\",\n    \"tag\": \"billing-policy\"\n  }\n}\n```\n\nSubsequent 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.\n\n---\n\n## `logic.forget@1`\n\nRetract facts from your fact space. Provide specific fact handles or a pattern to match against.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `handles` | array | conditionally | -- | Array of fact handles to retract (e.g. `[\"fact_a1\", \"fact_a2\"]`). Provide either `handles` or `pattern`, not both |\n| `pattern` | string | conditionally | -- | A match pattern to retract all matching facts (e.g. `\"entity:Acme Corp\"`) |\n| `namespace` | string | no | `\"default\"` | Isolated fact space. Retracts only from this namespace |\n\n### Example: by handle\n\n```json\n{\n  \"name\": \"data-grout@1/logic.forget@1\",\n  \"arguments\": {\n    \"handles\": [\"fact_a1\"]\n  }\n}\n```\n\n### Example: by pattern\n\n```json\n{\n  \"name\": \"data-grout@1/logic.forget@1\",\n  \"arguments\": {\n    \"pattern\": \"entity:Globex\"\n  }\n}\n```\n\nRetracts all facts about the entity \"Globex\".\n\n---\n\n## `logic.reflect@1`\n\nGet 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.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `entity` | string | no | -- | Focus reflection on a specific entity. Omit to reflect on the full fact space |\n| `summary_only` | boolean | no | false | Return only a high-level summary instead of listing individual facts |\n| `namespace` | string | no | `\"default\"` | Isolated fact space. Reflects only facts in this namespace |\n\n### Example\n\n```json\n{\n  \"name\": \"data-grout@1/logic.reflect@1\",\n  \"arguments\": {\n    \"entity\": \"Acme Corp\"\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"entity\": \"Acme Corp\",\n  \"fact_count\": 2,\n  \"facts\": [\n    { \"handle\": \"fact_a1\", \"attribute\": \"billing_contact\", \"value\": \"jane@acme.com\" },\n    { \"handle\": \"fact_a2\", \"attribute\": \"plan\", \"value\": \"enterprise\" }\n  ],\n  \"constraints_applied\": 1,\n  \"tags\": [\"contacts\"]\n}\n```\n\n---\n\n## `logic.hydrate@1`\n\nHydrate 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.\n\n### Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `goal` | string | yes | -- | The goal or current task in natural language |\n| `max_tokens` | integer | no | 4000 | Token budget for returned context. Set to -1 for unlimited |\n| `include_constraints` | boolean | no | true | Always include active constraints regardless of scoring |\n| `format` | string | no | `\"text\"` | `\"text\"` for a prompt-ready string, `\"structured\"` for raw fact maps |\n| `namespace` | string | no | `\"default\"` | Isolated fact space. Hydrates only from this namespace |\n\n### Example\n\n```json\n{\n  \"name\": \"data-grout@1/logic.hydrate@1\",\n  \"arguments\": {\n    \"goal\": \"Help the user migrate their payment system from Stripe to Adyen\",\n    \"max_tokens\": 2000\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"context\": \"Entity: Stripe\\nStripe: type = payment_provider\\nConstraint: PCI compliance required\\nAcme uses Stripe\",\n  \"facts_included\": 4,\n  \"facts_available\": 12,\n  \"subgoals\": [\"payment_system\", \"stripe_integration\", \"migration_constraints\"],\n  \"token_count\": 85,\n  \"budget\": 2000,\n  \"gaps\": [\"No facts found for 'adyen_knowledge' (Adyen integration details) — consider using discovery tools\"]\n}\n```\n\nThe `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.\n\n---\n\n## `logic.worlds@1`\n\nList 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.\n\n### Parameters\n\nThis tool has no required parameters.\n\n### Example\n\n```json\n{\n  \"name\": \"data-grout@1/logic.worlds@1\",\n  \"arguments\": {}\n}\n```\n\nResponse:\n\n```json\n{\n  \"worlds\": [\n    { \"namespace\": \"default\", \"fact_count\": 42, \"constraint_count\": 3 },\n    { \"namespace\": \"varenholm\", \"fact_count\": 12, \"constraint_count\": 1 }\n  ]\n}\n```\n\n---\n\n## Typical usage\n\nLogic tools are useful for maintaining state across conversations and workflows:\n\n1. **Worlds** — List namespaces with `logic.worlds` to see available fact spaces\n2. **Hydrate** context at the start of a task — get all relevant facts in one call (optionally scoped by `namespace`)\n3. **Remember** context an agent learns during a session (customer preferences, previous decisions)\n4. **Assert** structured facts directly when you know the exact shape (zero LLM cost)\n5. **Constrain** what future workflows can do based on business rules\n6. **Query** stored knowledge instantly when making decisions — use natural language for simplicity, patterns for structure, or raw Prolog for precision\n7. **Forget** outdated facts when information changes\n8. **Reflect** to audit what the system knows before taking action\n\nUse the `namespace` parameter to keep separate worlds (e.g. `\"project-alpha\"`, `\"varenholm\"`) fully isolated from each other.\n\n### Power user: raw Prolog\n\nFor 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.\n"
}