Osy#betaRun it on Osyrin: osyrin.com

Reference / Agent

The six decisions — what you say about an agent, and what holds if you say nothing

Every app with an agent in it decides six things: who the agent acts as and for whom; what it may do alone, proposes, and never does; what it costs and who pays; who answers its questions and where; who supervises, teaches and audits; what is kept and what is erased. Each has a default that is the careful one, so an app that says nothing gets an agent that only looks things up, for the person who asked, within fifty cents.

preview1 example compiled by CIagentsguide

You are here: Building with agents › Deciding what to build › The six decisions

Summary#

Read this page once before you write an agent, and again when you review one. Each decision is a question, the answer you get if you say nothing, and the line you write to say otherwise.

#The questionIf you say nothingYou say otherwise with
1Who does it act as, and for whom?its own account, for whoever is signed in[Actor(Roles = […])], .For(person)
2What may it do alone?only what reads; everything else is proposedMay, a skill's DecidedBy
3What may it spend?fifty cents for one piece of workTaskBudget, Limits, Budget, SetBudget
4Who answers its questions?it asks nobodyPeople, Consults
5Who is in charge of it?nobody: it does not run until somebody answers for itthe policies, AgentSeat
6What is kept, and for how long?what it said for a month, that it happened for seventhe Privacy kits

Description#

1. Who does it act as, and for whom?#

[Actor(Roles = [OfficeRole.Vetting])]            // the account it acts as holds exactly these roles
public class Vetter : Agent { … }

await Agents.Of<Vetter>().Do(skill, input);                    // for whoever is signed in
await Agents.Of<Vetter>().For(buyer).Do(skill, input);         // for somebody else — a process step, a batch
  • The agent is an account of the app's, with the roles its class names and no others.
  • Working for a person, it reads what both may read and changes what both may change. It can never do for somebody what that person may not do themselves.
  • In a process, where nobody is signed in, it works with its own roles alone, unless the step says .For(person).
  • Giving work for somebody else while signed in yourself needs the policy DirectsAgentsForOthers.

