NOULI

Examples

Energy conservation planning

The initial recorded plan meets the sample 250 kW target. After the battery reading changes, the model incorrectly retains that offer. Steps 7, 9, and 10 explain why the recorded output violates the reserve constraint.

The problem

A facility wants to reduce grid imports during a conservation request while preserving critical refrigeration and a battery reserve. It can discharge a battery and defer vehicle charging. A corrected energy reading arrives before the plan is executed, so the available battery contribution must be reconsidered.

The historical context is the Cal OES September 6, 2022 emergency conservation alert, issued at 17:45 and requesting reduced nonessential use until 21:00 where health permits. The facility, readings, reserve, operating constraints, and 250 kW target are illustrative. Stored energy is already AC-deliverable; conversion losses are accounted for.

What we will do

We will keep the external conservation request, battery telemetry, and facility obligations separate. Two blends will choose battery and flexible-load contributions, then a final blend will combine them. We will try an unsaved what-if, record corrected telemetry, and save a revised plan.

Before you start

Configure a provider and model, then run the commands in order in the same shell. Nouli reads credentials from your shell or the .env in your current directory. Each decision calls your configured provider and may incur charges.

The outputs below were captured with typesafe/jev-1.14.0. They are a recorded run, not guaranteed results. Probabilities, session IDs, and model answers may differ when you run the commands.

Set a session prefix for this walkthrough. Change it before repeating the example so the new sessions have unique names:

example=energy
show the steps as

Open the first session with nouli open request, then type each step's lines at the prompt. A question sends the turn.

01Record the conservation request

Record the external request separately from the facility limits. The two-hour planning interval is 18:00-20:00, after the historical alert.

$ nouli open request
new session "request" · /help for commands

request › Cal OES reported a September 6, 2022 emergency alert at 17:45, requesting reductions in nonessential electricity use where health permits until 21:00. Our illustrative facility is considering 18:00-20:00. This is a voluntary conservation request, not a market dispatch instruction.
request › ?request: Which type of response fits this request? [Reduce nonessential use while preserving critical operations, Include critical operations in load reductions, Request clarification before choosing any response]
  turn 1 · 1 fact · 1 question
  request     ▶ Reduce nonessential use while p… ███████████████████▊ 0.99
                Include critical operations in …                      0.00
                Request clarification before ch… ▎                    0.01
nouli --format text new "$example-request" \
  'Cal OES reported a September 6, 2022 emergency alert at 17:45, requesting
reductions in nonessential electricity use where health permits until 21:00.
Our illustrative facility is considering 18:00-20:00. This is a voluntary
conservation request, not a market dispatch instruction.' \
  -q 'request: Which type of response fits this request? [Reduce nonessential use while preserving critical operations,
    Include critical operations in load reductions,
    Request clarification before choosing any response]'

Output

  turn 1 · 1 fact · 1 question
  request     ▶ Reduce nonessential use while p… ███████████████████▊ 0.99
                Include critical operations in …                      0.00
                Request clarification before ch… ▎                    0.01
  typesafe/jev-1.14.0 · session 01a1878e-1ae9-7515-941a-960f8cda6716 · turn 01a1878e-1aed-726e-9ea3-f0f5505d4788

The model selects reducing nonessential use while preserving critical operations. This is voluntary conservation context, not a market dispatch instruction.

02Record battery availability

Record battery telemetry without applying a reserve requirement yet. Stored energy and the discharge power limit are distinct quantities.

request › /new battery
  new session "battery"
battery › Illustrative reading at 17:50: stored AC-deliverable energy is 800 kWh in a 1000 kWh battery. The discharge limit is 200 kW. No charging is planned during the two-hour interval. This telemetry record does not define the required reserve.
battery › ?battery: What battery power limit applies before reserve requirements are considered? [100 kW, 150 kW, 200 kW]
  turn 1 · 1 fact · 1 question
  battery       100 kW                                                0.00
                150 kW                                                0.00
              ▶ 200 kW                           ████████████████████ 1.00
nouli --format text new "$example-battery" \
  'Illustrative reading at 17:50: stored AC-deliverable energy is 800 kWh in a
