A lightweight, policy-driven governance framework for LLM agents with ReAct reasoning. Control agent behavior through declarative YAML policies and structured observabilityβensuring safe, compliant, and traceable AI operations.
Agent Governance Hub provides a policy-first architecture for governing LLM agents. Every action is evaluated against declarative policies before execution, with complete separation between policy enforcement and observability logging.
What makes this project unique: We investigate how governance becomes critical when agents make autonomous decisions about tool usage. Our RAG agent uses an LLM that independently decides whether to retrieve documents or answer directlyβand governance policies control both the query permission AND the retrieval tool execution. This demonstrates real-world scenarios where AI agents need guardrails on their decision-making process, not just their final actions.
- π Policy Enforcement: Automatic evaluation before every agent action
- π Separated Observability: Independent callbacks for governance and logging
- π€ ReAct Agent: OpenAI-powered reasoning with LangChain tool calling (gpt-3.5-turbo)
- π Vector Retrieval: Built-in semantic search with Qdrant (in-memory) + HuggingFace embeddings
- π Structured Logging: Complete traceability of LLM decisions, tool usage, and policy evaluations
- π― Autonomous RAG Decision-Making: LLM independently decides when to use retrieval tools vs. direct answers
- ποΈ RAG Usage Visibility: Clear indicators showing whether the agent used RAG (π) or answered directly (π¬)
- β‘ FastAPI Integration: RESTful API for policy evaluation and agent orchestration
- π‘οΈ Type Safety: Pydantic validation throughout the stack
The architecture demonstrates governance at two critical decision points:
ββββββββββββββββββββ
β User Query β
ββββββββββ¬ββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GovernedRAGAgent β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 1. Policy Check (ask_question action) β β
β β β PolicyEngine.evaluate() β β
β β β Decision: ALLOW/BLOCK β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 2. LLM Reasoning (ReAct pattern) β β
β β β OpenAI GPT-3.5-turbo β β
β β β Decides: Direct Answer vs Tool Call β β
β β β οΈ AUTONOMOUS DECISION - NOT CONTROLLED β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 3. Tool Execution (if needed) β β
β β β PolicyEnforcementCallback intercepts β β
β β β Gets tool.policy_action metadata β β
β β β PolicyEngine.evaluate(query_database) β β
β β β VectorRetrievalTool executes β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 4. Observability (throughout) β β
β β β ObservabilityCallback logs all events β β
β β β Timing, decisions, tool calls β β
β β β Tracks: π RAG vs π¬ Direct mode β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β Final Answer β
β + Metadata β
β (RAG used?) β
ββββββββββββββββββββ
Why This Matters: The LLM's autonomous decision-making (step 2) is the reason governance is critical. We can't predict when the agent will use tools, so we must:
- Enforce policies at the tool execution layer (step 3)
- Track which decision path was taken (step 4)
- Provide visibility into agent behavior patterns
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β YAML Policies (config/policies/default.yaml) β
β β’ Declarative rules: allow/block/verify/flag β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PolicyEngine (governance/policy_engine.py) β
β β’ Evaluates agent_id + action + context β
β β’ First-match rule strategy β
β β’ Returns EvaluationResult with decision β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PolicyEnforcementCallback (agents/callbacks.py) β
β β’ Intercepts tool calls before execution β
β β’ Reads tool.policy_action metadata β
β β’ Blocks execution if policy denies β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VectorRetrievalTool (tools/vector_retrieval.py) β
β β’ Metadata: policy_action = "query_database" β
β β’ Executes Qdrant similarity search β
β β’ Returns top-k relevant documents β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ObservabilityCallback (agents/callbacks.py) β
β β’ Logs LLM reasoning steps β
β β’ Tracks tool execution timing β
β β’ Records policy evaluation results β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Python 3.11+
- Poetry (dependency management)
- OpenAI API key
# Clone repository
git clone https://github.com/MiguelBarriosAl/agent-governance-hub.git
cd agent-governance-hub
# Install dependencies
poetry install
# Configure OpenAI API key
echo "OPENAI_API_KEY=your-api-key-here" > .env
# Run the demo
poetry run python main.py
# Run tests
poetry run pytest -vThe demo showcases governed RAG with autonomous LLM decision-making:
Query: What information do you have about machine learning?
------------------------------------------------------------
π RAG | Answer: I found some information about machine learning:
Machine learning models require large amounts of training data...
Decision: allow (R007)
Query: Hello! How are you?
------------------------------------------------------------
π¬ Direct | Answer: Hello! I'm here and ready to assist you. How can I help you today?
Decision: allow (R007)
Query: Find details about vector databases
------------------------------------------------------------
π RAG | Answer: Vector databases store data as high-dimensional vectors...
Decision: allow (R007)Key Observations:
- π RAG Mode: The LLM autonomously decided to use the vector retrieval tool for technical questions
- π¬ Direct Mode: The LLM answered the greeting directly without tool usage
- β
Governance Applied: Both the
ask_questionaction andquery_databasetool calls were evaluated by policies
What This Demonstrates: This simple demo reveals the complexity of governing autonomous agents. The LLM makes real-time decisions about when to use RAG, and our governance framework must control:
- Whether the agent can process the query (
ask_questionpolicy) - Whether the agent can execute retrieval tools (
query_databasepolicy) - Complete observability of which path the LLM chose
This two-layer governance is critical in production systems where AI agents have multiple tools and make autonomous decisions about when to use them.
version: "1.0"
policies:
- agent_id: "retriever"
description: "Rules for RAG agent with vector retrieval"
rules:
- id: "R007"
action: "ask_question"
decision: "allow"
conditions: {}
reason: "Users can ask questions to the agent"
- id: "R008"
action: "query_database"
decision: "allow"
conditions: {}
reason: "Agent can query the vector database for information"
- id: "R003"
action: "delete_data"
decision: "block"
conditions: {}
reason: "Destructive operations are forbidden"from langchain.tools import BaseTool
from langchain.pydantic_v1 import BaseModel, Field
class VectorRetrievalTool(BaseTool):
name: str = "vector_retrieval"
description: str = "Search the vector database for documents"
# Policy metadata - governance callback uses this
policy_action: str = "query_database"
def _run(self, query: str) -> str:
# Execute search
results = self.vectorstore.similarity_search(query, k=3)
return format_results(results)from pathlib import Path
from governance.policy_loader import PolicyLoader
from governance.policy_engine import PolicyEngine
from governance.models import DecisionType
from pipelines.document_pipeline import DocumentPipeline
from agents.rag_agent import GovernedRAGAgent
# Step 1: Load governance policies
loader = PolicyLoader(Path("config/policies"))
policies = loader.load_all_policies()
engine = PolicyEngine(policies=policies, default_decision=DecisionType.BLOCK)
# Step 2: Setup document pipeline (separate from agent)
pipeline = DocumentPipeline(collection_name="my_documents")
pipeline.load_documents(Path("data/docs"))
# Step 3: Create governed agent with pre-loaded vector store
agent = GovernedRAGAgent(
name="retriever",
policy_engine=engine,
vector_manager=pipeline.get_vector_manager(),
llm_model="gpt-3.5-turbo",
temperature=0.0
)
# Step 4: Query the agent - LLM decides RAG vs Direct
result = agent.ask("What is machine learning?")
# Result includes answer + governance metadata + RAG usage
print(f"{result['answer']}")
print(f"Decision: {result['decision']} (rule: {result['rule_id']})")
print(f"Used RAG: {result['used_rag']}") # True if vector retrieval was used
print(f"Tools: {result['tools_used']}") # List of tools the LLM invokedArchitecture Explanation:
- Separation of Concerns: Document loading happens in
DocumentPipeline, not the agent - Policy-First: Engine with
default_decision=BLOCKdenies everything not explicitly allowed - Autonomous Decision: The LLM independently chooses when to use RAGβgovernance tracks this
- Observable Behavior: Every result includes metadata about what the agent actually did
15:29:07 | INFO | agents.rag_agent | Processing user query | agent=retriever
15:29:07 | INFO | agents.rag_agent | Policy evaluation for query | decision=allow | rule_id=R007
15:29:07 | INFO | agents.callbacks | LLM reasoning started | model=gpt-3.5-turbo
15:29:08 | INFO | agents.callbacks | Agent action decided | tool=vector_retrieval
15:29:08 | INFO | agents.callbacks | Tool execution requested by LLM | tool=vector_retrieval
15:29:08 | INFO | agents.callbacks | Tool execution completed | elapsed_ms=12.32
15:29:09 | INFO | agents.rag_agent | Query processed successfully | elapsed_ms=2100.16
agent-governance-hub/
βββ agents/ # Agent execution logic
β βββ base_agent.py # Abstract base with policy evaluation
β βββ rag_agent.py # ReAct RAG agent with OpenAI
β βββ vector_store_manager.py # Manages embeddings and vectorstore
β βββ tool_manager.py # Configures tools and AgentExecutor
β βββ execution_coordinator.py # Executes with governance callbacks
β βββ callbacks.py # Separated callbacks
β β βββ PolicyEnforcementCallback
β β βββ ObservabilityCallback
β βββ prompts.py # LLM prompt templates
βββ pipelines/ # Setup and preparation logic
β βββ document_pipeline.py # Document loading (separate from agent)
βββ tools/ # LangChain tools with policy metadata
β βββ vector_retrieval.py # Vector search tool
βββ governance/ # Policy engine core
β βββ models.py # Pydantic models (Policy, Rule, DecisionType)
β βββ policy_loader.py # YAML loading & validation
β βββ policy_engine.py # Rule evaluation logic
βββ config/ # Configuration
β βββ settings.py # Application settings
β βββ policies/ # YAML policy files
β βββ default.yaml # Default governance rules
βββ data/docs/ # Sample documents for vector search
βββ tests/ # Test suite (11 passing tests)
β βββ agents/ # Agent tests
β βββ governance/ # Policy tests (11 tests)
βββ main.py # Clean demo showing RAG decision-making (69 lines)
βββ pyproject.toml # Dependencies (Poetry)
βββ .env # API keys (gitignored)
Key Design Decisions:
pipelines/handles setup;agents/handles execution (clear separation)ExecutionCoordinatortracks tool usage to provide RAG visibilitymain.pykept minimal and readable (69 lines) to demonstrate architecture clearly
allow: Action proceeds without restrictionsblock: Action is rejected immediatelyverify: Requires human approval (future: integration with approval workflows)flag: Action proceeds but is logged for audit
Add dynamic conditions to rules:
- id: "R005"
action: "analyze"
decision: "verify"
conditions:
max_tokens: 4000 # Only verify if exceeds threshold
reason: "Large analysis requires human oversight"- Observability Integration: Connect structured logs to monitoring platforms (Datadog, Grafana, Prometheus)
- Metrics Dashboard: Export key metrics (policy violations, RAG usage rate, tool execution time, LLM token consumption)
- Production Monitoring: Add alerting for governance failures, blocked actions, and abnormal agent behavior patterns
- Add more sophisticated policy conditions (regex patterns, context-aware rules)
- Implement VERIFY decision workflow (human-in-the-loop)
- Add policy versioning and A/B testing
- Multi-agent orchestration with shared governance
- Policy analytics dashboard (Streamlit/Gradio)
- Performance benchmarks for policy evaluation overhead
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feat/new-feature) - Write tests for new functionality (maintain >90% coverage)
- Ensure all tests pass (
poetry run pytest -v) - Update documentation as needed
- Submit a pull request with clear description
# Install dev dependencies
poetry install --with dev
# Run linter
poetry run ruff check .
# Format code
poetry run black .
# Type checking
poetry run mypy agents/ governance/