# Core Concepts

Documentation version: Edge

Canonical HTML: https://littleghostai.org/docs/core_concepts.html

Define an Agent in a Ruby class, then call it with `.ask`.

```ruby
class CustomerSupportAgent < LittleGhost::Agent
  model "openrouter:openai/gpt-5.6-luna"
  system_prompt "Answer customer questions clearly."
  tools HelpCenterLookupTool
end

run = CustomerSupportAgent.ask("Where is my order?")
run.response
```

From there, add only what the work needs. Give the agent a tool. Let it ask a specialist for help. Or coordinate several agents while the rest of your application keeps making the same call.

## An Agent owns one model loop

An **Agent** defines one model-driven behavior. It chooses the model, supplies the instructions and tools, and carries one request through to an answer.

The class holds the behavior you want to reuse. Each call brings its own input, history, context, settings, and attachments. Request data never needs to live on the class.

```text
CustomerSupportAgent
├── model selection
├── system prompt
├── HelpCenterLookupTool
└── limits and optional capabilities
```

An Agent can return text or checked, structured data. You can add streaming,
saved conversations, or callbacks later. None of them are required to begin.

## A Tool connects the model to Ruby

A **Tool** is one focused thing an agent can ask your application to do. It has a name, a description, an input schema, and the Ruby code that does the work.

```ruby
class HelpCenterLookupTool < LittleGhost::Tool
  description "Look up a help center entry by topic."
  input_schema(
    type: "object",
    properties: {topic: {type: "string"}},
    required: ["topic"],
    additionalProperties: false
  )

  def call(input)
    {"refunds" => "Refunds are available within 30 days."}
      .fetch(input.fetch("topic"))
  end
end
```

LittleGhost checks the model's arguments, calls the Tool, and gives the result
back to the model. The schema checks shape, not permission. Check permission
inside the Tool using identity and account information from your application.

[Tools](tools.md) follows that path from model input to application code,
including run-scoped bindings, concurrency, retries, and sandbox delegation.

## A Run owns one top-level execution

Every `.ask` or `.stream_ask` creates a **Run**. Think of it as the record of one trip through LittleGhost. It opens what the request needs, records how the work ended, and closes the resources it owns.

```ruby
run = CustomerSupportAgent.ask("Where is order 481?")

run.completed? # => true
run.response
# One possible response: Order 481 is out for delivery.
run.usage      # => normalized token usage
run.result     # => the complete LittleGhost::RunResult
```

The Agent defines reusable behavior; the Run records what happened this time.

### Follow one request

One Run owns the trip from request to result:

```text
Run
├── Invocation: caller input, history, and application context
├── RunContext: mutable working state for this execution
└── Agent and Tools ──> RunResult
```

An **Invocation** is the request in LittleGhost's standard shape. Its `context`
contains current request values supplied by your application. A Tool can read
those values through `run.invocation.context` when it checks permission.

A **Session** stores conversation state between Runs when persistence is
configured. The **RunContext** carries mutable working state in `context.state`
during one Run. LittleGhost loads saved Session state before adding the current
Invocation context. Recheck saved values before using them for permission
decisions.

A Tool's **Binding** gives the Tool access to objects created for this run,
including the Agent, Run, Workspace, and Sandbox. These objects are separate
from arguments chosen by the model. [Tools](tools.md) explains the binding;
[Workspaces and Sandboxes](sandboxing.md) explains delegated files and child
processes.

The final **RunResult** keeps the complete assembly result. Its `text` is the final text answer. Its `output` returns structured data when the Agent declared a result schema, and text otherwise. The top-level `Run#response` is always the caller-facing text.

### See how a call ended

Top-level calls normally return a Run, even when execution fails. The terminal event carries the same outcome when you stream:

| What happened | Run outcome | Terminal event | What Ruby does |
| --- | --- | --- | --- |
| The assembly completed | `completed` | `:run_stop` | Returns the Run |
| Model, provider, or assembly execution failed | `failed` | `:run_error` | Returns the Run; inspect `run.error` |
| The deadline stopped work | `partial` | `:run_partial` | Returns the Run with any response produced so far |
| Cancellation stopped work | `cancelled` | `:run_cancel` | Returns the Run without a response |
| Tool input or a `ToolError` failed | The model may recover | No terminal event by itself | Gives a safe error result back to the model |
| Input, configuration, or resources failed before a Run could start | No Run exists | None | Raises the exception |

Unexpected Tool exception messages are hidden from the model. The original
exception remains available to application callbacks and diagnostics.

Failures while closing resources, delivering events, or reporting
instrumentation sit outside the normal result path. They raise a Ruby exception
because LittleGhost can no longer promise that it delivered a clean ending.
[Running in Production](production.md) covers supervision and shutdown.

