Skip to content

AI scenarios

Multi-step agent

Bit.BlazorUI.AI Scripted transport

A plan published up front, then executed step by step with timings and per-step summaries.

Live demo

Give it something to research: the agent plans four steps, works through them, then answers.

\n**A run with a plan**: ```razor @implements IAsyncDisposable
``` ```csharp private readonly BitAiChatSession session = new(AiDemoAgentTransports.Research()); private readonly string[] suggestions = ["Do the sources agree on the mechanism?"]; public ValueTask DisposeAsync() => session.DisposeAsync(); ``` `AiDemoAgentTransports.cs`: ```csharp // AiScriptedTransport and AiScript are the demo's helpers - see the Basic chat scenario. public static IBitAiTransport Research() => new AiScriptedTransport((request, token) => AiScript.Stream(token, async yielder => { var runId = BitAiId.NewRunId(); await yielder(new BitAiMessageStartedEvent()); await yielder(new BitAiAgentRunStartedEvent(runId) { AgentName = "Research agent", Goal = AiScript.LastUserText(request), }); // The whole plan, up front. await yielder(new BitAiAgentPlanEvent(runId, [ new BitAiAgentPlanStep("s1", "Search for primary sources"), new BitAiAgentPlanStep("s2", "Read the three most relevant"), new BitAiAgentPlanStep("s3", "Reconcile the disagreements"), new BitAiAgentPlanStep("s4", "Write the summary"), ])); string[] summaries = [ "11 results, 3 look primary", "Two agree on the mechanism, one does not", "The disagreement is about scope, not direction", "Summary written", ]; // Then each step, started and completed with a summary of what it found. for (var i = 0; i < summaries.Length; i++) { var stepId = $"s{i + 1}"; await yielder(new BitAiAgentStepStartedEvent(runId, stepId)); // The search runs on the provider's side: the transport reports the call and its result itself, so the // session shows it - and the step names it - without running anything. if (i == 0) { var callId = BitAiId.NewToolCallId(); await yielder(new BitAiToolCallStartedEvent(callId, "web_search") { Arguments = AiScript.Json("""{"query":"primary sources on the disputed effect"}""") }); await yielder(new BitAiToolResultEvent(callId, BitAiToolOutcome.FromText("11 results, 3 of them primary studies."))); } await AiScript.Pause(700 + i * 200, token); await yielder(new BitAiAgentStepCompletedEvent(runId, stepId) { Summary = summaries[i] }); } await yielder(new BitAiAgentRunCompletedEvent(runId)); // And the answer, under the run. await foreach (var evt in AiScript.Write("The three primary sources agree on direction and disagree on scope. ...", 4, 11, token)) await yielder(evt); await yielder(new BitAiMessageCompletedEvent()); })); ```

How it works

A spinner is not an answer. A BitAiAgentRun holds the goal, the plan, the steps, their states and the elapsed time, built from BitAiAgentRunStartedEvent, BitAiAgentPlanEvent, BitAiAgentStepStartedEvent / BitAiAgentStepCompletedEvent and BitAiAgentRunCompletedEvent. The run is attached to the message that produced it, so scrolling back to an old answer still shows what the agent did to produce it.

In order, this transport sends BitAiAgentRunStartedEvent(runId) with the agent's AgentName and its Goal (the question asked), the whole plan as a BitAiAgentPlanEvent of four BitAiAgentPlanSteps, then for each step a BitAiAgentStepStartedEvent and a BitAiAgentStepCompletedEvent with the Summary of what it found, then BitAiAgentRunCompletedEvent - and only then the answer, as ordinary text deltas under the run.

The first step searches, and the search runs on the provider's side, as a hosted web search does: the transport reports the call (BitAiToolCallStartedEvent with its arguments) and its result (BitAiToolResultEvent) itself, so the session shows the call without running anything, and the step that was running when it started names it.

Plans are replaced rather than patched, because agents rewrite the tail of a plan as they learn. Steps that keep their identifier keep their state and timings.

Components used

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.