Concept¶
After reading this page you should have a good understanding of the core concepts and architecture of Protolink.
Architecture Overview¶
Protolink is designed around explicit separation of concerns, protocol agnosticism, and low boilerplate for agent authors.
At a high level, Protolink models an agent as a logical actor that communicates with other agents via well-defined client/server interfaces, backed by pluggable transports.
The core idea is simple:
Agents express intent. Clients and servers handle communication. Transports handle protocols.
This separation keeps agent logic clean, testable, and future-proof.
Core Concepts¶
Protolink is built from the following core components:
- Agent — business logic and orchestration
- Client — outgoing communication
- Server — incoming communication
- Transport — protocol + runtime implementation
- Registry — discovery and coordination
Each layer has a single responsibility and a clear dependency direction.
Agent¶
The Agent is the central abstraction in Protolink.
It represents:
- Identity (via
AgentCard) - Capabilities and skills
- Task handling logic
- Lifecycle orchestration
The agent does not perform networking and does not implement protocols.
Responsibilities¶
- Define how tasks are handled (
handle_task) - Manage tools, skills, and optional LLMs
- Coordinate startup and shutdown
- Orchestrate client/server components
- Register and discover peers via the registry
What the Agent Does Not Do¶
- Open sockets
- Handle HTTP requests
- Serialize messages
- Know about protocols (HTTP, WS, local, etc.)
This is intentional and enforced by design.
Client / Server Layer¶
Between the agent and the transport, Protolink introduces an explicit client/server layer.
This layer removes boilerplate from agent implementations while keeping responsibilities clear.
AgentClient (Outgoing)¶
The AgentClient handles agent-to-agent outgoing communication.
Responsibilities¶
- Sending tasks to other agents
- Sending messages to other agents
- Delegating all transport details
The client exposes intent-level methods only.
Example interface (simplified):
send_task(agent_url, task)
send_message(agent_url, message)
Key point: The client knows what it wants to send but does not know how it is sent.
AgentServer (Incoming)¶
The AgentServer handles incoming requests for an agent.
Responsibilities¶
- Wiring the agent’s task handler into the transport
- Starting and stopping the server runtime
- Enforcing lifecycle rules
The server:
- Receives tasks via the transport
- Delegates task execution to the agent
- Never contains business logic
Transport Layer¶
The Transport is the lowest layer in the system.
It encapsulates:
- Network protocol (HTTP, WS, local, etc.)
- Runtime concerns (ASGI, threads, event loops)
- Serialization and deserialization
- Request routing
Key Properties¶
- Protocol-agnostic
- Swappable without touching agent logic
- Reusable across agents
- Shared by client and server
The transport is never accessed directly by the agent.
Dependency Direction (Important)¶
The dependency graph is strictly one-way:
Agent ├── AgentClient │ └── Transport └── AgentServer └── Transport
Key points:
- The agent owns the client and server
- The client and server own the transport
- The agent never calls transport methods directly
This guarantees:
- Clean abstractions
- Easy testing
- No protocol leakage into business logic
Registry¶
The registry enables agent discovery and coordination.
Architecturally, it mirrors the agent model.
Registry Components¶
- Registry — logical registry service
- RegistryClient — outgoing discovery requests
- RegistryServer — incoming registry API
- Transport — protocol implementation
Registry Dependency Graph¶
Registry ├── RegistryClient │ └── Transport └── RegistryServer └── Transport
This symmetry is intentional and keeps the mental model consistent across the system.
Registry Responsibilities¶
- Agent registration
- Agent discovery
- Heartbeat and expiry (liveness)
- Filtering and metadata queries
Agents interact with the registry only via the
RegistryClient.
Agent Lifecycle¶
A typical agent lifecycle looks like this:
- Instantiation with:
AgentCard- Transport
-
Optional registry reference
-
Creation of:
AgentClient-
AgentServer -
Startup:
- Server runtime
-
Registry registration
-
Runtime: Agent runs autonomously
-
Shutdown:
- Server stopped
- Registry unregistration
All of this happens with minimal boilerplate for the user.
Autonomous Agents¶
Protolink supports autonomous behavior without external orchestration.
Agents can:
- Discover peers dynamically via the Registry
- Schedule and delegate tasks to specialized agents
- Call another agent's LLM for reasoning
- Invoke another agent's tools directly
- React to incoming tasks autonomously
This is done inside the agent, without manual wiring between agents.
Agents behave like independent actors, not manually invoked functions.
Agents are entities, not functions. They are autonomous, centralized objects that serve as the core unit of your system.
This creates a flexible mesh where specialized agents leverage each other's native capabilities without rigid orchestration bottlenecks. The programmer can be as invasive or hands-off as they want in the agent flow—Protolink gives you the freedom to choose.
Why This Architecture¶
This design is intentionally:
- Protocol-agnostic: swap transports without touching agent logic
- Low boilerplate: focus on what matters, not infrastructure
- Explicit: no hidden magic, you always know what's running
- Composable: mix and match LLMs, tools, transports, storage
- Testable: clean separation makes testing straightforward
It draws inspiration from:
- Actor models
- Ports & adapters (hexagonal architecture)
- Distributed systems design
- Google A2A concepts (agent cards, tasks, discovery)
Most importantly:
Care only about the logic. Leave the communication, agent lifecycle, inference, tooling, authentication, memory, and logging to Protolink.
Philosophy: Breaking Free from Lock-In¶
Traditional AI frameworks often trap you in a walled garden:
| Lock-In Type | The Problem | Protolink Solution |
|---|---|---|
| LLM Lock-In | Tied to one provider (OpenAI, Anthropic) | Plug in any LLM—API, local, or self-hosted |
| Transport Lock-In | Hardcoded HTTP or specific runtime | Swap transports with one line of code |
| Tooling Lock-In | Proprietary tool schemas | Native tools + MCP adapter for universal tooling |
| Runtime Lock-In | Only works in specific environments | Protocol-agnostic, runs anywhere Python runs |
Transport Independence¶
Protolink agents speak HTTP today, but can speak WebSockets, gRPC, or in-memory queues tomorrow—without changing agent code. Just change the transport:
# Switch from HTTP to WebSocket with one line
agent = Agent(card, transport="websocket") # That's it!
Universal Tooling¶
Protolink supports the Model Context Protocol (MCP) via a built-in adapter. Import tools from thousands of existing MCP servers (Google Drive, Slack, Postgres) instantly.
Resilience by Design¶
By decoupling the Brain (LLM) from the Body (Agent), you are immune to provider outages or pricing changes. Swap providers without rewriting your core logic.
Developer Freedom¶
The pluggable architecture means you own your stack. No vendor lock-in, no framework constraints—just clean, composable components.
Summary¶
- Agents express intent
- Clients and servers handle directionality
- Transports handle protocols
- Registries handle coordination
- Dependencies flow one way
- Boilerplate is minimized by design
This architecture makes it easy to:
- Add new transports
- Scale from local to distributed
- Swap protocols
- Keep agent logic clean and focused
LLM Inference Runtime¶
When an agent includes an LLM, Protolink provides a deterministic inference runtime that transforms stateless language models into reliable autonomous actors.
The Core Idea¶
The LLM declares intent. The runtime executes actions. The LLM observes results.
This separation ensures:
- Reproducible behavior across different LLM providers
- Full control over side effects (tool execution, agent delegation)
- Robust error handling with self-correction capabilities
ReAct-Style Execution Loop¶
The inference runtime implements a ReAct-style (Reasoning + Acting) pattern:
┌─────────────────────────────────────────────────────────┐
│ Inference Loop │
├─────────────────────────────────────────────────────────┤
│ 1. User query added to conversation history │
│ ↓ │
│ 2. LLM generates JSON action │
│ ↓ │
│ 3. Runtime parses and validates action │
│ ↓ │
│ 4. Action dispatched: │
│ • final → Return response │
│ • tool_call → Execute tool, inject result │
│ • agent_call → Delegate to agent, inject result │
│ ↓ │
│ 5. Loop continues until 'final' or limit exceeded │
└─────────────────────────────────────────────────────────┘
The LLM operates in a thought → action → observation cycle:
- Thought: Internal reasoning (not exposed)
- Action: JSON declaration of what to do
- Observation: Runtime executes, result injected into history
JSON Action Protocol¶
The LLM communicates via a strict JSON protocol with three action types:
Final Response¶
{"type": "final", "content": "The weather in Tokyo is 28°C and sunny."}
Tool Call¶
{"type": "tool_call", "tool": "get_weather", "args": {"location": "Tokyo"}}
Agent Delegation¶
{
"type": "agent_call",
"action": "tool_call",
"agent": "weather_agent",
"tool": "get_weather",
"args": {"location": "Tokyo"}
}
The runtime never trusts the LLM to execute actions directly. All actions are:
- Parsed and validated
- Executed by the runtime
- Results serialized and injected back
Safety Guardrails¶
The inference runtime implements multiple safety mechanisms:
Deduplication Detection¶
Tracks recent actions in a sliding window. If the LLM repeats an identical action:
- Action is not re-executed
- Corrective guidance is injected into history
- LLM is prompted to proceed or take different action
Parse Failure Circuit Breaker¶
After 3 consecutive JSON parse failures:
- Raises immediately rather than consuming step budget
- Each failure injects corrective feedback
- Helps LLM self-correct its output format
Self-Correcting Error Recovery¶
Instead of failing on validation errors, helpful context is injected:
| Error | Response |
|---|---|
| Unknown tool | Lists available tools |
| Missing fields | Shows expected format |
| Type errors | Prompts to check schema |
| Agent not found | Provides error details |
Bounded Execution¶
Hard limit of MAX_INFER_STEPS (default: 10) prevents runaway loops.
Why This Matters¶
This design enables:
- Provider-agnostic execution: Same loop works with OpenAI, Anthropic, Ollama, etc.
- Observable behavior: Every action is logged and traceable
- Graceful degradation: Self-correction reduces failures
- Predictable resource usage: Bounded steps prevent infinite loops
The runtime transforms chaotic LLM outputs into reliable, deterministic agent behavior.
Design Principles¶
Protolink’s architecture is guided by a small number of explicit design principles.
These principles explain why the system looks the way it does and help contributors extend it coherently.
1. Intent Over Mechanism¶
Agents express what they want to do, never how it is done.
- Agents send tasks
- Agents receive tasks
- Agents discover peers
They do not:
- Open sockets
- Serialize payloads
- Know transport details
This allows:
- Clean agent logic
- Easier testing
- Transport substitution without rewrites
2. Directional Communication Is Explicit¶
Outgoing and incoming communication are separate concerns.
That is why Protolink has:
- AgentClient for outgoing requests
- AgentServer for incoming requests
This avoids:
- Bidirectional “god objects”
- Hidden side effects
- Transport leakage into agents
3. Transport Is a Boundary, Not a Feature¶
Transports are infrastructure.
They are:
- Swappable
- Replaceable
- Reusable
- Shared between client and server
Agents should never depend on:
- HTTP
- ASGI
- WebSockets
- Threads
- Event loops
This keeps the system future-proof.
4. Registry Mirrors Agent Architecture¶
The registry is not “special”.
It follows the same architectural rules as agents:
- Logical registry object
- Client for outgoing calls
- Server for incoming calls
- Transport underneath
This symmetry:
- Reduces cognitive load
- Improves maintainability
- Makes distributed registries natural
5. Minimal Boilerplate, Explicit Control¶
Protolink aims to reduce boilerplate without hiding control.
- Defaults are sensible
- Explicit overrides are possible
- No magic global state
- No hidden background threads
You always know:
- What is running
- What is registered
- What is communicating
Agent ↔ Agent Sequence Diagram¶
This section describes the runtime flow when one agent sends a task to another.
Scenario¶
Agent A wants to send a task to Agent B.
Both agents are already running and registered.
Sequence¶
- Agent A creates a
Task - Agent A calls
send_task_to(agent_b_url, task) AgentClientforwards the task to its transport- Transport sends the request to Agent B’s server endpoint
- Agent B’s transport receives the request
AgentServerinvokes Agent B’shandle_task- Agent B processes the task
- The result task is returned through the same path
- Agent A receives the completed task
Responsibility Breakdown¶
- Agent: defines what to do
- Client: defines direction
- Server: defines entry point
- Transport: defines mechanism
No layer violates its responsibility.
Registry Interaction Sequence¶
This section explains how discovery works at runtime.
Agent Startup¶
- Agent starts its server
- Agent creates a
RegistryClient - Agent registers its
AgentCard - Registry stores the agent metadata
- Optional heartbeat begins
Discovery¶
- Agent requests discovery via
RegistryClient - Registry applies filters
- Registry returns matching
AgentCards - Agent decides what to do next
The registry never pushes behavior to agents.
Comparison With Raw Google A2A¶
Protolink is inspired by Google’s A2A spec, but intentionally diverges in structure.
What Is Preserved¶
- Agent Cards
- Task-based communication
- Explicit discovery
- Stateless requests
- Protocol neutrality
What Is Improved¶
1. Central Agent Abstraction¶
In Protolink, the agent is the primary unit, not a loose collection of endpoints.
This:
- Improves composability
- Makes agents easier to reason about
- Encourages reusable agent logic
2. Explicit Client / Server Split¶
Google A2A often conflates:
- Sending
- Receiving
- Hosting
Protolink separates them cleanly, which:
- Improves testability
- Clarifies ownership
- Reduces hidden coupling
3. Registry as a First-Class Component¶
Instead of being an afterthought, the registry is:
- Structured
- Extensible
- Transport-agnostic
- Distributed-ready
4. Lower Boilerplate for Users¶
A typical Protolink agent requires:
- One subclass
- One handle_task method
- One transport
Everything else is handled by composition.
Mental Model Summary¶
If you remember only one thing:
Agents think. Clients talk. Servers listen. Transports move bytes. Registries coordinate.
Each layer is small, focused, and replaceable.
That is the entire philosophy.
Protolink vs Google A2A Concepts¶
Protolink is inspired by Google’s A2A (Agent-to-Agent) concepts, but adds practical layers and abstractions to make building autonomous agents easier and more maintainable.
| Concept | Google A2A | Protolink | Notes |
|---|---|---|---|
| Agent | Logical actor with tasks | Logical actor with tasks, tools, skills, and optional LLMs | In Protolink, agents can include AI capabilities, not just task orchestration. |
| Communication | Agent-to-Agent messages | AgentClient / AgentServer with pluggable transports | Explicit client/server layer reduces boilerplate and separates network logic from business logic. |
| Discovery | Registry / Service Directory | Registry, RegistryClient, RegistryServer | Symmetric design; agents never talk to the registry directly except through RegistryClient. |
| Task Handling | Internal message routing | handle_task logic inside the Agent |
Agents remain autonomous; external orchestration is optional. |
| Protocol | Implicit (HTTP, WS, etc.) | Transport layer handles protocol, serialization, runtime | Protocol-agnostic and swappable without touching agent logic. |
| Extensibility | Limited by A2A spec | Tools, LLMs, and custom skills | Agents can mix AI and deterministic tools seamlessly. |
| Boilerplate | Manual wiring, repetitive | Minimal; agent owns clients and servers, which own transports | Focuses on developer productivity and clarity. |
| Autonomy | Agents are actors, often invoked manually | Agents run autonomously, discover peers, schedule and execute tasks | Protolink pushes complexity down into infrastructure layers. |
Key Differences¶
- LLM Integration
- In Protolink, an agent can include LLMs as part of its tools/skills, enabling advanced AI behavior.
-
Google A2A does not define AI capabilities natively.
-
Explicit Client/Server Layer
- Protolink separates intent from transport, reducing boilerplate and making testing easier.
-
Google A2A mixes communication concerns with agent logic in some implementations.
-
Registry Symmetry
- Registry, RegistryClient, and RegistryServer mirror the agent architecture for consistency.
-
A2A often has ad-hoc discovery mechanisms.
-
Protocol-Agnostic Transport
- Protolink agents never handle HTTP, WS, or serialization directly.
-
All networking is delegated to
Transport. -
Extensibility with Tools and Skills
- Developers can define custom tools, attach LLMs, or integrate external APIs without touching transport logic.
Developer Takeaways¶
- Agents are rich actors: Tasks, tools, skills, LLMs
- Clients/Servers handle communication, not the agent
- Transport is pluggable, reusable, and protocol-agnostic
- Registry abstracts discovery and coordination
- Minimal boilerplate allows focusing on agent logic, not infrastructure
Info
By explicitly layering Client, Server, Transport, and Registry, Protolink provides a professional-grade framework for autonomous agent development, while keeping A2A concepts at its core.