## An Assembly can look like one Agent

One model loop is not always enough. LittleGhost calls any unit that a caller can invoke like an Agent an **Assembly**.

An Agent is the smallest Assembly. Workflow, Swarm, and Graph coordinate several participants while preserving the same entrypoints:

```ruby
CustomerSupportAgent.ask(question)
ResponseWorkflow.ask(question)
ProblemSolverSwarm.ask(question)
SupportFlowGraph.ask(question)
```

That shared calling style is what makes composition feel natural. A controller, job, or CLI does not need to know whether one Agent answered or a whole support process worked together.

## Choose who controls the next step

The coordination types differ mainly in who decides what happens next:

| Need | Choose | Who controls the next step? |
| --- | --- | --- |
| One model-driven behavior | Agent | The active model loop |
| A model should delegate a named task | Subagent | The parent model |
| Ruby should enforce ordering or branching | Workflow | The workflow's Ruby code |
| Specialists should choose permitted handoffs | Swarm | The active agent |
| Allowed routes should be visible in advance | Graph | Declared nodes and edges |

### Subagents bring in a specialist

A **subagent** is a specialist that a parent Agent can call for help. The parent model chooses when to delegate, reads the result, and then continues its own answer.

```ruby
class CustomerSupportAgent < LittleGhost::Agent
  model "openrouter:openai/gpt-5.6-luna"
  subagent ResearchAgent, kind: "research"
end
```

Use a subagent when delegation is part of one model's decision-making. Use a Workflow when application code must guarantee that a step happens.

When an Agent also uses code mode, subagent controls stay in the Agent's
conversation. Code-mode programs can compose ordinary Tools, while spawning,
messaging, and checking on subagents remain decisions for the parent model.

### Workflows make Ruby the coordinator

A **Workflow** coordinates work with ordinary Ruby. Its `perform` method can call an Agent or another Assembly, read a result, choose a branch, or run independent steps together.

`invoke` prepares a lazy child call. Reading `.output` runs an intermediate child. Return the final `invoke` itself, without reading its output, so that answer can stream to the caller.

```ruby
class ResponseWorkflow < LittleGhost::Workflow
  private

  def perform
    research = invoke(ResearchAgent).output
    invoke CustomerSupportAgent, input: <<~PROMPT
      #{input.text}

      Research:
      #{research}
    PROMPT
  end
end
```

Workflow children receive the caller's history and application context by default. Pass `history: []`, `context: {}`, or redacted values when a participant should receive less.

### Swarms let agents hand work to one another

A **Swarm** is a group of Agents that can hand work to one another. One member is active at a time. It can answer the caller or choose one of its allowed specialists.

```ruby
class ProblemSolverSwarm < LittleGhost::Swarm
  member TriageAgent
  member BillingAgent
  member AccountAgent

  start TriageAgent
  handoff TriageAgent, to: [BillingAgent, AccountAgent]
end
```

A Swarm is intentionally agent-to-agent. Its members are Agents, not other
kinds of Assembly. Caller history and application context stay hidden unless a
member opts in. A handoff message comes from another model, so a receiving
Agent should use it as context rather than proof that an action is permitted.

### Graphs make routes visible

A **Graph** connects named Assembly nodes with declared edges. Nodes can contain Agents, Workflows, Swarms, or other Graphs.

```ruby
class SupportFlowGraph < LittleGhost::Graph
  node :triage, TriageAgent
  node :billing, BillingAgent
  node :general, CustomerSupportAgent
  node :respond, CustomerSupportAgent

  start :triage
  edge :triage, :billing do |state|
    state.result(:triage).output == "billing"
  end
  edge :triage, :general
  edge :billing, :respond
  edge :general, :respond
  finish :respond
end
```

Graph nodes receive the original task and results from the nodes immediately
before them. They do not receive caller history or application context unless
their declarations opt in. [Compose Agents](assemblies.md) explains parallel
routes, joins, input mapping, and data boundaries.

## One result, even when several agents help

Every assembly produces the same top-level `Run` and final `RunResult`. Composite assemblies also keep a size-limited record of the participants that ran:

```ruby
run = SupportFlowGraph.ask("Why was I charged twice?")

run.response
run.result.steps
run.result.trajectory.transitions
```

This record shows which participants ran. [Compose Agents](assemblies.md)
explains builders, detailed routing records, and live events from nested Agents.

The pieces now fit together: Agents define behavior. Tools connect them to Ruby. Runs record one execution. Assemblies let the system grow without changing the caller.

Continue with [Models and Providers](models_and_providers.md) to choose model
targets and configure provider connections. When you need several agents to
work together, [Compose Agents](assemblies.md) builds on the same concepts.
