Skip to content
Back to skills

Agentfly Memory Augmented Agents

ASecurity

Enable agent learning through episodic memory and neural case selection without fine-tuning the underlying LLM, achieving efficient continual adaptation via policy updates in memory space.

  • 6 stars
  • 0 votes
  • 0 copies
  • 7 views
  • Added September 9, 2026
ai-agentspythongitapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add ADu2021/skillXiv --skill agentfly-memory-augmented-agents --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Agentfly Memory Augmented Agents?

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

Security grade badge for Agentfly Memory Augmented Agents
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adu2021-agentfly-memory-augmented-agents/badge)](https://www.skillsdirectory.com/skills/adu2021-agentfly-memory-augmented-agents)

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: agentfly-memory-augmented-agents
title: "AgentFly: Memory-Augmented Learning Without LLM Fine-tuning"
version: 0.0.2
engine: skillxiv-v0.0.2-claude-opus-4.6
license: MIT
url: https://arxiv.org/abs/2508.16153
keywords: [memory-augmented-learning, agent-adaptation, episodic-memory, reinforcement-learning, in-context-learning]
description: "Enable agent learning through episodic memory and neural case selection without fine-tuning the underlying LLM, achieving efficient continual adaptation via policy updates in memory space."
---

# AgentFly: Memory-Augmented Learning Without LLM Fine-tuning

## Core Concept

AgentFly (Memento) enables language model agents to learn and adapt without fine-tuning the base LLM. Instead, it uses memory-augmented online reinforcement learning with episodic memory storage and a learned case-selection policy. Past experiences are stored in differentiable or non-parametric memory, and a neural policy selects relevant experiences to guide decision-making. This approach achieves rapid adaptation on research-oriented tasks while maintaining the benefits of frozen, pre-trained models.

## Architecture Overview

- **Episodic Memory System**: Stores experience tuples (state, action, outcome)
- **Neural Case-Selection Policy**: Learns which memories are relevant for decisions
- **Memory-Augmented MDP (M-MDP)**: Markov decision process with memory state
- **Continual Adaptation**: Policy updates through memory rewriting
- **Frozen LLM Base**: No gradient updates to base model parameters

## Implementation Steps

### 1. Design Episodic Memory Structure

Create abstraction for experience storage:

```python
from dataclasses import dataclass
from typing import Dict, List, Any, Optional
import numpy as np
from collections import deque

@dataclass
class Experience:
    """Single experience stored in memory."""
    state: str  # Task context or observation
    action: str  # Agent action taken
    observation: str  # Result/feedback
    reward: float  # Task reward signal
    success: bool  # Was action successful?
    timestamp: float
    metadata: Dict[str, Any] = None  # Additional context

@dataclass
class MemoryTrace:
    """Linked traces of related experiences."""
    experiences: List[Experience]
    task_id: str
    success_rate: float
    last_accessed: float

class EpisodicMemory:
    """Storage for agent experiences."""

    def __init__(
        self,
        max_size: int = 10000,
        similarity_metric: str = "embedding"
    ):
        self.experiences: deque = deque(maxlen=max_size)
        self.max_size = max_size
        self.similarity_metric = similarity_metric
        self.task_groups: Dict[str, List[Experience]] = {}

    def store_experience(self, experience: Experience):
        """Add experience to memory."""
        self.experiences.append(experience)

        # Group by task for later retrieval
        task_id = experience.metadata.get("task_id", "default")
        if task_id not in self.task_groups:
            self.task_groups[task_id] = []
        self.task_groups[task_id].append(experience)

    def retrieve_similar(
        self,
        query_state: str,
        k: int = 5,
        filter_task: Optional[str] = None
    ) -> List[Tuple[Experience, float]]:
        """Retrieve k most similar experiences to query."""

        candidates = self.task_groups.get(filter_task) if filter_task else list(self.experiences)

        if not candidates:
            return []

        # Compute similarity scores
        similarities = []
        for exp in candidates:
            similarity = self._compute_similarity(query_state, exp.state)
            similarities.append((exp, similarity))

        # Sort by similarity and return top-k
        similarities.sort(key=lambda x: x[1], reverse=True)
        return similarities[:k]

    def _compute_similarity(self, state1: str, state2: str) -> float:
        """Compute similarity between two states."""

        if self.similarity_metric == "embedding":
            # Use embedding-based similarity
            emb1 = self._encode(state1)
            emb2 = self._encode(state2)
            return np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2) + 1e-8)

        elif self.similarity_metric == "string":
            # Simple string overlap
            set1 = set(state1.split())
            set2 = set(state2.split())
            intersection = len(set1 & set2)
            union = len(set1 | set2)
            return intersection / (union + 1e-8)

        return 0.0

    def _encode(self, text: str) -> np.ndarray:
        """Encode text to embedding."""
        # In practice: use sentence encoder
        pass

    def get_memory_statistics(self) -> Dict[str, Any]:
        """Compute memory health metrics."""
        return {
            "total_experiences": len(self.experiences),
            "unique_tasks": len(self.task_groups),
            "avg_task_size": len(self.experiences) / max(len(self.task_groups), 1),
            "memory_utilization": len(self.experiences) / self.max_size
        }
```

### 2. Implement Neural Case-Selection Policy

Learn which memories to retrieve for decisions:

```python
import torch
import torch.nn as nn

class CaseSelectionPolicy(nn.Module):
    """Policy for selecting relevant cases from memory."""

    def __init__(
        self,
        embedding_dim: int = 768,
        hidden_dim: int = 512,
        max_cases: int = 5
    ):
        super().__init__()

        self.embedding_dim = embedding_dim
        self.max_cases = max_cases

        # Encoder for current state
        self.state_encoder = nn.Sequential(
            nn.Linear(embedding_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, embedding_dim)
        )

        # Relevance scorer for each candidate case
        self.relevance_scorer = nn.Sequential(
            nn.Linear(embedding_dim * 2, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, 1)
        )

        # Selection gate for whether to use memory at all
        self.use_memory_gate = nn.Sequential(
            nn.Linear(embedding_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, 1),
            nn.Sigmoid()
        )

    def forward(
        self,
        current_state: torch.Tensor,  # (embedding_dim,)
        candidate_cases: List[torch.Tensor],  # [(embedding_dim,), ...]
        return_scores: bool = False
    ) -> torch.Tensor:
        """
        Select top cases and return their indices/scores.
        """
        # Encode current state
        state_encoded = self.state_encoder(current_state)

        # Compute relevance for each candidate
        relevance_scores = []
        for case in candidate_cases:
            combined = torch.cat([state_encoded, case])
            score = self.relevance_scorer(combined)
            relevance_scores.append(score)

        relevance_scores = torch.stack(relevance_scores).squeeze(-1)

        # Decide whether to use memory
        memory_use_prob = self.use_memory_gate(state_encoded)

        # Select top-k
        topk_scores, topk_indices = torch.topk(
            relevance_scores,
            k=min(self.max_cases, len(candidate_cases))
        )

        if return_scores:
            return topk_indices, topk_scores

        return topk_indices

    def train_on_trajectory(
        self,
        states: List[torch.Tensor],
        selected_cases: List[int],
        outcomes: List[float],
        gamma: float = 0.99
    ):
        """Train policy on trajectory data."""

        # Compute returns
        returns = []
        G = 0
        for outcome in reversed(outcomes):
            G = outcome + gamma * G
            returns.insert(0, G)

        returns = torch.tensor(returns, dtype=torch.float32)
        returns = (returns - returns.mean()) / (returns.std() + 1e-8)

        # Policy gradient loss
        total_loss = 0.0
        for state, case_idx, ret in zip(states, selected_cases, returns):
            # Get probability of selected case
            with torch.no_grad():
                logits = self.relevance_scorer(state)

            # Compute policy gradient
            log_prob = torch.log_softmax(logits, dim=-1)[case_idx]
            loss = -log_prob * ret

            total_loss += loss

        return total_loss / len(states)
```

### 3. Implement Memory-Augmented MDP

Integrate memory into decision process:

```python
class MemoryAugmentedMDP:
    """Markov decision process with episodic memory."""

    def __init__(
        self,
        base_model: "LLM",
        memory: EpisodicMemory,
        selection_policy: CaseSelectionPolicy,
        max_memory_context_length: int = 2000
    ):
        self.base_model = base_model
        self.memory = memory
        self.selection_policy = selection_policy
        self.max_context_length = max_memory_context_length

    def get_action(
        self,
        task_description: str,
        current_observation: str,
        use_memory: bool = True
    ) -> Tuple[str, Dict[str, Any]]:
        """
        Select action considering memory context.
        """

        # Retrieve relevant experiences from memory
        relevant_experiences = []
        if use_memory and len(self.memory.experiences) > 0:
            relevant_experiences = self.memory.retrieve_similar(
                current_observation,
                k=5,
                filter_task=task_description
            )

        # Build context for LLM
        context = self._build_context(
            task_description,
            current_observation,
            relevant_experiences
        )

        # Get action from LLM
        prompt = f"{context}\n\nNext action:"
        action = self.base_model.generate(prompt, max_tokens=100)

        return action, {"context_length": len(context), "memories_used": len(relevant_experiences)}

    def _build_context(
        self,
        task: str,
        observation: str,
        experiences: List[Tuple[Experience, float]]
    ) -> str:
        """Build LLM context including relevant memories."""

        context = f"Task: {task}\n\nCurrent Observation: {observation}\n"

        if experiences:
            context += "\nRelevant Past Experiences:\n"
            for i, (exp, score) in enumerate(experiences[:3]):  # Top 3
                context += f"{i+1}. Previous: {exp.state}\n"
                context += f"   Action: {exp.action}\n"
                context += f"   Result: {exp.observation}\n"
                context += f"   Success: {exp.success}\n"

        return context

    def collect_rollout(
        self,
        task_description: str,
        max_steps: int = 20
    ) -> List[Experience]:
        """Collect trajectory using memory-augmented decisions."""

        trajectory = []
        observation = "Initial state"

        for step in range(max_steps):
            # Get action from MDP
            action, metadata = self.get_action(task_description, observation)

            # Execute action (simulated or real)
            new_observation, reward, done = self._execute_action(
                task_description,
                action
            )

            # Store experience
            experience = Experience(
                state=observation,
                action=action,
                observation=new_observation,
                reward=reward,
                success=(reward > 0.5),
                timestamp=0,
                metadata={"task_id": task_description, **metadata}
            )

            trajectory.append(experience)

            if done:
                break

            observation = new_observation

        return trajectory

    def _execute_action(
        self,
        task: str,
        action: str
    ) -> Tuple[str, float, bool]:
        """Execute action and get feedback."""
        # In practice: execute in real environment or simulator
        pass
```

### 4. Implement Continual Adaptation

Update memory and policy without LLM fine-tuning:

```python
class ContinualAdapter:
    """Handles continual learning without LLM updates."""

    def __init__(
        self,
        mdp: MemoryAugmentedMDP,
        selection_policy: CaseSelectionPolicy,
        learning_rate: float = 1e-4
    ):
        self.mdp = mdp
        self.selection_policy = selection_policy
        self.optimizer = torch.optim.Adam(
            selection_policy.parameters(),
            lr=learning_rate
        )

    def adapt_to_trajectory(self, trajectory: List[Experience]):
        """Update memory and policy based on new trajectory."""

        # Store all experiences in memory
        for exp in trajectory:
            self.mdp.memory.store_experience(exp)

        # Train selection policy on successful outcomes
        successful_trajectory = [e for e in trajectory if e.success]

        if successful_trajectory:
            self._train_selection_policy(successful_trajectory)

        # Clean up memory if needed
        if len(self.mdp.memory.experiences) > self.mdp.memory.max_size * 0.9:
            self._prune_memory()

    def _train_selection_policy(self, trajectory: List[Experience]):
        """Train policy on successful trajectory."""

        # Encode states
        states = [self._encode_state(e.state) for e in trajectory]
        outcomes = [e.reward for e in trajectory]

        # Get selected cases (which ones would the policy choose?)
        selected_cases = []
        for state in states:
            candidates = self._get_candidate_embeddings(state)
            indices = self.selection_policy(
                state, candidates, return_scores=False
            )
            selected_cases.append(indices[0].item())

        # Compute policy gradient loss
        loss = self.selection_policy.train_on_trajectory(
            states, selected_cases, outcomes
        )

        # Update policy
        self.optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(self.selection_policy.parameters(), 1.0)
        self.optimizer.step()

        return loss.item()

    def _prune_memory(self):
        """Remove least useful experiences."""
        # Keep only high-success experiences or recently accessed
        pass

    def _encode_state(self, state: str) -> torch.Tensor:
        """Encode state to embedding."""
        pass

    def _get_candidate_embeddings(self, state_embedding) -> List[torch.Tensor]:
        """Get embeddings of candidate memory cases."""
        pass
```

### 5. Evaluate Agent Learning

Measure adaptation efficiency without LLM fine-tuning:

```python
def evaluate_agentfly_learning(
    mdp: MemoryAugmentedMDP,
    test_tasks: List[Dict],
    num_rollouts_per_task: int = 3
) -> Dict[str, float]:
    """
    Evaluate memory-augmented agent performance.
    """

    task_successes = []
    memory_hit_rates = []
    trajectory_lengths = []

    for task in test_tasks:
        task_desc = task["description"]
        expected_solution = task["expected"]

        for _ in range(num_rollouts_per_task):
            # Collect trajectory
            trajectory = mdp.collect_rollout(task_desc, max_steps=50)

            # Evaluate success
            final_obs = trajectory[-1].observation if trajectory else ""
            success = evaluate_task_success(final_obs, expected_solution)
            task_successes.append(success)

            # Track memory efficiency
            memory_hits = sum(
                1 for exp in trajectory
                if exp.metadata.get("memories_used", 0) > 0
            )
            memory_hit_rates.append(memory_hits / len(trajectory))
            trajectory_lengths.append(len(trajectory))

            # Adapt to this trajectory
            mdp_adapter = ContinualAdapter(mdp, mdp.selection_policy)
            mdp_adapter.adapt_to_trajectory(trajectory)

    return {
        "avg_success_rate": sum(task_successes) / len(task_successes),
        "avg_memory_hit_rate": sum(memory_hit_rates) / len(memory_hit_rates),
        "avg_trajectory_length": sum(trajectory_lengths) / len(trajectory_lengths),
        "memory_size": len(mdp.memory.experiences)
    }
```

## Practical Guidance

### When to Use AgentFly

- Research tasks requiring rapid adaptation (GAIA, DeepResearcher)
- Scenarios where LLM fine-tuning is expensive/prohibited
- Agents needing to learn from few examples
- Continual learning systems with non-stationary tasks
- Open-source or proprietary model deployment

### When NOT to Use

- Tasks where base model knowledge is insufficient
- Extremely low-latency inference requirements
- Scenarios with no clear episodic structure
- Tasks requiring fundamental capability changes

### Key Hyperparameters

- **max_memory_size**: 5000-20000 experiences
- **memory_context_length**: 1000-3000 tokens
- **learning_rate**: 1e-4 to 1e-3
- **k (top cases retrieved)**: 3-5 usually sufficient
- **gamma (discount factor)**: 0.99

### Performance Expectations

- GAIA Benchmark: 87.88% Pass@3
- Adaptation Speed: 2-5 examples sufficient for new task class
- Memory Efficiency: Sub-linear growth with task diversity
- No LLM Fine-tuning: Entire approach requires 0 LLM updates

## Reference

Researchers. (2024). AgentFly: Fine-tuning LLM Agents without Fine-tuning LLMs. arXiv preprint arXiv:2508.16153.

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.