picoflow.io

Lesson 6: Explore Step

The design of ExploreStep is very cooky-cutter like.

  1. implement getPrompt()
  2. implement defineTool()
  3. implement getTool()
  4. implement capture_choices() tool handler.

Screen Shot


explore step screen shot

Prompting Explanation

  1. The system prompt is reasonably packed in this example.
  2. Notice we have a prompt variable {{HOTEL_JSON}} that will be dynamically substituted with a JSON structure in the getPrompt() function. This is a technique LangChain has used since the early days.
  3. We also list all the task this step needs to interact with a user.
  4. When all information are collected, a tool call capture_choices is mandated.
  5. This prompt will allow a user to navigate to any portion of the previously entered information and revise them if needed.
  6. We could embed the {{HOTEL_JSON}} json file in the prompt, but we keep it on a separate file for readability at the project source code level. It is listed below as well.

Full step prompt

## Main Instructions ##
  - **Variable**
    - `HotelJSON`={{HOTEL_JSON}} 
    - you must refer to this `HotelJSON` at all time when executing instructions.
    
  - **Tasks List** In the `Tasks List Section`, you must execute each task in sequence, follow the instruction within each task.
  - **Show Preference**
    - If at anytime, ask to show search criteria or preference, show the accumulated preferences from user in bullet form

## Tasks List Section ##
  - **Task1**
    -  you must tell user, you can only provide Hotel booking in Portland, OR metropolitan area only. Ask if user is looking to book hotels in that vicinity.
    - If `yes`, go to `Task2 `
    - If `no`, 
      - Immediately call tool `end_chat`, set the property `prompt` to: "Thank the user for choosing Hilton Hotel, ask them to visit `http://www.hilton.com`, or call 1-888-4HONORS for further assistance."

  - **Task2**
    - Ask user what date range the hotel stay is going to be.
    - You must tell start date must be a day greater than today's date in `Variable`: `HotelJSON.currentDate`
    - If year is omitted, assume it is the same year as in `Variable` `HotelJSON.currentDate`
    - The end date must greater than start date
    - User can choose to enter individual days or sub-ranges of dates, subsequent days must be greater than the one before
      - Examples:
      - 7/1, 7,2
      - 7/1 to 7/4, 7/6, 7/9 to 7/11
      - Update my stay date to July 1st to July 7.
      - My dates are 7/1 -7/4, 7/5, 7/10
      - Change my date to 7/1 to 7/6
      - Or simply a date specification like `7/1 to 7/6` or any variants of date range.
    - You must collect begin and end dates, and set it in `Variable` `HotelJSON.cDate` properties.
    - Important! You must figure out individual days and set it in `Variable` `HotelJSON.cDateArray` properties.
    - If no dates can be provided, immediately call `end_chat`
    - If date range is provided, goto `Task3`
  
  - **Task3**
    - Ask user what price range per night they desires, 
    - They can prefer the following patterns: (min, max), (min, no max), (no min, max), (no min, no max). Set the min, max in `Variable` `HotelJSON.cPriceRange`
    - If price range is provided,  goto `Task4`
    - Examples:
      1. min is 100, max is 500
      2. max $700
      3. min is 200.
      4. Update min/max to 100/300
      5. Change my hotel budget per night
      6. I don't care about min/max
      7. remove min, but add max to 700

  - **Task4**
    - Ask user what room type they prefer, you can use the list from JSON `roomType`
    - Capture  user's choice and set it in JSON `cRoomType`, goto `Task5`
    - User may not care about the room type, goto `Task5`
      - Examples:
        1. one bed,
        2. two beds,
        3. suite.
        4. Change my room to a one bed/two beds/suite
        5. Upgrade room type to 2 beds room.

  - **Task5**
    - Ask user to provide a list of amenities they desired. Try your best to map user's input with the list in `Variable` `HotelJSON.amenities` array.
    - If not sure confirm with user, by showing the closet amenities in `Variable`  `HotelJSON.amenities` array.
    - If user is confused, show the top 8 in list of amenities in `Variable`  `HotelJSON.amenities` array, for them to choose from.
    - Collect all amenities user has entered and put all in `Variable` `HotelJSON.cAmenities` array.
    - User can choose not to specify any amenity. 
    - Once amenities choices including not choosing is decided goto `Task6`
    - Examples:
      1. free wifi, parking
      2. I want indoor pool
      3. add free breakfast 
      4. include tennis court
      5. remove electric charging and new hotel
      6. add digital key and airport shuttle

  - **Task6**
    - Ask if user cares how close to Airport or City Center from hotel in miles.
    - User can specify distance to Airport, and or, to City Center.
    - Set both values if set in `Variable` `HotelJSON.cDistance` accordingly.
    - Goto `Task7`
    - Examples:
      1. distance to airport is 3 miles
      2. distance to city center is 20
      3. add distance to airport 3, city center 5. 
      4. remove distance to airport , and city center
      5. change distance to airport to 5, city center to 10.
      6. airport 5, city center 0

  - **Task7**
    - Ask user if they want to make any changes, or perform search
      - if perform search, tell user you will search hotels for them. immediately call tool `capture_choices`, by setting the property `json` with the JSON structure you accumulated from the users.
      - if more changes, confirm using `Variable` `HotelJSON` so far the information you already collected: `cDate`,`cPriceRange`,`cRoomType`,`cAmenities`,`cDistance`
      - Examples:
      1. Search Hotels
      2. Run Search
      3. re run 
      4. search again
  
  - **Task8**
    - if the `capture_choices` tool returns `no hotel found`, user may want to change search criteria, so goto `Task7`
  
  - **Situational go to cases** 
    - Analyze the last user response, determine which task you should go to executing.
    - Examples:
      1. (user response) change room type to 1 bed , (task designation) goto Task 4. 
      2. (user response) change max spending $400, (task designation) goto Task 3.

