Skip to content

Using FCC as a Python Library

This tutorial walks through using the FCC Agent Team Framework as a Python library rather than through the CLI. By the end you will know how to load the persona registry, run simulations, generate documentation, and query the cross-reference matrix -- all from Python code.

Prerequisites

Install the package in editable mode:

pip install -e .

Verify the installation:

import fcc
print(fcc.__version__)  # "0.2.0"

Step 1: Understand Resource Paths

FCC bundles all data files (personas, schemas, workflows, scenarios, governance, docs templates) inside the package. Use fcc._resources to resolve paths reliably regardless of your working directory.

from fcc._resources import (
    get_personas_dir,
    get_workflows_dir,
    get_scenarios_dir,
    get_governance_dir,
    get_schemas_dir,
    get_data_dir,
    get_templates_dir,
)

print(f"Personas: {get_personas_dir()}")
print(f"Workflows: {get_workflows_dir()}")
print(f"Scenarios: {get_scenarios_dir()}")
print(f"Governance: {get_governance_dir()}")

Step 2: Load the Persona Registry

The PersonaRegistry is the central data structure. Loading from the directory picks up all 24 personas across 5 YAML files plus dimension profiles.

from fcc._resources import get_personas_dir
from fcc.personas.registry import PersonaRegistry

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())

print(f"Total personas: {len(registry)}")
print(f"Categories: {sorted(set(p.category for p in registry))}")
print(f"FCC phases: {sorted(set(p.fcc_phase for p in registry))}")

Explore individual personas:

rc = registry.get("RC")
print(f"Name: {rc.name}")
print(f"Phase: {rc.fcc_phase}")
print(f"Role: {rc.riscear.role}")
print(f"Archetype: {rc.riscear.archetype}")
print(f"Responsibilities: {rc.riscear.responsibilities}")
print(f"Skills: {rc.riscear.role_skills}")
print(f"Collaborators: {rc.riscear.role_collaborators}")

List personas by category:

for category in ["core", "integration", "governance", "stakeholder", "champion"]:
    personas = registry.by_category(category)
    ids = [p.id for p in personas]
    print(f"{category}: {ids}")

Explore champion relationships:

for champ in registry.champions():
    base = registry.base_of(champ.id)
    print(f"{champ.name} ({champ.id}) champions {base.name} ({base.id})")
    print(f"  Orchestrates: {champ.orchestrates}")

Step 3: Load and Explore a Workflow Graph

FCC ships three workflow definitions of increasing complexity.

from fcc._resources import get_workflows_dir
from fcc.workflow.graph import WorkflowGraph

workflows_dir = get_workflows_dir()

base = WorkflowGraph.from_json(workflows_dir / "base_sequence.json")
extended = WorkflowGraph.from_json(workflows_dir / "extended_sequence.json")
complete = WorkflowGraph.from_json(workflows_dir / "complete_24.json")

for name, graph in [("Base", base), ("Extended", extended), ("Complete", complete)]:
    print(f"{name}: {len(graph)} nodes, {len(graph.edges)} edges")
    print(f"  Handoffs: {len(graph.handoffs())}")
    print(f"  Feedbacks: {len(graph.feedbacks())}")

Inspect the topology:

graph = base

# Topological execution order (handoff edges only)
order = graph.topological_order()
print(f"Execution order: {order}")

# BFS traversal from the starting node
bfs = graph.bfs_from("RC")
print(f"BFS from RC: {bfs}")

# For each node, show its successors
for node_id in graph.node_ids:
    node = graph.get_node(node_id)
    succs = [s.id for s in graph.successors(node_id)]
    print(f"  {node.name} ({node_id}) -> {succs}")

Step 4: Run a Deterministic Simulation

The deterministic SimulationEngine propagates messages through the graph without any external API calls.

from fcc.simulation.engine import SimulationEngine

engine = SimulationEngine(graph=base, max_steps=200, max_history=16)
history = engine.run(start_node="RC", initial_payload="API migration plan")

