Explainable Neurosymbolic AI Platform
AmberTrace AI builds a neurosymbolic platform from your data and a plain-English description of your rules. Every answer comes back as an Amber Report — a confidence breakdown, the symbolic rules that fired, and a machine-checked proof. Build it in the web app or drive the matching SDK — a non-technical team, a developer, or an AI agent can do it all.
The problem with black-box AI
In lending, healthcare, hiring and compliance, “the model said so” isn’t good enough. You need to know which rule fired, on which facts, and be able to prove the decision was sound.
How it works
Write your rules in plain English. AmberTrace generates an ontology — entities, relationships and symbolic rules.
Upload a CSV, or pull live from a built-in connector (FRED, Yahoo Finance, Coinbase, REST).
The rule set is checked for soundness; facts are gated by a confidence threshold τ.
Every query returns an Amber Report — answer, confidence, the rules that fired, and a proof certificate.
Train prediction models on the same platform for time-series and what-if scenarios.
# pip install ambertraceai from ambertraceai import AmbertraceAPI api = AmbertraceAPI( base_url="https://app.ambertrace.ai", api_key="at_...", ) # 1. Describe the domain — rules in plain English domain = api.domains.create( name="Loan Approval Assessment", description="Applicants must be 18 or older; " "debt-to-income must not exceed 43%; ...", ) # 2. Bring your data ds = api.datasets.upload(domain_id=domain["id"], file_path="loans.csv") # 3. Build the ontology + a VERIFIED platform api.domains.build_ontology(domain["id"]) platform = api.platforms.create( domain_id=domain["id"], dataset_id=ds["id"], verified_profile=True, verified_min_confidence=0.6, ) # 4. Ask — every answer is a proof-carrying Amber Report report = api.platforms.query(platform["id"], query="Can a 17-year-old apply for a £3,000 loan?") print(report["proof_checked"]) # True — certified
Anatomy of an Amber Report
A real response from the loan platform above, asked “Should we approve a loan — income £28,000, DTI 0.46, credit score 610, age 25?”
Check Applicant Age Below Threshold rule fires —
proof_checked: true, decision certified against the kernel.
Worked examples
Each was built end-to-end against the live platform — a description, a dataset, a verified build, and queries that return proof-carrying Amber Reports. The forecasting domains pull live data straight from connectors.
Click any example to see the actual code.
# Rules in plain English become an ontology domain = api.domains.create(name="Loan Approval Assessment", description="Applicants must be 18 or older; debt-to-income " "must not exceed 43%; minimum credit score per loan " "tier; employment verification required.") api.datasets.upload(domain_id=domain["id"], file_path="loan_applications.csv") api.domains.build_ontology(domain["id"]) platform = api.platforms.create(domain_id=domain["id"], dataset_id=ds["id"], verified_profile=True, verified_min_confidence=0.6) report = api.platforms.query(platform["id"], query="Can a 17-year-old with no income apply for a £3,000 loan?")
domain = api.domains.create(name="Insurance Claims Fraud Detection", description="Claims exceeding the policy limit must be denied; " "claims filed within 90 days of policy start flagged; " "claimants with 4+ prior claims flagged high-frequency...") report = api.platforms.query(platform["id"], query="A property flood claim for £55,000 on a £50,000 policy. Approve?")
# The platform DERIVES the clinical knowledge from primitive facts domain = api.domains.create(name="Clinical Prescribing Safety", description="From the drug class, eGFR, concurrent medication and " "allergy, derive drug interactions, contraindications and " "allergy cross-reactions; then block, require a dose " "adjustment, flag, or permit.") report = api.platforms.query(platform["id"], query="Prescribe ibuprofen to an 82-year-old with chronic kidney disease?")
domain = api.domains.create(name="HR Recruitment Compliance", description="Applicants must be 18+; salary within the role band; " "right to work required; references must pass; flag " "qualified candidates rejected without interview as bias.") report = api.platforms.query(platform["id"], query="Can we hire a 16-year-old for a junior developer role?")
domain = api.domains.create(name="Environmental Regulatory Compliance", description="Readings must not exceed the regulatory or permit " "limit; facilities need an active permit; coastal-protected " "zones have stricter limits; breaches require enforcement.") report = api.platforms.query(platform["id"], query="NO2 recorded at 44.8 ug/m3 against a 40.0 limit. In breach?")
# 20k access requests. The policy is a chain: classify raw fields into named # conditions (device trust, privilege, restricted zone), then conclude permit or # deny — including the cross-field rule "deny when clearance < target sensitivity". # The request is supplied as structured facts — the facts ARE the certified base. domain = api.domains.create(name="Access Governance PDP", description="…") platform = api.platforms.create(domain_id=domain["id"], dataset_id=ds["id"], verified_profile=True, verified_min_confidence=0.6) report = api.platforms.query(platform["id"], query="Should this be permitted?", facts={"clearance_level": 2, "target_sensitivity": 3, "access_type": "write", "mfa_passed": True})
# 20k tracks; the request is supplied as structured facts (the facts ARE # the certified base). Policy stated in the description — incl. the safety rule: # "emergency tracks must always be escalated to a human operator." domain = api.domains.create(name="Air Track Triage", description="…") api.datasets.upload(domain_id=domain["id"], file_path="air_tracks.csv") platform = api.platforms.create(domain_id=domain["id"], dataset_id=ds["id"], verified_profile=True, verified_min_confidence=0.6) report = api.platforms.query(platform["id"], query="Triage this track.", facts={"iff_mode": "emergency", "squawk_emergency": True, "in_restricted_zone": False, "flight_plan_correlated": False})
# The SAME clear / monitor / escalate policy as the air-track demo above — # but over a 26-field ISR feed (ASTERIX Cat 021/062 + MISB ST 0601). You # describe the policy once; the platform finds the few fields that decide it. domain = api.domains.create(name="Air Track Triage (High-Spec ISR)", description="…") api.datasets.upload(domain_id=domain["id"], file_path="air_tracks_hispec.csv") platform = api.platforms.create(domain_id=domain["id"], dataset_id=ds["id"], verified_profile=True, verified_min_confidence=0.6) report = api.platforms.query(platform["id"], query="Triage this track.", facts={"position_source": "fused", "iff_mode": "emergency", "emergency_squawk": True, "in_restricted_zone": True, "track_confidence": 0.93})
# Upload a broad, neutral 24-series US macro panel (bundled, public-domain FRED) api.datasets.upload(domain_id=domain["id"], file_path="gs10_macro_panel.csv") # Train with autoregression OFF — the model must explain via macro drivers, not momentum cfg = api.predictions.create_config(platform["id"], target_field="GS10", time_index_field="date", horizon=1, frequency="monthly", autoregressive="none") # What did the system select? Feature importance + readable WHEN-THEN rules base = api.predictions.predict(platform["id"], prediction_config_id=cfg["id"]) sf = api.predictions.symbolic_forecast(platform["id"], prediction_config_id=cfg["id"])
# 1) Conditional forecast — YOU supply the macro view; the gate shows which rules fire f = api.predictions.symbolic_forecast(platform["id"], prediction_config_id=cfg["id"], feature_overrides={"FEDFUNDS": 6.0}) # "what if the Fed stays at 6%?" # f["forecast"] -> 4.45% ; f["why"] -> the rules, with fired_on_latest_row=True for the 3 that fire # 2) Does the symbolic layer earn its place? Preview the discovered rules read-only. cmp = api.predictions.neurosymbolic_comparison(platform["id"], prediction_config_id=cfg["id"], include_pending=True) # what-if preview of accepted-but-unapproved rules
feature_overrides: setting fed funds to 6% lifts the implied 10-year from 4.32% → 4.45%, and the gate names the 3 rules that fired (e.g. “WHEN CPI is rising AND fed funds is rising → yield up 0.13”). A mid-range reading fires nothing and the forecast holds at baseline — conditional and honest.# Live-fetch a 25-series monthly macro panel from FRED (rates, inflation, labour, ...) panel_csv = fetch_fred_panel(api_key) # resamples daily+monthly to clean monthly api.datasets.upload(domain_id=domain["id"], file_path=panel_csv) # Train with autoregression OFF — explain the index through macro, not momentum cfg = api.predictions.create_config(platform["id"], target_field="SP500", time_index_field="date", horizon=1, frequency="monthly", autoregressive="none") # What did the system select? base = api.predictions.predict(platform["id"], prediction_config_id=cfg["id"]) sf = api.predictions.symbolic_forecast(platform["id"], prediction_config_id=cfg["id"])
# Upload a bundled monthly panel: BTC + ETH (Coinbase) + 25 US macro series (FRED) api.datasets.upload(domain_id=domain["id"], file_path="btc_macro_panel.csv") # Train with autoregression OFF — explain BTC through the panel, not momentum cfg = api.predictions.create_config(platform["id"], target_field="BTC_USD", time_index_field="date", horizon=1, frequency="monthly", autoregressive="none") # What did the system select? base = api.predictions.predict(platform["id"], prediction_config_id=cfg["id"]) sf = api.predictions.symbolic_forecast(platform["id"], prediction_config_id=cfg["id"])
# Pull 18 UK + euro + US macro series LIVE from the FRED connector — one call, merged by date ds = api.datasets.fetch(domain_id=domain["id"], connector_type="fred", config={"series_ids": PANEL, "api_key": key, "start_date": "1999-01-01"}) # Forecast cable from a SYMBOLIC-ONLY model: induced WHEN->THEN rules, no neural network sf = api.predictions.symbolic_forecast(platform["id"], prediction_config_id=cfg["id"]) sf["skill_vs_persistence"] # +0.19 — beats a last-value baseline on a walk-forward backtest sf["why"] # 20 readable rules: contribution, hit-rate, support
# a sample of the 20 induced rules — each carries a contribution, hit-rate and support count WHEN UK confidence falling AND UK leading indicator falling → GBP/USD down 0.022 (hit 0.70, n=33) WHEN UK confidence 3m-change low AND 12m-deviation low → GBP/USD down 0.020 (hit 0.77, n=47) WHEN UK confidence jumps from a depressed level → GBP/USD up 0.017 (hit 0.86, n=7) WHEN UK industrial production running above trend → GBP/USD up 0.006 (hit 0.63, n=65)
What can you build?
Every domain below was built from a plain-English description and a features-only dataset — no labels, no training loop, no model selection. The platform derives the rules itself.
Clearance level vs target sensitivity. Device trust gate, MFA requirement, change-control policy — a compact binary PDP.
Loan amount vs collateral value. High DTI tolerated when credit is strong; weak credit tolerated when well-secured — rules that interact, not just stack.
A staged-accident pattern is a composite atom: early claim AND high-value AND repeat claimant. Claim amount vs policy limit is a cross-field comparison.
Salary expectation vs role band boundaries. A fairness guard prevents bias on protected characteristics — a precedence rule that outranks the threshold cascade.
A breach meta-atom aggregates three sub-conditions — measurement vs limit, consecutive breaches, permit status — then feeds three different severity tiers.
Sensor data → identification status → triage verdict. A derived condition (kinematic implausibility) feeds a split: unidentified tracks escalate, identified ones monitor.
Drug × patient state → interaction type → severity → outcome. The same drug pair produces block or flag depending on the interaction severity — three layers of derivation.
ASTERIX + MISB 0601 sensor fields. Same triage policy as standard air-track but with 26 input columns and 2k training rows — the platform selects features from a wide, sparse schema.
The verified profile
A verified platform won’t hand you an answer it can’t prove.
proof_checked and a human-readable summary.# A fact below the threshold is rejected, not used report["explanation"]["rejected_facts"] # → confidence 0.598 < threshold 0.600 # "field not declared in the domain schema" report["proof_checked"] # True report["proof_summary"] # "Decision independently certified against the # trusted kernel: 2 rule(s) fired, 2 fact(s) # derived from 4 input fact(s)."
Real output, loan platform, τ = 0.6.
For AI agents
Everything here — domains, ontologies, verified platforms, queries, forecasts, connectors — is a clean, typed SDK. No console clicks required.
The eight examples on this page were created by an AI agent working only from the public SDK and docs — the same path your agent would take.
# An agent provisions a narrow key, then builds key = api.api_keys.create(scope="platform", platform_id=platform["id"]) # ...and forecasts straight off a live connector ds = api.datasets.fetch(domain_id=domain["id"], connector_type="coinbase", config={"product_ids": ["BTC-USD"]})
Agent Policy Gate
Write the rules an agent must obey in plain English. Ambertrace compiles them to a verified policy and proves every proposed tool-call permit or deny against it — fail-closed, with a machine-checked proof. The model only proposes; the kernel proves.
# Author the rules — plain English in api.agent_policy.author( "Only allow deploy or rollback. Permit a deploy only when " "ci_passed, security_scan_passed, code_review_approved and " "within_change_window are true and rollout_pct is at most 10.") # Gate every proposed action — permit/deny WITH PROOF v = api.agent_policy.authorize_action(platform_id, tool="deploy", args={"target_environment": "production", "ci_passed": True, "code_review_approved": False, ...}) v["decision"] # "deny" v["proof_checked"] # True — the kernel certified the firing set v["denied_reason"] # "Action failed at least one policy requirement"
An untrusted deploy agent cannot ship outside policy. Action-type and environment allowlists, the green-build gates (CI, scan, review, change-ticket), your change-window and review-before-merge rules, and a canary cap — each verdict carries its proof. A rollback stays available as a safety valve.
# Author the trading limits — plain English in api.agent_policy.author( "Only allow orders. Permit an order only when the side is buy " "or sell, the asset_class is equity or etf, the symbol is not " "restricted, and quantity times price is at most 50000.") # A $60k order is over the per-order notional cap v = api.agent_policy.authorize_action(platform_id, tool="order", args={"side": "buy", "asset_class": "equity", "restricted": False, "quantity": 1000, "price": 60}) v["decision"] # "deny" v["proof_checked"] # True v["denied_reason"] # "Order quantity times price exceeds 50000."
A trading agent cannot place an off-policy order. Side and asset-class allowlists, a restricted-symbol block, and a per-order notional cap — an order over the limit is denied, with a proof of exactly which requirement it failed.
The gate checks itself. When you author a policy, Ambertrace compiles it and runs its own probes. If the compiled gate would ever decide against your stated intent, it tells you — it will not silently ship a policy that doesn't mean what you wrote.
Describe your rules, bring your data, and get back decisions with proofs attached.
Get started at app.ambertrace.ai