Skip to content
GitHub

Architecture

Internal design of the lexigram-ai-agents package.


lexigram-ai-agents is the agent execution framework, orchestrating reasoning loops, tool execution, memory, governance, and observability.

flowchart BT
    LLM[LLMClientProtocol]
    Tools[ToolRegistryImpl]
    Memory[WorkingMemoryProtocol]
    Gov[AIGovernanceProtocol]
    Guards[GuardPipelineProtocol]
    Session[SessionManagerProtocol]
    Skills[SkillExecutor / SkillRegistry]
    Obs[AgentMetrics + AgentTracer]
    Events[EventBusProtocol]
    Exec[AgentExecutorImpl]
    Strat[Strategies<br/>ReAct · PlanExecute · Reflexion · Supervisor]
    Agent[AgentBase / AgentBuilder]

    Exec --> LLM & Strat & Tools & Memory & Gov & Guards & Session & Skills & Obs & Events
    Strat --> LLM & Tools
    Agent --> Strat & Tools

Import rule: Agent code imports from lexigram and lexigram-contracts only. Cross-package communication goes through protocols resolved via the DI container.


Agents declare identity, capabilities, and persona via two paths:

class OrderAgent(AgentBase):
name = "order_agent"
system_prompt = "You are a helpful order support agent."
@property
def tools(self):
return [lookup_order, search_products]
agent = (
AgentBuilder("order_agent")
.with_system_prompt("...").with_tools(lookup_order)
.with_strategy("react", max_iterations=10)
.with_memory(memory_backend).with_guards(pii_guard)
.with_governance(budget_limit=10.0).with_temperature(0.7)
.build()
)
AgentProtocol → AgentBase
├── name — Unique identifier
├── system_prompt — Persona and constraints
├── tools — ToolProtocol instances
├── strategy — Optional StrategyProtocol override
├── memory — Optional MemoryProtocol backend
└── temperature — LLM temperature (default 0.7)

AgentExecutorImpl wraps strategy execution with full infrastructure in prescribed order: governance check → input guard → session resume → memory assembly → skill merge → strategy execution → output guard → metrics → memory save → cost tracking → events.

sequenceDiagram
    actor User as Client
    participant Exec as AgentExecutorImpl
    participant Gov as Governance
    participant Guard as GuardPipeline
    participant Mem as WorkingMemory
    participant Strat as Strategy
    participant LLM as LLMClient
    participant Tools as ToolRegistry

    User->>Exec: run(agent, message, session_id)
    Exec->>Gov: check_request()
    Exec->>Guard: check_input()
    Exec->>Mem: assemble(query, token_budget)
    Exec->>Strat: execute(message, tools, history, llm)
    activate Strat
    loop Think → Act → Observe
        Strat->>LLM: complete(messages)
        Strat->>Tools: execute(tool_name, **args)  [if tool call]
    end
    Strat-->>Exec: Ok(AgentResponse)
    deactivate Strat
    Exec->>Guard: check_output()
    Exec->>Exec: record_metrics() + save_memory() + track_cost()
    Exec-->>User: Ok(AgentResponse)

Streaming is supported via astream() which yields AgentEvent objects (STARTED, MESSAGE, TOOL_CALL, ERROR, FINISHED) with a unique run_id.


Two paths to create tools:

Subclass AbstractTool:

class OrderLookupTool(AbstractTool):
name = "lookup_order"
description = "Look up an order by its ID"
parameters_schema = {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}
async def execute(self, **kwargs: Any) -> Any:
return await self.order_service.find(kwargs["order_id"])

@tool decorator (auto-generates JSON schema from type hints + docstring):

@tool
async def lookup_order(order_id: str) -> dict:
"""Look up an order by its ID."""
return await order_service.find(order_id)
registry = ToolRegistryImpl()
registry.register(lookup_order, module_class=OrdersModule)
registry.set_module_graph(compiled_graph) # optional visibility
registry.set_caller_module(SupportModule)
result = await registry.execute("lookup_order", order_id="123")

