{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.12.14"
    },
    "execution": {
      "method": "Python subprocess, exact code cell source",
      "deterministic_replay_passed": true
    },
    "runtime": {
      "packages": [],
      "local_assets": []
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "id": "question",
      "metadata": {},
      "source": [
        "# When should a ReAct-style agent refresh its observations?\n",
        "Refreshing evidence turns three invalid takes into two completions and one honest abstention. It still cannot make a later mutation atomic.\n",
        "\nScripted state-machine mechanism study; not ReAct training or an LLM benchmark.\n"
      ]
    },
    {
      "cell_type": "code",
      "id": "experiment",
      "metadata": {},
      "execution_count": 1,
      "source": [
        "\"\"\"When should a ReAct-style agent refresh its observations?\n",
        "Deterministic, standard-library experiment. RESULTS contains the full data.\n",
        "\"\"\"\n",
        "STATES = [(1, 1), (0, 1), (1, 0), (0, 0), (2, 0), (0, 2)]\n",
        "\n",
        "def choose(observation):\n",
        "    return next((b for b in ('A', 'B') if observation[b] > 0), None)\n",
        "\n",
        "def run(refresh):\n",
        "    totals = dict(completed=0, invalid=0, abstained=0, observations=0)\n",
        "    for stocks in STATES:\n",
        "        world = dict(A=1, B=1)\n",
        "        observed = world.copy()\n",
        "        totals['observations'] += 1\n",
        "        world.update(zip(('A', 'B'), stocks))\n",
        "        if refresh:\n",
        "            observed = world.copy()\n",
        "            totals['observations'] += 1\n",
        "        target = choose(observed)\n",
        "        if target is None:\n",
        "            totals['abstained'] += 1\n",
        "        elif world[target] <= 0:\n",
        "            totals['invalid'] += 1\n",
        "        else:\n",
        "            world[target] -= 1\n",
        "            totals['completed'] += 1\n",
        "    assert sum(totals[k] for k in ('completed', 'invalid', 'abstained')) == 6\n",
        "    return totals\n",
        "\n",
        "for policy in (False, True):\n",
        "    print('refresh=' + str(policy), run(policy))\n",
        "assert run(False)['invalid'] == 3\n",
        "assert run(True)['completed'] == 5\n",
        "\n",
        "\n",
        "rows = []\n",
        "for index, stocks in enumerate(STATES):\n",
        "    for policy in ('cached', 'refreshed'):\n",
        "        observed = (1, 1) if policy == 'cached' else stocks\n",
        "        choice = choose(dict(zip(('A', 'B'), observed)))\n",
        "        outcome = 'abstained' if choice is None else ('completed' if stocks[('A', 'B').index(choice)] > 0 else 'invalid')\n",
        "        rows.append(dict(case=index, stock=list(stocks), policy=policy, observation=list(observed), choice=choice, outcome=outcome))\n",
        "races = []\n",
        "for middle in STATES:\n",
        "    for final in STATES:\n",
        "        choice = choose(dict(zip(('A', 'B'), middle)))\n",
        "        changed = middle != final\n",
        "        unguarded = 'abstained' if choice is None else ('completed' if final[('A','B').index(choice)] > 0 else 'invalid')\n",
        "        guarded = 'abstained' if choice is None else ('canceled' if changed else unguarded)\n",
        "        races.append(dict(observed=list(middle), final=list(final), choice=choice, refresh_only=unguarded, revision_checked=guarded))\n",
        "assert all(r['revision_checked'] != 'invalid' for r in races)\n",
        "RESULTS = dict(cases=rows, totals={p:run(p == 'refreshed') for p in ('cached','refreshed')}, post_observation_changes=races)\n",
        "print('Post-observation cross product:', len(races), 'cases; revision-check invalid actions:', sum(r['revision_checked']=='invalid' for r in races))\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "refresh=False {'completed': 3, 'invalid': 3, 'abstained': 0, 'observations': 6}\n",
            "refresh=True {'completed': 5, 'invalid': 0, 'abstained': 1, 'observations': 12}\n",
            "Post-observation cross product: 36 cases; revision-check invalid actions: 0\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from pathlib import Path\n",
        "import base64\n",
        "print('__NOTEBOOK_IMAGE__svg+xml:' + base64.b64encode(Path('figure.svg').read_bytes()).decode())\n"
      ]
    }
  ]
}
