Files
agentic-govbot/ARCHITECTURE_EXAMPLE.md
Nathan Schneider bda868cb45 Implement LLM-driven governance architecture with structured memory
This commit completes the transition to a pure LLM-driven agentic
governance system with no hard-coded governance logic.

Core Architecture Changes:
- Add structured memory system (memory.py) for tracking governance processes
- Add LLM tools (tools.py) for deterministic operations (math, dates, random)
- Add audit trail system (audit.py) for human-readable decision explanations
- Add LLM-driven agent (agent_refactored.py) that interprets constitution

Documentation:
- Add ARCHITECTURE.md describing process-centric design
- Add ARCHITECTURE_EXAMPLE.md with complete workflow walkthrough
- Update README.md to reflect current LLM-driven architecture
- Simplify constitution.md to benevolent dictator model for testing

Templates:
- Add 8 governance templates (petition, consensus, do-ocracy, jury, etc.)
- Add 8 dispute resolution templates
- All templates work with generic process-based architecture

Key Design Principles:
- "Process" is central abstraction (not "proposal")
- No hard-coded process types or thresholds
- LLM interprets constitution to understand governance rules
- Tools ensure correctness for calculations
- Complete auditability with reasoning and citations

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 14:24:23 -07:00

15 KiB

Governance Architecture: Complete Example

This document shows how the agentic governance system works through a complete process lifecycle, using a proposal as the example.

Note: The system uses "process" as the generic concept for any governance activity. Proposals are just one type of process. The same architecture handles disputes, elections, discussions, do-ocracy actions, jury selection, and any other process type defined in your constitution.

Example Process: Standard Proposal

User @alice initiates a proposal process. Let's see how the system handles it WITHOUT hard-coded logic.

Step 1: User Submits Proposal

@alice: "@govbot I propose we change the moderation policy to require 2 moderators for bans"

Step 2: Agent Parses Intent (LLM)

agent._parse_intent_with_llm(request, actor="@alice")

LLM Response:

{
    "intent_type": "create_proposal",
    "query": "What are the rules for creating a proposal about moderation policy?",
    "parameters": {
        "proposal_text": "change the moderation policy to require 2 moderators for bans",
        "topic": "moderation_policy"
    },
    "confidence": "high"
}

Step 3: Query Constitution

constitution.query(
    question="What are the rules for creating a proposal about moderation policy?",
    context="Actor: @alice, Request: [proposal text]"
)

Constitutional Reasoner Returns:

{
    "answer": "Standard proposals address routine governance matters including policy changes. They require a 6-day discussion period and pass when more people vote agree than disagree.",
    "sections": [
        {
            "title": "Article 3, Section 3.1: Proposal Types",
            "content": "Standard Proposals address routine governance matters:\n- Discussion period: 6 days minimum\n- Passage threshold: More Agree than Disagree votes"
        }
    ],
    "confidence": "high"
}

Step 4: Query Memory for Context

memory.get_active_processes()

Memory Returns:

[
    ProcessMemory(
        id="process_123",
        type="standard_proposal",
        status=ACTIVE,
        created_by="@bob",
        deadline=datetime(2026, 2, 10),
        # ... other active proposals
    ),
    # ... more processes
]

Memory Context Summary:

{
    "active_processes": [
        "Process process_123: standard_proposal by @bob, deadline 2026-02-10",
        "Process process_124: urgent_proposal by @carol, deadline 2026-02-09"
    ],
    "overdue_processes": []
}

Step 5: LLM Makes Decision

agent._make_decision_with_llm(intent, constitution, memory, actor, context)

LLM Prompt (simplified):

You are a governance bot interpreting a community constitution.

INTENT:
User wants to create a proposal about moderation policy.

CONSTITUTIONAL GUIDANCE:
Standard proposals need 6-day discussion period and pass when more agree than disagree.

CURRENT MEMORY STATE:
2 active proposals currently running.

What should be done?

LLM Decision:

