Skip to main content

A biologically-inspired reactive agent framework for Python

Project description

Arachnite

A biologically-inspired reactive agent framework for Python.

The architecture models the nervous system of arachnids — sense → context → reflex → instinct → decide → act. Developers extend abstract base classes to build agents that run on edge devices (Raspberry Pi, Jetson Nano), laptops, or cloud servers, all connected by a pluggable transport layer.

  • Async-first: every node interface is asyncio-native
  • Typed: strict type annotations throughout (mypy strict)
  • Pluggable transports: in-process, MQTT, NATS, Redis
  • Distributed by manifest: declarative multi-device deployment
  • Reflex co-location: safety-critical reflexes are validated at deploy time
  • Python 3.10+

Install

pip install arachnite

With optional extras:

pip install "arachnite[all]"            # every optional dependency
pip install "arachnite[mqtt]"           # MQTT transport
pip install "arachnite[nats]"           # NATS transport
pip install "arachnite[redis]"          # Redis transport
pip install "arachnite[web]"            # bundled signal dashboard
pip install "arachnite[llm]"            # Anthropic LLM provider
pip install "arachnite[benchmarks]"     # psutil for RSS measurement

Development install from source:

git clone https://github.com/memrecolak/arachnite-oss.git arachnite
cd arachnite
pip install -e ".[all,dev]"

Quick start

A minimal agent: a sensor that reads temperature, an instinct that fires when it gets hot, and an action that cools things down.

import asyncio
import time

from arachnite import (
    BaseActionNode,
    BaseInstinctNode,
    BaseSenseNode,
    Proposal,
    Result,
    RuntimeBuilder,
    Signal,
)


class TempSense(BaseSenseNode):
    node_id = "TempSense"
    signal_kind = "temperature"

    async def read(self) -> Signal:
        return Signal(
            source=self.node_id,
            kind=self.signal_kind,
            value=42.0,
            confidence=1.0,
            timestamp=time.monotonic(),
        )


class HotInstinct(BaseInstinctNode):
    node_id = "HotInstinct"
    priority = 80

    async def evaluate(self, ctx) -> Proposal | None:
        hot = [s for s in ctx.signals if s.kind == "temperature" and s.value > 40.0]
        if hot:
            return Proposal(
                instinct_id=self.node_id,
                action_id="CoolDown",
                priority=self.priority,
                urgency=0.9,
            )
        return None


class CoolDown(BaseActionNode):
    node_id = "CoolDown"

    async def execute(self, proposal) -> Result:
        print(f"Cooling down! params={proposal.parameters}")
        return Result(action_id=self.node_id, success=True)


async def main() -> None:
    rt = (
        RuntimeBuilder()
        .sense(TempSense)
        .instinct(HotInstinct)
        .action(CoolDown)
        .tick_rate(5.0)
        .build()
    )
    await rt.start()
    await asyncio.sleep(5.0)
    await rt.stop()


asyncio.run(main())

See the examples/ directory for more complete programs: reflex nodes, multi-step actions, supervisor restart policies, and a web dashboard.

Documentation

  • tutorials/ — step-by-step lessons starting at tutorials/01_welcome.md. Advanced topics (multi-step actions, supervisors, distributed deployment, LLM instincts, active inference, safety monitors) live under tutorials/advanced/.
  • spec/ — the formal framework specification, eight numbered sections covering architecture, nodes, runtime, distributed deployment, infrastructure, and the benchmark suite.

Core concepts

Nodes

Five node families, each with an abstract base and a master that owns the registered instances:

Node Returns When to use
BaseSenseNode Signal Read hardware/state, emit one signal per tick
BaseInstinctNode Proposal or None Evaluate context, propose an action
BaseReflexInstinctNode Proposal or None Same as instinct, but bypasses the decision layer (priority ≥ 200, co-located with target action)
BaseDecisionNode Decision Pick which proposal to execute (Greedy / Weighted / Random / ActiveInference built-ins, or your own)
BaseActionNode Result Carry out the work — must always return, never raise
MultiStepActionNode Result Long-running actions with interrupt/rollback policies

Priority convention

  • 200+ — reflex instincts only
  • 100–199 — safety / survival
  • 50–99 — goal-directed
  • 1–49 — exploratory / maintenance
  • 0 — reserved (inactive)

Architectural rules

  1. Nodes never hold references to each other; communication goes through SignalBus.
  2. ReflexInstinctNode and its target ActionNode must be on the same AgentNode.
  3. MultiStepActionNode mandatory blocks cannot be interrupted (except emergency_stop).
  4. execute() on any ActionNode must always return a Result — never raise.
  5. evaluate() on any InstinctNode must return None when not applicable — never raise.
  6. All node I/O must be async — wrap blocking hardware calls in asyncio.to_thread().