More: [[agent-agents#rights]], [[agent-ask#for]], [Actor] — a principal that code may run as.

2. What may it do alone, what does it propose, what does it never do?#

public override Permits May => new Permits {
  Alone = [Actions.Of(NoteRisk)],                 // by itself
  ProposeEveryTime = [Actions.Of(ApproveSupplier)],   // a person, every time: no standing rule
  Never = [Actions.Of(DeleteSupplier)],           // not offered to the model at all
};
  • Said nothing, a tool that only reads runs alone, and every tool that changes, sends or spends is proposed.
  • What a tool does is what the compiler worked out from its body, so nothing is declared safer than it is. See [Action] — what an app offers to be done, by name.
  • Permits.ChangesAlone() is the common middle: the app's own data alone; anything that leaves the app proposed.
  • A proposal goes to the person the work is for. A skill's DecidedBy sends it to a queue or to one person instead, and then the person the work is for cannot approve it.
  • "From now on, do this alone" is a [[agent-guide-glossary#standing-rule|standing rule]]. Only whoever WidensAgentAutonomy admits may give one, and it never covers a tool the agent never runs.

More: What an agent does alone, what it proposes, what it never does — the gate and proposals (Agents kit), Guards and trying again — the app's own checks on an agent's work, and what happens when work fails (Agents kit) for the app's own checks. Choosing between the three: [[agent-guide-choosing#alone-or-propose]].

3. What does it cost, and who pays?#

app.Agents = new AgentsSetup { Agents = […], TaskBudget = 0.25m };          // every piece of work, at most
public override AgentLimits Limits => new AgentLimits { PerTask = 0.10m, Daily = 5 };   // this agent
public override decimal? Budget => 0.02m;                                    // this skill
SetProcessBudget("Qualification", 2.00m);                                    // each run of this process
  • Every piece of work has a hard limit: the skill's Budget, else the agent's Limits.PerTask, else the app's TaskBudget. Room is reserved before each model call leaves.
  • Work whose money runs out is stopped, and keeps what it did.
  • A day's limit for an agent, a limit for each run of a process, a limit for an assignment and a limit for anything of your own (.On("customer:acme", 10)) bind the same calls.
  • Nobody reads the meter or sets a budget until the app says who: ReadsModelUsage, ManagesModelSpend.

More: What an agent may spend — a hard limit on every piece of work (Agents kit), Model spend — budgets, the meter and the price book (Llm kit), [[agent-processes#cost]].

4. Who answers its questions, and where?#

public override AsksPeople People => new AsksPeople { MayChoose = true, About = "A payment term over 30 days." };
public override string Consults => "Careful";            // a stronger model, asked first
  • An agent asks a person only when its class says it may.
  • It asks the person the work is for; its [[agent-guide-glossary#sponsor|sponsor]] when the work is for nobody.
  • The work stops where it stands and costs nothing while it waits. It goes on from the same point when the answer comes, a minute or a week later.
  • With HumanRequests in the app, questions and proposals are requests in that person's inbox, with a deadline and reminders. Without it, your app shows them wherever it likes.
  • Before a person, an agent can consult a more capable model, charged to the same piece of work.

More: An agent asks a person, and waits — the person rung of the ladder of help (Agents kit), Asking for help — an agent that consults a more capable model (Agents kit), An agent's proposals and questions in a person's inbox, and an inbox item in an agent's hands (Agents.HumanRequests kit).

5. Who supervises, who teaches, who audits?#

policy UsesAgents       => IsOffice;          // gives agents work, reads the work they asked for
policy SupervisesAgents => IsLead;            // sponsors, pauses, calls work off, decides others' proposals
policy AuditsAgents     => IsRiskOfficer;     // reads everything, with what it said
policy TeachesAgents    => IsRiskOfficer;     // makes an instruction active, retires one, rolls back
new AgentSeat { Agent = "Vetter", Sponsor = lead };      // somebody answers for it
  • Each of these is nobody's until the app answers it. An agent with no sponsor does not run.
  • Supervising brings four things with it — naming a sponsor, pausing, reading every receipt, deciding other people's proposals — and each can be handed to somebody else in one line.
  • A supervisor reads that each step happened. Only an auditor, a teacher, and the people the work was for or given by read what it said.
  • A person corrects a piece of work; an instruction drawn from corrections reaches the agent only after a person who teaches has made it active. The agent cannot activate its own.

More: Who is in charge of an app's agents — every policy in one table, pausing, sponsoring, and trying work again has every policy in one table. Correcting an agent, and what it is told from then on — corrections, instructions, instruction sets and rollback (Agents kit), The management screens — where a person runs an app's agents (Agents.Management kit).

6. What is kept, and what is erased?#

MarkAgentWorkAbout(outcome.Task, new DataSubject { Emails = [loan.BorrowerEmail] });    // whose data it is
  • Every piece of work leaves a receipt and a record. The record is chained, so a changed line is found.
  • With Privacy in the app, what the work said is kept a month and that it happened seven months, by default. The tenant can change both.
  • A person can ask for their copy, or to be erased. After an erasure the record still checks.
  • Work done for a signed-in person is found by itself. Work about somebody needs the one line above.

More: The receipt and the record — what every piece of an agent's work leaves (Agents kit), Privacy for the agent family — agents, the model meter, assignments and work repositories under a person's request. When to add each Privacy kit: [[agent-guide-choosing#privacy]].

What does an agent do when its class says nothing?#

The careful thing, each time. This app decides as little as an app can: one agent with a purpose, a skill and two tools. The tests show what that gets you.

app Bureau {
  use Osysharp.Agents;
  use Osysharp.Agents.Testing;
  use Osysharp.Llm;
  use Osysharp.Identity;
  use Osysharp.Observability;
  use Osysharp.Workflow;
  model "model/**/*.osy";
}

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

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 Filer()] };          // no TaskBudget: fifty cents

[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);

// The two an app cannot leave out: who may give agents work, and who is in charge of them.
policy UsesAgents       => IsTranslator || IsManager;
policy SupervisesAgents => IsManager;

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

entity Commission {
  [Required, Unique, MaxLength(40)] string Reference;
  [MaxLength(400)] string Note = "";
  security { allow read, create, update when IsTranslator || IsManager || IsAssistant; }
}

public class Filing { public bool Filed; }

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

[Action("Write a note on the commission")]
public string NoteCommission(Commission commission, string note) { commission.Note = note; return "Noted."; }

public class FileCommission : Skill<Commission, Filing> {
  public override string Description => "File one commission.";
  public override List<ActionInfo> Tools => [Actions.Of(HouseTerms), Actions.Of(NoteCommission)];
  public override string Brief(Commission c) => $"Commission {c.Reference}. Its id is {c.Id}.";
}

/// It says who it is and what it can do, and nothing else.
[Actor(Roles = [BureauRole.Assistant])]
public class Filer : Agent {
  public static Person Principal => new Person { Email = "[email protected]", DisplayName = "Filer" };
  public override string Purpose => "You file the commissions of a small translation bureau.";
  public override List<SkillBase> Skills => [new FileCommission()];
}

[TestFixture]
void Office() {
  var ines = new Person { Email = "[email protected]", DisplayName = "Ines Varga" };
  var otto = new Person { Email = "[email protected]", DisplayName = "Otto Reyes" };
  new RoleGrant { Member = ines, Role = BureauRole.Translator };
  new RoleGrant { Member = otto, Role = BureauRole.Manager };
  new Commission { Reference = "C-31" };
}
principal Ines => Person.Single(p => p.Email == "[email protected]");
principal Otto => Person.Single(p => p.Email == "[email protected]");

[Test(Office)]
void Said_nothing_it_is_careful() {
  // Decision 5: nobody answers for it yet, so it does not run.
  runas (Ines) {
    var commission = Commission.Single();
    Assert.Throws<AgentNotSponsored>(() => await Agents.Of<Filer>().Do(new FileCommission(), commission));
  }
  runas (Otto) { SponsorAgent("Filer", Otto); UnitOfWork.Commit(); }

  runas (Ines) {
    var commission = Commission.Single();
    var script = AgentScript.Of(
      Says.Tool(Actions.Of(HouseTerms), "{\"language\":\"Finnish\"}"),
      Says.Tool("One house term applies.", Actions.Of(NoteCommission), "{\"commission\":\"" + commission.Id.ToString() + "\",\"note\":\"sopimus: agreement\"}"),
      Says.HandBack(new Filing { Filed = true }));
    ScriptedAnswer.Stub((model, request) => script.Answer(model, request));

    var outcome = await Agents.Of<Filer>().Do(new FileCommission(), commission);

    // Decision 2: it looked the terms up alone. The note changes data, so it is proposed, to her, and the work waits.
    Assert.True(outcome.Waiting);
    Assert.Equal(["HouseTerms"], Trajectory.Of(outcome.Task));
    Assert.Equal("", Commission.Single().Note);
    Assert.Equal("Write a note on the commission", ProposalsForMe().Single().Label);
    // Decision 3: fifty cents for one piece of work, in millionths of a dollar.
    Assert.Equal(500000, AgentTask.Single().LimitMicros);
  }
}

Go deeper#

Related

Building with agents — the developer's guide

Where a developer starts with agents in an Osy# app. Decide what you are building, read the smallest working app for…

Deciding what to build — four shapes of app with agents in it

Start from what the app is for, not from the kits. There are four shapes: an assistant for one person, work done for…

Choosing — I want X, so use Y, not Z, because

Seven tables for the choices that come up in every app with agents in it: an agent or a plain model call or a workflow…

Writing an agent — a class with a purpose, skills and an account of its own (Agents kit)

An agent is a class of the `Osysharp.Agents` kit: what it is for, the skills it holds, what it may spend. It runs as an…

What an agent does alone, what it proposes, what it never does — the gate and proposals (Agents kit)

An agent's class says what it may do alone, what it must propose to a person first, and what it never does — by what a…

What an agent may spend — a hard limit on every piece of work (Agents kit)

Every piece of work has a limit in dollars — the skill's `Budget`, else the agent's `Limits.PerTask`, else the app's…

An agent asks a person, and waits — the person rung of the ladder of help (Agents kit)

An agent that says so may put one focused question to a person: the model chooses to (a tool it is offered), or work…

Who is in charge of an app's agents — every policy in one table, pausing, sponsoring, and trying work again

One table of who may do what with an app's agents. An app answers UsesAgents and SupervisesAgents; supervising brings…

Privacy for the agent family — agents, the model meter, assignments and work repositories under a person's request

An app that uses `Osysharp.Privacy` beside the agent kits gets a holder for each without writing one:…