Architecture Guide¶
Use these capability snippets inside your application’s native runtime. Installation is through PyFly Agentic extras.
Copyright 2026 Firefly Software Foundation. Licensed under the Apache License 2.0.
This document describes the high-level architecture of PyFly Agentic, the relationships between its modules, and the design principles that guided its construction.
Design Principles¶
The framework follows five guiding principles:
-
Explicit extension contracts -- Tools, memory stores, reasoning patterns and other extension points use Python protocols or abstract base classes. Configuration models, builders and concrete facades expose their own public APIs; implement the documented contract for the component you want to replace.
-
Convention over configuration -- Sensible defaults are provided for every setting. A single
AgenticConfigobject (backed by Pydantic validation model) centralises configuration and supports environment-variable overrides through PyFly’s native binder. -
Layered composition -- Modules are organised into layers (Core, Security, Agent, Intelligence, Orchestration, plus optional Experimentation dev-tooling). These categories describe responsibilities and composition, not an enforced import hierarchy. Components can collaborate across them through public contracts.
-
Optional dependencies -- Heavy third-party libraries (embedding providers, vector store clients, storage backends, document-conversion tooling) are declared as extras. The core framework imports them lazily so that users only install what they need.
-
Pure in-process library -- PyFly Agentic is a library, not a server. It serves no HTTP port and consumes no message broker; the host service owns all serving, hosting, and inbound authentication. The framework emits model and agent spans and metrics through the OpenTelemetry API, but configuring the OTel SDK, exporters, and cross-service trace propagation is the host's responsibility.
Application boundary¶
Application code uses Agent, @agent_tool / ToolKit, MemoryManager,
and typed ModelOptions. PyFly Agentic resolves options against the selected provider
on every run, including per-run model overrides, and rejects unsupported controls.
ModelSpec and ModelFactory handle credential and endpoint configuration for
applications that maintain a model catalogue. Pydantic AI 2.x remains the model
transport and tool-execution engine; native settings and capabilities are advanced
integration surfaces.
Existing openai: / azure: configurations retain Chat Completions. Select
openai-responses: / azure-responses: explicitly for Responses. See
Models and the migration guide for compatibility rules.
Layer Diagram¶
Arrows below show which components invoke or use others. This is a conceptual map, not a complete Python import graph. Optional behaviors are enabled by the host.
flowchart TB
HOST["Host application<br/>configuration, credentials, serving"]
FLOW["Pipelines and workflows<br/>routing, state, checkpoints"]
REASON["Reasoning and review<br/>explicit multi-step execution"]
AGENT["Agent<br/>run, run_sync, run_stream"]
MODELS["Models<br/>ModelSpec, ModelFactory, ModelOptions"]
PAI["Pydantic AI<br/>provider requests and tool dispatch"]
PROVIDER["Selected provider and API"]
TOOLS["PyFly Agentic tools<br/>schemas, guards, listeners"]
MEMORY["MemoryManager<br/>history and scoped facts"]
RETRIEVAL["Content, embeddings, vector stores"]
POLICY["Configured middleware<br/>logging, guards, telemetry, caching"]
SUPPORT["Shared services<br/>configuration, storage, registries, plugins"]
DEV["Lab and experiments<br/>optional evaluation workflows"]
HOST --> AGENT
HOST --> FLOW
FLOW --> AGENT
FLOW --> REASON
REASON --> AGENT
AGENT --> MODELS
AGENT --> PAI
PAI --> PROVIDER
PAI --> TOOLS
AGENT --> MEMORY
AGENT --> POLICY
FLOW --> RETRIEVAL
AGENT -. uses .-> SUPPORT
MEMORY -. uses .-> SUPPORT
RETRIEVAL -. uses .-> SUPPORT
DEV --> AGENT
Protocol & Class Hierarchy¶
The representative contracts below use structural typing: a component implements the required members rather than inheriting a concrete framework class. Abstract base classes provide reusable behavior where shown. Runtime protocol checks verify member presence, not argument types or application correctness.
Other contracts are documented alongside their implementations: guards and tool listeners, pipeline steps, delegation, compression, storage, and validation.
classDiagram
class AgentLike {
<<Protocol>>
+run(prompt, **kwargs) Any
}
class ToolProtocol {
<<Protocol>>
+name str
+description str
+execute(**kwargs) Any
}
class MemoryStore {
<<Protocol>>
+save(namespace, entry)
+load(namespace) list
+load_by_key(namespace, key) MemoryEntry
+delete(namespace, entry_id)
+clear(namespace)
}
class ReasoningPattern {
<<Protocol>>
+execute(agent, input, **kwargs) ReasoningResult
}
class AgentTool {
<<abstract>>
}
class AbstractReasoningPattern {
<<abstract>>
}
AgentLike <|.. Agent
ToolProtocol <|.. AgentTool
AgentTool <|-- HttpTool
MemoryStore <|.. InMemoryStore
MemoryStore <|.. PostgreSQLStore
MemoryStore <|.. MongoDBStore
ReasoningPattern <|.. AbstractReasoningPattern
AbstractReasoningPattern <|-- ReActPattern
ReasoningPattern <|.. ReasoningPipeline
Module Responsibilities¶
Core Layer¶
The Core layer provides foundational types, configuration, exceptions, and the plugin system. Every other module depends on at least one Core component.
- types.py -- The
AgentLikeprotocol, generic TypeVars, shared type aliases, and re-exported multimodal content types. (The other extension-point protocols --ToolProtocol,GuardProtocol,ReasoningPattern,StepExecutor,DelegationStrategy,CompressionStrategy,MemoryStore,ValidationRule-- live in their respective modules, not intypes.py.) - config.py --
AgenticConfig, a runtime-owned Pydantic validation model object that reads from the effective PyFly configuration tree and environment overrides. Direct construction accepts explicit values; it does not load.envfiles. It actively rejects removed serving/exposure config fields (e.g.otlp_endpoint,rbac_enabled,cors_allowed_origins,cost_calculator) with aValueError. - exceptions.py -- A structured exception hierarchy of 42 classes rooted at
AgenticError. - plugin.py --
PluginDiscoverydiscovers and loads entry-point plugins at startup. - resilience/circuit_breaker.py --
CircuitBreaker(withCircuitStateandCircuitBreakerOpenError) andCircuitBreakerMiddlewarefor guarding agent runs against cascading failures. - storage/ --
DatabaseStoreandLocalBackend(behind theStorageBackendprotocol) for binary/large-object persistence, withWriteSession,LockTokenleasing,RetryPolicy,StorageMetadata, and a family of storage error types.
Security Layer¶
The Security layer provides input sanitisation and prompt injection defence.
- security/prompt_guard.py --
PromptGuardscans user prompts for 25 known injection patterns (including encoding bypass, zero-width evasion, multi-language, jailbreak, and system prompt extraction), reports matches, and optionally sanitises suspicious fragments.default_prompt_guardprovides a shared instance. - security/output_guard.py --
OutputGuardscans LLM responses for PII (6 patterns), secrets (9 patterns), harmful content (4 patterns), custom patterns, and deny patterns.default_output_guardprovides a shared instance. See the Security Guide. - security/encryption.py --
EncryptionProviderprotocol withAESEncryptionProvider, andEncryptedMemoryStore(store, encryption_key, provider=None)which transparently encryptsMemoryEntry.contentat rest (keys, metadata, and timestamps stay plaintext).
Inbound-request authorization (RBAC/JWT) is intentionally not part of the framework -- it is a hosting concern owned by the host service.
Agent Layer¶
The Agent layer wraps Pydantic AI's Agent class and adds lifecycle management,
an application-scoped registry, delegation strategies, and declarative decorators.
- base.py --
Agentwrapspydantic_ai.Agentwith metadata, hooks, middleware chain, run timeout, and streaming usage tracking. - registry.py --
AgentRegistryis a configuration for the active runtime that maps names to agents. - lifecycle.py --
AgentLifecyclehandles init, warmup, and shutdown hooks. - delegation.py -- Multi-agent delegation. Strategies return
RoutingDecisionobjects (ranked, scoredCandidatetuples plus metadata). Built-ins:RoundRobinStrategy,CapabilityStrategy,ContentBasedStrategy(LLM routing), andCostAwareStrategy(priced via the cost resolver chain, pool-relative normalisation). CombinatorsChainStrategy,FallbackStrategy, andWeightedStrategynest strategies without subclassing.DelegationRouterseparatesdecide()(pure, emits thepyfly.agentic.routing.decisionOTel event) fromexecute()(runs the chosen agent, forks memory);route()remains the one-call convenience. - context.py --
AgentContextcarries request-scoped data through an agent run. - decorators.py --
@agentregisters an agent declaratively. - middleware.py --
AgentMiddlewareprotocol,MiddlewareContext, andMiddlewareChainfor pluggable before/after hooks on every agent run. - builtin_middleware.py -- The concrete middleware stack:
LoggingMiddleware,PromptGuardMiddleware,CostGuardMiddleware,ObservabilityMiddleware,ExplainabilityMiddleware,CacheMiddleware,OutputGuardMiddleware,ValidationMiddleware, andRetryMiddleware. Two more live elsewhere:PromptCacheMiddleware(prompt_cache.py) andCircuitBreakerMiddleware(resilience/) -- 11 concrete middleware in total. By defaultAgentauto-wiresLoggingMiddlewarealways, andObservabilityMiddlewarewhenconfig.observability_enabledis set; the rest are opt-in. (Rate-limit retry is handled insideAgent.run()rather than byRetryMiddleware.) - prompt_cache.py --
PromptCacheMiddlewareandCacheStatisticsfor prompt-level response caching. - fallback.py --
FallbackModelWrapperandrun_with_fallback()for automatic model failover. Accepts bothstrandModelobjects for cross-provider fallback chains. - model_utils.py -- Centralized model identity extraction
(
extract_model_info,get_model_identifier,detect_model_family) for uniform handling of both"provider:model"strings andModelobjects across the framework's observability and resilience layers. - cache.py --
ResultCachewith TTL, LRU eviction, and thread-safe access. - templates/ -- Pre-built template agents (summarizer, classifier, extractor, conversational, router) available as factory functions. See the Template Agents Guide.
Intelligence Layer¶
- reasoning/ -- Pluggable reasoning patterns (ReAct, Chain of Thought, etc.) with a pipeline for chaining patterns sequentially.
- observability/ -- Emits OpenTelemetry spans (
AgentTracer), custom metrics (AgentMetrics), and events (AgentEvents) through the OTel API;UsageTrackerrolls up token usage, cost is resolved via the resolver chain (resolve_cost,genai_prices_cost,provider_reported_cost,DEFAULT_RESOLVERS; gated by thecost_strictconfig flag), andBudgetGateenforces budgets. Configuring the OTel SDK and exporters is the host service's responsibility. - explainability/ -- Decision recording (
TraceRecorder.record(category, ...)with a.recordsproperty), natural-language explanation generation (ExplanationGenerator), audit trails (AuditTrail.append(actor, action, ...)), and report building (ReportBuilder(title=...).build(records)).
Memory Layer¶
- memory/conversation.py --
ConversationMemory: token-aware, per-conversation chat history wrapping pydantic-ai'smessage_historymechanism. - memory/working.py --
WorkingMemory: scoped key-value scratchpad for session facts, entities, and intermediate state. - memory/store.py --
MemoryStoreprotocol withInMemoryStore,FileStore, andSQLiteStorebackends for working facts.memory/database_store.pyaddsRelationalMemoryStore,PostgreSQLStoreandMongoDBStore, borrowing native Data resources and units of work on the application loop. Conversation history remains in process; use export/import to persist snapshots.memory/summarization.pyprovidescreate_llm_summarizer. - memory/manager.py --
MemoryManagerfacade composing conversation and working memory, withfork()for multi-agent scope isolation.
Embeddings & Vector Store Layer¶
These modules are reusable building blocks for retrieval-augmented workflows; the framework
ships no turnkey corpus/RAG agent, but RetrievalStep and EmbeddingStep (orchestration)
let you assemble retrieval pipelines.
- embeddings/ --
BaseEmbedder(theEmbeddingProtocol),EmbedderRegistry, similarity helpers (cosine_similarity,dot_product,euclidean_distance), and 8 provider backends underembeddings/providers/: OpenAI, Azure, Cohere, Google, Mistral, Voyage, Bedrock, and Ollama. - vectorstores/ --
BaseVectorStore(theVectorStoreProtocol),VectorStoreRegistry,VectorDocument,SearchFilter/SearchResult, and 6 backends:InMemoryVectorStore,ChromaVectorStore,PineconeVectorStore,QdrantVectorStore,PgVectorVectorStore, andSqliteVecVectorStore. The scoped layer (ScopedVectorStore,TenantScopedVectorStore,scope_namespace,parse_scope_namespace) partitions a shared store by tenant/scope.
Content Layer¶
- content/chunking.py --
TextChunker,DocumentSplitter,ImageTiler, andBatchProcessorfor splitting large inputs into model-friendly chunks. - content/markdown_chunker.py --
MarkdownChunkerfor structure-aware Markdown splitting. - content/compression.py --
ContextCompressorwith pluggable strategies (TruncationStrategy,SummarizationStrategy,MapReduceStrategy) andSlidingWindowManager.ContextCompressor.compress(...)is async. - content/binary/ (the
[agentic-binary]extra) -- A document-conversion subsystem.BinaryNormalizer(configured byBinaryConfig) turns uploaded files intoBinaryArtifacts:sniff_media_typedetects the type, office documents are converted viabuild_office_converter(GotenbergConverter/LibreOfficeConverter/NoOpOfficeConverter, all implementingOfficeConverter),PdfGuardrejects encrypted or corrupt PDFs,ImageNormalizerstandardises images, andArchiveUnpacker/EmailUnpackerexpand archives and email attachments.
Validation Layer¶
- validation/rules.py -- Composable validation rules (
RegexRule,FormatRule,RangeRule,EnumRule,CustomRule),FieldValidator,OutputValidator, andValidationReport. - validation/qos.py --
ConfidenceScorer,ConsistencyChecker,GroundingChecker, andQoSGuard(returningQoSResult) for post-generation quality checks. - validation/reviewer.py --
OutputReviewerandRubricReviewer(LLM-as-judge, withfrom_rubric_file(...)) for criteria-based review, returningReviewResultwithRetryAttempthistory.
Orchestration Layer¶
- pipeline/dag.py --
DAG,DAGNode,DAGEdgewith topological sort, cycle detection, execution-level grouping, and per-nodeFailureStrategy. - pipeline/engine.py --
PipelineEngineruns DAGs with eager scheduling, concurrency, retries, timeouts, condition gates, and failure strategy enforcement. - pipeline/builder.py -- Fluent
PipelineBuilderfor constructing pipelines. - pipeline/steps.py -- Step executors implementing the
StepExecutorprotocol:AgentStep,ReasoningStep,CallableStep,BranchStep,FanOutStep,FanInStep,BatchLLMStep,EmbeddingStep, andRetrievalStep(RetrievalStep(store, *, embedder=None, top_k=5, input_key="input")). - pipeline/context.py --
PipelineContextshared data bus, with state reducers (append,extend,merge_dict,replace) and control signals (Pause,Send). - pipeline/result.py --
NodeResult,PipelineResult, andExecutionTraceEntry. - pipeline checkpointing & audit --
Checkpointer/FileCheckpointer(recordingCheckpointRecords) for resumable runs, and the audit-log familyAuditLog/FileAuditLog/LoggingAuditLog/OtelAuditLog/QueryableAuditLog(emittingAuditEntrys). Event hooks are wired viaEventHandler/PipelineEventHandler.
Experimentation Layer (optional dev-tooling)¶
These are optional, leaf dev-tooling modules; the core framework never imports them.
- experiments/ -- Define experiments with named variants, run them through an
ExperimentRunner(experiment, agent_factory, *, context=None), track metrics withExperimentTracker(storage_path=...), and compare results withVariantComparator. - lab/ -- Interactive sessions, benchmarks, datasets, side-by-side comparisons, and pluggable evaluators.
Request Flow¶
This is the successful run() path. Tools execute only when requested by the model.
Reasoning patterns, output review, and explainability recording are explicit choices;
an ordinary run does not automatically invoke those components. Model failures call
configured error hooks before propagating the exception.
sequenceDiagram
participant Caller
participant Agent as Agent
participant MW as Middleware chain
participant Memory as ConversationMemory
participant Options as ModelOptions resolver
participant PAI as Pydantic AI agent
participant Tools as PyFly Agentic tools
Caller->>Agent: await run(prompt, conversation_id=cid)
Agent->>MW: before_run in registration order
alt Cache middleware supplies a result
Agent->>MW: after_run in reverse order
else Model invocation
opt Memory attached and no explicit message_history
Agent->>Memory: get_message_history(cid)
Memory-->>Agent: typed provider messages
end
Agent->>Options: merge and validate settings for effective model
Agent->>PAI: run(prompt, history, settings)
opt Model requests function tools
PAI->>Tools: execute arguments through guards and listeners
Tools-->>PAI: tool result
end
PAI-->>Agent: run result
opt Completed output and conversation_id supplied
Agent->>Memory: add_turn with new_messages()
end
Agent->>Agent: record usage when cost tracking is enabled
Agent->>MW: after_run in reverse order
end
Agent-->>Caller: result with output and messages
Pipeline Execution Flow¶
For an acyclic graph without runtime routers, PipelineEngine schedules each node
as soon as its dependencies are resolved; it does not wait for a whole level. Cycles
and runtime routers use a frontier scheduler with a recursion limit. Port-based
steps implement StepExecutor; state-based nodes return updates merged by reducers.
flowchart TD
BUILD["PipelineBuilder or PipelineEngine builder"] --> DAG["Nodes, edges and optional state schema"]
DAG --> START["engine.run: new run or checkpoint resume"]
START --> MODE{"Cycle or runtime router?"}
MODE -->|no| READY["Schedule nodes whose dependencies are resolved"]
READY --> RUN["Execute ready nodes concurrently"]
RUN --> SAVE["Record results, state updates and checkpoints"]
SAVE --> MORE{"Pending nodes?"}
MORE -->|yes| READY
MODE -->|yes| FRONTIER["Follow runtime frontier with recursion_limit"]
FRONTIER --> STEP["Execute node or Send fan-out"]
STEP --> ROUTE["Apply reducers and choose next targets"]
ROUTE --> CONTINUE{"Continue?"}
CONTINUE -->|yes| FRONTIER
CONTINUE -->|no| RESULT["PipelineResult"]
MORE -->|no| RESULT
STEP -->|Pause returned| PAUSED["Checkpoint and return paused result"]
Memory Architecture¶
MemoryManager composes process-local ConversationMemory and WorkingMemory.
Only working facts use the configured MemoryStore; selecting a database backend
does not persist chat history. The host stores and restores conversation snapshots.
flowchart TD
MM["MemoryManager"] --> CM["ConversationMemory<br/>process-local typed messages"]
MM --> WM["WorkingMemory<br/>scoped facts"]
CM --> BUDGET["Token budget and optional summarization"]
CM <-->|export and import| SNAPSHOT["Conversation snapshot<br/>host persists it"]
WM --> STORE["MemoryStore contract"]
STORE --> LOCAL["InMemoryStore, FileStore, SQLiteStore"]
STORE --> DB["RelationalMemoryStore, PostgreSQLStore, MongoDBStore"]
DB --> LOOP["PyFly Data unit of work<br/>borrowed datasource or async Mongo client"]
Reasoning Pattern Architecture¶
All six reasoning patterns extend AbstractReasoningPattern, which provides a
template-method loop with an early-stop check after _reason, an iteration limit,
and optional final review. The diagram shows that base loop; Tree of Thoughts and
Goal Decomposition override execute() with their own flows while reusing helper
methods. See Reasoning for each pattern's behavior.
flowchart TD
CALL["Explicit pattern.execute or agent.run_with_reasoning"] --> INIT["Initialize trace and optional memory scope"]
INIT --> THINK["_reason"]
THINK --> STOP{"_should_stop?"}
STOP -->|yes| OUTPUT["_extract_output"]
STOP -->|no| ACT["_act"]
ACT --> OBSERVE["_observe"]
OBSERVE --> AGAIN{"_should_continue?"}
AGAIN -->|no| OUTPUT
AGAIN -->|yes| LIMIT{"Below max_steps?"}
LIMIT -->|yes| THINK
LIMIT -->|no| ERROR["ReasoningStepLimitError"]
OUTPUT --> REVIEW["Optional reviewer"]
REVIEW --> RESULT["Persist final working-memory fact<br/>return ReasoningResult"]
Multi-Provider Support¶
The framework is provider-agnostic: it never re-implements a provider client.
PyFly Agentic normalizes model aliases and resolves factory configuration, then delegates
provider requests to Pydantic AI. Providers Pydantic AI supports — OpenAI, Anthropic, Google/Gemini, Groq, Bedrock, Mistral, Cohere,
DeepSeek, xAI, OpenRouter, Azure, Ollama, … — are selected with either a
"provider:model" string or a pydantic-ai Model object to any agent. Everything
the framework adds on top is built to be provider-uniform:
- Identity normalisation —
model_utils(get_model_identifier,extract_model_info,detect_model_family) turns both"provider:model"strings andModelobjects (reading the provider from the model's own_provider.name) into a canonicalprovider:modelstring. This single key feeds cost lookup, quota/backoff, and usage grouping, so a model object never "drops" its provider. - Cost — pricing keys off that identifier across all providers, with
provider-aware nuances: Gemini
thoughts_tokensare counted (OpenAI/Anthropic fold reasoning into output), Bedrock vendor-prefixed ids get a retry on the bare model name, and an authoritative provider cost (OpenRouterusage.cost) wins when available. See Observability → Cost Resolution. - Prompt caching — routed by model family: Anthropic writes
cache_controlbreakpoints, OpenAI supplies a routing key (its caching is automatic), Gemini usescachedContent; Claude via Bedrock/OpenRouter is skipped with a warning (those backends cache differently). See Agents → PromptCacheMiddleware. - Tool schemas — real
python_types keep tool schemas portable, with one caveat: Gemini rejects free-formdict[str, Any]object schemas — use a JSON string or a nested model instead. See Tools. - Failover & rate limits —
FallbackModelWrapper/run_with_fallbackfail over across providers, and rate-limit backoff prefers a provider's structured retry hint (e.g. Geminiretry_delay) before falling back to exponential backoff.
The test suite includes controlled SDK-boundary tests for Chat Completions and Responses, plus credential-gated live OpenAI and Anthropic tests. A passing offline contract test does not establish availability in a provider account; see tests/README.
Plugin System¶
Plugins are discovered via Python entry points under three well-known groups:
pyfly.agentic.agents, pyfly.agentic.tools, and
pyfly.agentic.reasoning_patterns. The PluginDiscovery class scans
these groups and loads the referenced objects into a DiscoveryResult. Discovery
does not automatically register returned objects; the host registers them, or an
imported plugin module may do so as an explicit side effect.
flowchart LR
PACKAGE["Installed package entry points<br/>agents, tools, reasoning_patterns"] --> DISCOVERY["PluginDiscovery<br/>loads referenced objects"]
DISCOVERY --> RESULT["DiscoveryResult<br/>successful objects and load errors"]
RESULT --> HOST["Host selects and registers components"]
HOST --> AGENTS["AgentRegistry"]
HOST --> TOOLS["ToolRegistry"]
HOST --> REASON["Reasoning registry"]
To create a plugin, add entry points in your package's pyproject.toml:
[project.entry-points."pyfly.agentic.agents"]
my_agent = "my_package.agents:MyAgent"
[project.entry-points."pyfly.agentic.tools"]
my_tool = "my_package.tools:MyTool"
Then call discovery at startup:
from pyfly.agentic.plugin import PluginDiscovery
result = PluginDiscovery.discover_all()
print(f"Loaded {len(result.successful)} plugins, {len(result.failed)} failed")
Configuration¶
All configuration is managed through AgenticConfig, which reads values from
environment variables prefixed with PYFLY_AGENTIC_. For example:
export PYFLY_AGENTIC_DEFAULT_MODEL=openai-responses:gpt-6-luna
export PYFLY_AGENTIC_LOG_LEVEL=DEBUG
export PYFLY_AGENTIC_OBSERVABILITY_ENABLED=true
export PYFLY_AGENTIC_NATIVE_INSTRUMENTATION_ENABLED=true # native pydantic-ai GenAI spans (see observability.md)
export PYFLY_AGENTIC_REASONING_OUTPUT_MODE=prompted # reasoning structured-output strategy (see reasoning.md)
The framework emits telemetry through the OpenTelemetry API but does not configure the OTel SDK or any exporter. Wiring up the SDK/exporter endpoint (including any OTLP endpoint) is the host service's responsibility;
config.observability_enabledonly gates whether the framework emits spans and metrics. Supplying removed serving/exposure fields (e.g.otlp_endpoint,rbac_enabled,cors_allowed_origins) raises aValueError.
The application owns configuration through AgenticRuntime. Construct one with
AgenticConfig(...), or let PyFly auto-configuration bind pyfly.agentic.
get_config() resolves through the active runtime; it is not a process-wide
configuration switch. Registries, usage, quotas and telemetry helpers follow the
same application ownership. Agents created by runtime.agent_factory.create(...)
retain that runtime for nested execution and streams.
See native DI and lifecycle for complete offline and injected service examples, explicit resource ownership, stream cancellation and shutdown.