{
    "action": "create_process",
    "reasoning": "This is a standard proposal about moderation policy. According to Article 3, Section 3.1, standard proposals require a 6-day discussion period and pass when more members vote agree than disagree. I should create a new process with these parameters.",
    "constitution_citations": [
        "Article 3, Section 3.1: Proposal Types"
    ],
    "parameters": {
        "process_type": "standard_proposal",
        "deadline_days": 6,
        "threshold_expression": "agree > disagree",
        "initial_state": {
            "proposal_text": "change the moderation policy to require 2 moderators for bans",
            "proposer": "@alice",
            "topic": "moderation_policy",
            "votes": {}
        }
    },
    "calculations": [
        {
            "tool": "datetime_add",
            "days": 6,
            "result": "2026-02-14T18:30:00Z"
        }
    ]
}

Step 6: Execute Decision

agent._execute_decision(decision, actor="@alice", context)

Memory Operations:

  1. Create Process:
process = memory.create_process(
    process_id="process_125",
    process_type="standard_proposal",
    created_by="@alice",
    constitution_basis=["Article 3, Section 3.1: Proposal Types"],
    deadline=datetime(2026, 2, 14, 18, 30),
    initial_state={
        "proposal_text": "change the moderation policy to require 2 moderators for bans",
        "votes": {}
    }
)
  1. Log Event:
memory.add_event(
    process_id="process_125",
    actor="@alice",
    event_type="process_created",
    data={"proposal_text": "..."},
    context="Proposal created by @alice"
)
  1. Log Decision:
memory.add_decision(
    process_id="process_125",
    decision_type="process_creation",
    reasoning="This is a standard proposal about moderation policy...",
    constitution_citations=["Article 3, Section 3.1"],
    result="created"
)

Step 7: Generate Response

Bot Response:

@alice Your proposal has been created as Process #125.

Type: Standard Proposal
Discussion Period: 6 days
Deadline: February 14, 2026 at 18:30 UTC
Voting Threshold: More Agree than Disagree votes

Constitutional Basis: Article 3, Section 3.1 - Standard Proposals

The discussion period is now open. Community members may discuss and
vote on this proposal until the deadline.

Scenario: Voting

User @bob votes on the proposal.

Step 1: User Votes

@bob: "@govbot I vote agree on proposal 125"

Step 2-4: Intent Parsing + Constitution + Memory

Intent: cast_vote on process_125

Constitution: "Members vote by replying to proposal thread. Votes can be changed before deadline."

Memory: Gets process_125, checks if active, checks if @bob already voted

Step 5: LLM Decision

{
    "action": "record_vote",
    "reasoning": "@bob is voting 'agree' on an active proposal. The constitution allows members to vote before the deadline. This vote should be recorded.",
    "constitution_citations": ["Article 4, Section 4.2: Voting Process"],
    "parameters": {
        "process_id": "process_125",
        "vote_type": "agree"
    }
}

Step 6: Execute

Memory Operations:

  1. Update Process State:
votes = process.state["votes"]
votes["@bob"] = {"vote": "agree", "timestamp": "2026-02-09T10:15:00Z"}
memory.update_process("process_125", state_updates={"votes": votes})
  1. Log Event:
memory.add_event(
    process_id="process_125",
    actor="@bob",
    event_type="vote_cast",
    data={"vote": "agree"},
    context="@bob voted agree"
)

Bot Response:

@bob Your vote has been recorded: Agree

Current tally for Proposal #125:
- Agree: 1
- Disagree: 0
- Abstain: 0

Deadline: February 14, 2026 at 18:30 UTC

Scenario: Deadline Reached

Bot checks deadlines and evaluates proposal.

Step 1: Scheduled Check

agent.check_deadlines()

Memory Query:

overdue = memory.get_overdue_processes()
# Returns: [process_125]

Step 2: Evaluate Process

agent._evaluate_process_deadline(process_125)

Get Votes:

votes = process.state["votes"]
# {"@bob": {"vote": "agree"}, "@carol": {"vote": "agree"}, "@dave": {"vote": "disagree"}}

