A Pollock-style Normative Defeasible Engine (NDE) for defeasible reasoning, with LLM-assisted argument generation and defeat discovery.
This project implements a computational model of defeasible reasoning based on John Pollock's philosophical work on epistemology and argumentation theory. The system maintains a strict separation between:
- Normative Core: The NDE is the sole authority on defeat status and warrant
- Heuristic Agents: Generate candidate arguments and defeaters (including LLM-assisted)
- Environment: External source of evidence and interests
Key features:
- Incremental fixpoint computation for defeat status
- Support for rebutting and undercutting defeaters
- Epistemic interest management
- LLM integration (Ollama) for defeater generation
- Optional LangChain integration for retrieval-augmented reasoning
- SQLite + JSON persistence for snapshots and audit trails
- Event-driven architecture with pluggable environments
┌─────────────────────────────────────────────────────────────────┐
│ Runtime Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────────────┐ │
│ │ Daemon │ │ EventBus │ │ Environment │ │
│ │ (Control) │◄─┤ (I/O) │◄─┤ (Exogenous Events) │ │
│ └──────┬──────┘ └─────────────┘ └────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Agent Layer │ │
│ │ ┌────────────┐ ┌─────────────┐ ┌───────────────────┐ │ │
│ │ │ Support │ │ Defeater │ │ Undercutter │ │ │
│ │ │ Agent │ │ Hunter │ │ Agent │ │ │
│ │ └────────────┘ └─────────────┘ └───────────────────┘ │ │
│ │ ┌────────────┐ ┌─────────────┐ │ │
│ │ │ Info │ │ Explainer │ │ │
│ │ │ Seeker │ │ Service │ │ │
│ │ └────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Normative Core (NDE) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Argument │ │ Defeat │ │ Epistemic │ │ │
│ │ │ Graph │ │ Operator │ │ Interests │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LLM Layer │ │
│ │ ┌──────────────────┐ ┌─────────────────────────────┐ │ │
│ │ │ OllamaClient │ │ LangChainAdapter │ │ │
│ │ │ (Defeaters) │ │ (Retrieval, optional) │ │ │
│ │ └──────────────────┘ └─────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Rational_agent/
├── core/ # Normative reasoning core
│ ├── nde.py # Normative Defeasible Engine
│ ├── graph.py # Argument graph with defeat edges
│ ├── formulas.py # Propositional formula representation
│ ├── interests.py # Epistemic interest tracking
│ ├── reasons.py # Pollock-style reason schemas
│ └── belief_store.py # Belief state projection
│
├── agents/ # Heuristic agents (non-normative)
│ ├── base.py # Agent protocol and context
│ ├── prediction.py # SimpleSupportAgent
│ ├── defeater_hunter.py # SimpleDefeaterHunterAgent (rebutters)
│ ├── undercutter_agent.py# SimpleUndercutterAgent (LLM-based)
│ ├── info_seeker.py # InfoSeekerAgent (LLM-based)
│ └── explainer.py # ExplanationService
│
├── runtime/ # Execution infrastructure
│ ├── daemon.py # Main control loop
│ ├── events.py # Event model and EventBus
│ ├── inference_queue.py # Agent scheduler
│ ├── persistence.py # SQLite + JSON snapshots
│ └── logger.py # Centralized logging
│
├── environment/ # External event sources
│ └── __init__.py # Environment, ScriptedEnvironment
│
├── llm/ # LLM integration
│ ├── ollama_client.py # Ollama API client
│ ├── langchain_adapter.py# Optional LangChain wrapper
│ └── prompts/ # Prompt templates (placeholder)
│
├── scripts/ # Entry points
│ ├── run_local.py # Demo runner
│ ├── plan_checker.py # Interactive plan feasibility checker
│ └── query_explanations.py
│
├── data/ # Runtime data (gitignored)
│ └── chroma_index/ # Chroma vector store for retrieval
│
├── tests/ # Unit tests
│ ├── test_defeat_fixpoint.py
│ ├── test_reinstatement.py
│ └── test_interest_control.py
│
├── pyproject.toml # Project metadata (PEP 621)
├── requirements.txt # Python dependencies
└── LICENSE # Apache License 2.0
Each node (argument) in the graph has a status:
- UNDEFEATED (U): The argument currently stands
- DEFEATED (D): At least one undefeated defeater exists
The defeat operator:
status(a) = DEFEATED if ∃b: b defeats a ∧ status(b) = UNDEFEATED
UNDEFEATED otherwise
The system distinguishes two types of defeat (following Pollock):
| Type | Description | Example |
|---|---|---|
| REBUT | Attacks the conclusion directly | "It is not raining" defeats "It is raining" |
| UNDERCUT | Attacks the inference from premises to conclusion | "The sensor is unreliable" defeats the inference from sensor reading to conclusion |
A defeated argument can be reinstated if all its defeaters become defeated:
A: "It rained" (initially undefeated)
B: "The sprinklers ran" defeats A → A becomes DEFEATED
C: "The sprinklers are broken" defeats B → B becomes DEFEATED → A is REINSTATED
Interests represent queries the system cares about:
- ACTIVE: The system should reason about this
- RESOLVED: A warranted answer has been found
- CANCELLED: No longer relevant
# Clone the repository
git clone <repo-url>
cd Rational_agent
# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # Linux/macOS
# or: venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txtThe undercutter and info-seeker agents use a local Ollama server:
# Install Ollama (see https://ollama.ai)
# Then pull a model:
ollama pull llama3.2
# Set environment variables (optional)
export OLLAMA_MODEL=llama3.2
export OLLAMA_BASE_URL=http://localhost:11434The plan checker and defeater hunter agents support retrieval-augmented generation using LangChain with Chroma as the vector store:
pip install langchain langchain-community chromadb sentence-transformersTo use retrieval, you'll need a Chroma index at data/chroma_index/. You can build one by indexing your domain documents (see scripts/build_retriever.py for a template).
PYTHONPATH=. python scripts/run_local.pyThis will:
- Create an NDE with an initial interest ("Sky is blue")
- Run agents that add supporting arguments and generate defeaters
- Process defeat status updates incrementally
- Print the belief state at each step
- Save snapshots to
data/snapshots/anddata/nde_snapshots.db
The plan checker is an interactive tool that evaluates the feasibility of user-submitted plans using defeasible reasoning:
PYTHONPATH=. python -m scripts.plan_checkerFeatures:
- Registers a plan as an epistemic interest and asserts provisional success
- Runs multiple reasoning ticks (agents generate defeaters and counter-defeaters)
- Uses LangChain + Chroma for retrieval-augmented defeater generation
- Outputs a verdict (provisionally-feasible or currently-defeated)
- Provides both text and structured JSON explanations with full defeat trees
Example session:
Plan: I plan on quitting my job and becoming a day trader with no experience.
Verdict: currently-defeated (node status = D)
Explanation (text):
Node plan-success is D – undefeated defeater exists.
Defeaters:
- def-plan-success-bf65806d (rebut, status=U): 'Lack of experience and
training in day trading typically results in significant financial loss...'
from core.formulas import Formula
from core.graph import ArgumentGraph
from core.nde import NormativeDefeasibleEngine
# Create the engine
graph = ArgumentGraph()
nde = NormativeDefeasibleEngine(graph=graph)
# Add premises
nde.add_premise("A", Formula("It is raining"))
nde.add_premise("B", Formula("It is not raining"))
# Declare defeat
nde.add_defeat("B", "A")
# Compute defeat status
nde.process_pending_updates()
# Query status
print(nde.is_undefeated("A")) # False
print(nde.is_undefeated("B")) # True
# Get explanation
print(nde.explain_status("A"))from agents.base import AgentContext
from agents.prediction import SimpleSupportAgent
from core.belief_store import BeliefStore
# Create context
belief_store = BeliefStore(nde.graph)
ctx = AgentContext(nde=nde, graph=nde.graph, belief_store=belief_store)
# Create an interest
phi = Formula("The sky is blue")
nde.create_interest("I1", phi, priority=1.0)
# Create and run an agent
agent = SimpleSupportAgent("supporter", ctx)
agent.step() # Adds a premise supporting the interest
# Update and check
nde.process_pending_updates()
nde.update_interests()
print(nde.is_formula_warranted(phi)) # TruePYTHONPATH=. pytest tests/ -v| Method | Description |
|---|---|
add_premise(id, formula) |
Add a bare premise node |
add_argument(id, formula, premise_ids) |
Add an argument with premises |
add_defeat(defeater, target, defeat_type) |
Declare a defeat relation |
process_pending_updates() |
Compute defeat statuses |
is_undefeated(node_id) |
Check if a node is undefeated |
is_formula_warranted(formula) |
Check if any undefeated node concludes formula |
create_interest(id, formula, ...) |
Register an epistemic interest |
update_interests() |
Resolve interests based on warrant |
explain_status(node_id) |
Human-readable status explanation |
explain_status_dict(node_id) |
JSON-serializable explanation |
explain_status_tree(node_id, max_depth) |
Recursive defeat tree |
| Agent | Purpose |
|---|---|
SimpleSupportAgent |
Adds premises supporting active interests |
SimpleDefeaterHunterAgent |
Generates rebutting defeaters (synthetic negation) |
SimpleUndercutterAgent |
Uses LLM to generate undercutting defeaters |
InfoSeekerAgent |
Uses LLM to propose information needs as sub-interests |
ExplanationService |
Provides structured explanations for nodes and interests |
-
Normative Separation: Only the NDE assigns defeat status. Agents propose; the NDE decides.
-
Incremental Fixpoint: Status is computed incrementally via a worklist algorithm, not recomputed globally.
-
Observational Logging: Logging never affects reasoning or control flow.
-
LLM as Heuristic: LLMs generate candidate defeaters and questions—they don't determine warrant.
-
Event-Driven: External inputs come through an EventBus; the Environment is the source of exogenous events.
-
Persistence for Audit: Snapshots and events are persisted for debugging and reproducibility.
- Pollock, J. L. (1995). Cognitive Carpentry: A Blueprint for How to Build a Person. MIT Press.
- Pollock, J. L. (1987). Defeasible Reasoning. Cognitive Science, 11(4), 481–518.
- Pollock, J. L. (2001). Defeasible Reasoning with Variable Degrees of Justification. Artificial Intelligence, 133(1-2), 233–282.
Copyright 2025 Robert J. Hartung III
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the LICENSE file for the specific language governing permissions and limitations under the License.