Skip to content

Adapt Experta

Adapt Experta — mvp.adapt_experta

Cluster: Agents & LLM | Type: component | MCP Tools: 44

Overview

Forward-chaining expert system block built on the Experta library (a Python CLIPS port) with a pure-Python fallback that runs without any optional dependency. Maintains a persistent working memory of typed facts across calls and fires production rules in priority order until no more rules match. Supports crisp production rules and Prolog-style clause rules via a Prolog delegate, making it suitable for both symbolic reasoning and logic-program-style inference.

When to use:

  • Encoding domain knowledge as IF-THEN production rules for automated reasoning
  • Running forward-chaining inference over a fact base assembled by upstream agent steps
  • Querying the current working memory for all facts of a given type
  • Combining rule-based expert reasoning with LLM-extracted facts in a hybrid pipeline

Launch-readiness caveat: adapt_experta is most useful when the user already has a structured fact schema and explicit rules, or when an upstream template/agent converts domain language into those structures. It provides real value for deterministic diagnostics, compliance checks, configuration decisions, and auditable fallback logic, but it is not yet a standalone non-developer rule-authoring experience. For first-user onboarding, pair it with guided templates, examples, or natural-language fact extraction so a domain specialist does not have to design __type__ facts and rule objects from scratch.

Backend honesty: when the preferred Experta backend is unavailable or fails during execution, results use the pure-Python fallback but are marked degraded with backend metadata. MCP also exposes backend_capabilities and audited, allowlisted backend_native_read; native backend mutation remains disabled by default.

Example:

from mvp.adapt_experta import AdaptExpertaBlock, ExpertaInput, Rule

block = AdaptExpertaBlock(name="experta")
# Add rules then run inference
block.infer(ExpertaInput(operation="add_rules", rules=[
    Rule(name="fever_rule", conditions=["Temperature"], actions=["Fever"], priority=10),
]))
block.infer(ExpertaInput(operation="add_facts", facts=[{"__type__": "Temperature", "value": 39.5}]))
result = block.infer(ExpertaInput(operation="run"))
# result.value.rules_fired → ["fever_rule"]; result.value.inferred_facts contains Fever fact

Works well with: adapt_eurisko, formal_methods, goal_engine

Public API

ExpertaFactRecommendation

Validated natural-language -> facts recommendation decision record.

Field Type Default
facts tuple[dict, ...] required
source str required
domain str ''
rationale str ''
notes tuple[str, ...] ()

Methods:

fact_count() -> int

type_count() -> int

to_metadata() -> dict[str, Any]

ExpertaDecisionError(ValueError)

The LLM did not produce a usable, validated fact recommendation.

LLMExpertaRuntime

Provider-neutral natural-language -> facts recommendation runtime over G6's

Constructor:

Parameter Type Default
llm LLMCaller \| None None

Methods:

synthesize(text: str, domain: str = '') -> Result[list[dict]]

ExpertaFactPlanner

Runtime-first facade with the deterministic floor as honest fallback.

Constructor:

Parameter Type Default
runtime ExpertaRuntime \| None None

Methods:

recommend_facts(text: str, domain: str = '') -> ExpertaFactRecommendation

AuditRecord

Field Type Default
id str required
run_id str required
step_type str required
timestamp str required
llm_model str required
prompt_text str required
response_text str required
latency_ms float required
token_in int required
token_out int required
cf_value float required
metadata_json str required

AuditLogger

Synchronous SQLite audit logger.

Constructor:

Parameter Type Default
conn sqlite3.Connection required

Methods:

log(step_type: str, run_id: str, llm_model: str = '', prompt_text: str = '', response_text: str = '', latency_ms: float = 0.0, token_in: int = 0, token_out: int = 0, cf_value: float = 0.0, metadata: dict[str, Any] | None = None) -> str

Write one audit record. Returns the record id. Raises on write failure.

get_run_audit(run_id: str) -> list[AuditRecord]

Return all audit records for a run_id, ordered by timestamp ascending.

list_runs(limit: int = 50) -> list[dict[str, Any]]

Return summary rows: run_id, started, ended, step_count.

AdaptExpertaBlock(AIBlock[ExpertaInput, ExpertaOutput, dict])