print(f"Total events: {len(history.events)}")
for event in history.events:
    print(f"  Step {event['step']:3d}: {event['actor']:30s} [{event['edge_label']}]")

Save traces to disk:

history = engine.run_and_save(
    output_path="traces_deterministic.json",
    start_node="RC",
    initial_payload="API migration plan",
)
print("Traces written to traces_deterministic.json")

Step 5: Run an AI-Powered Simulation

The AISimulationEngine calls an LLM at each workflow node. Use the mock client for testing without API keys, or plug in a real provider.

from fcc.simulation.ai_client import AIClient, AIProvider
from fcc.simulation.ai_engine import AISimulationEngine, write_ai_traces

# Mock client (no API key needed)
client = AIClient(provider=AIProvider.MOCK)

engine = AISimulationEngine(
    graph=base,
    ai_client=client,
    max_steps=50,
    max_history=8,
    registry=registry,  # enables rich persona-aware prompts
)

result = engine.run(
    start_node="RC",
    initial_payload="Evaluate microservices migration strategy",
    scenario_id="DEMO-001",
    scenario_name="Microservices evaluation",
)

print(f"Success: {result.success}")
print(f"Steps: {result.total_steps}")
print(f"AI calls: {result.total_ai_calls}")
print(f"Tokens: {result.total_tokens}")
print(f"Duration: {result.duration_seconds:.2f}s")

# Write traces
write_ai_traces([result], "traces_ai.json")

To use a real provider, set environment variables and omit the provider argument:

import os
os.environ["ANTHROPIC_API_KEY"] = "sk-..."  # or set in .env

client = AIClient()  # auto-detects Anthropic
# Or with caching to avoid duplicate API calls:
client = AIClient(use_cache=True, cache_dir=".ai_cache")

Step 6: Query the Cross-Reference Matrix

The cross-reference matrix maps persona-to-persona interactions.

from fcc._resources import get_personas_dir
from fcc.personas.cross_reference import CrossReferenceMatrix

# Load the hand-curated matrix
yaml_matrix = CrossReferenceMatrix.from_yaml(
    get_personas_dir() / "cross_reference.yaml"
)

# Auto-generate from persona collaboration links
auto_matrix = CrossReferenceMatrix.from_personas(registry)

# Merge (hand-curated takes precedence)
matrix = yaml_matrix.merge_with_precedence(auto_matrix)
print(f"Total interactions: {len(matrix)}")

# Who feeds into the Blueprint Crafter?
print("\nUpstream of BC:")
for entry in matrix.upstream("BC"):
    print(f"  {entry.source_id} -> BC: {entry.interaction} ({entry.relationship_type})")

# Who does the Research Crafter hand off to?
print("\nDownstream of RC:")
for entry in matrix.downstream("RC"):
    print(f"  RC -> {entry.target_id}: {entry.interaction}")

# Coordination peers of DE
print("\nPeers of DE:")
for entry in matrix.peers("DE"):
    other = entry.target_id if entry.source_id == "DE" else entry.source_id
    print(f"  DE <-> {other}")

Step 7: Generate Documentation

Generate the full docs-as-code output from Python.

from pathlib import Path
from fcc._resources import get_data_dir
from fcc.scaffold.doc_generator import DocGenerator

gen = DocGenerator(registry, data_dir=get_data_dir())

# Generate for all 24 personas
total = gen.generate_all_docs(Path("generated_docs"))
print(f"Generated {total} documentation files")

# Or just for a subset
total = gen.generate_all_docs(
    Path("generated_docs"),
    persona_ids=["RC", "BC", "DE"],
)
print(f"Generated {total} files for 3 personas")

Step 8: Validate Simulation Results

Use FCCValidator to verify that simulation output meets quality standards.

from fcc.scenarios.validators import FCCValidator

validator = FCCValidator.from_registry(registry)

