Observed since 20 Oct, 20:23 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ael-sdk
Python SDK for Agent Execution Ledger - Audit trail for AI agents
Git for AI Agent Decisions - Complete audit trail, replay, and divergence detection for enterprise AI agents.
When your AI agent approves a $10,000 refund or denies a loan application, can you answer:
AEL captures every decision your agent makes with full context, enabling audit trails, replay verification, and divergence detection.
pip install ael-sdk
With framework integrations:
pip install ael-sdk[langchain] # LangChain
pip install ael-sdk[google-adk] # Google ADK
pip install ael-sdk[crewai] # CrewAI
pip install ael-sdk[openai] # OpenAI Assistants
pip install ael-sdk[all] # All integrations
from ael import AELClient, ActionOption, ResultType
ael = AELClient(api_key="ael_your_key")
with ael.intent("Process refund request", agent_id="support-agent") as intent:
# 1. Capture what the agent saw
intent.snapshot_context({
"customer_tier": "gold",
"order_amount": 149.99,
"days_since_delivery": 5,
})
# 2. Record the decision with options considered
intent.decide(
options=[
ActionOption(action="approve_refund", score=0.95, reason="Gold customer, within policy"),
ActionOption(action="deny_refund", score=0.05, reason="N/A"),
],
chosen_action="approve_refund",
confidence=0.95,
reasoning="Customer eligible per 30-day return policy"
)
# 3. Record execution
intent.execute(action="approve_refund", target={"order_id": "ORD-123"}, result=ResultType.SUCCESS)
Now query any decision: "Why did we approve this refund?" - Full context, reasoning, and alternatives considered.
Store the prompt and re-run decisions to verify consistency:
from ael import AELClient, ActionOption, ReplayConfig
ael = AELClient(api_key="ael_your_key")
# Record decision WITH replay data
with ael.intent("Handle support ticket") as intent:
intent.snapshot_context({"ticket_id": "T-123", "priority": "high"})
intent.decide(
options=[ActionOption(action="escalate", score=0.9)],
chosen_action="escalate",
confidence=0.9,
reasoning="High priority ticket requires escalation",
# Enable replay
replay_prompt="You are a support agent. Ticket: high priority. Action?",
replay_config=ReplayConfig(model="gpt-4o-mini", temperature=0.0),
)
exec_id = intent.execute(action="escalate", target={"ticket_id": "T-123"}, result=ResultType.SUCCESS)
# Later: Verify the decision is still consistent
replay_result = ael.replay(exec_id, force_llm_call=True)
if replay_result["diverged"]:
print(f"WARNING: Model behavior changed!")
print(f"Original: {replay_result['original_decision']['chosen_action']}")
print(f"Replayed: {replay_result['replayed_decision']['chosen_action']}")
print(f"Reason: {replay_result['divergence_reason']}")
else:
print("Decision verified - model behavior consistent")
Track complex workflows with multiple agents or MCP tool calls:
with ael.intent("Process customer request", agent_id="orchestrator") as parent:
parent.snapshot_context({"request": "refund for order 123"})
# Spawn child intent for sub-agent
with parent.child_intent("Search order history", mcp_server="database") as child:
child.snapshot_context({"query": "order 123"})
child.decide(
options=[ActionOption(action="query_db", score=1.0)],
chosen_action="query_db",
confidence=1.0
)
child.execute(action="query_db", target={"order_id": "123"}, result=ResultType.SUCCESS)
# Parent continues with child's result
parent.decide(...)
parent.execute(...)
When humans override agent decisions:
intent.execute(
action="deny_refund", # Human chose differently
target={"order_id": "123"},
result=ResultType.SUCCESS,
actor=ActorType.HUMAN,
override_reason="Customer has history of fraud"
)
Go to agent-execution-ledger.vercel.app and sign in with Google.
After signing in, create a workspace (e.g., "Production Agents").
Navigate to API Keys > Create API Key > Copy your key (ael_xxxx...).
from ael import AELClient
ael = AELClient(api_key="ael_your_key")
from langchain_openai import ChatOpenAI
from ael import AELClient, ActionOption, ResultType
ael = AELClient(api_key="ael_xxx")
llm = ChatOpenAI(model="gpt-4o-mini")
def run_agent(query: str):
with ael.intent(query, agent_id="langchain-agent") as intent:
intent.snapshot_context({"query": query})
response = llm.invoke(query)
intent.decide(
options=[ActionOption(action="respond", score=0.9)],
chosen_action="respond",
confidence=0.9,
reasoning=response.content[:100]
)
intent.execute(action="respond", result=ResultType.SUCCESS)
return response.content
from google.adk.agents import Agent
from ael import AELClient
from ael.integrations.google_adk import AELTracker
ael = AELClient(api_key="ael_xxx")
tracker = AELTracker(ael, agent_id="support-agent")
agent = Agent(name="support", model="gemini-2.0-flash")
with tracker.track("Handle customer request") as t:
t.context({"customer_id": "123"})
# ... run agent ...
t.decision(chosen="resolve", confidence=0.92, options=[...])
t.execute(action="resolve", result="success")
from crewai import Agent, Task, Crew
from ael import AELClient
from ael.integrations.crewai import AELCrewTracker
ael = AELClient(api_key="ael_xxx")
tracker = AELCrewTracker(ael)
crew = Crew(agents=[...], tasks=[...])
with tracker.track_crew("Content pipeline") as t:
result = crew.kickoff()
t.record_result(result, agents=[...], tasks=[...])
ael = AELClient(
api_key="ael_xxx", # Required
endpoint="https://ael-backend.onrender.com", # Optional (default: production)
timeout=30.0 # Optional
)
| Method | Description |
|---|---|
ael.intent(goal, agent_id) | Context manager for tracking an intent |
intent.snapshot_context(inputs) | Capture what the agent sees |
intent.decide(options, chosen_action, confidence) | Record a decision |
intent.execute(action, target, result) | Record execution |
ael.replay(execution_id, force_llm_call) | Replay and verify a decision |
ael.get_execution(id) | Get execution details |
ael.get_session_timeline(session_id) | Get full session timeline |
| Method | Description |
|---|---|
ael.replay(exec_id) | Compare against original (no LLM call) |
ael.replay(exec_id, force_llm_call=True) | Re-run LLM and check for divergence |
| Method | Description |
|---|---|
intent.child_intent(goal, mcp_server) | Create child intent |
ael.get_intent_hierarchy(id) | Get parent chain (breadcrumbs) |
ael.get_intent_tree(id) | Get full descendant tree |
View your agent's decisions at agent-execution-ledger.vercel.app:
| Industry | Use Case |
|---|---|
| FinTech | Audit loan decisions, fraud detection reasoning |
| Healthcare | Track triage recommendations, document clinical AI decisions |
| E-commerce | Refund approvals, pricing decisions, inventory management |
| Legal | Contract analysis decisions, compliance checking |
| HR | Resume screening, candidate ranking explanations |
MIT License - see LICENSE for details.
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
httpxEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | httpx>=0.26.0 | runtime |
pydanticEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pydantic>=2.5.0 | runtime |
google-adkEnvironment{
"extras": "",
"marker": "extra == \"google-adk\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | google-adk>=1.0.0; extra == "google-adk" | runtime |
langchain-coreEnvironment{
"extras": "",
"marker": "extra == \"langchain\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | langchain-core>=0.1.0; extra == "langchain" | runtime |
crewaiEnvironment{
"extras": "",
"marker": "extra == \"crewai\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | crewai>=0.1.0; extra == "crewai" | runtime |
openaiEnvironment{
"extras": "",
"marker": "extra == \"openai\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | openai>=1.0.0; extra == "openai" | runtime |
pyautogenEnvironment{
"extras": "",
"marker": "extra == \"autogen\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pyautogen>=0.2.0; extra == "autogen" | runtime |
google-adkEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | google-adk>=1.0.0; extra == "all" | runtime |
langchain-coreEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | langchain-core>=0.1.0; extra == "all" | runtime |
crewaiEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | crewai>=0.1.0; extra == "all" | runtime |
openaiEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | openai>=1.0.0; extra == "all" | runtime |
pyautogenEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pyautogen>=0.2.0; extra == "all" | runtime |
bdist_wheel · 24.33 KB · Python py3
sdist · 21.74 KB · Python source
Shared repository data · GitHub
Observed since 20 Oct, 20:23 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:7084af025d4ae0ffa8385c5bc44b0818b9dff73c3f863a659e734f3b45586dc3