Skip to content

Audit Log Export

ARTEMIS provides a comprehensive audit log system for exporting debate records in multiple formats.

Overview

The AuditLog class captures every aspect of a debate:

  • Debate metadata (topic, agents, timestamps)
  • Complete argument transcript
  • Evaluation scores for each turn
  • Safety alerts and interventions
  • Final verdict with reasoning

Basic Usage

from artemis.core.debate import Debate
from artemis.core.agent import Agent
from artemis.utils.audit import AuditLog

# Run a debate
agents = [
    Agent(name="pro", role="Advocate", model="gpt-4o"),
    Agent(name="con", role="Opponent", model="gpt-4o"),
]

debate = Debate(topic="Should we adopt renewable energy?", agents=agents)
debate.assign_positions({
    "pro": "supports renewable energy adoption",
    "con": "advocates for traditional energy sources",
})

result = await debate.run(rounds=3)

# Generate audit log
audit = AuditLog.from_debate_result(result)

Export Formats

JSON Export

Full structured data suitable for programmatic processing:

# Export to file
audit.to_json("audit_output.json")

# Get as string
json_string = audit.to_json()
print(json_string)

JSON Structure:

{
  "debate_id": "debate_abc123",
  "topic": "Should we adopt renewable energy?",
  "metadata": {
    "started_at": "2025-06-28T14:30:00Z",
    "ended_at": "2025-06-28T14:35:00Z",
    "agents": ["pro", "con"],
    "jury_size": 3,
    "safety_monitors": ["SandbagDetector", "DeceptionMonitor"]
  },
  "entries": [
    {
      "timestamp": "2025-06-28T14:30:05Z",
      "event_type": "argument_generated",
      "agent": "pro",
      "round": 1,
      "details": {
        "turn_id": "turn_pro_1",
        "level": "strategic",
        "content": "Full argument content here...",
        "evidence": [
          {
            "type": "study",
            "content": "Full evidence content",
            "source": "Nature 2024",
            "confidence": 0.92,
            "verified": true
          }
        ],
        "causal_links": [
          {"cause": "renewable adoption", "effect": "reduced emissions", "strength": 0.85}
        ],
        "rebuts": [],
        "supports": ["turn_pro_0"],
        "ethical_score": 0.87
      }
    },
    {
      "timestamp": "2025-06-28T14:30:10Z",
      "event_type": "argument_evaluated",
      "agent": "pro",
      "round": 1,
      "details": {
        "total_score": 0.82,
        "scores": {
          "logical_coherence": 0.85,
          "evidence_quality": 0.80,
          "causal_reasoning": 0.78,
          "ethical_alignment": 0.88,
          "persuasiveness": 0.82
        },
        "weights": {
          "logical_coherence": 0.25,
          "evidence_quality": 0.25,
          "causal_reasoning": 0.20,
          "ethical_alignment": 0.15,
          "persuasiveness": 0.15
        },
        "feedback": "Strong argument with credible evidence",
        "strengths": ["Clear thesis", "Well-sourced claims"],
        "weaknesses": ["Could address counterarguments"]
      }
    }
  ]
}

Markdown Export

Human-readable report format:

# Export to file
audit.to_markdown("audit_output.md")

# Get as string
md_string = audit.to_markdown()

Markdown Structure:

# Debate Audit Log

## Metadata

| Field | Value |
|-------|-------|
| Topic | Should we adopt renewable energy? |
| Started | 2025-06-28 14:30:00 |
| Duration | 5m 32s |

## Agents

- **pro**: Advocate (gpt-4o)
- **con**: Opponent (gpt-4o)

## Transcript

### Round 1

**pro** (Strategic):
> Renewable energy is essential for our future...

*Evidence*: 2 sources cited
*Evaluation*: 0.82

---

**con** (Strategic):
> While renewable energy has merits...

*Evidence*: 1 source cited
*Evaluation*: 0.78

## Verdict

**Winner**: pro
**Confidence**: 85%

The jury determined that the pro side presented...

## Safety Alerts

No safety alerts recorded.

HTML Export

Comprehensive styled report with full content:

# Export to file
audit.to_html("audit_output.html")

# Get as string
html_string = audit.to_html()

The HTML export includes:

  • Table of contents with navigation links
  • Full argument content (not truncated)
  • Color-coded agents with distinct border colors
  • Complete evidence with source, confidence, and verification status
  • Causal links visualization (cause → effect)
  • Expandable evaluation details with criteria breakdown
  • Strengths and weaknesses for each argument
  • Inline safety warnings with severity indicators
  • Verdict section with score cards and reasoning
  • Safety analysis section with all alerts

Working with Entries

Audit Entry Types

Event Type Description
argument_generated An agent produced an argument
argument_evaluated An argument was scored
safety_alert A safety monitor raised an alert
round_completed A debate round finished
verdict_reached Final verdict was determined

Filtering Entries

# Get all safety alerts
alerts = [e for e in audit.entries if e.event_type == "safety_alert"]

# Get entries for a specific agent
pro_entries = [e for e in audit.entries if e.agent == "pro"]

# Get entries by round
round_2 = [e for e in audit.entries if e.round == 2]

Integration with Safety Monitors

When safety monitors are active, their alerts are captured in the audit log:

from artemis.safety import SandbagDetector, DeceptionMonitor

debate = Debate(
    topic="Your topic",
    agents=agents,
    safety_monitors=[
        SandbagDetector(sensitivity=0.7).process,
        DeceptionMonitor(sensitivity=0.6).process,
    ],
)

result = await debate.run()
audit = AuditLog.from_debate_result(result)

# Check for safety alerts in the audit
for entry in audit.entries:
    if entry.event_type == "safety_alert":
        print(f"Alert: {entry.details['alert_type']}")
        print(f"Severity: {entry.details['severity']}")
        print(f"Message: {entry.details['message']}")

Complete Example

import asyncio
from pathlib import Path

from artemis.core.debate import Debate
from artemis.core.agent import Agent
from artemis.core.jury import JuryPanel
from artemis.utils.audit import AuditLog

async def run_and_export():
    # Create agents with different models
    agents = [
        Agent(name="pro", role="Advocate", model="gpt-4o"),
        Agent(name="con", role="Opponent", model="claude-sonnet-4-20250514"),
    ]

    # Create jury with multiple perspectives
    jury = JuryPanel(
        evaluators=3,
        models=["gpt-4o", "gemini-2.0-flash", "claude-sonnet-4-20250514"],
    )

    # Run debate
    debate = Debate(
        topic="Should AI systems have legal personhood?",
        agents=agents,
        jury=jury,
    )
    debate.assign_positions({
        "pro": "supports AI legal personhood",
        "con": "opposes AI legal personhood",
    })

    result = await debate.run(rounds=2)

    # Generate audit log
    audit = AuditLog.from_debate_result(result)

    # Export all formats
    output_dir = Path("audit_output")
    output_dir.mkdir(exist_ok=True)

    audit.to_json(output_dir / "debate_audit.json")
    audit.to_markdown(output_dir / "debate_audit.md")
    audit.to_html(output_dir / "debate_audit.html")

    print(f"Audit logs exported to {output_dir}/")
    print(f"Verdict: {result.verdict.decision} ({result.verdict.confidence:.0%})")

if __name__ == "__main__":
    asyncio.run(run_and_export())

Next Steps