# Check FCC cycle coverage
result = validator.validate_fcc_cycle(
    personas=["Research Crafter", "Blueprint Crafter", "Documentation Evangelist"],
)
print(f"FCC cycle valid: {result.passed}")
print(f"Phases covered: {result.details['phases_covered']}")

# Check governance gate
result = validator.validate_governance_gate(
    personas_executed=["RC", "BC", "DGS", "DE"],
)
print(f"Governance gate: {result.passed}")

Step 9: Load and Run Scenarios

Scenarios define structured test cases for simulations.

from fcc._resources import get_scenarios_dir
from fcc.scenarios.loader import ScenarioLoader

loader = ScenarioLoader.from_directory(get_scenarios_dir())
print(f"Available scenarios: {loader.ids()}")

# Run a specific scenario
scenario = loader.get("GEN-001")
if scenario:
    print(f"Running: {scenario['name']}")
    result = engine.run(
        start_node=scenario["setup"].get("start_node", "RC"),
        initial_payload=scenario["name"],
        scenario_id=scenario["id"],
        scenario_name=scenario["name"],
    )
    print(f"  Steps: {result.total_steps}, Success: {result.success}")

Step 10: Explore Governance Tags and Quality Gates

from fcc._resources import get_governance_dir
from fcc.governance.tags import TagRegistry
from fcc.governance.quality_gates import QualityGateRunner

# Tags
tags = TagRegistry.from_yaml(get_governance_dir() / "tag_registry.yaml")
print(f"Tags: {len(tags)}")
print(f"Categories: {tags.categories}")

# Quality gates
runner = QualityGateRunner.from_yaml(get_governance_dir() / "quality_gates.yaml")
print(f"Quality gates: {len(runner)}")

for persona_id in ["RC", "BC", "DE"]:
    gates = runner.gates_for_persona(persona_id)
    print(f"  {persona_id}: {len(gates)} gates")

Complete End-to-End Script

Here is a self-contained script that loads everything, runs a simulation, validates the output, and generates documentation:

#!/usr/bin/env python3
"""FCC library usage -- end-to-end example."""

from pathlib import Path

from fcc._resources import (
    get_data_dir,
    get_governance_dir,
    get_personas_dir,
    get_scenarios_dir,
    get_workflows_dir,
)
from fcc.governance.quality_gates import QualityGateRunner
from fcc.governance.tags import TagRegistry
from fcc.personas.cross_reference import CrossReferenceMatrix
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator
from fcc.scenarios.loader import ScenarioLoader
from fcc.scenarios.validators import FCCValidator
from fcc.simulation.ai_client import AIClient, AIProvider
from fcc.simulation.ai_engine import AISimulationEngine, write_ai_traces
from fcc.workflow.graph import WorkflowGraph

# 1. Load registry and workflow
registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
graph = WorkflowGraph.from_json(get_workflows_dir() / "base_sequence.json")
print(f"Personas: {len(registry)}, Nodes: {len(graph)}")

# 2. Run AI simulation (mock mode)
client = AIClient(provider=AIProvider.MOCK)
engine = AISimulationEngine(graph=graph, ai_client=client, registry=registry)
result = engine.run(start_node="RC", initial_payload="Cloud migration strategy")
write_ai_traces([result], "traces.json")
print(f"Simulation: {result.total_steps} steps, {result.total_ai_calls} AI calls")

# 3. Validate
validator = FCCValidator.from_registry(registry)
v = validator.validate_fcc_cycle(
    personas=["Research Crafter", "Blueprint Crafter", "Documentation Evangelist"],
)
print(f"FCC cycle valid: {v.passed}")

# 4. Query cross-references
matrix = CrossReferenceMatrix.from_personas(registry)
print(f"Cross-references: {len(matrix)} interactions")

# 5. Generate docs
gen = DocGenerator(registry, data_dir=get_data_dir())
total = gen.generate_all_docs(Path("output_docs"), persona_ids=["RC", "BC"])
print(f"Generated {total} doc files")

print("\nDone.")