agentic-football-coach/kiro-football-coach/.kiro/skills/langgraph-agents/SKILL.md
Activate when the user chooses LangGraph or asks about LangChain/ReAct patterns
npx skillsauth add aws-samples/sample-ai-possibilities langgraph-agentsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive reference for building, enhancing, and deploying football agents with LangGraph and the LangChain ReAct pattern.
langchain-agent/
├── src/main.py # Main agent with LangGraph ReAct
├── test_local.py # Local testing (--llm flag for Bedrock)
├── requirements.txt # langgraph, langchain-aws dependencies
└── .bedrock_agentcore.yaml # AgentCore config
What the sample does:
create_react_agent with Amazon Bedrockpython3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Key packages: langgraph, langchain-aws, langchain-core, boto3
from langchain_aws import ChatBedrock
llm = ChatBedrock(
model_id="us.amazon.nova-micro-v1:0",
region_name="us-east-1",
model_kwargs={"temperature": 0.1}
)
Available models via Bedrock: Amazon Nova Micro (fast, low cost), Amazon Nova Lite, Amazon Nova Pro, Anthropic Claude (more capable reasoning).
python test_local.py # Without LLM (rule-based fallback)
python test_local.py --llm # With Bedrock LLM integration
LangGraph's create_react_agent implements the Reasoning + Acting (ReAct) loop:
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model=llm,
tools=[calculate_distance, evaluate_shot],
prompt=system_prompt
)
# Invoke the agent
result = agent.invoke({
"messages": [{"role": "user", "content": state_summary}]
})
The system prompt defines your agent's tactical identity:
system_prompt = """You are a football player agent in a 5v5 match.
You receive the current game state and must return ONE action.
Available actions: MOVE_TO, SHORT_PASS, LONG_PASS, THROUGH_PASS,
CROSS, SHOOT, DRIBBLE, TACKLE, SLIDE_TACKLE, INTERCEPT, MARK,
SPRINT, WALK, RUN, STOP, IDLE, HEADER
Rules:
- You have 500ms to respond
- Consider your stamina before sprinting
- Prioritize team coordination
- Return format: ACTION_TYPE param1 param2
"""
Tools give your agent callable functions for tactical analysis. LangGraph uses LangChain's @tool decorator:
from langchain_core.tools import tool
@tool
def calculate_distance(x1: float, y1: float, x2: float, y2: float) -> float:
"""Calculate distance between two points on the pitch.
Args:
x1: First point x coordinate
y1: First point y coordinate
x2: Second point x coordinate
y2: Second point y coordinate
"""
return ((x2 - x1)**2 + (y2 - y1)**2) ** 0.5
@tool
def evaluate_shot(player_x: float, player_y: float, goal_x: float) -> dict:
"""Evaluate whether a shot is advisable from current position.
Args:
player_x: Player x coordinate
player_y: Player y coordinate
goal_x: Goal x coordinate (55 or -55)
"""
distance = abs(player_x - goal_x)
return {
"should_shoot": distance < 30,
"power": max(0.5, min(1.0, 1.0 - distance / 100)),
"angle_quality": "good" if abs(player_y) < 15 else "wide"
}
@tool
def find_open_teammate(game_state: str) -> str:
"""Analyze game state to find the best passing option.
Args:
game_state: JSON string of current game state
"""
# Parse state, evaluate teammate positions, return best option
return "Player 3 is open at (20, 10)"
tools = [calculate_distance, evaluate_shot, find_open_teammate]
agent = create_react_agent(
model=llm,
tools=tools,
prompt=system_prompt
)
LangGraph uses a graph-based state model. The default MessagesState tracks conversation history:
from langgraph.graph import MessagesState
# Default state includes a "messages" list
# Each invocation appends to the message history
result = agent.invoke({"messages": [("user", state_summary)]})
final_message = result["messages"][-1].content
For tracking match-specific data across ticks:
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
class FootballState(TypedDict):
messages: Annotated[list, add_messages]
possession_count: int
shots_taken: int
last_action: str
def analyze_state(state: FootballState) -> FootballState:
"""Node that analyzes the current game state."""
# Process messages, update counters
return {"possession_count": state.get("possession_count", 0) + 1}
def decide_action(state: FootballState) -> FootballState:
"""Node that decides the next action."""
result = llm.invoke(state["messages"])
return {"messages": [result], "last_action": result.content}
graph = StateGraph(FootballState)
graph.add_node("analyze", analyze_state)
graph.add_node("decide", decide_action)
graph.add_edge("analyze", "decide")
graph.set_entry_point("analyze")
app = graph.compile()
For football agents, each tick is typically stateless (fresh invocation). If you want cross-tick memory:
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
agent = create_react_agent(model=llm, tools=tools, checkpointer=memory)
# Each invocation with the same thread_id shares history
config = {"configurable": {"thread_id": "match-001"}}
result = agent.invoke({"messages": [("user", state_summary)]}, config)
For more control than create_react_agent, build a graph manually:
from langgraph.graph import StateGraph, MessagesState, START, END
def call_model(state: MessagesState):
response = llm.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: MessagesState):
last = state["messages"][-1]
if last.tool_calls:
return "tools"
return END
graph = StateGraph(MessagesState)
graph.add_node("agent", call_model)
graph.add_node("tools", tool_node)
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph.add_edge("tools", "agent")
app = graph.compile()
Step 1: Test locally
python test_local.py --llm
Step 2: Deploy with AgentCore CLI
Install the CLI: https://github.com/aws/bedrock-agentcore-starter-toolkit
agentcore deploy
After deployment you receive a Runtime ARN — use it to register your agent for matches.
Step 3: View logs
aws logs tail /aws/bedrock-agentcore/runtimes/<runtime-id>-DEFAULT \
--log-stream-name-prefix "$(date +%Y/%m/%d)/[runtime-logs" --follow
Step 4: Register for matches
Use the Runtime ARN from the AgentCore console. You can use one ARN for all 5 players or create separate agents per player.
For a Lambda-based deployment:
# Lambda handler pattern
def lambda_handler(event, context):
game_state = event.get("game_state", {})
state_summary = summarize_game_state(game_state)
result = agent.invoke({"messages": [("user", state_summary)]})
return parse_action(result["messages"][-1].content)
Integrate Amazon Bedrock Guardrails for responsible AI:
llm = ChatBedrock(
model_id="us.amazon.nova-micro-v1:0",
guardrails={"guardrailIdentifier": "your-id", "guardrailVersion": "1"}
)
Use guardrails to filter inappropriate content and ensure agent responses stay within game action boundaries.
test_local.py with various game states to cover edge cases--llm flag to validate Bedrock integration| Technique | Description | |-----------|-------------| | Model selection | Nova Micro for speed, Claude for complex reasoning | | Parallel tool calls | Enable concurrent tool execution to reduce latency | | Efficient prompts | Minimize token count while preserving decision quality | | Selective tool use | Only invoke tools when the situation warrants analysis | | Temperature tuning | Lower temperature (0.0–0.2) for consistent tactical decisions | | Caching | Cache repeated calculations (distances, angles) across ticks |
Scale beyond a single agent per player:
from langgraph.graph import StateGraph
# Build a multi-step pipeline
graph = StateGraph(MessagesState)
graph.add_node("analyzer", analyze_game_state)
graph.add_node("strategist", plan_strategy)
graph.add_node("executor", execute_action)
graph.add_edge("analyzer", "strategist")
graph.add_edge("strategist", "executor")
graph.set_entry_point("analyzer")
pipeline = graph.compile()
| Aspect | LangGraph | Strands SDK |
|--------|-----------|-------------|
| Agent creation | create_react_agent(model, tools) | Agent(model=model, tools=tools) |
| Tool decorator | @tool from langchain_core.tools | @tool from strands |
| State model | Graph-based MessagesState | Key-value agent.state |
| Multi-agent | StateGraph with nodes and edges | Swarm, GraphBuilder, agents-as-tools |
| Conversation | MemorySaver checkpointer | ConversationManager classes |
| Streaming | .stream() / .astream() | agent.stream_async() |
| Model config | ChatBedrock(model_id=...) | BedrockModel(model_id=...) |
content-reference/en/langgraph-guide.md for full detaildevelopment
Activate when the user chooses Strands or asks about Strands SDK concepts
data-ai
Activate when discussing strategy, tactics, or agent behavior design
development
Activate when discussing game rules, player actions, scoring, fouls, cards, or match format
tools
Activate when discussing deployment, AgentCore CLI, or production infrastructure