Visibility rules: No module graph → all visible. No caller → all visible. Unowned tool → visible. Same module → visible. Caller imports tool’s module → visible. Global → visible.

Boundary: Tools are atomic, stateless, single-purpose. Skills (from lexigram-ai-skills) are composed multi-step workflows. Skills may invoke tools. Tools must NOT invoke skills.


Two systems, with WorkingMemoryProtocol preferred:

Executor.run()
├── working_memory.assemble() → history (preferred)
└── memory.get_messages() → history (legacy fallback)
...
├── working_memory.add() → persist (preferred)
├── session_manager.add_turn() → persist (session store)
└── memory.add() → persist (legacy)

Memory failures are logged but never break execution.


All strategies extend AbstractStrategy (implements StrategyProtocol).

StrategyPatternBest For
ReActStrategyThink→Act→Observe (default)General-purpose tool-using agents
PlanAndExecuteStrategyPlan→Execute→Replan→SynthesizeComplex multi-step tasks
ReflexionStrategyGenerate→Critique→RefineQuality-critical responses
SupervisorStrategyAssess→Delegate→Review→DecideMulti-agent orchestration
flowchart TD
    Input[Agent config] --> Has{Strategy set?}
    Has -->|No| R[ReActStrategy max_iterations=10]
    Has -->|Yes| Reg[AgentStrategyRegistry.resolve(name)]
    Reg --> F{Found?}
    F -->|Yes| I[Instantiate with kwargs]
    F -->|No| E[ValueError]
    R & I --> Ex[Strategy.execute()]

    subgraph Registry[AgentStrategyRegistry]
        react["react → ReActStrategy"]
        pe["plan-execute → PlanAndExecuteStrategy"]
        ref["reflexion → ReflexionStrategy"]
        ep["entry-points → discoverable"]
    end
    Reg --> Registry

Think→Act→Observe loop (Yao et al., 2022). LLM output format:

THOUGHT: <reasoning>
ACTION: <tool_name>
ACTION_INPUT: <json args>

Produce FINAL_ANSWER: to terminate. Config: max_iterations (10), tool_timeout (30s), tool_max_retries (3).

Two-phase (Wang et al., 2023): LLM creates a numbered plan → each step executes (tool or reasoning) → on failure LLM replans → final synthesis. Config: max_steps (10), max_replans (2).

Generate → Critique → Refine. LLM evaluates its own response; loop stops on NO_CHANGES_NEEDED. Config: max_iterations (3).

Hierarchical: supervisor LLM delegates to sub-agents (wrapped as AgentAsToolAdapter), reviews responses, and produces final answer. Config: max_delegations (5).


AgentsProvider (priority DOMAIN, config key ai_agents):

sequenceDiagram
    participant C as Container
    participant AP as AgentsProvider

    Note over C,AP: REGISTER (ContainerRegistrarProtocol)
    C->>AP: register()
    AP->>C: singleton(AgentConfig)
    AP->>C: singleton(ToolRegistryImpl + ToolRegistryProtocol)
    AP->>C: singleton(AgentExecutorImpl + AgentExecutorProtocol)
    AP->>C: singleton(AgentMetrics, AgentTracer)
    AP->>C: singleton(AgentStrategyRegistry.with_defaults())
    AP->>AP: _discover_strategies() via entry-points

    Note over C,AP: BOOT (ContainerResolverProtocol)
    C->>AP: boot()
    AP->>C: resolve(LLMClientProtocol)  -- REQUIRED
    AP->>C: resolve(Governance, Memory, Metrics, Tracing, Events, Session, Skills)  -- optional
    AP->>AP: construct AgentExecutorImpl with all resolved deps
    AP->>C: override(AgentExecutorImpl + AgentExecutorProtocol)

register(): Binds config, creates ToolRegistryImpl, registers executor/metrics/tracer/strategy registry, discovers entry-point strategies. Exits early if config.enabled is False.

boot(): Resolves required LLMClientProtocol, resolves all optional integrations (governance, memory, metrics, tracing, event bus, session manager, skill executor/registry, module graph), constructs fully-wired AgentExecutorImpl, and overrides both concrete and protocol tokens.