Runtime vs. runtime-less

The full deliberative runtime (RuntimeBuilderInstinctMasterDecisionMaster → the free-running tick loop) is not required to use Arachnite. The node primitives work standalone with a bare SignalBus:

bus = SignalBus()
action_master = ActionMasterNode(bus)
action_master.register(MyActionNode(bus))
# Drive it on a loop you own — no runtime, no tick loop:
result = await action_master.dispatch(proposal)

Reach for the deliberative runtime only when ≥2 autonomous behaviors compete continuously on dynamic priority — the robotics / edge control shape it was built for. For a single agent or a human-in-the-loop app with a static priority order (a coding cockpit, a one-shot pipeline, a request-driven service), prefer direct dispatch and your own arbitration. See spec/03_runtime.md §7.2.2 for the direct-drive pattern.

Distributed deployments

A DeploymentManifest declares which nodes run on which AgentNode. The manifest validator enforces co-location rules and fails loudly on missing environment variables.

agents:
  vision:
    transport: nats
    transport_url: ${NATS_URL}
    nodes:
      - ProximitySense
      - ObjectDetectionSense
  control:
    transport: nats
    transport_url: ${NATS_URL}
    nodes:
      - JointPositionSense
      - CollisionReflex     # priority 250, reflex
      - EmergencyRetract    # co-located target
      - GraspInstinct
      - PickAndPlace

See examples/robot_arm/ for a runnable two-agent case study.

Benchmarks

A reproducible benchmark suite ships under benchmarks/:

# Full suite (30 runs, JSON output)
python benchmarks/suite.py

# Quick run (5 runs)
python benchmarks/suite.py --runs 5

# Individual benchmarks
python benchmarks/tick_latency.py
python benchmarks/reflex_latency.py
python benchmarks/scalability_sweep.py
python benchmarks/transport_latency.py

Benchmarks include tick latency, per-stage breakdown, reflex arc timing, memory footprint, scalability sweeps, multi-step action interrupt latency, long-horizon stability soak, and transport publish-to-deliver latency. All emit JSON with bootstrap CIs for median / P95 / P99.

Cross-framework comparison (optional)

baselines/ holds comparison harnesses against py_trees, ROS 2, and the Jason AgentSpeak BDI engine. These are not part of the framework — they are not installed by pip install arachnite and are excluded from the wheel. They require external toolchains (JVM, ROS 2) to run. See baselines/README.md for setup.

Development

# Run all tests
pytest

# With coverage
pytest --cov=arachnite --cov-report=term-missing

# Type check
mypy arachnite

# Lint
ruff check arachnite tests benchmarks

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arachnite-0.11.5.tar.gz (6.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

arachnite-0.11.5-py3-none-any.whl (5.6 MB view details)

Uploaded Python 3

File details

Details for the file arachnite-0.11.5.tar.gz.

File metadata

  • Download URL: arachnite-0.11.5.tar.gz
  • Upload date:
  • Size: 6.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for arachnite-0.11.5.tar.gz
Algorithm Hash digest
SHA256 9ce566165f5cb40a7b74a8861042b1729e727f3aac391924b7b53841e35fcf15
MD5 c5cb16bcda89896cac47dc095ad87f59
BLAKE2b-256 cb9cda56e93f05b07197fbef0d76a4a52cc93f78d62a4223a9f679c46471371e

See more details on using hashes here.

Provenance

The following attestation bundles were made for arachnite-0.11.5.tar.gz:

Publisher: publish.yml on memrecolak/arachnite-oss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arachnite-0.11.5-py3-none-any.whl.

File metadata

  • Download URL: arachnite-0.11.5-py3-none-any.whl
  • Upload date:
  • Size: 5.6 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for arachnite-0.11.5-py3-none-any.whl
Algorithm Hash digest
SHA256 c44a288d5b366513c50a9450ce39c8d5dc56d75cdf69f2e7b0923864a1ba5511
MD5 13aeadd6bf8446acfe87f22aec6d6d15
BLAKE2b-256 0ab5387b6aee7fe8dae06c9882c120d590fe87bfd28c3e0e203b8770ff2b922b

See more details on using hashes here.

Provenance

The following attestation bundles were made for arachnite-0.11.5-py3-none-any.whl:

Publisher: publish.yml on memrecolak/arachnite-oss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page