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:
Verify the installation:
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.")