# Shape 3 — agents inside a business process

> A business process is a workflow, and an agent's work is one of its steps: an ordinary awaited call that answers the skill's typed result. The process decides what happens when the step is done, fails, is stopped or runs out of money, and a person can take over. It adds `Osysharp.Processes`; the bridge that tells each agent step on the run arrives by itself.

<!-- id: agent-guide-process · area: agent · stability: preview · html: https://osysharp.com/reference/agent/guide-process/ -->

> **You are here:** [Building with agents](https://osysharp.com/reference/agent/guide/) › [Deciding what to build](https://osysharp.com/reference/agent/guide-deciding/) › Shape 3

## Summary        {#summary}
At the translation bureau every commission now goes through the same steps: it is prepared, then it is ready for a
translator. Preparing is the agent's job. If the agent cannot finish — the model is down, the money ran out,
somebody stopped it — the commission does not get stuck: the process sends it to the manager instead.

This is the third shape: **agents inside a business process.** The process is in charge. The agent does one step
of it.

![One run of a business process, told from start to now, with its agent steps](https://osysharp.com/media/ref/364e22-agents-processes-run-step-open-wide-light.png)

*One run of a supplier-qualification process, from a tool maker's app: where it stands, each state it was in, and
what each agent did there.*

## Description    {#description}

### What does it add?   {#takes}

| You add | It gives you |
|---|---|
| a [[workflow-index\|workflow]] | the process itself: its states, and what moves it from one to the next |
| [[agent-kit-processes\|Osysharp.Processes]] | every run listed, drawn and told in order, for somebody who was not there |
| [[agent-kit-agents-processes\|Osysharp.Agents.Processes]] | nothing to write: it arrives by itself, tells each agent step on the run, and charges the step to the run |
| [[agent-kit-agents-inspector\|Osysharp.Agents.Inspector]] | one piece of an agent's work on one page, opened from the run |

### What do you decide now?   {#decisions}

1. **Who a step works for.** In a process nobody is signed in. Said nothing, the agent works with its own roles
   alone. `Agents.Of<X>().For(person)` makes a step work for a person — the buyer who asked, say — and then it never
   does more than that person may. See [[agent-processes#for]].
2. **What the process does when a step does not finish.** The call answers an outcome, not an exception:
   `Done`, or not. Your workflow branches on it like on anything else. See [[agent-processes#failed]].
3. **What each run may spend.** `SetProcessBudget("CommissionFlow", 2.00m)` holds every run of that workflow to
   two dollars: its agent steps and its own model calls together. See [[agent-processes#cost]].
4. **Who follows processes.** The policy `FollowsProcesses` says who may open the list of runs and a run. Each
   thing on a run's page is then shown under the rule of the record it is read from: the money to whoever reads
   model spend, an agent's work to whoever may read that work. See [[workflow-processes-kit#who]].
5. **Who an approval inside a step goes to.** As in [shape 2](https://osysharp.com/reference/agent/guide-approvals/). When the step works for
   nobody, a proposal goes to the agent's [[agent-guide-glossary#sponsor|sponsor]]. The process waits, for days if
   it takes days.

### What do you get without writing it?   {#free}
- A step that waits on a person makes the whole process wait, and it goes on from the same point.
- Stopping the process calls off the work it was waiting for. See [[agent-processes#stopped]].
- Every agent step is a line on the run: given, waiting on a person, finished, failed, stopped.
- The receipt of each piece of work says which run, which workflow and which state gave it.
- What the run cost is what its agent steps cost. Nothing is counted twice.

### What does the whole app look like?   {#app}
What is new since [shape 1](https://osysharp.com/reference/agent/guide-assistant/) is marked `NEW`. Approvals work here exactly as in
[shape 2](https://osysharp.com/reference/agent/guide-approvals/); they are left out so the page stays short.

```osy title="preparing is an agent's step; when it cannot finish, the process hands the commission to the manager" run app=agent-guide-process
app Bureau {
  use Osysharp.Agents;
  use Osysharp.Agents.Testing;
  use Osysharp.Agents.Inspector;         // NEW
  use Osysharp.Agents.Processes;         // NEW: the bridge
  use Osysharp.Processes;                // NEW
  use Osysharp.Llm;
  use Osysharp.Identity;
  use Osysharp.Observability;
  use Osysharp.Workflow;
  use Osysharp.Diagrams;
  use Osysharp.Ui;
  model "model/**/*.osy";
}

using Osysharp.Identity;
using Osysharp.Llm;
using Osysharp.Workflow;
using Osysharp.Agents;
using Osysharp.Agents.Testing;
using Osysharp.Agents.Inspector;
using Osysharp.Processes;
using Osysharp.Agents.Processes;

app.Llm = new LlmSetup {
  Models = [ new LlmModel { Name = "Everyday", Client = new ScriptedModel { Name = "Everyday" }, Model = "scripted-everyday",
                            Price = new LlmPrice { Input = 1, Output = 5 }, MaxOutputTokens = 1000 } ],
  Default = "Everyday",
};
app.Agents = new AgentsSetup { Agents = [new Preparer()] };
app.Processes = new ProcessesSetup { Processes = [new CommissionHandling()] };      // NEW

[Role] enum BureauRole { Authenticator, Translator, Manager, Assistant }
partial entity RoleGrant { security { allow read when IsAuthenticated; } }
policy IsTranslator => RoleGrant.Any(g => g.Member == user && g.Role == BureauRole.Translator);
policy IsManager    => RoleGrant.Any(g => g.Member == user && g.Role == BureauRole.Manager);
policy IsAssistant  => RoleGrant.Any(g => g.Member == user && g.Role == BureauRole.Assistant);

policy UsesAgents        => IsTranslator || IsManager;
policy SupervisesAgents  => IsManager;
policy FollowsProcesses  => IsTranslator || IsManager;      // NEW — decision 4
policy ReadsModelUsage   => IsManager;
policy ManagesModelSpend => IsManager;

entity Person : Principal {
  security { allow read when IsAuthenticated || IsAuthenticator; allow create when IsAuthenticator; }
}

// NEW: where a commission stands.
enum CommissionStage { Preparing, Ready, [Label("With the manager")] WithTheManager }

entity Commission {
  [Required, MaxLength(200)] string Title;
  [MaxLength(40)] string Into = "";
  [MaxLength(400)] string Note = "";
  CommissionStage Stage = CommissionStage.Preparing;
  security { allow read, create, update when IsTranslator || IsManager || IsAssistant; }
}

public class Preparation {
  public int Terms;
  public string Summary = "";
}

[Action("Look up the house terms for a language", Query)]
public List<string> HouseTerms(string language) {
  return language == "Finnish" ? ["sopimus: agreement", "toimitus: delivery"] : [];
}

public class PrepareCommission : Skill<Commission, Preparation> {
  public override string Description => "Prepare one commission for its translator.";
  public override List<ActionInfo> Tools => [Actions.Of(HouseTerms)];
  public override string Brief(Commission c) => $"{c.Title}, into {c.Into}. Its id is {c.Id}.";
}

[Actor(Roles = [BureauRole.Assistant])]
public class Preparer : Agent {
  public static Person Principal => new Person { Email = "preparer@bureau.example", DisplayName = "Preparer" };
  public override string Purpose => "You prepare commissions for the translators of a small bureau.";
  public override List<SkillBase> Skills => [new PrepareCommission()];
}

/// NEW — the process. Its agent step is an ordinary call: typed in, typed out, and the outcome decides.
workflow CommissionFlow {
  Tracks = Commission.Stage; Autostart = false; Initial = Preparing;
  state Preparing {
    enter {
      var prepared = await Agents.Of<Preparer>().Do(new PrepareCommission(), this.Item, about: "Preparing " + this.Item.Title);
      if (!prepared.Done) {
        // Decision 2: the step did not finish. The process does not get stuck: a person takes it.
        Processes.Problem("The preparer could not finish, so the manager prepares this one.");
        goto WithTheManager;
      }
      this.Item.Note = prepared.Result.Summary;
      goto Ready;
    }
  }
  terminal success Ready { }
  terminal cancel  WithTheManager { Message = "handed to the manager"; }
}

/// NEW — says which workflow is a business process, so its runs are listed and told.
public class CommissionHandling : Process<Commission> {
  public override string Name => "Commission handling";
  public override string Workflow => "CommissionFlow";
  public override WorkflowGraph Graph => CommissionFlow.Graph;
  public override string TitleOf(Commission c) => c.Title;
  public override List<WorkflowAuditEntry> HistoryOf(Commission c) => CommissionFlow.For(c).Audit;
}

Commission TakeIn(string title) {
  var commission = new Commission { Title = title, Into = "Finnish" };
  UnitOfWork.Commit();
  Workflow.Run(commission);
  return commission;
}

[TestFixture]
void Office() {
  var otto = new Person { Email = "otto@bureau.example", DisplayName = "Otto Reyes" };
  new RoleGrant { Member = otto, Role = BureauRole.Manager };
  new AgentSeat { Agent = "Preparer", Sponsor = otto };
}
principal Otto => Person.Single(p => p.Email == "otto@bureau.example");

[Test(Office)]
void The_agents_step_moves_the_process_on_and_is_told_on_its_run() {
  // The preparer, scripted: it looks the house terms up and hands back what it found.
  ScriptedAnswer.Stub((model, request) => {
    if (request.Messages.Count(m => m.Role == "assistant") == 0) { return Says.Tool(Actions.Of(HouseTerms), "{\"language\":\"Finnish\"}", 300, 20); }
    return Says.HandBack(new Preparation { Terms = 2, Summary = "Two house terms apply." }, 400, 40);
  });
  runas (Otto) {
    var commission = TakeIn("Supply agreement");
    Workflow.Settle(commission);
    Assert.Equal(CommissionStage.Ready, Commission.Single().Stage);          // the typed result decided
    Assert.Equal("Two house terms apply.", Commission.Single().Note);

    // The receipt says which run of which workflow gave it the work, and in which state.
    var step = AgentTask.Single();
    Assert.Equal("CommissionFlow", step.RunWorkflow);
    Assert.Equal("Preparing", step.RunState);

    // The run tells the step, in the state that gave it — and costs what the step cost.
    var run = ProcessRunOf(ProcessRunAbout("CommissionFlow", commission.Id)!.Id)!;
    Assert.Equal("Supply agreement", run.Card.Title);
    Assert.Equal(ProcessStatus.Finished, run.Card.Status);
    Assert.Equal(["Preparer was given work: prepare commission.", "Preparer finished: prepare commission."], run.Stays[0].Moments.Select(m => m.Said).ToList());
    Assert.Equal(step.SpentMicros / 1000000m, run.Cost.Spent);
  }
}

[Test(Office)]
void A_limit_for_each_run_stops_the_step_and_a_person_takes_over() {
  // The preparer, scripted: it looks the house terms up and hands back what it found.
  ScriptedAnswer.Stub((model, request) => {
    if (request.Messages.Count(m => m.Role == "assistant") == 0) { return Says.Tool(Actions.Of(HouseTerms), "{\"language\":\"Finnish\"}", 300, 20); }
    return Says.HandBack(new Preparation { Terms = 2, Summary = "Two house terms apply." }, 400, 40);
  });
  runas (Otto) {
    // Decision 3: each run may spend a tenth of a cent. The preparer's first call would hold more: nothing is sent.
    SetProcessBudget("CommissionFlow", 0.001m);
    UnitOfWork.Commit();
    var commission = TakeIn("Supply agreement");
    Workflow.Settle(commission);

    Assert.Equal(CommissionStage.WithTheManager, Commission.Single().Stage);
    Assert.Equal(AgentTaskStatus.Stopped, AgentTask.Single().Status);
    var run = ProcessRunOf(ProcessRunAbout("CommissionFlow", commission.Id)!.Id)!;
    Assert.Equal("The preparer could not finish, so the manager prepares this one.", run.Stays[0].Moments.Last().Said);
  }
}
```

### What would make this the wrong shape?   {#not-this}
- There are no fixed steps: somebody owns an outcome and tries things until it holds. That is an
  [[agent-guide-glossary#assignment|assignment]], not a process. See [[agent-guide-choosing#assignment-or-process]].
- There is only one step. That is [shape 1](https://osysharp.com/reference/agent/guide-assistant/) or [shape 2](https://osysharp.com/reference/agent/guide-approvals/).

## What to read next   {#read-next}
- [Shape 4 — a team of agents with a coordinator](https://osysharp.com/reference/agent/guide-team/) — the last shape: one agent in charge hands pieces to specialists
- [A supplier is qualified — five days, four agents, one that failed, and a three-day wait](https://osysharp.com/reference/agent/story-supplier/) — a real process of this shape, told day by day
- [One piece of work, followed end to end](https://osysharp.com/reference/agent/guide-work-end-to-end/) — one piece of work followed through every kit

## Go deeper   {#see-also}
- [Agents in business processes — an agent's work as a step of a workflow, told on the run (Agents.Processes kit)](https://osysharp.com/reference/agent/processes/) — an agent's work as a step of a workflow
- [Processes — every run of a business process, listed and read from start to now (Processes kit)](https://osysharp.com/reference/workflow/processes-kit/) — the Processes kit: runs, the picture, the telling
- [The inspector — one piece of an agent's work, readable by a person who was not there (Agents.Inspector kit)](https://osysharp.com/reference/agent/inspector/) — one piece of work on one page
- [Workflows (the run that outlives the request)](https://osysharp.com/reference/workflow/index/) — workflows: states, events, waiting
- [[agent-testing#process]] — testing a whole process without a model
