picoflow.io

Lesson 4: Multi-Call Capture (WeatherStep)


Goal

Capture two city names, but only accept NYC and LA. Persist each success, loop until both are collected, then branch into logic.


Prompt and Tool Surface

public getPrompt(): string {
  return `
    ${DemoPrompt.TravelRole}
    Ask user to enter 2 city names to compare their current day temperature.
    Capture the names of the two cities and call tool 'get_weather' for each city
    If user prefer to exit, call tool 'end_chat'.
  `;
}

public defineTool(): ToolType[] {
  return [{
    name: 'get_weather',
    description: 'capture the weather of one city',
    schema: z.object({ cityName: z.string() }),
  }];
}
public getTool(): string[] {
  return ['get_weather', 'end_chat'];
}
Tip

Restrict tools per step to minimize hallucinated actions and keep the LLM on the rail.


Handler Logic

protected async get_weather(tool: ToolCall): Promise<ToolResponseType> {
  if (tool.args?.cityName === 'NYC' || tool.args?.cityName === 'LA') {
    this.saveState({ [`cityName_${tool.args.cityName}`]: true });
    const done = this.getState('cityName_LA') && this.getState('cityName_NYC');
    return done ? FooLogicStep : WeatherStep;
  }
  return { step: WeatherStep, tool: 'Only LA and NYC cities are allowed' };
}
  • Valid city → set a boolean state flag.
  • Both flags present → transition to FooLogicStep.
  • Invalid city → retry in place, surfacing the error as a tool result.

Patterns Illustrated

  • Multi-call capture: stay in the same step until N items are collected.
  • Retry-in-place: { step: SameStep, tool: 'error' } keeps the LLM focused.
  • Stateful guardrails: simple flags in step state drive deterministic branching.

Next we’ll capture the user’s name, inject runtime context, and run an InContext sub-agent.