This commit is contained in:
Eugene Yurtsev
2024-12-10 22:52:17 -05:00
parent 51f85ffa84
commit 789c732866
+109 -75
View File
@@ -13,10 +13,37 @@ A **human-in-the-loop** (or "on-the-loop") workflow integrates human input into
Key use cases for **human-in-the-loop** workflows in LLM-based applications include:
1. **🛠️ [Reviewing tool calls](#review-and-edit)**: Humans can review, edit, or approve tool calls requested by the LLM before tool execution.
1. **🛠️ Reviewing tool calls**: Humans can review, edit, or approve tool calls requested by the LLM before tool execution.
2. **✅ Validating LLM outputs**: Humans can review, edit, or approve content generated by the LLM.
3. **💡 Providing context**: Enable the LLM to explicitly request human input for clarification or additional details or to support multi-turn conversations.
## `interrupt`
The [`interrupt` function][langgraph.types.interrupt] in LangGraph enables human-in-the-loop workflows by pausing the graph at a specific node, presenting information to a human, and resuming the graph with their input. This function is useful for tasks like approvals, edits, or collecting additional input. The [`interrupt` function][langgraph.types.interrupt] is used in conjunction with the [`Command`](../reference/types.md#langgraph.types.Command) object to resume the graph with a value provided by the human.
```python
from langgraph.types import interrupt
def human_node(state: State):
value = interrupt(
# Any JSON serializable value to surface to the human.
# For example, a question or a piece of text or a set of keys in the state
some_data
)
...
# Update the state with the human's input or route the graph based on the input.
...
# Run the graph and hit the breakpoint
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke(some_input, config=thread_config)
# Resume the graph with the human's input
graph.invoke(Command(resume=value_from_human), config=thread_config)
```
Please read the [Breakpoints](breakpoints.md) guide for more information on using the `interrupt` function.
## Design Patterns
1. **Approval**: Pause the graph before a critical step, such as an API call, to review and approve the action. If the action is rejected, you can prevent the graph from executing the step, and potentially take an alternative action.
@@ -24,103 +51,103 @@ Key use cases for **human-in-the-loop** workflows in LLM-based applications incl
3. **Input**: Explicitly request human input at a particular step in the graph. This is useful for collecting additional information or context to inform the agent's decision-making process or for supporting **multi-turn conversations**.
### Approval
=== "Approval"
<figure markdown="1">
![image](img/human_in_the_loop/approve-or-reject.png){: style="max-height:400px"}
<figcaption>Depending on the human's approval or rejection, the graph can proceed with the action or take an alternative path.</figcaption>
</figure>
<figure markdown="1">
![image](img/human_in_the_loop/approve-or-reject.png){: style="max-height:400px"}
<figcaption>Depending on the human's approval or rejection, the graph can proceed with the action or take an alternative path.</figcaption>
</figure>
Pause the graph before a critical step, such as an API call, to review and approve the action. If the action is rejected, you can prevent the graph from executing the step, and potentially take an alternative action.
```python
from langgraph.types import interrupt
def human_approval(state: State):
...
is_approved = interrupt(
{
"question": "Is this correct?",
# Surface the output that should be
# reviewed and approved by the human.
"llm_output": state["llm_output"]
}
)
if is_approved:
# Proceed with the action
...
else:
# Do something else
...
# Add the node to the graph in an appropriate location
# and connect it to the relevant nodes.
graph_builder.add_node("human_approval", human_approval)
graph = graph_builder.compile(checkpointer=checkpointer)
...
# After running the graph and hitting the breakpoint, the graph will pause.
# Resume it with either an approval or rejection.
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke(Command(resume=True), config=thread_config)
```
### Edit
<figure markdown="1">
![image](img/human_in_the_loop/tool-call-review.png){: style="max-height:400px"}
<figcaption>A human can review and edit the output from the LLM before proceeding. This is particularly
critical in applications where the tool calls requested by the LLM may be sensitive or require human oversight.
</figcaption>
</figure>
=== "Review tool calls"
TODO: Create an example for tool call review.
=== "Review text output from the LLM and make any necessary edits."
Pause the graph before a critical step, such as an API call, to review and approve the action. If the action is rejected, you can prevent the graph from executing the step, and potentially take an alternative action.
```python
from langgraph.types import interrupt
def human_editing(state: State):
def human_approval(state: State):
...
result = interrupt(
# Interrupt information to surface to the client.
# Can be any JSON serializable value.
is_approved = interrupt(
{
"task": "Review the output from the LLM and make any necessary edits.",
"question": "Is this correct?",
# Surface the output that should be
# reviewed and approved by the human.
"llm_output": state["llm_output"]
}
)
# Update the state with the edited text
return {
"llm_output": result["edited_text"]
}
if is_approved:
# Proceed with the action
...
else:
# Do something else
...
# Add the node to the graph in an appropriate location
# and connect it to the relevant nodes.
graph_builder.add_node("human_editing", human_editing)
graph_builder.add_node("human_approval", human_approval)
graph = graph_builder.compile(checkpointer=checkpointer)
...
# After running the graph and hitting the breakpoint, the graph will pause.
# Resume it with the edited text.
# Resume it with either an approval or rejection.
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke(
Command(resume={"edited_text": "The edited text"}),
config=thread_config
)
graph.invoke(Command(resume=True), config=thread_config)
```
### Multi-turn conversation (Input)
=== "Review & Edit"
<figure markdown="1">
![image](img/human_in_the_loop/tool-call-review.png){: style="max-height:400px"}
<figcaption>A human can review and edit the output from the LLM before proceeding. This is particularly
critical in applications where the tool calls requested by the LLM may be sensitive or require human oversight.
</figcaption>
</figure>
=== "Review tool calls"
TODO: Create an example for tool call review.
=== "Review text output from the LLM and make any necessary edits."
```python
from langgraph.types import interrupt
def human_editing(state: State):
...
result = interrupt(
# Interrupt information to surface to the client.
# Can be any JSON serializable value.
{
"task": "Review the output from the LLM and make any necessary edits.",
"llm_output": state["llm_output"]
}
)
# Update the state with the edited text
return {
"llm_output": result["edited_text"]
}
# Add the node to the graph in an appropriate location
# and connect it to the relevant nodes.
graph_builder.add_node("human_editing", human_editing)
graph = graph_builder.compile(checkpointer=checkpointer)
...
# After running the graph and hitting the breakpoint, the graph will pause.
# Resume it with the edited text.
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke(
Command(resume={"edited_text": "The edited text"}),
config=thread_config
)
```
### Multi-turn conversation
<figure markdown="1">
![image](img/human_in_the_loop/multi-turn-conversation.png){: style="max-height:400px"}
@@ -164,6 +191,13 @@ graph.invoke(
)
```
## Best practices
* Use the [`interrupt`](breakpoints.md#the-interrupt-function) function to set breakpoints and collect user input.
* Use [`Command`](breakpoints.md#the-command-primitive) to resume execution and control the graph state.
* Consider putting all side effects (e.g., API calls) after the `interrupt` to prevent duplication.
* Understand [how resuming from a breakpoint works](breakpoints.md#how-does-resuming-from-a-breakpoint-work) to avoid common gotchas.
## Additional Resources 📚
- [**Conceptual Guide: Persistence**](persistence.md#replay): Read the persistence guide for more context on replaying.