1000 kWh battery. The discharge limit is 200 kW. No charging is planned during
the two-hour interval. This telemetry record does not define the required
reserve.' \
  -q 'battery: What battery power limit applies before reserve requirements are considered? [100 kW,
    150 kW,
    200 kW]'

Output

  turn 1 · 1 fact · 1 question
  battery       100 kW                                                0.00
                150 kW                                                0.00
              ▶ 200 kW                           ████████████████████ 1.00
  typesafe/jev-1.14.0 · session 01a1878e-1af5-754b-a8f2-d740fe0e7696 · turn 01a1878e-1af7-7361-bc56-f90fb9783887

The battery power limit is 200 kW. The reserve policy and interval duration will constrain how much of that limit can actually be used.

03Record facility obligations

Record facility obligations in a separate history so both planning branches can reuse them. The reserve is 500 kWh and refrigeration cannot be curtailed.

battery › /new facility
  new session "facility"
facility › Illustrative facility policy: retain at least 500 kWh of AC-deliverable battery energy after 20:00 for critical operations. Vehicle charging can be deferred by 100 kW throughout 18:00-20:00 without affecting departures. Critical refrigeration uses 150 kW and cannot be curtailed. There are no other flexible loads. The local conservation goal is 250 kW for the interval.
facility › ?facility: Which load can be reduced without disrupting critical operations? [Defer vehicle charging, Curtail refrigeration, Request a review of operating constraints]
  turn 1 · 1 fact · 1 question
  facility    ▶ Defer vehicle charging           ████████████████████ 1.00
                Curtail refrigeration                                 0.00
                Request a review of operating c…                      0.00
nouli --format text new "$example-facility" \
  'Illustrative facility policy: retain at least 500 kWh of AC-deliverable
battery energy after 20:00 for critical operations. Vehicle charging can be
deferred by 100 kW throughout 18:00-20:00 without affecting departures.
Critical refrigeration uses 150 kW and cannot be curtailed. There are no other
flexible loads. The local conservation goal is 250 kW for the interval.' \
  -q 'facility: Which load can be reduced without disrupting critical operations? [Defer vehicle charging,
    Curtail refrigeration,
    Request a review of operating constraints]'

Output

  turn 1 · 1 fact · 1 question
  facility    ▶ Defer vehicle charging           ████████████████████ 1.00
                Curtail refrigeration                                 0.00
                Request a review of operating c…                      0.00
  typesafe/jev-1.14.0 · session 01a1878e-1aff-766c-a28c-7a1ec4690846 · turn 01a1878e-1b01-7730-b028-e6655f869436

The model selects deferring vehicle charging. The 100 kW flexible load can be removed for the whole interval without affecting departures.

04Choose the battery contribution

Blend telemetry and facility policy to choose constant battery output for two hours. The plan has not executed.

facility › /blend battery facility as battery-plan
  new session "battery-plan" from battery + facility · no standing questions
battery-plan › Choose the greatest constant battery output for 18:00-20:00 that leaves at least the recorded reserve. Use the latest telemetry. The proposed plan has not executed.
battery-plan › ?battery_plan: What constant battery contribution is feasible? [Discharge at 100 kW for two hours, Discharge at 150 kW for two hours, Discharge at 200 kW for two hours]
  turn 1 · 1 fact · 1 question
  battery_pl…   Discharge at 100 kW for two hou… ███▋                 0.18
              ▶ Discharge at 150 kW for two hou… █████████            0.45
                Discharge at 200 kW for two hou… ███████▍             0.37
nouli --format text new "$example-battery-plan" --from "$example-battery" --from "$example-facility" \
  'Choose the greatest constant battery output for 18:00-20:00 that leaves at
least the recorded reserve. Use the latest telemetry. The proposed plan has
not executed.' \
  -q 'battery_plan: What constant battery contribution is feasible? [Discharge at 100 kW for two hours,
    Discharge at 150 kW for two hours,
    Discharge at 200 kW for two hours]'

Output

  turn 1 · 1 fact · 1 question
  battery_pl…   Discharge at 100 kW for two hou… ███▋                 0.18
              ▶ Discharge at 150 kW for two hou… █████████            0.45
                Discharge at 200 kW for two hou… ███████▍             0.37
  typesafe/jev-1.14.0 · session 01a1878e-1b08-7111-a1a7-4d934c8bf0d7 · turn 01a1878e-1b0b-7192-9708-1a3f48b3b135