vote_tally = tools.tally([{"vote": v["vote"]} for v in votes.values()], "vote")
# Result: {"agree": 2, "disagree": 1}

Step 3: Query Constitution for Threshold

constitution.query(
    question="For a standard_proposal, what voting threshold is required for passage?",
    context="Votes: agree=2, disagree=1"
)

Constitution Returns:

{
    "answer": "Standard proposals pass when there are more Agree votes than Disagree votes.",
    "sections": ["Article 3, Section 3.1"],
    "confidence": "high"
}

Step 4: LLM Evaluates Threshold

LLM Prompt:

Process process_125 has reached its deadline.

Votes cast:
- Agree: 2
- Disagree: 1
- Abstain: 0

Constitution says: "More Agree than Disagree votes"

Does this proposal pass?

Use the calculate tool to evaluate: agree > disagree

LLM Decision:

{
    "action": "complete_process",
    "reasoning": "The proposal has reached its deadline. According to the constitution, standard proposals pass when more members vote agree than disagree. The vote tally shows 2 agree and 1 disagree. Using the calculate tool: 2 > 1 = True. Therefore, the proposal passes.",
    "constitution_citations": ["Article 3, Section 3.1: Passage threshold"],
    "parameters": {
        "process_id": "process_125",
        "outcome": "passed"
    },
    "calculations": [
        {
            "tool": "calculate",
            "expression": "agree > disagree",
            "variables": {"agree": 2, "disagree": 1},
            "result": true
        }
    ]
}

Step 5: Execute Completion

Memory Operations:

  1. Update Status:
memory.update_process("process_125", status=ProcessStatus.COMPLETED)
  1. Log Decision:
memory.add_decision(
    process_id="process_125",
    decision_type="process_completion",
    reasoning="The proposal has reached its deadline...",
    constitution_citations=["Article 3, Section 3.1"],
    result="passed",
    calculation_used="agree > disagree",
    calculation_variables={"agree": 2, "disagree": 1},
    calculation_result=True
)
  1. Log Event:
memory.add_event(
    process_id="process_125",
    actor="bot",
    event_type="process_completed",
    data={"outcome": "passed"},
    context="Process completed: proposal passed"
)

Step 6: Generate Audit Trail

audit = AuditTrail.format_decision(process.decisions[-1], process)

Audit Trail (excerpt):

# GOVERNANCE DECISION AUDIT TRAIL

**Decision ID**: process_125_2
**Timestamp**: 2026-02-14 18:30:15 UTC
**Decision Type**: process_completion
**Result**: passed

## Constitutional Basis

- Article 3, Section 3.1: Passage threshold

## Reasoning

The proposal has reached its deadline. According to the constitution,
standard proposals pass when more members vote agree than disagree.
The vote tally shows 2 agree and 1 disagree. Using the calculate tool:
2 > 1 = True. Therefore, the proposal passes.

## Calculation Details

**Expression**: `agree > disagree`
**Variables**:
- `agree` = 2
- `disagree` = 1
**Result**: `True`

Step 7: Announce Result

Bot Post:

📊 Proposal #125 has PASSED

Proposal: "Change the moderation policy to require 2 moderators for bans"
Proposed by: @alice

Final Vote Tally:
✅ Agree: 2 (66.7%)
❌ Disagree: 1 (33.3%)
⚪ Abstain: 0

Constitutional Threshold: More Agree than Disagree
Result: 2 > 1 ✓

This proposal has passed and will now be implemented.

Full audit trail: [link]

Key Differences from Old System

Old System (Hard-Coded)

# Hard-coded threshold check
def check_threshold(counts, "simple_majority"):
    return counts['agree'] > counts['disagree']

# Hard-coded in agent
if threshold_type == "simple_majority":
    passed = agree > disagree

New System (Agentic)

# Constitution defines threshold in natural language
"More Agree than Disagree votes"

