Initial commit
This commit is contained in:
338
skills/langgraph-master/02_graph_architecture_agent.md
Normal file
338
skills/langgraph-master/02_graph_architecture_agent.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Agent (Autonomous Tool Usage)
|
||||
|
||||
A pattern where the LLM dynamically determines tool selection to handle unpredictable problem-solving.
|
||||
|
||||
## Overview
|
||||
|
||||
The Agent pattern follows **ReAct** (Reasoning + Acting), where the LLM dynamically selects and executes tools to solve problems.
|
||||
|
||||
## ReAct Pattern
|
||||
|
||||
**ReAct** = Reasoning + Acting
|
||||
|
||||
1. **Reasoning**: Think "What should I do next?"
|
||||
2. **Acting**: Take action using tools
|
||||
3. **Observing**: Observe the results
|
||||
4. **Repeat steps 1-3** until reaching a final answer
|
||||
|
||||
## Implementation Example: Basic Agent
|
||||
|
||||
```python
|
||||
from langgraph.graph import StateGraph, START, END, MessagesState
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from typing import Literal
|
||||
|
||||
# Tool definitions
|
||||
@tool
|
||||
def search(query: str) -> str:
|
||||
"""Execute web search"""
|
||||
return perform_search(query)
|
||||
|
||||
@tool
|
||||
def calculator(expression: str) -> float:
|
||||
"""Execute calculation"""
|
||||
return eval(expression)
|
||||
|
||||
tools = [search, calculator]
|
||||
|
||||
# Agent node
|
||||
def agent_node(state: MessagesState):
|
||||
"""LLM determines tool usage"""
|
||||
messages = state["messages"]
|
||||
|
||||
# Invoke LLM with tools
|
||||
response = llm_with_tools.invoke(messages)
|
||||
|
||||
return {"messages": [response]}
|
||||
|
||||
# Continue decision
|
||||
def should_continue(state: MessagesState) -> Literal["tools", "end"]:
|
||||
"""Check if there are tool calls"""
|
||||
last_message = state["messages"][-1]
|
||||
|
||||
# Continue if there are tool calls
|
||||
if last_message.tool_calls:
|
||||
return "tools"
|
||||
|
||||
# End if no tool calls (final answer)
|
||||
return "end"
|
||||
|
||||
# Build graph
|
||||
builder = StateGraph(MessagesState)
|
||||
|
||||
builder.add_node("agent", agent_node)
|
||||
builder.add_node("tools", ToolNode(tools))
|
||||
|
||||
builder.add_edge(START, "agent")
|
||||
|
||||
# ReAct loop
|
||||
builder.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
{
|
||||
"tools": "tools",
|
||||
"end": END
|
||||
}
|
||||
)
|
||||
|
||||
# Return to agent after tool execution
|
||||
builder.add_edge("tools", "agent")
|
||||
|
||||
graph = builder.compile()
|
||||
```
|
||||
|
||||
## Tool Definitions
|
||||
|
||||
### Basic Tools
|
||||
|
||||
```python
|
||||
from langchain_core.tools import tool
|
||||
|
||||
@tool
|
||||
def get_weather(location: str) -> str:
|
||||
"""Get weather for the specified location.
|
||||
|
||||
Args:
|
||||
location: City name (e.g., "Tokyo", "New York")
|
||||
"""
|
||||
return fetch_weather_data(location)
|
||||
|
||||
@tool
|
||||
def send_email(to: str, subject: str, body: str) -> str:
|
||||
"""Send an email.
|
||||
|
||||
Args:
|
||||
to: Recipient email address
|
||||
subject: Email subject
|
||||
body: Email body
|
||||
"""
|
||||
return send_email_api(to, subject, body)
|
||||
```
|
||||
|
||||
### Structured Output Tools
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class WeatherResponse(BaseModel):
|
||||
location: str
|
||||
temperature: float
|
||||
condition: str
|
||||
humidity: int
|
||||
|
||||
@tool(response_format="content_and_artifact")
|
||||
def get_detailed_weather(location: str) -> tuple[str, WeatherResponse]:
|
||||
"""Get detailed weather information"""
|
||||
data = fetch_weather_data(location)
|
||||
|
||||
weather = WeatherResponse(
|
||||
location=location,
|
||||
temperature=data["temp"],
|
||||
condition=data["condition"],
|
||||
humidity=data["humidity"]
|
||||
)
|
||||
|
||||
message = f"Weather in {location}: {weather.condition}, {weather.temperature}°C"
|
||||
|
||||
return message, weather
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pattern 1: Multi-Agent Collaboration
|
||||
|
||||
```python
|
||||
# Specialist agents
|
||||
def research_agent(state: State):
|
||||
"""Research specialist agent"""
|
||||
response = research_llm_with_tools.invoke(state["messages"])
|
||||
return {"messages": [response]}
|
||||
|
||||
def coding_agent(state: State):
|
||||
"""Coding specialist agent"""
|
||||
response = coding_llm_with_tools.invoke(state["messages"])
|
||||
return {"messages": [response]}
|
||||
|
||||
# Router
|
||||
def route_to_specialist(state: State) -> Literal["research", "coding"]:
|
||||
"""Select specialist based on task"""
|
||||
last_message = state["messages"][-1]
|
||||
|
||||
if "research" in last_message.content or "search" in last_message.content:
|
||||
return "research"
|
||||
elif "code" in last_message.content or "implement" in last_message.content:
|
||||
return "coding"
|
||||
|
||||
return "research" # Default
|
||||
```
|
||||
|
||||
### Pattern 2: Agent with Memory
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[list, add_messages]
|
||||
context: dict # Long-term memory
|
||||
|
||||
def agent_with_memory(state: AgentState):
|
||||
"""Agent utilizing context"""
|
||||
messages = state["messages"]
|
||||
context = state.get("context", {})
|
||||
|
||||
# Add context to prompt
|
||||
system_message = f"Context: {context}"
|
||||
|
||||
response = llm_with_tools.invoke([
|
||||
{"role": "system", "content": system_message},
|
||||
*messages
|
||||
])
|
||||
|
||||
return {"messages": [response]}
|
||||
|
||||
# Compile with checkpointer
|
||||
checkpointer = MemorySaver()
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
```
|
||||
|
||||
### Pattern 3: Human-in-the-Loop Agent
|
||||
|
||||
```python
|
||||
from langgraph.types import interrupt
|
||||
|
||||
def careful_agent(state: State):
|
||||
"""Confirm with human before important actions"""
|
||||
response = llm_with_tools.invoke(state["messages"])
|
||||
|
||||
# Request confirmation for important tool calls
|
||||
if response.tool_calls:
|
||||
for tool_call in response.tool_calls:
|
||||
if tool_call["name"] in ["send_email", "delete_data"]:
|
||||
# Wait for human approval
|
||||
approved = interrupt({
|
||||
"action": tool_call["name"],
|
||||
"args": tool_call["args"],
|
||||
"message": "Approve this action?"
|
||||
})
|
||||
|
||||
if not approved:
|
||||
return {
|
||||
"messages": [
|
||||
{"role": "assistant", "content": "Action cancelled by user"}
|
||||
]
|
||||
}
|
||||
|
||||
return {"messages": [response]}
|
||||
```
|
||||
|
||||
### Pattern 4: Error Handling and Retry
|
||||
|
||||
```python
|
||||
class RobustAgentState(TypedDict):
|
||||
messages: Annotated[list, add_messages]
|
||||
retry_count: int
|
||||
errors: list[str]
|
||||
|
||||
def robust_tool_node(state: RobustAgentState):
|
||||
"""Tool execution with error handling"""
|
||||
last_message = state["messages"][-1]
|
||||
tool_results = []
|
||||
|
||||
for tool_call in last_message.tool_calls:
|
||||
try:
|
||||
result = execute_tool(tool_call)
|
||||
tool_results.append(result)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Tool {tool_call['name']} failed: {str(e)}"
|
||||
|
||||
# Check if retry is possible
|
||||
if state.get("retry_count", 0) < 3:
|
||||
tool_results.append({
|
||||
"tool_call_id": tool_call["id"],
|
||||
"error": error_msg,
|
||||
"retry": True
|
||||
})
|
||||
else:
|
||||
tool_results.append({
|
||||
"tool_call_id": tool_call["id"],
|
||||
"error": "Max retries exceeded",
|
||||
"retry": False
|
||||
})
|
||||
|
||||
return {
|
||||
"messages": tool_results,
|
||||
"retry_count": state.get("retry_count", 0) + 1
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Tool Features
|
||||
|
||||
### Dynamic Tool Generation
|
||||
|
||||
```python
|
||||
def create_tool_for_api(api_spec: dict):
|
||||
"""Dynamically generate tool from API specification"""
|
||||
|
||||
@tool
|
||||
def dynamic_api_tool(**kwargs) -> str:
|
||||
f"""
|
||||
{api_spec['description']}
|
||||
|
||||
Args: {api_spec['parameters']}
|
||||
"""
|
||||
return call_api(api_spec['endpoint'], kwargs)
|
||||
|
||||
return dynamic_api_tool
|
||||
```
|
||||
|
||||
### Conditional Tool Usage
|
||||
|
||||
```python
|
||||
def conditional_agent(state: State):
|
||||
"""Change toolset based on situation"""
|
||||
context = state.get("context", {})
|
||||
|
||||
# Basic tools only for beginners
|
||||
if context.get("user_level") == "beginner":
|
||||
tools = [basic_search, simple_calculator]
|
||||
# Advanced tools for advanced users
|
||||
else:
|
||||
tools = [advanced_search, scientific_calculator, code_executor]
|
||||
|
||||
llm_with_selected_tools = llm.bind_tools(tools)
|
||||
response = llm_with_selected_tools.invoke(state["messages"])
|
||||
|
||||
return {"messages": [response]}
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Flexibility**: Dynamically responds to unpredictable problems
|
||||
✅ **Autonomy**: LLM selects optimal tools and strategies
|
||||
✅ **Extensibility**: Extend functionality by simply adding tools
|
||||
✅ **Adaptability**: Solves complex multi-step tasks
|
||||
|
||||
## Considerations
|
||||
|
||||
⚠️ **Unpredictability**: May behave differently with same input
|
||||
⚠️ **Cost**: Multiple LLM calls occur
|
||||
⚠️ **Infinite Loops**: Proper termination conditions required
|
||||
⚠️ **Tool Misuse**: LLM may use tools incorrectly
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Clear Tool Descriptions**: Write detailed tool docstrings
|
||||
2. **Maximum Iterations**: Set upper limit for loops
|
||||
3. **Error Handling**: Handle tool execution errors appropriately
|
||||
4. **Logging**: Make agent behavior traceable
|
||||
|
||||
## Summary
|
||||
|
||||
The Agent pattern is optimal for **dynamic and uncertain problem-solving**. It autonomously solves problems using tools through the ReAct loop.
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [02_graph_architecture_workflow_vs_agent.md](02_graph_architecture_workflow_vs_agent.md) - Differences between Workflow and Agent
|
||||
- [04_tool_integration_overview.md](04_tool_integration_overview.md) - Tool details
|
||||
- [05_advanced_features_human_in_the_loop.md](05_advanced_features_human_in_the_loop.md) - Human intervention
|
||||
Reference in New Issue
Block a user