Skip to main content

made_core/ports/
agent.rs

1//! [`AgentPort`] — provider-agnostic interface for deliberation agents.
2//!
3//! MADE does not know or care whether an agent is backed
4//! by a local vLLM server, an Anthropic or OpenAI API, a deterministic
5//! rule engine, or a human in the loop. Every agent implementation
6//! lives behind this trait; no provider is privileged.
7//!
8//! The three methods mirror the peer-deliberation algorithm in the
9//! reference implementation: an agent can **propose**, **critique** a
10//! peer's proposal, and **revise** its own proposal given feedback.
11
12use async_trait::async_trait;
13
14use crate::entities::{ExternalContextBundle, TaskConstraints};
15use crate::error::DomainError;
16use crate::value_objects::{AgentId, Specialty, TaskDescription};
17
18/// Input for a fresh proposal.
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct DraftRequest {
21    pub task: TaskDescription,
22    pub constraints: TaskConstraints,
23    pub diverse: bool,
24    pub external_context: Option<ExternalContextBundle>,
25}
26
27/// A critique is a piece of free-form feedback targeting a peer's
28/// proposal content.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Critique {
31    pub feedback: String,
32}
33
34/// A revised proposal content. The caller wraps the new content into
35/// a [`crate::entities::Proposal`] with identity preserved.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Revision {
38    pub content: String,
39}
40
41#[async_trait]
42pub trait AgentPort: std::fmt::Debug + Send + Sync {
43    /// Stable identity of this agent. Used for attribution and logging.
44    fn id(&self) -> &AgentId;
45
46    /// Specialty the agent claims expertise in. Must match the council
47    /// specialty at registration time.
48    fn specialty(&self) -> &Specialty;
49
50    /// Produce an initial proposal for a task.
51    async fn generate(&self, request: DraftRequest) -> Result<Revision, DomainError>;
52
53    /// Produce a critique of a peer's proposal content.
54    async fn critique(
55        &self,
56        peer_content: &str,
57        constraints: &TaskConstraints,
58    ) -> Result<Critique, DomainError>;
59
60    /// Produce a revised content given a peer's critique.
61    async fn revise(&self, own_content: &str, critique: &Critique)
62        -> Result<Revision, DomainError>;
63}