# LLM interprets and uses tool
decision = llm.decide(
    constitution="More Agree than Disagree votes",
    votes={"agree": 2, "disagree": 1}
)
# Returns: "Use calculate tool with expression 'agree > disagree'"

result = tools.calculate("agree > disagree", {"agree": 2, "disagree": 1})
# Returns: True

Auditability Example

Community member @eve wants to understand why proposal 125 passed.

Query Memory

process = memory.get_process("process_125")
audit = AuditTrail.generate_process_audit_file(process)

Audit File Contents

# Governance Process Audit: process_125

**Generated**: 2026-02-15 09:00:00 UTC

---

# Process Summary: process_125

**Type**: standard_proposal
**Status**: completed
**Created By**: @alice
**Created At**: 2026-02-08 18:30:00 UTC
**Deadline**: 2026-02-14 18:30:00 UTC

## Constitutional Basis

- Article 3, Section 3.1: Proposal Types

## Current State

- proposal_text: change the moderation policy...
- proposer: @alice
- topic: moderation_policy
- votes: 3 items

## Event Timeline

**[2026-02-08 18:30]** process_created
- Actor: @alice
- Context: Proposal created by @alice

**[2026-02-09 10:15]** vote_cast
- Actor: @bob
- Context: @bob voted agree

**[2026-02-10 14:22]** vote_cast
- Actor: @carol
- Context: @carol voted agree

**[2026-02-11 09:45]** vote_cast
- Actor: @dave
- Context: @dave voted disagree

**[2026-02-14 18:30]** process_completed
- Actor: bot
- Context: Process completed: proposal passed

## Decisions

**1. [2026-02-08 18:30]** process_creation
- Result: created
- Reasoning: This is a standard proposal about moderation policy...

**2. [2026-02-14 18:30]** process_completion
- Result: passed
- Reasoning: The proposal has reached its deadline. According to...

---

# Detailed Decision Log

[Full decision details with calculations, reasoning, citations...]

---

## Audit Trail Integrity

This audit trail represents the complete record of this governance process.
All decisions were made according to the community constitution...

Key Point: Every step is logged with:

  • What happened
  • When it happened
  • Who did it
  • Why (bot reasoning)
  • What constitutional rule applied
  • What calculations were used

Humans can inspect and verify every decision.


Handling Different Templates

The beauty of this system: the SAME code works for different governance models!

Example: Consensus (from templates)

Constitution excerpt:

### Section 4.4: Block

"I block this proposal because..."
- Fundamental objection preventing consensus
- Must include explanation and reasoning
- Triggers deeper discussion

User Action:

@frank: "@govbot I block proposal 126 because it violates our privacy principles"

Agent Flow:

  1. LLM parses intent: "cast_vote with type=block"
  2. Queries constitution: "What happens when someone blocks?"
  3. Constitution: "Triggers deeper discussion, proposal cannot pass until block resolved"
  4. LLM decides: "Record block vote, update process status to 'blocked', notify proposer"
  5. Memory updated with block and reasoning
  6. Audit trail shows: "Block recorded. Constitutional requirement: must address concern"

No code changes needed! The agent interprets the constitution's block rules.

Example: Jury/Sortition (from templates)

Constitution excerpt:

Juries selected randomly:
- 5-7 members from eligible pool
- Random selection ensures fairness

Agent Flow:

  1. LLM interprets: "Need to select 5-7 random members"
  2. Uses tool: random_select(eligible_members, count=5)
  3. Creates jury process in memory
  4. Logs selection with seed for reproducibility
  5. Audit shows: "Selected using random sortition per Article X"

Summary

The system:

No hard-coded governance logic - All rules come from constitution LLM interprets and decides - Based on natural language rules Structured memory tracks state - Queryable by LLM and humans Tools ensure correctness - Math done by calculator, not LLM reasoning Complete audit trails - Every decision explained and cited Works with any template - Consensus, do-ocracy, jury, etc.

The key insight: Separate interpretation from execution. LLM interprets what to do, tools execute it correctly, memory tracks everything, audits explain it all.