Skip to content

Quick Start

Need more background information?

Read the Introduction

Installation

To install Vulcan within your project, simply add vulcan-core as a dependency using your preferred package manager:

Bash
pip install vulcan-core
Bash
poetry add vulcan-core

The package provides a number of optional extras depending on how you intend to integrate Vulcan into your project.

Simple Rules

Before we can begin expressing rules, we must declare facts that represent the domain for which we want to reason:

Python
1
2
3
4
5
6
7
8
9
from vulcan_core import Fact


class Inventory(Fact):  # (1)!
    apples: int


class QueuedOrder(Fact):
    apples: int = 0
  1. Classes that inherit from Fact are created automatically as Python dataclasses. Subclasses must declare the types for all fields.
API Reference: Fact

Once the facts are declared, they may be used to express logical conditions and actions:

Python
from vulcan_core import RuleEngine, action, condition

engine = RuleEngine()

# Rule to order 10 apples if the inventory is less than or equal to 10
engine.rule(
    when=condition(lambda: Inventory.apples <= 10),  # (1)!
    then=action(QueuedOrder(apples=10)),  # (2)!
)

# Rule to order 0 apples if the inventory is greater than 10
engine.rule(
    when=condition(lambda: Inventory.apples > 10),
    then=action(QueuedOrder(apples=0)),
)
  1. Vulcan uses advanced Python AST parsing to simplify condition and action expressions. This allows for a more natural and readable syntax without the need to instantiate Fact objects.
  2. Vulcan facts are immutable, therefore the then block of a rule must create a new Fact instance.
API Reference: RuleEngine | action | condition

A rule has two required parts: when and then. You can think of the when statement like an "if" statement in code. However, unlike imperative coding, rules are only evaluated when the facts they depend on change. The result of a rule is a change in another fact, expressed in the then statement.

Once we have our rules, we can define our initial knowledge base and run the rules:

Python
engine.fact(Inventory(apples=5))
engine.evaluate()

We can "peek" at the state of the knowledge base at any time:

Python
print(engine.facts)

Text Output
{'Inventory': Inventory(apples=5), 'QueuedOrder': QueuedOrder(apples=10)}
From this, we can see that one of our rules has fired, as we did not initially have a QueuedOrder fact, but now we do. If we change our Inventory amount, we can see the QueuedOrder fact value change:

Python
1
2
3
engine.fact(Inventory(apples=15))
engine.evaluate()
print(engine.facts)

Text Output
{'Inventory': Inventory(apples=15), 'QueuedOrder': QueuedOrder(apples=0)}
Now we see that the QueuedOrder fact has a value of 0 rather than the previous 10. This is because our second rule has fired given the criteria of inventory being greater than 10.

AI Rules

Unlike a traditional rules engine, Vulcan rules can also express natural language conditions. In order to use AI rules, you will need to install Vulcan with the correct dependency for the type of LLM you wish to use (or implement your own adapter). For this example, we will be using OpenAI:

OpenAI Credentials

To run AI examples in this guide, you must have an OpenAI account and an OPENAI_API_KEY environment variable present.

Bash
pip install "vulcan-core[openai]"
Bash
poetry add "vulcan-core[openai]"
Python
class Inventory(Fact):
    apples: int
    apple_kind: str


engine.fact(Inventory(apples=5, apple_kind="fuji"))


# Rule to order 50 apples if the type of apple is delicious
engine.rule(
    when=condition(f"Are {Inventory.apple_kind} considered delicious by most people?"),
    then=action(QueuedOrder(apples=50)),
)

engine.evaluate()
print(engine.facts)
Text Output
mappingproxy({Inventory: Inventory(apples=5, apple_kind='fuji'),
              QueuedOrder: QueuedOrder(apples=50)})

Data-Augmented Rules

Not only can we express rules using natural language and evaluate facts, we can also define facts with similarity search properties and use them as RAG rules. This allows AI rules to evaluate vast amounts of information by limiting information to only what's relevant to the inquiry.

Defining a similarity attribute on a Fact is simple:

Python
1
2
3
4
5
from vulcan_core import Similarity


class News(Fact):
    headlines: Similarity
API Reference: Similarity

For the purpose of this example, we will now create a simple Chroma VectorStore using the LangChain API via an adapter:

Python
from langchain.schema import Document
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from vulcan_core.models import RetrieverAdapter

headlines = [
    "Potato prices are on a decline due to a surplus in the market.",
    "Brussel sprout futures are trading at a premium.",
    "Fuji apples are in short supply due to a poor harvest.",
]
documents = [Document(value) for value in headlines]

# Create a store that returns the topmost similar document
store = Chroma(embedding_function=OpenAIEmbeddings(model="text-embedding-3-small"))
retriever = store.as_retriever(search_kwargs={"k": 1})
retriever.add_documents(documents)

adapter = RetrieverAdapter(store=retriever)

From here we can instantiate the fact and add it to the knowledge base along with a rule that references the similarity attribute of the fact:

Python
news_fact = News(headlines=adapter)
engine.fact(news_fact)

# Define a rule to check if the news headline is relevant to apples
engine.rule(
    when=condition(
        f"Is there any indication in {News.headlines} that {Inventory.apple_kind} apple prices may increase?"
    ),
    then=action(QueuedOrder(apples=100)),
)

# Evaluate the engine
engine.evaluate()

# Print the facts to see the result
print(engine.facts)

Text Output
1
2
3
mappingproxy({Inventory: Inventory(apples=5, apple_kind='fuji'),
              News: News(headlines=RetrieverAdapter(search_type=similarity)),
              QueuedOrder: QueuedOrder(apples=100)})
We can see from the updated facts that the AI rule determined from the relevant news headlines that apple prices may increase. It subsequently ordered more apples to be stocked, anticipating a future shortage.

Explainability Reporting

A powerful feature of Vulcan is the ability to explain the reasoning behind rule-based decisions. Explainability reports allow insight into how the rules engine arrived at a result, especially in complex scenarios where multiple rules may interact or when AI rules are involved.

To enable explainability reporting, call engine.evaluate(audit=True) and then inspect the result of engine.yaml_report():

Python
from functools import partial

engine = RuleEngine()

engine.rule(
    name="Order more apples if delicious",
    when=condition(f"Are {Inventory.apple_kind} considered delicious by most people?"),
    then=action(partial(QueuedOrder, apples=50)),
)

engine.fact(Inventory(apples=5, apple_kind="fuji"))

engine.evaluate(audit=True)

engine.yaml_report()
YAML
report:
  iterations:
  - id: 0
    timestamp: '2025-07-14T16:07:14.742891Z'
    elapsed: 2.441
    matches:
    - rule: 205a7ab3:Order more apples if delicious
      timestamp: '2025-07-14T16:07:14.742920Z'
      elapsed: 2.441
      evaluation: True = Are {Inventory.apple_kind|fuji|} considered delicious by most people?
      consequences:
        QueuedOrder.apples: 50
      rationale: Fuji apples are generally considered delicious by most people due to their sweetness and crisp texture.

The above report shows the values used during rule evaluation, the consequences of the rules, and the AI's reasoning. By decomposing complex LLM prompts into smaller queries, Vulcan allows logical composition through computation, not prediction, with full explainability of how complex decisions are made.

Read more about reporting