The recorded 150 kW contribution uses 300 kWh and leaves the 500 kWh reserve: 800 - (150 x 2) = 500. It is below the 200 kW power limit.

05Choose the load contribution

Blend the conservation request with facility policy to choose a load response independently of battery dispatch.

battery-plan › /blend request facility as load-plan
  new session "load-plan" from request + facility · no standing questions
load-plan › Choose a voluntary load reduction consistent with the conservation request and the facility obligations.
load-plan › ?load_plan: Which load response should the facility offer? [Defer 100 kW of vehicle charging for two hours, Defer charging and refrigeration for two hours, Request a revised operating plan before offering a reduction]
  turn 1 · 1 fact · 1 question
  load_plan   ▶ Defer 100 kW of vehicle chargin… ███████████████████▊ 0.99
                Defer charging and refrigeratio…                      0.00
                Request a revised operating pla… ▎                    0.01
nouli --format text new "$example-load-plan" --from "$example-request" --from "$example-facility" \
  'Choose a voluntary load reduction consistent with the conservation request and
the facility obligations.' \
  -q 'load_plan: Which load response should the facility offer? [Defer 100 kW of vehicle charging for two hours,
    Defer charging and refrigeration for two hours,
    Request a revised operating plan before offering a reduction]'

Output

  turn 1 · 1 fact · 1 question
  load_plan   ▶ Defer 100 kW of vehicle chargin… ███████████████████▊ 0.99
                Defer charging and refrigeratio…                      0.00
                Request a revised operating pla… ▎                    0.01
  typesafe/jev-1.14.0 · session 01a1878e-1b12-7791-a137-7ba51dd513e2 · turn 01a1878e-1b15-7313-9996-4af074c5af0b

The model selects deferring 100 kW of charging for two hours while preserving refrigeration. This branch shares facility policy with the battery branch.

06Combine the two plans

Combine both planning branches into one offer toward the sample local target.

load-plan › /blend battery-plan load-plan as plan
  new session "plan" from battery-plan + load-plan · no standing questions
plan › Choose a combined offer consistent with battery reserve, power limit, flexible loads, and the local goal. Prefer meeting the goal when feasible. Otherwise choose the largest feasible offer and report its shortfall. All proposed plans are alternatives before execution, not successive discharges.
plan › ?plan: Which conservation plan is feasible for 18:00-20:00? [Reduce imports by 250 kW: 150 kW battery plus 100 kW deferred charging, Reduce imports by 200 kW: 100 kW battery plus 100 kW deferred charging; report a 50 kW shortfall, Request a revised plan because neither offer fits the constraints]
  turn 1 · 1 fact · 1 question
  plan        ▶ Reduce imports by 250 kW: 150 k… █████████████▍       0.67
                Reduce imports by 200 kW: 100 k… █████▊               0.29
                Request a revised plan because … ▊                    0.04
nouli --format text new "$example-plan" --from "$example-battery-plan" --from "$example-load-plan" \
  'Choose a combined offer consistent with battery reserve, power limit, flexible
loads, and the local goal. Prefer meeting the goal when feasible. Otherwise
choose the largest feasible offer and report its shortfall. All proposed plans
are alternatives before execution, not successive discharges.' \
  -q 'plan: Which conservation plan is feasible for 18:00-20:00? [Reduce imports by 250 kW: 150 kW battery plus 100 kW deferred charging,
    Reduce imports by 200 kW: 100 kW battery plus 100 kW deferred charging; report a 50 kW shortfall,
    Request a revised plan because neither offer fits the constraints]'

Output

  turn 1 · 1 fact · 1 question
  plan        ▶ Reduce imports by 250 kW: 150 k… █████████████▍       0.67
                Reduce imports by 200 kW: 100 k… █████▊               0.29
                Request a revised plan because … ▊                    0.04
  typesafe/jev-1.14.0 · session 01a1878e-1b1c-7263-b398-6f29cdc74cc3 · turn 01a1878e-1b1f-733e-98ad-5fdd03704689

The model selects 150 kW from the battery plus 100 kW of deferred charging, a 250 kW reduction in grid imports. The command records an offer; it does not operate equipment.

