Tutorial: Human-in-the-Loop (E18)¶
This tutorial corresponds to the example file examples/E18_human_in_loop_example.py
.
It demonstrates how to add a human approval step before executing critical actions. It shows how to:
- Configure a HumanApprovalRequestPlugin
(e.g., cli_hitl_approver
).
- See how genie.run_command()
automatically triggers the HITL flow before executing a tool.
- Understand the approval/denial workflow from the user's perspective.
Example Code¶
examples/E18_human_in_loop_example.py¶
"""
Example: Using Human-in-the-Loop (HITL) (genie.human_in_loop
)
This example demonstrates how to configure and use the Human-in-the-Loop
feature for approvals. It uses the CliApprovalPlugin
by default,
which will prompt on the command line.
This example will primarily show HITL integrated with genie.run_command
.
To Run:
1. Ensure Genie Tooling is installed (poetry install --all-extras
).
2. Ensure Ollama is running and 'mistral:latest' is pulled for the LLM-assisted processor.
3. Run from the root of the project:
poetry run python examples/E18_human_in_loop_example.py
You will be prompted in the console to approve/deny tool execution.
"""
import asyncio
import json
import logging
from typing import Optional
from genie_tooling.config.features import FeatureSettings from genie_tooling.config.models import MiddlewareConfig from genie_tooling.genie import Genie
async def run_hitl_demo(): print("--- Human-in-the-Loop (HITL) Example ---") logging.basicConfig(level=logging.INFO)
app_config = MiddlewareConfig(
features=FeatureSettings(
llm="ollama",
llm_ollama_model_name="mistral:latest",
command_processor="llm_assisted",
tool_lookup="embedding",
hitl_approver="cli_hitl_approver"
),
tool_configurations={
"calculator_tool": {}
}
)
genie: Optional[Genie] = None
try:
print("\nInitializing Genie with HITL enabled...")
genie = await Genie.create(config=app_config)
print("Genie initialized!")
print("\n--- `run_command` with HITL ---")
command_text = "What is 15 multiplied by 7?"
print(f"Sending command: '{command_text}'")
print("The system will now select the 'calculator_tool' and then prompt for human approval before execution.")
command_result = await genie.run_command(command_text)
print("\nCommand Result after HITL:")
print(json.dumps(command_result, indent=2, default=str))
if command_result and command_result.get("tool_result"):
print(f"\nTool Result: {command_result['tool_result']}")
elif command_result and "hitl_decision" in command_result and command_result["hitl_decision"].get("status") != "approved":
print(f"\nTool execution was {command_result['hitl_decision']['status']}. Reason: {command_result['hitl_decision'].get('reason')}")
elif command_result and command_result.get("error"):
print(f"\nCommand Error: {command_result['error']}")
else:
print(f"\nCommand did not result in a tool call or expected HITL flow: {command_result}")
except Exception as e:
print(f"\nAn error occurred: {e}")
logging.exception("HITL demo error details:")
finally:
if genie:
await genie.close()
print("\nGenie torn down.")
if name == "main": asyncio.run(run_hitl_demo())