Forward-chaining expert system block.

Field Type Default
name str 'adapt_experta'
resource_bounds ResourceBounds \| None None

Methods:

infer(data: ExpertaInput) -> Result[ExpertaOutput]

Rule(BaseModel)

A production rule in the expert system.

Field Type Default
name str required
conditions list[str] required
actions list[str] required
priority int 0
type Literal['crisp', 'prolog'] 'crisp'
clause str ''

ExpertaInput(BaseModel)

Input to AdaptExpertaBlock.

Field Type Default
operation Literal['add_facts', 'add_rules', 'run', 'query', 'reset'] 'run'
facts list[dict[str, object]] Field(default_factory=list)
rules list[Rule] Field(default_factory=list)
query_type str ''
max_cycles int 20

ExpertaOutput(BaseModel)

Output from AdaptExpertaBlock.

Field Type Default
inferred_facts list[dict[str, object]] required
rules_fired list[str] required
n_cycles int required
query_results list[dict[str, object]] required
n_facts int required
backend str required
degraded bool False
degradation_reason str ''
capability_status str 'available'
capability_missing list[str] Field(default_factory=list)
backend_metadata dict[str, object] Field(default_factory=dict)

AdaptExpertaMCPBlock(AIBlock[MCPExpertaInput, MCPExpertaOutput, dict])

44-op expert system block with SQLite persistence.

Field Type Default
name str 'adapt_experta_mcp'
state dict \| None None
db_path str ':memory:'
resource_bounds ResourceBounds \| None None

Methods:

infer(data: MCPExpertaInput) -> Result[MCPExpertaOutput]

MCPExpertaInput(BaseModel)

Field Type Default
op Literal['run', 'add_facts', 'add_rules', 'query', 'reset', 'save_session', 'load_session', 'list_sessions', 'delete_session', 'store_ruleset', 'retrieve_ruleset', 'list_rulesets', 'update_cf', 'get_cf', 'resolve_goal', 'record_run', 'query_runs', 'summarize_runs', 'explain', 'why', 'how', 'submit_job', 'get_job', 'search', 'info', 'extract_facts', 'run_semantic', 'add_prolog_rule', 'get_run_audit', 'list_audit_runs', 'list_approval_queue', 'approve_rule', 'reject_rule', 'get_conflicts', 'prolog_query', 'record_outcome', 'record_goal_outcome', 'get_rule_performance', 'list_weak_rules', 'recommend_facts', 'list_patterns', 'backend_capabilities', 'backend_native_read', 'backend_native_mutate'] required
facts list[dict] Field(default_factory=list)
rules list[dict] Field(default_factory=list)
query_type str ''
max_cycles int 20
resolution_strategy str 'log_only'
name str ''
notes str ''
rules_json str ''
description str ''
domain str ''
tags_csv str ''
fact_type str ''
rule_name str ''
cf_value float 1.0
goal_type str ''
max_depth int 5
session_name str ''
rules_fired list[str] Field(default_factory=list)
n_cycles int 0
n_facts int 0
limit int 50
params_json str ''
priority int 0
job_id str ''
query str ''
top_k int 5
native_operation str ''
requested_backend str 'active'
cursor str ''
text str ''
domain_hint str ''
semantic_threshold float 0.5
run_id str ''
candidate_id str ''
reviewer_note str ''
found bool False
min_fires int 10
max_accuracy float 0.2
production_mode bool False
ruleset_spec_json str ''
ruleset_name str ''

MCPExpertaOutput(BaseModel)

Field Type Default
op str ''
key str ''
value Any None
found bool False
count int 0
records list[MCPExpertaRecord] Field(default_factory=list)
retrieved list[Any] Field(default_factory=list)
scores list[float] Field(default_factory=list)
summary str ''
message str ''
facts list[dict] Field(default_factory=list)
rules_fired list[str] Field(default_factory=list)
n_cycles int 0
n_facts int 0
metadata dict[str, Any] Field(default_factory=dict)
degraded bool False
degradation_reason str ''
capability_status str 'ok'
capability_missing list[str] Field(default_factory=list)
ruleset_spec_missing list[str] Field(default_factory=list)

ExpertaStore

Sync SQLite store with 7 tables.