07Try an unsaved alternative

Use whatif to evaluate a hypothetical 700 kWh reading without saving a new decision or updating the telemetry session. A session keeps asking its questions, so --drop plan leaves the plan question out of this one turn.

This what-if asks a question of its own, so it runs from the shell:

nouli --format text whatif "plan" \
  'What-if only: replace the stored-energy reading with 700 kWh for this
evaluation. Keep the 500 kWh reserve, 200 kW power limit, 100 kW deferrable
charging, and two-hour interval. Choose the largest feasible offer toward the
250 kW goal. No plan has executed.' \
  -q 'what_if: Which conservation plan is feasible for 18:00-20:00? [Reduce imports by 250 kW: 150 kW battery plus 100 kW deferred charging,
    Reduce imports by 200 kW: 100 kW battery plus 100 kW deferred charging; report a 50 kW shortfall,
    Request a revised plan because neither offer fits the constraints]' \
  --drop plan
nouli --format text whatif "$example-plan" \
  'What-if only: replace the stored-energy reading with 700 kWh for this
evaluation. Keep the 500 kWh reserve, 200 kW power limit, 100 kW deferrable
charging, and two-hour interval. Choose the largest feasible offer toward the
250 kW goal. No plan has executed.' \
  -q 'what_if: Which conservation plan is feasible for 18:00-20:00? [Reduce imports by 250 kW: 150 kW battery plus 100 kW deferred charging,
    Reduce imports by 200 kW: 100 kW battery plus 100 kW deferred charging; report a 50 kW shortfall,
    Request a revised plan because neither offer fits the constraints]' \
  --drop plan

Output

  (not saved) · 1 fact · 1 question
  what_if     ▶ Reduce imports by 250 kW: 150 k… ████████████▍        0.62
                Reduce imports by 200 kW: 100 k… ██████               0.30
                Request a revised plan because … █▋                   0.08
  typesafe/jev-1.14.0

Review finding: the model keeps the full 250 kW offer. With 700 kWh stored and a 500 kWh reserve, only 200 kWh can be used over two hours, allowing 100 kW from the battery. The feasible combined offer is 200 kW, with a 50 kW shortfall. The captured what-if answer is incorrect.

08Record corrected telemetry

Continue the telemetry session with the corrected 700 kWh reading. Unlike the what-if, this becomes saved source evidence for new decisions. The same question ID replaces the session's standing question with the reworded one.

plan › /use battery
  battery · 1 standing question
battery › Confirmed illustrative correction at 17:55, replacing the 17:50 reading: stored AC-deliverable energy is 700 kWh. The power limit remains 200 kW. No discharge has occurred; this is a corrected measurement before the interval starts.
battery › ?battery: What stored-energy reading should the next plan use? [700 kWh, 800 kWh, Request a fresh reading]
  turn 2 · 1 fact · 1 question · re-asked 1
  battery     ▶ 700 kWh                          ███████████████████▎ 0.96
                800 kWh                          ▎                    0.01
                Request a fresh reading          ▋                    0.03
nouli --format text add "$example-battery" \
  'Confirmed illustrative correction at 17:55, replacing the 17:50 reading:
stored AC-deliverable energy is 700 kWh. The power limit remains 200 kW. No
discharge has occurred; this is a corrected measurement before the interval
starts.' \
  -q 'battery: What stored-energy reading should the next plan use? [700 kWh,
    800 kWh,
    Request a fresh reading]'

Output

  turn 2 · 1 fact · 1 question · re-asked 1
  battery     ▶ 700 kWh                          ███████████████████▎ 0.96
                800 kWh                          ▎                    0.01
                Request a fresh reading          ▋                    0.03
  typesafe/jev-1.14.0 · session 01a1878e-1af5-754b-a8f2-d740fe0e7696 · turn 01a1878e-1b32-751a-b1a9-ebb627c4c708

The model identifies 700 kWh as the latest reading. The original plan has still not executed, so no discharge needs to be subtracted from that reading.

09Recompute the battery plan from its sources

Create a new battery plan from updated telemetry and unchanged facility obligations. This brings the corrected source into a fresh blend.

battery › /blend battery facility as battery-plan-updated
  new session "battery-plan-updated" from battery + facility · no standing questions
