You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AgentLoom can compose LLM calls, tool steps, routers, and subworkflows into a DAG, but it has no first-class concept of an Agent: a unit that owns (system prompt + tool registry subset + memory + decision loop policy) and can be invoked as a single step. Building an agent today requires:
Manual wiring of system prompt, tool subset, max iterations, exit conditions per workflow.
This is mechanically possible but ergonomically painful, and it leaks the agent's internals across the YAML. It also makes Agents non-portable: there is no way to publish "the customer-support agent" as a reusable artifact and import it into another workflow.
The PhD's Simulator depends on multi-agent orchestration (single-agent-vs-user, cooperation, negotiation, A2A — see agenttest-planteamiento.md). Each "agent" in those scenarios is a coherent unit that needs to be defined once and reused across many simulation runs with different counterparties. Without an Agent primitive, the Simulator becomes a YAML factory.
Proposal
Introduce an Agent definition as a high-level construct that compiles down to existing primitives (llm_call with tools + conversation + bounded loop), and a step type that invokes it.
1. Agent definition (YAML):
# agents/customer_support.yamlname: customer_supportdescription: "Resolves billing and account questions."model: claude-sonnet-4-5system_prompt: | You are a customer support agent for ACME Corp. Use tools to look up account info before answering.tools:
- lookup_account
- search_knowledge_base
- create_ticketmemory:
type: conversationtoken_budget: 8000trim_policy: summarize_oldestloop_policy:
max_iterations: 10exit_condition: "no_tool_call"# or expressionon_max_iterations: error # or "warn_and_return"output_schema:
type: pydanticmodel: examples.schemas.SupportReplyconstraints:
max_cost_usd: 0.50max_latency_ms: 30000forbidden_patterns: ["I'm just an AI", "As a language model"]
2. Agent step type:
- id: handle_customertype: agentagent: agents/customer_support.yaml # path or inlineinput: state.user_message # user turn appended to conversationconversation: state.support_chat # optional; auto-creates if absentoutput: state.support_chat # the agent's reply (and updated conversation)
Enforces constraints — fails the step if violated.
Persists the updated conversation.
3. Tool-registry scoping:
The agent's tools: [lookup_account, ...] declares which tools it may call. The engine creates a scoped ToolRegistry view that only exposes those names — even if the parent registry has more tools, the agent can't reach them. Defense-in-depth against prompt injection making the agent call an unintended tool.
4. Programmatic API:
fromagentloomimportAgent, ProviderGatewayagent=Agent.from_yaml("agents/customer_support.yaml", gateway=gateway, tool_registry=registry)
result=awaitagent.invoke(
user_message="My bill seems wrong",
conversation=existing_conversation, # optional
)
# result.reply: SupportReply (typed per output_schema)# result.conversation: Conversation (with new turns)# result.cost_usd: float# result.iterations: int
Without these prereqs the Agent step has no underlying mechanics — list as blocker dependencies.
Backward compat: nothing existing breaks; this is purely additive.
Agents become reusable across workflows and across repos: a published agent definition + its tool dependencies is the canonical way to share LLM behavior.
This is the cleanest answer to the Simulator's needs in agenttest-planteamiento.md: each simulated participant is an Agent; the Simulator orchestrates Agent-to-Agent message passing.
Description
AgentLoom can compose LLM calls, tool steps, routers, and subworkflows into a DAG, but it has no first-class concept of an Agent: a unit that owns (system prompt + tool registry subset + memory + decision loop policy) and can be invoked as a single step. Building an agent today requires:
Conversationin state (add conversation history primitive with token-budget trimming and multi-agent threads #119).llm_call(add native tool/function calling with streaming and parallel-call support #116).loopstep (add bounded retry loops with feedback #38, open) wrapping the LLM call to iterate until no tool call.This is mechanically possible but ergonomically painful, and it leaks the agent's internals across the YAML. It also makes Agents non-portable: there is no way to publish "the customer-support agent" as a reusable artifact and import it into another workflow.
The PhD's Simulator depends on multi-agent orchestration (single-agent-vs-user, cooperation, negotiation, A2A — see
agenttest-planteamiento.md). Each "agent" in those scenarios is a coherent unit that needs to be defined once and reused across many simulation runs with different counterparties. Without an Agent primitive, the Simulator becomes a YAML factory.Proposal
Introduce an
Agentdefinition as a high-level construct that compiles down to existing primitives (llm_callwithtools+conversation+ bounded loop), and a step type that invokes it.1. Agent definition (YAML):
2. Agent step type:
The step:
Conversation.output_schema(per add structured output (JSON schema, response_format) across providers #117) if specified.constraints— fails the step if violated.3. Tool-registry scoping:
The agent's
tools: [lookup_account, ...]declares which tools it may call. The engine creates a scopedToolRegistryview that only exposes those names — even if the parent registry has more tools, the agent can't reach them. Defense-in-depth against prompt injection making the agent call an unintended tool.4. Programmatic API:
5. Agent introspection:
6. Composition:
Agents can be nested via subworkflow: an agent's tool can itself be another agent, enabling supervisor / worker patterns:
(Implementation: tool registry accepts
Agentinstances as tools whoseparameters_schemais derived from the agent'sinputschema.)7. Observability:
agent:<name>wrapping the step, with attributes:agent.name,agent.iterations,agent.tool_calls_count,agent.tokens_total,agent.cost_usd,agent.output_valid.agentloom_agent_invocations_total{name, status}.agentloom_agent_iterations{name}.Scope
src/agentloom/core/models.py—AgentDefinition,LoopPolicy,MemoryConfig,Constraints.src/agentloom/core/agent.py—Agentclass withinvoke()method.src/agentloom/steps/agent.py— new step typeagent.src/agentloom/cli/agent.py—agentloom agent inspectcommand.src/agentloom/observability/observer.py— agent-level hooks.examples/— at least one full agent definition + workflow that uses it.Regression tests
test_agent_loads_from_yamltest_agent_invoke_runs_tool_call_looptest_agent_max_iterations_enforcedtest_agent_tool_registry_scoping_blocks_other_toolstest_agent_output_schema_validates_replytest_agent_constraint_max_cost_aborts_when_exceededtest_agent_forbidden_patterns_marks_step_failedtest_agent_step_in_workflow_persists_conversationtest_nested_agent_as_toolNotes
agenttest-planteamiento.md: each simulated participant is an Agent; the Simulator orchestrates Agent-to-Agent message passing.