Skip to content
Back to skills

Multi Agent Reinforcement Learning

ASecurity

Use when designing multi-agent RL systems and environments.

  • 2 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 10, 2026
ai-agentspythongo

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add LoopyLuci/Skills --skill multi-agent-reinforcement-learning --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Multi Agent Reinforcement Learning?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Multi Agent Reinforcement Learning
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/loopyluci-multi-agent-reinforcement-learning/badge)](https://www.skillsdirectory.com/skills/loopyluci-multi-agent-reinforcement-learning)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: multi-agent-reinforcement-learning
description: "Use when designing multi-agent RL systems and environments."
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
  hermes:
    tags: [reinforcement-learning, multi-agent, MARL, cooperation, competition]
    related_skills: [reinforcement-learning, deep-reinforcement-learning, hierarchical-swarm-architectures, swarm-communication-protocols]
---

# Multi-Agent Reinforcement Learning (MARL)

Designing and implementing multi-agent reinforcement learning systems — cooperative, competitive, and mixed-motive environments with multiple learning agents interacting simultaneously.

## When to Use

- Training multiple AI agents that interact in a shared environment
- Cooperative scenarios (robots moving boxes together, traffic light coordination)
- Competitive scenarios (game-playing, bidding, negotiation)
- Mixed-motive scenarios (resource sharing with individual incentives)
- Swarm intelligence systems that learn rather than being programmed

## MARL Taxonomy

```
MARL
├── Cooperative (common reward)
├── Competitive (opposing rewards)
└── Mixed (individual + shared rewards)
    ├── Fully Decentralized
    ├── Centralized Training, Decentralized Execution (CTDE)
    └── Fully Centralized
```

## Algorithm Families

| Algorithm | Type | Centralization | Best For |
|-----------|------|----------------|----------|
| IQL | Value-based | Decentralized | Simple tasks |
| VDN | Value-based | CTDE (value decomposition) | Cooperative |
| QMIX | Value-based | CTDE (mixing network) | Cooperative |
| MADDPG | Actor-Critic | CTDE | Mixed/Competitive |
| MAPPO | Policy-gradient | CTDE | Cooperative |
| COMA | Actor-Critic | CTDE | Cooperative |
| IPPO | Policy-gradient | Decentralized | Simple (surprisingly good) |

## CTDE Architecture (Centralized Training, Decentralized Execution)

```python
class CTDEMADDPG:
    """Multi-Agent DDPG with Centralized Training, Decentralized Execution."""
    
    class Critic(nn.Module):
        """Centralized critic: observes all agents' states and actions."""
        def __init__(self, num_agents, state_dims, action_dims, hidden=256):
            super().__init__()
            total_state = sum(state_dims)
            total_action = sum(action_dims)
            self.net = nn.Sequential(
                nn.Linear(total_state + total_action, hidden),
                nn.ReLU(),
                nn.Linear(hidden, hidden),
                nn.ReLU(),
                nn.Linear(hidden, 1)
            )
        
        def forward(self, all_states, all_actions):
            x = torch.cat([*all_states, *all_actions], dim=-1)
            return self.net(x)
    
    class Actor(nn.Module):
        """Decentralized actor: observes only local state."""
        def __init__(self, state_dim, action_dim, hidden=256):
            super().__init__()
            self.net = nn.Sequential(
                nn.Linear(state_dim, hidden),
                nn.ReLU(),
                nn.Linear(hidden, hidden),
                nn.ReLU(),
                nn.Linear(hidden, action_dim),
                nn.Tanh()  # Bounded actions
            )
        
        def forward(self, state):
            return self.net(state)
    
    def __init__(self, num_agents, state_dims, action_dims, lr=1e-4):
        self.num_agents = num_agents
        self.actors = [self.Actor(s, a) for s, a in zip(state_dims, action_dims)]
        self.critics = [self.Critic(num_agents, state_dims, action_dims) for _ in range(num_agents)]
        
        # One critic per agent (or shared critic for cooperative)
        self.target_actors = [self.Actor(s, a) for s, a in zip(state_dims, action_dims)]
        self.target_critics = [self.Critic(num_agents, state_dims, action_dims) for _ in range(num_agents)]
        
        for ta, a in zip(self.target_actors, self.actors):
            ta.load_state_dict(a.state_dict())
        for tc, c in zip(self.target_critics, self.critics):
            tc.load_state_dict(c.state_dict())
        
        self.actor_opts = [torch.optim.Adam(a.parameters(), lr=lr) for a in self.actors]
        self.critic_opts = [torch.optim.Adam(c.parameters(), lr=lr) for c in self.critics]
    
    def update(self, experiences, gamma=0.95, tau=0.01):
        """Update all agents from a batch of joint experiences."""
        states = [exp['states'] for exp in experiences]
        actions = [exp['actions'] for exp in experiences]
        rewards = torch.stack([exp['rewards'] for exp in experiences])  # (num_agents, batch)
        next_states = [exp['next_states'] for exp in experiences]
        dones = torch.stack([exp['dones'] for exp in experiences])
        
        for i in range(self.num_agents):
            # Critic update (centralized)
            with torch.no_grad():
                next_actions = [ta(ns) for ta, ns in zip(self.target_actors, next_states)]
                target_q = self.target_critics[i](next_states, next_actions).squeeze()
                target_value = rewards[i] + gamma * (1 - dones[i]) * target_q
            
            current_q = self.critics[i](states, actions).squeeze()
            critic_loss = F.mse_loss(current_q, target_value)
            
            self.critic_opts[i].zero_grad()
            critic_loss.backward()
            self.critic_opts[i].step()
            
            # Actor update (decentralized — looks only at own critic)
            new_actions = [a(s) if j == i else a(s).detach() 
                          for j, (a, s) in enumerate(zip(self.actors, states))]
            actor_loss = -self.critics[i](states, new_actions).mean()
            
            self.actor_opts[i].zero_grad()
            actor_loss.backward()
            self.actor_opts[i].step()
        
        # Soft update targets
        for i in range(self.num_agents):
            for tp, p in zip(self.target_actors[i].parameters(), self.actors[i].parameters()):
                tp.data.copy_(tau * p + (1 - tau) * tp)
            for tp, p in zip(self.target_critics[i].parameters(), self.critics[i].parameters()):
                tp.data.copy_(tau * p + (1 - tau) * tp)
```

## QMIX (Value Decomposition)

```python
class QMIX:
    """QMIX: monotonic value decomposition for cooperative MARL."""
    
    class AgentNetwork(nn.Module):
        """Per-agent Q-network (receives own observation + action)."""
        def __init__(self, obs_dim, n_actions, hidden=64):
            super().__init__()
            self.net = nn.Sequential(
                nn.Linear(obs_dim, hidden),
                nn.ReLU(),
                nn.Linear(hidden, hidden),
                nn.ReLU(),
                nn.Linear(hidden, n_actions)
            )
        
        def forward(self, obs):
            return self.net(obs)
    
    class MixingNetwork(nn.Module):
        """Mixing net: combines agent Q-values into joint Q-value.
        Enforces monotonicity (positive weights) for CTDE consistency."""
        def __init__(self, num_agents, state_dim, hidden=32):
            super().__init__()
            self.hyper_w1 = nn.Linear(state_dim, hidden * num_agents)
            self.hyper_b1 = nn.Linear(state_dim, hidden)
            self.hyper_w2 = nn.Linear(state_dim, hidden)
            self.hyper_b2 = nn.Sequential(
                nn.Linear(state_dim, hidden),
                nn.ReLU(),
                nn.Linear(hidden, 1)
            )
        
        def forward(self, agent_qs, states):
            """agent_qs: (batch, num_agents), states: (batch, state_dim)"""
            batch = agent_qs.shape[0]
            
            w1 = torch.abs(self.hyper_w1(states)).view(batch, -1, self.num_agents)
            b1 = self.hyper_b1(states).view(batch, -1, 1)
            hidden = torch.bmm(w1, agent_qs.unsqueeze(-1)) + b1
            hidden = F.elu(hidden)
            
            w2 = torch.abs(self.hyper_w2(states)).view(batch, 1, -1)
            b2 = self.hyper_b2(states).view(batch, 1, 1)
            q_total = torch.bmm(w2, hidden) + b2
            
            return q_total.squeeze()
```

## Environment Design for MARL

```python
class MARLEnvironment:
    """Template for multi-agent environments."""
    def __init__(self, num_agents=4):
        self.num_agents = num_agents
        self.agents = [f'agent_{i}' for i in range(num_agents)]
    
    def reset(self):
        """Return initial observations for all agents."""
        self.step_count = 0
        return {agent: self._get_obs(agent) for agent in self.agents}
    
    def step(self, actions):
        """actions: dict {agent_id: action}. Returns obs, rewards, done, info."""
        self.step_count += 1
        
        # Apply actions, update state
        rewards = {}
        dones = {}
        
        for agent in self.agents:
            self._apply_action(agent, actions[agent])
        
        # Compute rewards (can be shared or per-agent)
        if self.cooperative_mode:
            team_reward = self._compute_team_reward()
            rewards = {a: team_reward for a in self.agents}
        else:
            rewards = {a: self._compute_individual_reward(a) for a in self.agents}
        
        dones = {a: self._check_done(a) for a in self.agents}
        dones['__all__'] = all(dones.values()) or self.step_count >= self.max_steps
        
        return {a: self._get_obs(a) for a in self.agents}, rewards, dones, {}
```

## Training Patterns

### Centralized Training Loop

```python
def train_mappo(env, num_episodes=1000):
    """MAPPO: CTDE policy gradient for cooperative MARL."""
    agents = [PPOAgent(obs_dim, action_dim) for _ in range(env.num_agents)]
    
    for episode in range(num_episodes):
        obs = env.reset()
        done = False
        
        while not done:
            actions = {}
            log_probs = {}
            values = {}
            
            for i, agent in enumerate(agents):
                action, log_prob = agent.network.act(torch.FloatTensor(obs[f'agent_{i}']))
                value = agent.network.get_value(torch.FloatTensor(obs[f'agent_{i}']))
                actions[f'agent_{i}'] = action
                log_probs[f'agent_{i}'] = log_prob
                values[f'agent_{i}'] = value
            
            next_obs, rewards, dones, _ = env.step(actions)
            
            # Store trajectory (shared reward for cooperative)
            for i, agent in enumerate(agents):
                agent.store_transition(obs[f'agent_{i}'], actions[f'agent_{i}'], 
                                      rewards[f'agent_{i}'], next_obs[f'agent_{i}'],
                                      dones[f'agent_{i}'], log_probs[f'agent_{i}'], values[f'agent_{i}'])
            
            obs = next_obs
            done = dones['__all__']
        
        # Update all agents
        for agent in agents:
            if len(agent.buffer) >= agent.batch_size:
                agent.update()
```

## Common Pitfalls

1. **Non-stationarity** — other agents' policies change, making the environment non-stationary from any one agent's view; CTDE helps
2. **Credit assignment** — in cooperative settings, which agent caused the reward? VDN/QMIX decomposes the joint Q
3. **Scalability** — joint action space grows exponentially with agents; use parameter sharing or mean-field approximations
4. **Relative overgeneralization** — agents converge to suboptimal cooperative behavior; use population-based training
5. **Communication overhead** — agents sharing full observations defeats decentralization; limit communication bandwidth
6. **Symmetric agents** — identical agents shouldn't learn different policies; use parameter sharing for homogeneous teams

## Verification Checklist

- [ ] Environment correctly implements MARL interface (per-agent obs, actions, rewards)
- [ ] Training converges in simple cooperative task (e.g., spread, push)
- [ ] Agents learn to cooperate (joint reward increases) OR compete (individual reward gaps widen)
- [ ] CTDE implementation has correct gradient flow (centralized critic, decentralized actor)
- [ ] Communication protocols (if any) have bandwidth limits verified
- [ ] Homogeneous agents share parameters correctly
- [ ] Evaluation: trained agents vs heuristic baselines

## See Also

- deep-reinforcement-learning — single-agent deep RL
- hierarchical-swarm-architectures — combining MARL with hierarchy
- swarm-communication-protocols — agent communication in MARL

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.