battery-plan-updated › Choose the greatest constant battery output for 18:00-20:00 that leaves at least the recorded reserve. Use the latest telemetry. The proposed plan has not executed.
battery-plan-updated › ?battery_plan_updated: What constant battery contribution is feasible? [Discharge at 100 kW for two hours, Discharge at 150 kW for two hours, Discharge at 200 kW for two hours]
  turn 1 · 1 fact · 1 question
  battery_pl…   Discharge at 100 kW for two hou… ████▊                0.24
                Discharge at 150 kW for two hou… ███████▎             0.36
              ▶ Discharge at 200 kW for two hou… ████████             0.40
nouli --format text new "$example-battery-plan-updated" --from "$example-battery" --from "$example-facility" \
  'Choose the greatest constant battery output for 18:00-20:00 that leaves at
least the recorded reserve. Use the latest telemetry. The proposed plan has
not executed.' \
  -q 'battery_plan_updated: What constant battery contribution is feasible? [Discharge at 100 kW for two hours,
    Discharge at 150 kW for two hours,
    Discharge at 200 kW for two hours]'

Output

  turn 1 · 1 fact · 1 question
  battery_pl…   Discharge at 100 kW for two hou… ████▊                0.24
                Discharge at 150 kW for two hou… ███████▎             0.36
              ▶ Discharge at 200 kW for two hou… ████████             0.40
  typesafe/jev-1.14.0 · session 01a1878e-1b39-70d4-8f12-2299cfd138a4 · turn 01a1878e-1b3c-71a1-90b6-b934076a2479

Review finding: the model selects 200 kW, the full power limit. That would leave 700 - (200 x 2) = 300 kWh, below the 500 kWh reserve. The largest feasible candidate is 100 kW.

10Save a revised combined plan

Combine the revised battery branch with the unchanged load plan to save a new overall recommendation.

battery-plan-updated › /blend battery-plan-updated load-plan as plan-updated
  new session "plan-updated" from battery-plan-updated + load-plan · no standing questions
plan-updated › Choose a combined offer consistent with battery reserve, power limit, flexible loads, and the local goal. Prefer meeting the goal when feasible. Otherwise choose the largest feasible offer and report its shortfall. All proposed plans are alternatives before execution, not successive discharges.
plan-updated › ?plan_updated: Which conservation plan is feasible for 18:00-20:00? [Reduce imports by 250 kW: 150 kW battery plus 100 kW deferred charging, Reduce imports by 200 kW: 100 kW battery plus 100 kW deferred charging; report a 50 kW shortfall, Request a revised plan because neither offer fits the constraints]
  turn 1 · 1 fact · 1 question
  plan_updat… ▶ Reduce imports by 250 kW: 150 k… █████████████▎       0.66
                Reduce imports by 200 kW: 100 k… ██████               0.30
                Request a revised plan because … ▊                    0.04
nouli --format text new "$example-plan-updated" --from "$example-battery-plan-updated" --from "$example-load-plan" \
  'Choose a combined offer consistent with battery reserve, power limit, flexible
loads, and the local goal. Prefer meeting the goal when feasible. Otherwise
choose the largest feasible offer and report its shortfall. All proposed plans
are alternatives before execution, not successive discharges.' \
  -q 'plan_updated: Which conservation plan is feasible for 18:00-20:00? [Reduce imports by 250 kW: 150 kW battery plus 100 kW deferred charging,
    Reduce imports by 200 kW: 100 kW battery plus 100 kW deferred charging; report a 50 kW shortfall,
    Request a revised plan because neither offer fits the constraints]'

Output

  turn 1 · 1 fact · 1 question
  plan_updat… ▶ Reduce imports by 250 kW: 150 k… █████████████▎       0.66
                Reduce imports by 200 kW: 100 k… ██████               0.30
                Request a revised plan because … ▊                    0.04
  typesafe/jev-1.14.0 · session 01a1878e-1b44-7215-b729-118c218eae6e · turn 01a1878e-1b48-7150-9f61-8b58975e9a05

Review finding: the model retains the 250 kW offer. The supplied constraints support 100 kW of battery discharge plus 100 kW of deferred charging, or 200 kW total. This recorded recommendation should be rejected by an independent reserve check before any equipment action.