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}