refactor(agents): reduce protocol.py to the live AgentError
Post-coordination-removal sweep: the coordination wire protocol (AgentRequest, AgentResponse, DelegationIntent, CoordinationResult, DelegationReason, TaskComplexity, ToolCallRecord, AgentTimeoutError, AgentUnavailableError, DelegationError) had zero importers left in src/ - only its own test module. AgentError stays (raised by run_librarian, mapped to user-safe failures by delegation.py). Also drops the stale coordination.py line from the README tree. Import-cycle sanity: python -c 'import src.main' passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbFZyDvYksazX6nYQYZ67L
This commit is contained in:
+5
-192
@@ -1,183 +1,11 @@
|
||||
"""
|
||||
Agent communication protocol for multi-agent coordination.
|
||||
Agent error protocol.
|
||||
|
||||
Defines standardized request/response formats for communication between:
|
||||
- Steward (request analysis) → Tatlock (coordination)
|
||||
- Tatlock (coordination) → Expert agents (Librarian, Developer, etc.)
|
||||
Structured exceptions raised by expert agents (e.g. The Librarian) so
|
||||
callers - the delegation wrappers in src/agents/delegation.py - can
|
||||
report success=False and map failures to curated user-safe messages
|
||||
while exception detail stays in the logs.
|
||||
"""
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class DelegationReason(str, Enum):
|
||||
"""Why a task is being delegated to an expert agent."""
|
||||
DOMAIN_EXPERTISE = "domain_expertise" # Expert has specialized knowledge
|
||||
TOOL_ACCESS = "tool_access" # Expert has required tools
|
||||
RESOURCE_EFFICIENCY = "resource_efficiency" # Better handled by specialist
|
||||
USER_PREFERENCE = "user_preference" # User requested specific agent
|
||||
|
||||
|
||||
class TaskComplexity(str, Enum):
|
||||
"""Complexity estimate for task execution."""
|
||||
SIMPLE = "simple" # Single tool call, fast
|
||||
MODERATE = "moderate" # Multiple steps, moderate time
|
||||
COMPLEX = "complex" # Multi-agent, significant processing
|
||||
|
||||
|
||||
class AgentRequest(BaseModel):
|
||||
"""
|
||||
Request to an expert agent.
|
||||
|
||||
Contains everything the agent needs to execute a task,
|
||||
including context from the conversation and delegation intent.
|
||||
"""
|
||||
task: str = Field(
|
||||
...,
|
||||
description="Clear description of what the agent should do"
|
||||
)
|
||||
context: str = Field(
|
||||
default="",
|
||||
description="Relevant context from conversation history"
|
||||
)
|
||||
constraints: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Any constraints or requirements for the task"
|
||||
)
|
||||
delegation_reason: DelegationReason = Field(
|
||||
default=DelegationReason.DOMAIN_EXPERTISE,
|
||||
description="Why this task was delegated to this agent"
|
||||
)
|
||||
user_id: str = Field(
|
||||
default="default",
|
||||
description="User identifier for multi-tenant operations"
|
||||
)
|
||||
max_tokens: int | None = Field(
|
||||
default=None,
|
||||
description="Optional token limit for response"
|
||||
)
|
||||
timeout_seconds: int | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Maximum time for task completion; None uses the configured "
|
||||
"expert budget (LIBRARIAN_TIMEOUT)"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ToolCallRecord(BaseModel):
|
||||
"""Record of a tool call made during execution."""
|
||||
tool_name: str
|
||||
arguments: dict[str, Any]
|
||||
result: str
|
||||
duration_ms: int
|
||||
|
||||
|
||||
class AgentResponse(BaseModel):
|
||||
"""
|
||||
Response from an expert agent.
|
||||
|
||||
Contains the result, reasoning, and metadata about execution.
|
||||
"""
|
||||
success: bool = Field(
|
||||
...,
|
||||
description="Whether the task completed successfully"
|
||||
)
|
||||
result: str = Field(
|
||||
...,
|
||||
description="The main output/answer from the agent"
|
||||
)
|
||||
reasoning: str = Field(
|
||||
default="",
|
||||
description="Agent's reasoning process (for transparency)"
|
||||
)
|
||||
tool_calls: list[ToolCallRecord] = Field(
|
||||
default_factory=list,
|
||||
description="Tools called during execution"
|
||||
)
|
||||
confidence: float = Field(
|
||||
default=1.0,
|
||||
ge=0.0,
|
||||
le=1.0,
|
||||
description="Agent's confidence in the result (0.0-1.0)"
|
||||
)
|
||||
sources: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Sources or references used"
|
||||
)
|
||||
error_message: str | None = Field(
|
||||
default=None,
|
||||
description="Error details if success=False"
|
||||
)
|
||||
duration_ms: int = Field(
|
||||
default=0,
|
||||
description="Total execution time in milliseconds"
|
||||
)
|
||||
|
||||
|
||||
class DelegationIntent(BaseModel):
|
||||
"""
|
||||
Intent to delegate a task to an expert agent.
|
||||
|
||||
Created by Tatlock when deciding to delegate, based on
|
||||
Steward's recommendations.
|
||||
"""
|
||||
target_agent: str = Field(
|
||||
...,
|
||||
description="Name of the expert agent to delegate to"
|
||||
)
|
||||
task: str = Field(
|
||||
...,
|
||||
description="Task description for the agent"
|
||||
)
|
||||
reason: DelegationReason = Field(
|
||||
default=DelegationReason.DOMAIN_EXPERTISE,
|
||||
description="Why delegating to this agent"
|
||||
)
|
||||
expected_outcome: str = Field(
|
||||
default="",
|
||||
description="What we expect the agent to provide"
|
||||
)
|
||||
priority: int = Field(
|
||||
default=1,
|
||||
ge=1,
|
||||
le=10,
|
||||
description="Priority (1=highest, 10=lowest)"
|
||||
)
|
||||
depends_on: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Other delegation IDs this depends on (for sequencing)"
|
||||
)
|
||||
|
||||
|
||||
class CoordinationResult(BaseModel):
|
||||
"""
|
||||
Result of multi-agent coordination.
|
||||
|
||||
Aggregates results from multiple expert agents into
|
||||
a single coherent response.
|
||||
"""
|
||||
final_response: str = Field(
|
||||
...,
|
||||
description="Synthesized response from all agents"
|
||||
)
|
||||
agent_responses: dict[str, AgentResponse] = Field(
|
||||
default_factory=dict,
|
||||
description="Individual responses keyed by agent name"
|
||||
)
|
||||
delegation_intents: list[DelegationIntent] = Field(
|
||||
default_factory=list,
|
||||
description="All delegations that were executed"
|
||||
)
|
||||
total_duration_ms: int = Field(
|
||||
default=0,
|
||||
description="Total coordination time"
|
||||
)
|
||||
agents_consulted: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Names of agents that contributed"
|
||||
)
|
||||
|
||||
|
||||
class AgentError(Exception):
|
||||
@@ -187,18 +15,3 @@ class AgentError(Exception):
|
||||
self.message = message
|
||||
self.agent_name = agent_name
|
||||
super().__init__(f"[{agent_name}] {message}")
|
||||
|
||||
|
||||
class AgentTimeoutError(AgentError):
|
||||
"""Agent execution timed out."""
|
||||
pass
|
||||
|
||||
|
||||
class AgentUnavailableError(AgentError):
|
||||
"""Agent is not available or registered."""
|
||||
pass
|
||||
|
||||
|
||||
class DelegationError(AgentError):
|
||||
"""Error during task delegation."""
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user