1111## What this agent does
1212
1313Agent with ** Human-in-the-Loop (HITL) approval** that pauses execution before running sensitive tools (e.g.
14- ` send_email ` ) and waits for human review. Safe tools (e.g. ` search ` ) run automatically without approval. Built with
15- LangGraph and LangChain.
14+ ` create_file ` ) and waits for human review. Simple questions are answered directly without triggering the approval loop.
15+ Built with LangGraph and LangChain.
1616
1717** How it works:**
1818
@@ -28,7 +28,7 @@ User Input → LLM decides tool → Is it sensitive?
2828
2929### Preconditions:
3030
31- - You need to change .env.template file to .env
31+ - You need to change template .env file to .env
3232- Decide what way you want to go ` local ` or ` RH OpenShift Cluster ` and fill needed values
3333- use ` ./init.sh ` that will add those values from .env to environment variables
3434
@@ -162,10 +162,10 @@ uv run examples/execute_ai_service_locally.py
162162
163163This agent classifies tools into two categories:
164164
165- | Category | Tools | Behavior |
166- | ---------------| --------------| - -------------------------------------------|
167- | ** Safe** | ` search ` | Executed automatically , no approval needed |
168- | ** Sensitive** | ` send_email ` | Paused for human review before execution |
165+ | Category | Tools | Behavior |
166+ | ---------------| --------------- | -------------------------------------------|
167+ | ** Safe** | general chat | Responded to directly , no approval needed |
168+ | ** Sensitive** | ` create_file ` | Paused for human review before execution |
169169
170170When the LLM decides to call a sensitive tool, the agent:
171171
@@ -182,7 +182,7 @@ When the LLM decides to call a sensitive tool, the agent:
182182curl -X POST http://localhost:8000/chat/completions \
183183 -H " Content-Type: application/json" \
184184 -d ' {
185- "messages": [{"role": "user", "content": "Send an email to alice@example.com about the meeting tomorrow "}],
185+ "messages": [{"role": "user", "content": "Create a file named report.md with info about LangChain "}],
186186 "stream": false,
187187 "thread_id": "conversation-1"
188188 }'
@@ -196,7 +196,7 @@ curl -X POST http://localhost:8000/chat/completions \
196196 {
197197 "message" : {
198198 "role" : " assistant" ,
199- "content" : " {\" question\" : \" Do you approve the following tool call(s)?\" , \" tool_calls\" : [\" Tool: send_email , Args: {...}\" ], \" options\" : [\" yes\" , \" no\" ]}"
199+ "content" : " {\" question\" : \" Do you approve the following tool call(s)?\" , \" tool_calls\" : [\" Tool: create_file , Args: {...}\" ], \" options\" : [\" yes\" , \" no\" ]}"
200200 },
201201 "finish_reason" : " pending_approval"
202202 }
@@ -299,36 +299,84 @@ COPY the route URL and PASTE into the CURL below
299299oc get route langgraph-hitl-agent -o jsonpath=' {.spec.host}'
300300```
301301
302- Send a test request :
302+ Send test requests (3-step HITL flow) :
303303
304- Non-streaming (safe tool - no approval needed)
304+ ** Step 1: Ask a general question ( no approval needed)**
305305
306306``` bash
307307curl -X POST https://< YOUR_ROUTE_URL> /chat/completions \
308308 -H " Content-Type: application/json" \
309- -d ' {"messages": [{"role": "user", "content": "Search for RedHat OpenShift"}], "stream": false}'
309+ -d ' {
310+ "messages": [{"role": "user", "content": "What is RedHat OpenShift Cluster"}],
311+ "stream": false,
312+ "thread_id": "demo-1"
313+ }'
310314```
311315
312- Non-streaming (sensitive tool - triggers approval)
316+ ** Step 2: Ask to write that info into a file ( triggers approval)**
313317
314318``` bash
315319curl -X POST https://< YOUR_ROUTE_URL> /chat/completions \
316320 -H " Content-Type: application/json" \
317321 -d ' {
318- "messages": [{"role": "user", "content": "Send an email to alice@example.com about the meeting "}],
322+ "messages": [{"role": "user", "content": "Write that information into a file called demo.md "}],
319323 "stream": false,
320- "thread_id": "test-hitl -1"
324+ "thread_id": "demo -1"
321325 }'
322326```
323327
324- Streaming
328+ The agent will pause and return ` finish_reason: "pending_approval" ` with the ` create_file ` tool call details.
329+
330+ ** Step 3: Approve the file creation**
325331
326332``` bash
327333curl -X POST https://< YOUR_ROUTE_URL> /chat/completions \
328334 -H " Content-Type: application/json" \
329335 -d ' {
330- "messages": [{"role": "user", "content": "Search for RedHat OpenShift"}],
331- "stream": true
336+ "messages": [{"role": "user", "content": ""}],
337+ "thread_id": "demo-1",
338+ "approval": "yes"
339+ }'
340+ ```
341+
342+ The agent resumes, executes ` create_file ` , and returns the final result.
343+
344+ Streaming (3-step HITL flow with ` stream: true ` ):
345+
346+ ** Step 1: Ask a general question (no approval needed)**
347+
348+ ``` bash
349+ curl -X POST https://langgraph-hitl-agent-tguzik-agents.apps.rosa.ai-eng-gpu.socc.p3.openshiftapps.com/chat/completions \
350+ -H " Content-Type: application/json" \
351+ -d ' {
352+ "messages": [{"role": "user", "content": "What is RedHat OpenShift Cluster"}],
353+ "stream": true,
354+ "thread_id": "demo-2"
355+ }'
356+ ```
357+
358+ ** Step 2: Ask to write that info into a file (triggers approval)**
359+
360+ ``` bash
361+ curl -X POST https://< YOUR_ROUTE_URL> /chat/completions \
362+ -H " Content-Type: application/json" \
363+ -d ' {
364+ "messages": [{"role": "user", "content": "Write that information into a file called demo.md"}],
365+ "stream": true,
366+ "thread_id": "demo-2"
367+ }'
368+ ```
369+
370+ ** Step 3: Approve the file creation**
371+
372+ ``` bash
373+ curl -X POST https://< YOUR_ROUTE_URL> /chat/completions \
374+ -H " Content-Type: application/json" \
375+ -d ' {
376+ "messages": [{"role": "user", "content": ""}],
377+ "stream": true,
378+ "thread_id": "demo-2",
379+ "approval": "yes"
332380 }'
333381```
334382
@@ -377,11 +425,15 @@ This agent extends the base LangGraph ReAct agent with:
377425
378426** Customization:**
379427
380- Edit ` src/human_in_the_loop/agent.py ` :
428+ Edit ` src/human_in_the_loop/agent.py ` to add more sensitive tools to the interrupt list :
381429
382430``` python
383- # Add more sensitive tools that require approval
384- SENSITIVE_TOOLS = {send_email.name, " delete_record" , " transfer_funds" }
431+ hitl_middleware = HumanInTheLoopMiddleware(
432+ interrupt_on = {
433+ " create_file" : True ,
434+ " delete_record" : True ,
435+ },
436+ )
385437```
386438
387439Edit ` src/human_in_the_loop/tools.py ` to add new tools:
0 commit comments