A unified, extensible, and easy-to-use evaluation framework for memory agents.
This project is designed to evaluate memory-augmented agents under a common protocol: given a stream of memory chunks, an agent incrementally builds memory, answers questions at specified positions, and is scored with benchmark-specific metrics in a shared output format.
The framework supports both simple baselines such as concat/RAG and tool-using memory agents such as MemAgent, MemAlpha, MemT, UMA, and SMA. SMA training is maintained in a separate reproduction project; this repository stays focused on benchmark execution, scoring, and result analysis.
Memory-agent results are often hard to compare because different projects use different data formats, prompts, generation scripts, judge prompts, output files, and metrics. This repository aims to make those comparisons cleaner.
Core goals:
- Unified evaluation protocol: all agents consume the same
EvalDataformat and emit the sameresponses_*.jsonl/evaluated_*.jsonlfiles. - Extensible benchmark registry: benchmarks declare their loader, evaluator, metric profile, and agent data source in one place.
- Extensible agent registry: agents declare import path, adapter behavior, and runtime defaults in one place.
- Reusable runners: generation, evaluation, resume, sharding, and JSONL repair are handled by shared runner/runtime modules.
- Easy baseline execution:
maa generateruns baselines with explicit task lists or--tasks alland automatic service management. - Clean runtime boundary: training code is kept out of the arena, while lightweight UMA/SMA evaluation loops remain available through
external/memory_agent.
agents/ Memory-agent implementations and agent registry/adapters
benchmarks/ BenchmarkSpec and central benchmark registry
data/ Dataset builders and processed benchmark data
evaluation/ Evaluator implementations and evaluator registry
scripts/experimental/ Experiment-specific helper scripts
scripts/patches/ Environment compatibility patch helpers
scripts/run/ Optional shell wrappers around the `maa` CLI
scripts/tools/ Standalone utility scripts
external/ External agent runtimes and third-party benchmark code
docs/ Analysis notes and implementation docs
assets/ Figures
evaluate_async.py CLI entry point for generation/evaluation
evaluation_runner.py Shared generation/evaluation runners
benchmark_runtime.py JSONL output, resume, and metric policy helpers
generate_stats.py Result aggregation and plotting
The framework is organized around three registries/adapters:
benchmarks/registry.py: defines benchmark behavior throughBenchmarkSpec.agents/registry.py: defines available agents throughAgentSpec, including runtime defaults such as env vars, model, and concurrency.agents/adapters.py: adapts different agent APIs into one runner-facing protocol.evaluation/registry.py: maps benchmark specs to evaluator implementations.
At runtime, the flow is:
BenchmarkSpec -> load EvalData
AgentSpec + AgentAdapter -> generate responses
JsonlOutputStore -> write/resume responses_*.jsonl
Evaluator -> score responses
MetricPolicy -> summarize evaluated_*.jsonl
This means adding a benchmark or agent should not require editing the core runner.
- Python 3.10+
uv- CUDA-capable GPU for local vLLM/SGLang inference
uv sync
source .venv/bin/activateCUDA/SGLang compatibility patches used by this project:
python scripts/patches/patch_triton.py
python scripts/patches/patch_sglang.pyIf your environment needs a prebuilt FlashAttention wheel, install it after uv sync.
Create a .env file when using local or remote OpenAI-compatible APIs.
Common variables:
LOCAL_API_BASE=http://127.0.0.1:8000/v1
OPENAI_API_KEY=EMPTY
OPENROUTER_API_KEY=...For UMA/SMA agent loops:
PROMPT_TEMPLATE_PATH=external/memory_agent/memory_agent_runtime/sma/prompts/prompt_template.yaml
UMA_PROMPT_TEMPLATE_PATH=external/memory_agent/memory_agent_runtime/uma/prompts/prompt_template.yaml
VERL_TOKENIZER_PATH=/path/to/tokenizerStart a local OpenAI-compatible inference server, for example vLLM:
source .venv/bin/activate
vllm serve Qwen/Qwen3-4B-Instruct-2507 \
--host 0.0.0.0 \
--port 8000 \
--max-model-len 32768Run one agent on one task:
python evaluate_async.py \
--task locomo \
--agent concat \
--output-dir results/qwen3-4b/locomo \
--generate-onlyScore an existing responses file:
python evaluate_async.py \
--task locomo \
--input-file results/qwen3-4b/locomo/responses_concat.jsonl \
--output-dir results/qwen3-4b/locomoUse DeepSeek as the judge. DeepSeek scores are written into the same
evaluated_*.jsonl files as metric.deepseek_llm_score:
python evaluate_async.py \
--task locomo \
--input-file results/qwen3-4b/locomo/responses_concat.jsonl \
--output-dir results/qwen3-4b/locomo \
--deepseek-evalMemoryAgent Arena provides a unified CLI, similar in spirit to lm_eval.
Without reinstalling the environment, use the module form:
python -m memory_agent_arena.cli tasks
python -m memory_agent_arena.cli tasks --detail
python -m memory_agent_arena.cli agentsAfter uv sync installs the project scripts, use either command name:
memory-agent-arena tasks
ma_eval tasks
maa agents
maa wizardUse tasks to inspect every registered benchmark. Use tasks --detail to show
processed dataset cache status and dataset statistics, and
tasks build <task|all> to build missing processed data:
maa tasks --detail
maa tasks build ledgerqa[ss2]
maa tasks build 'locomo[refined]'
maa tasks build all --fail-fastTask aliases use family[config] syntax. For example, ledgerqa[ss2]
expands to the canonical task ledgerqa-ss2, and
locomo[refined] expands to locomor. Result directories and
evaluate_async.py --task values still use canonical task ids.
Teacher-style LoCoMo prompts are not benchmark variants. They are private
SMA experiment settings; public benchmark runs should use locomo or
locomo[refined]. If you need the teacher-style SMA memory prompt for an
ablation, pass --teacher with the SMA agent only.
Use maa wizard for an interactive command builder. It supports generate,
score, and run, prints the exact CLI command(s), and asks before execution.
For a nicer selector UI, install the optional dependency with:
uv sync --extra wizardGenerate responses:
maa generate \
--tasks locomo,locomor \
--agent sma \
--agent-id SMA_EXAMPLE \
--model /path/to/model-or-checkpoint \
--results-dir results/qwen3-4b \
--concurrency 4Score existing responses:
maa score --results-dir results/qwen3-4b
maa score \
--tasks locomo,locomor \
--agent-id SMA_EXAMPLE \
--results-dir results/qwen3-4b \
--judge localmaa score scans responses_*.jsonl files and evaluates only candidates whose
evaluated_*.jsonl file is missing or incomplete. Use --tasks and
--agent-id as filters; --agent-id refers to the response file suffix, not an
agent registry key. With --judge deepseek, scoring fills
metric.deepseek_llm_score in the same evaluated_*.jsonl files.
Run generation, scoring, and optional aggregation:
maa run \
--tasks locomo \
--agent sma \
--agent-id SMA_EXAMPLE \
--model /path/to/model-or-checkpoint \
--results-dir results/qwen3-4b \
--statsRun multiple agents sequentially:
maa run \
--tasks locomo,ledgerqa-ss2 \
--agents concat,rag,sma \
--agent-id concat,rag,SMA_EXAMPLE \
--model Qwen/Qwen3-4B-Instruct-2507,Qwen/Qwen3-4B-Instruct-2507,/path/to/model \
--results-dir results/qwen3-4b \
--statsWhen --agents is used, agents are executed in order and each agent runs all
requested tasks before the next agent starts. If --agent-id or --model is
provided, it must have the same number of values as --agents; otherwise
agent ids default to agent names and models default to each agent profile. A
failed agent is recorded and later agents continue to run; use --fail-fast to
stop at the first failed agent. With multi-agent run --stats, aggregation runs
once after all agents finish.
Summarize results:
maa stats --results-dir results/qwen3-4b --save-txt
maa stats ledgerqa --results-dir results/qwen3-4bBy default, stats uses .agentignore and .benchignore found under the
results directory or its parents. maa stats ledgerqa switches to
.agentignore_ledgerqa and .benchignore_ledgerqa, while keeping the same glob
matching rules such as DS_*.
Manage local services:
maa generate --tasks locomo --agent sma --model /path/to/model
maa score --tasks locomo --agent-id SMA_EXAMPLE
maa run --tasks locomo --agent sma --model /path/to/modelgenerate, score, and run automatically check the ports declared by the
agent profile. If the expected model is already served, it is reused. If a port
is occupied by a different model, the old service is stopped and the expected
one is launched. Use --no-auto-serve to skip service management when you want
to manage vLLM or embedding servers manually. Remote endpoints such as
OPENAI_API_BASE=http://10.x.x.x:8000/v1 are detected and skipped with a
message.
Each CLI invocation creates one service log session under
runtime/services/logs/ by default. Override the root with --runtime-dir or
MAA_RUNTIME_DIR.
Within the same session, repeated service restarts append to the same role/port
log with a separator; separate CLI invocations use separate timestamped
directories.
Agent workspaces and other intermediate memory artifacts default to
runtime/agents/<agent_id>/<task>/ when an agent needs a writable workspace.
Services are not stopped automatically after standalone generate or score,
so one agent can be reused across multiple benchmarks. run stops local agent
vLLM ports before local judge scoring to avoid GPU conflicts. By convention,
8000 and 8001 are agent model ports, 8002 is the local judge port, and
8080 is the embedding service port.
Extra vLLM flags can be supplied with MAA_VLLM_EXTRA_ARGS for all vLLM
services, or MAA_VLLM_JUDGE_EXTRA_ARGS for the local judge only. For example,
MAA_VLLM_JUDGE_EXTRA_ARGS="-tp 4 --gpu-memory-utilization 0.5" runs the judge
with a smaller tensor-parallel group and lower memory target.
By default, agent models use MAA_AGENT_CUDA_VISIBLE_DEVICES=4,5,6,7. A single
agent model uses that whole list. Two-agent-model setups split that list in
half: 8000 gets the first half and 8001 gets the second half. Tensor
parallel size is inferred from the visible GPU count. Local judge also defaults
to GPUs 4,5,6,7 with -tp 4. Default --gpu-memory-utilization is 0.7.
Default agent and QA model context window is --max-model-len 65536; local
judge uses 16384.
vLLM services also default to VLLM_USE_FLASHINFER_SAMPLER=0, unless you
export a different value before running the CLI.
For agents with baseline-specific settings, maa generate and maa run apply
the runtime defaults declared in agents/registry.py. These defaults set
missing environment variables such as EMBEDDING_SERVICE_ENDPOINT,
OPENAI_API_BASE, JUDGE_API_BASE, MEMT_*, UMA_*, or GAM_*, but they do
not overwrite variables you have already exported.
Inspect or disable profiles:
maa agents
maa generate --agent memt --tasks locomo --print-env --dry-run
maa generate --agent memt --tasks locomo --no-profile --model EdwinYue/Mem-T-4BRun baselines directly through maa generate. This keeps task expansion,
service startup, model defaults, and resume behavior in one CLI path:
maa generate --agent concat --tasks locomo
maa generate --agent memt --tasks "locomo hotpotqa"
maa generate --agent uma --tasks all--tasks all expands to every registered benchmark. For a smaller run, pass an
explicit comma- or space-separated task expression.
More examples:
maa generate --agent memagent --tasks all
maa generate --agent memagent_woq --tasks locomo
maa generate --agent rag --tasks ledgerqa-ss2
maa run --agent sma --tasks locomo --statsThe central benchmark registry is in benchmarks/registry.py.
Currently supported tasks include:
banking77 clinic convomem hotpotqa knowmebench
locomo locomor
ledgerqa-* longmemeval longmemeval_v2_small
memalpha msc nlu perltqa pubmed_rct squad
trec_coarse trec_fine
Dynamic task names are also supported:
ledgerqa-<suffix>, for exampleledgerqa-ss10hotpotqa_<num_docs>, for examplehotpotqa_10
The central agent registry is in agents/registry.py.
Common agent keys:
concat memagent memalpha memt rag gam amem mem1 rlm uma sma
Notes:
umapoints to the older UMA agent implementation.smapoints toSMAAgent.
Generation writes JSONL files:
results/<run>/<task>/responses_<agent_id>.jsonl
Each row contains:
qidqueryexpected_answerresponsegeneration_time- optional
intermediate_paths - optional
tool_call_stats
Evaluation writes:
results/<run>/<task>/evaluated_<agent_id>.jsonl
Metrics include task-dependent fields such as:
official_scorellm_scoref1_scoreexact_matchsub_emrouge_score
Use generate_stats.py to aggregate results:
python generate_stats.py --results-dir results/qwen3-4b- Implement or register a loader that returns
list[EvalData]. - Add a
BenchmarkSpecinbenchmarks/registry.py. - If needed, add an evaluator in
evaluation/evaluators.pyand map it inevaluation/registry.py.
Minimal shape:
BenchmarkSpec(
name="my_benchmark",
loader=load_my_benchmark,
evaluator_key="default",
metric_profile="qa",
data_source="my_benchmark",
)- Implement an agent under
agents/. - Register it in
agents/registry.py. - If the agent follows the standard interface, use the default adapter.
- If it needs special construction or return parsing, add an adapter in
agents/adapters.py.
Standard agent interface:
class MyAgent(BaseAgent):
async def add_memory_async(self, chunk: str) -> None:
...
async def QA_batch_async(self, query_list: list[str]) -> list[str]:
...Registry example:
"myagent": AgentSpec(
name="myagent",
module_name="agents.my_agent",
class_name="MyAgent",
default_model="Qwen/Qwen3-4B-Instruct-2507",
default_concurrency=4,
env_defaults={
"OPENAI_API_KEY": "EMPTY",
"OPENAI_API_BASE": "http://127.0.0.1:8000/v1",
},
)If the agent needs special behavior:
"myagent": AgentSpec(
name="myagent",
module_name="agents.my_agent",
class_name="MyAgent",
adapter_key="my_adapter",
)SMA training has been split out of this arena repository. Use this project for
benchmarking memory agents and scoring generations; use the separate sma-training
reproduction project for verl-based training scripts, dataset conversion, checkpoint
merging, and paper-specific launchers.
Processed datasets are built through the unified task registry:
maa tasks build <task>
maa tasks build all
maa tasks build locomo[base] hotpotqa[200]Raw benchmark datasets are not committed to this repository. Public datasets are
downloaded by their builders when possible, and the downloaded files are cached
under data/raw/.
| Benchmark task | Data source behavior |
|---|---|
locomo[base] |
Auto-downloads data/raw/locomo10.json from the official LoCoMo GitHub URL. Override with LOCOMO_RAW_URL. |
locomo[refined] |
Uses data/raw/locomor/locomo_refined.json. Set LOCOMOR_RAW_URL to download it from your own URL, or place the file manually. |
knowmebench |
Auto-downloads the Hugging Face dataset realty2333/knowMe-Bench into data/raw/KnowMeBench/ after your HF account has access to the gated dataset. Set HF_TOKEN if needed; override with KNOWMEBENCH_HF_REPO. |
hotpotqa[200] and other HotpotQA configs |
Auto-downloads the corresponding file, for example data/raw/hotpotqa/eval_200.json. |
convomem |
Auto-downloads Salesforce/ConvoMem into data/raw/ConvoMem/; this is a larger multi-file download. Override the local source with CONVOMEM_RAW_DIR. |
longmemeval |
Auto-downloads the small LongMemEval JSON file into data/raw/. |
longmemeval_v2_small |
Requires LongMemEval-V2 files under data/raw/longmemeval-v2/, or set LONGMEMEVAL_V2_DATA_DIR. |
msc |
Auto-downloads MemGPT/MSC-Self-Instruct through Hugging Face datasets. |
MemoryAgentBench tasks (nlu, banking77, clinic, trec_*) |
Require a MemoryAgentBench parquet file. Default: data/raw/memoryagentbench/test.parquet; override with MEMORYAGENTBENCH_PARQUET_PATH. |
MemAlpha tasks (perltqa, pubmed_rct, squad) |
Require a MemAlpha parquet file. Default: data/raw/memalpha/test.parquet; override with MEMALPHA_PARQUET_PATH. |
For Hugging Face downloads, set HF_ENDPOINT or HF_HUB_ENDPOINT if you need a
mirror. After raw data is present or downloadable, rerun maa tasks build <task>
to create the processed cache under data/.cache/.
Agent runtimes that are needed for integrated baselines live under external/.
For example, deltamem defaults to external/delta-Mem, while gam loads the
vendored external/gam/src package. Delta-Mem model adapters are still external
artifacts; pass --adapter-dir when running that agent.
Recommended local checks:
python -m py_compile evaluate_async.py evaluation_runner.py benchmark_runtime.py
python -m py_compile agents/registry.py agents/adapters.py
python -m py_compile benchmarks/registry.py evaluation/registry.py evaluation/evaluators.py
bash -n scripts/run/run.sh scripts/run/run_generation.sh scripts/run/run_score.sh
bash -n scripts/experimental/run_memt_remote_shards.shFor a quick SMA smoke test:
python tests/test_sma_agent.pyMemoryAgent Arena is distributed under the license in LICENSE. Third-party
source snapshots under external/ retain their own licenses and attribution;
see external/THIRD_PARTY.md and the license files in each vendored package.