JSON structure use in {{HOTEL_JSON}}

{
  "currentDate": null,
  "amenities": [
    "freeWiFi",
    "nonSmoking",
    "freeBreakfast",
    "freeParking",
    "airportShuttle",
    "roomService",
    "fitnessCenter",
    "petFriendly",

    "digitalKey",
    "boutique",
    "onSiteRestaurant",
    "indoorPool",
    "businessCenter",
    "meetingRoom",
    "evCharging",
    "connectingRooms",
    "eveningReception",
    "concierge",
    "streaming",
    "kitchen",
    "tennis",
    "outdoorPool",
    "newHotel"
  ],
  "roomType": ["one bed", "two beds", "suite"],
  "cAmenities": [],
  "cRoomType": [],
  "cPriceRange": { "min": null, "max": null },
  "cDistance": {"cityCenter":null,"airport":null},
  "cDate": { "start": null, "end": null },
  "cDateArray": [],
  "hotelFound":[]
}

Coding Explanation

  1. The main action occurs in the tool handler capture_choices.
  2. We first save the user's choices to the step's sate persistently:
 this.saveState({
      json: choices,
    });
  1. We feed all the user inputs, collected by LLM, into our simulated hotel search engine.
  2. If the search result is empty, inform the user to modify the search criterial to re-run again.
  3. If a list of hotels are found, transition to the Present step for presentation to the user. The found hotel list is also passed to Present step at the same time.
 return {
        step: PresentStep,
        state: {
          hotelFound: hotelFoundInfo,
        },
      };

Full step code:

const ExplorePrompt = Prompt.file('prompt/explore.md');
const HotelJSON = Prompt.file('prompt/explore.json');
export class ExploreStep extends Step {
  constructor(flow: Flow, isActive?: boolean) {
    super(ExploreStep, flow, isActive);
  }

  public getPrompt(): string {
    const hotelJson = JSON.parse(HotelJSON);
    set(hotelJson, 'currentDate', moment().utc().format());

    const hotelFound = this.getState('hotelFound');
    if (hotelFound) {
      set(hotelJson, 'hotelFound', hotelFound);
    }

    const prompt = Prompt.replace(ExplorePrompt, {
      HOTEL_JSON: JSON.stringify(hotelJson),
    });
    return prompt;
  }

  public defineTool(): ToolType[] {
    return [
      {
        name: 'capture_choices',
        description: 'Capture user choice for hotel search criteria',
        schema: z.object({
          json: z.string().describe('JSON object'),
        }),
      },
    ];
  }
  public getTool(): string[] {
    return ['capture_choices'];
  }

  protected async capture_choices(tool: ToolCall): Promise<ToolResponseType> {
    //do a hotel search here.
    let choices;
    try {
      choices = JSON.parse(tool.args?.json);
    } catch (_ex) {}
    this.saveState({
      json: choices,
    });

    const startDate = choices['cDate']['start'];
    const endDate = choices['cDate']['end'];
    const roomType = choices['cRoomType'];
    const amenities = choices['cAmenities'];
    const maxBudget = choices['cPriceRange']['max'] ?? null;
    const minBudget = choices['cPriceRange']['min'] ?? null;
    const cityCenter = choices['cDistance']['cityCenter'];
    const airport = choices['cDistance']['airport'];

    const hotelEntries = await PricingEngine.searchHotel(
      startDate,
      endDate,
      amenities,
      roomType,
      maxBudget,
      minBudget,
      airport,
      cityCenter,
    );
    if (hotelEntries && hotelEntries.length > 0) {
      const hotelFoundInfo = hotelEntries.map((entry) => {
        return {
          hotelName: entry.hotelName,
          total: entry.total,
          prices: entry.prices,
        };
      });
      return {
        step: PresentStep,
        state: {
          hotelFound: hotelFoundInfo,
        },
      };
    } else {
      return {
        step: ExploreStep,
        tool: 'No hotel found, please adjust your criteria and try again.',
      };
    }
  }

Wrapping up

This step is the most complex, as it demonstrates a more advanced prompting approach to gather multiple user inputs within a single interaction. Next, we’ll explore how to present the selected hotel options to the user.