Skip to content

Agent

Agent is the framework’s general-purpose implementation of AgentProtocol: one LLMClient calls the model, runs tool calls through a ToolProvider, and streams AgentEvents back to the caller. Storage is the orchestrator’s concern — Agent takes history in and streams events out.

Generated from name and description using Agent.defaultSystemPrompt(name:description:):

public init(
name: String,
description: String = "",
model: any LLMClient,
tools: (any ToolProvider)? = nil,
ui: UIPolicy = .forward,
maxToolRounds: Int = 20,
saveChat: Bool = true
)
public init(
name: String,
description: String = "",
model: any LLMClient,
tools: (any ToolProvider)? = nil,
systemPrompt: String?, // your own string, or nil for no system message
ui: UIPolicy = .forward,
maxToolRounds: Int = 20,
saveChat: Bool = true
)

Pass systemPrompt: nil to send no system message at all. This initializer never falls back to the default prompt.

ParameterDefaultNotes
nameHuman-readable label; also drives the default system prompt and id.
description""Included in the default system prompt and the classifier’s agent list.
modelAny LLM client.
toolsnilAny ToolProvider: native Swift tools, MCP, or a mix.
systemPrompt(generated)Override the auto-generated prompt or suppress it entirely with nil.
ui.forward.forward emits .widget events; .suppress folds UI data into text.
maxToolRounds20Cap on model calls per turn when tools are present. See below.
saveChattrueWhen false, the orchestrator does not persist this agent’s turns.
public enum UIPolicy: Sendable {
case forward // emit .widget events (default)
case suppress // fold tool data into text; no .widget emitted
}

See UI for how UIPayload is declared and consumed downstream.

maxToolRounds caps model calls per turn. The effective cap is computed as:

public var maxToolRounds: Int { tools == nil ? 1 : toolRoundCap }

With no tools the cap is always 1, regardless of what was passed at init. A turn that hits the cap leaves remaining tool requests un-run and may emit an empty .final.

When you use the no-systemPrompt initializer, Agent inserts:

public static func defaultSystemPrompt(name: String, description: String) -> String

The generated prompt instructs the model to engage in open-ended multi-turn conversation using the agent’s name and description. Supply your own systemPrompt: whenever you need task-specific instructions.

import AgentSquad
let agent = Agent(
name: "Support Agent",
description: "Handles customer support questions.",
model: myLLMClient,
tools: myToolProvider
)
let context = AgentContext(userId: "u1", sessionId: "s1")
let stream = agent.process(.text("What is my order status?"), history: [], context: context)
for try await event in stream {
switch event {
case .textDelta(let chunk): print(chunk, terminator: "")
case .final(let msg): print("\nDone: \(msg)")
default: break
}
}
  • Agents overviewAgentProtocol, AgentInput, AgentContext, and AgentEvent.
  • GroundedAgent — the two-LLM anti-hallucination variant.
  • Custom agents — roll your own AgentProtocol conformance.
  • Tools — building a ToolProvider.
  • LLM clients — available LLMClient implementations.
  • UIUIPayload and .widget event consumption.
  • Tracing — spans and AgentContext.