Constructor:

Parameter Type Default
db_path str ':memory:'

Methods:

close() -> None

save_session(name: str, facts_json: str, rules_json: str, notes: str) -> str

load_session(name: str) -> dict[str, Any] | None

list_sessions() -> list[dict[str, Any]]

delete_session(name: str) -> bool

save_ruleset(name: str, rules_json: str, description: str, domain: str, tags: list[str]) -> str

load_ruleset(name: str) -> dict[str, Any] | None

increment_ruleset_use(name: str) -> None

save_ruleset_spec(ruleset_id: str, name: str, spec_json: str) -> None

Persist a RulesetSpec attestation alongside a ruleset.

load_ruleset_spec(name: str) -> str

Return the RulesetSpec JSON for a ruleset name, or '' if none.

list_rulesets(domain: str = '') -> list[dict[str, Any]]

add_run(session_name: str, rules_fired: list[str], n_cycles: int, n_facts: int, notes: str, tags: list[str]) -> str

query_runs(session_name: str = '', limit: int = 50) -> list[dict[str, Any]]

summarize_runs(session_name: str = '') -> dict[str, Any]

get_cf(fact_type: str) -> float | None

add_cf(fact_type: str, rule_name: str, cf_value: float, combined_cf: float) -> str

add_job(op: str, params_json: str, priority: int) -> str

get_job(job_id: str) -> dict[str, Any] | None

update_job(job_id: str, status: str, result_json: str) -> None

text_search(query: str, top_k: int = 5) -> list[dict[str, Any]]

count_all() -> dict[str, int]

add_approval_candidate(run_id: str, rule_json: str, cf_score: float, proposer: str = '') -> str

list_approval_queue(status: str = 'pending') -> list[dict]

get_approval_candidate(candidate_id: str) -> dict | None

Return a single approval-queue row by id (any status), or None.

update_approval_status(candidate_id: str, status: str, reviewer_note: str = '', reviewer_id: str = '') -> None

get_pending_count() -> int

add_durable_fact(fact_json: str, cf_value: float, run_id: str) -> str

list_durable_facts() -> list[dict]

get_rule_performance(rule_name: str) -> dict | None

upsert_rule_performance(rule_name: str, fires_delta: int = 0, positives_delta: int = 0, negatives_delta: int = 0, cf_adjustment: float | None = None, status: str | None = None) -> None

list_weak_rules(min_fires: int = 10, max_accuracy: float = 0.2) -> list[dict]

Functions

agentic_planner_enabled(default_enabled: bool = True) -> bool

Decide whether the agentic experta fact-planner should be used.

validate_fact_recommendation(facts: list[dict]) -> list[dict]

Reject any fact recommendation outside the canonical shape / bounds.

recommend_facts_floor(text: str, domain: str = '') -> list[dict]

Deterministic, deliberately-brittle NL->facts selector (the honest floor).

MCP Tools

Operation Source
run experta_mcp
add_facts experta_mcp
add_rules experta_mcp
query experta_mcp
reset experta_mcp
save_session experta_mcp
load_session experta_mcp
list_sessions experta_mcp
delete_session experta_mcp
store_ruleset experta_mcp
retrieve_ruleset experta_mcp
list_rulesets experta_mcp
update_cf experta_mcp
get_cf experta_mcp
resolve_goal experta_mcp
record_run experta_mcp
query_runs experta_mcp
summarize_runs experta_mcp
explain experta_mcp
why experta_mcp
how experta_mcp
submit_job experta_mcp
get_job experta_mcp
search experta_mcp
info experta_mcp
extract_facts experta_mcp
run_semantic experta_mcp
add_prolog_rule experta_mcp
get_run_audit experta_mcp
list_audit_runs experta_mcp
list_approval_queue experta_mcp
approve_rule experta_mcp
reject_rule experta_mcp
get_conflicts experta_mcp
prolog_query experta_mcp
record_outcome experta_mcp
record_goal_outcome experta_mcp
get_rule_performance experta_mcp
list_weak_rules experta_mcp
recommend_facts experta_mcp
list_patterns experta_mcp
backend_capabilities experta_mcp
backend_native_read experta_mcp
backend_native_mutate experta_mcp