shutdown(): Logging cleanup.


ContractImport PathPurpose
AgentExecutorProtocollexigram.contracts.aiAgent execution entry point
ToolRegistryProtocollexigram.contracts.aiTool registration and lookup
AgentProtocollexigram.contracts.ai.agentsAgent identity and capabilities
StrategyProtocollexigram.contracts.ai.agentsReasoning strategy interface
ToolProtocollexigram.contracts.ai.agentsAtomic tool interface
AgentResponse / AgentEventlexigram.contracts.ai.agentsResult and streaming types
MemoryProtocollexigram.contracts.ai.agentsAgent-attached memory
AgentError / ToolError / StrategyErrorlexigram.contracts.ai.agentsDomain error bases
LLMClientProtocollexigram.contracts.ai.llmLLM completion
ChatMessage / Rolelexigram.contracts.ai.llmConversation types
AIGovernanceProtocollexigram.contracts.ai.governanceBudget/rate limits
GuardPipelineProtocollexigram.contracts.ai.guardsInput/output guardrails
WorkingMemoryProtocollexigram.contracts.ai.memoryContext assembly
SessionManagerProtocollexigram.contracts.ai.sessionMulti-turn sessions
SkillExecutorProtocol / SkillRegistryProtocollexigram.contracts.ai.skillsComposable skills
EventBusProtocollexigram.contracts.events.protocolsDomain events
MetricsRecorderProtocollexigram.contracts.observability.metricsPrometheus metrics
TracerProtocollexigram.contracts.observability.tracingOpenTelemetry tracing

LexigramError (contracts)
├── AgentError (contracts)
│ ├── AgentConfigurationError — Invalid config (LEX_ERR_AGT_004)
│ ├── AgentExecutionError — Unrecoverable exec (LEX_ERR_AGT_005)
│ └── BudgetExceededError — Governance exceeded (LEX_ERR_AGT_010)
├── ToolError (contracts)
│ ├── ToolNotFoundError — Not in registry (LEX_ERR_AGT_006)
│ ├── ToolExecutionError — Runtime failure (LEX_ERR_AGT_007)
│ ├── ToolAccessDeniedError — Visibility blocked (LEX_ERR_AGT_008)
│ └── ToolValidationError — Bad arguments (LEX_ERR_AGT_011)
└── StrategyError (contracts)
└── MaxIterationsExceededError — Iteration limit (LEX_ERR_AGT_009)

PointMechanism
Custom agentSubclass AgentBase, set name, system_prompt, tools property
Fluent agentAgentBuilder chain
Custom tool (class)Subclass AbstractTool
Custom tool (function)@tool decorator
Custom strategySubclass AbstractStrategy, implement execute()
Strategy registrationAgentStrategyRegistry or lexigram.agent.strategies entry-point
Custom plannerImplement PlannerProtocol
Custom observerImplement ObserverProtocol
Tool visibilityToolRegistryImpl.set_module_graph()
Agent as toolAgentAsToolAdapter wraps agents for delegation
Crew processConfigure via Crew / CrewBuilder
Governance/Guard/MemoryResolve via container — automatically integrated

SymbolDescription
ENV_PREFIXLEX_AI_AGENTS__
METRIC_AGENT_EXECUTIONS_TOTALCounter: total executions
METRIC_AGENT_EXECUTION_DURATION_MSHistogram: execution duration
METRIC_AGENT_EXECUTION_TOKENSHistogram: tokens per execution
METRIC_AGENT_EXECUTION_STEPSHistogram: steps per execution
METRIC_AGENT_EXECUTIONS_ERRORSCounter: execution errors
METRIC_AGENT_GOVERNANCE_DENIEDCounter: governance denials
METRIC_AGENT_TOOL_CALLS_TOTALCounter: total tool calls
METRIC_AGENT_TOOL_CALL_DURATION_MSHistogram: tool call duration
SPAN_AGENT_EXECUTE / SPAN_AGENT_LLM / SPAN_AGENT_TOOLSpan names for tracing
__version__Package version