Mem0 Platform SDK
mem0ai/mem0
Adds persistent memory to AI apps with the Mem0 Python and TypeScript SDKs: store, search, update and delete user memories, with framework integrations.
Decision protocol for the map-reduce / dynamic fan-out pattern in LM pipelines — "given list L, run f(item) for each item in parallel, then combine".
$ npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install agentsope/SkillAlchemy agentsop-map-reduce-fanout --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/agentsope/SkillAlchemy.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/agentsop-map-reduce-fanout .claude/skills/agentsop-map-reduce-fanout && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "agentsop-map-reduce-fanout" agent skill from https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanout into .claude/skills/agentsop-map-reduce-fanout/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "agentsop-map-reduce-fanout", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanoutType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install agentsope/SkillAlchemy agentsop-map-reduce-fanout --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/agentsope/SkillAlchemy.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/agentsop-map-reduce-fanout .agents/skills/agentsop-map-reduce-fanout && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "agentsop-map-reduce-fanout" agent skill from https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanout into .agents/skills/agentsop-map-reduce-fanout/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "agentsop-map-reduce-fanout", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install agentsope/SkillAlchemy agentsop-map-reduce-fanout --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/agentsope/SkillAlchemy.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/agentsop-map-reduce-fanout .cursor/skills/agentsop-map-reduce-fanout && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "agentsop-map-reduce-fanout" agent skill from https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanout into .cursor/skills/agentsop-map-reduce-fanout/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "agentsop-map-reduce-fanout", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/agentsope/SkillAlchemy.git --path skills/agentsop-map-reduce-fanout--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install agentsope/SkillAlchemy agentsop-map-reduce-fanout --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/agentsope/SkillAlchemy.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/agentsop-map-reduce-fanout .gemini/skills/agentsop-map-reduce-fanout && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "agentsop-map-reduce-fanout" agent skill from https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanout into .gemini/skills/agentsop-map-reduce-fanout/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "agentsop-map-reduce-fanout", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install agentsope/SkillAlchemy agentsop-map-reduce-fanoutInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/agentsope/SkillAlchemy.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/agentsop-map-reduce-fanout .github/skills/agentsop-map-reduce-fanout && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "agentsop-map-reduce-fanout" agent skill from https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanout into .github/skills/agentsop-map-reduce-fanout/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "agentsop-map-reduce-fanout", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install agentsope/SkillAlchemy agentsop-map-reduce-fanout --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/agentsope/SkillAlchemy.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/agentsop-map-reduce-fanout .opencode/skills/agentsop-map-reduce-fanout && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "agentsop-map-reduce-fanout" agent skill from https://github.com/agentsope/SkillAlchemy/tree/master/skills/agentsop-map-reduce-fanout into .opencode/skills/agentsop-map-reduce-fanout/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "agentsop-map-reduce-fanout", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
agentsop-map-reduce-fanoutDecision protocol for the map-reduce / dynamic fan-out pattern in LM pipelines — "given list L, run f(item) for each item in parallel, then combine".
Agentsop Map Reduce Fanout is an agent skill from agentsope/SkillAlchemy. Decision protocol for the map-reduce / dynamic fan-out pattern in LM pipelines — "given list L, run f(item) for each item in parallel, then combine". Activates when the coder agent is about to process N items with N LM calls (per-doc summarize, per-query retrieve, per-candidate rank, parallel tool fan-out). Encodes the when, how many at once, what to do when one fails, and how to reduce — not the API of any single framework. Cross-framework: LangGraph Send, CrewAI parallel tasks / Flow, asyncio.gather…
Its SKILL.md is about 8.1k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in AI & LLM Engineering, covering Building AI agents. It works with LangGraph, CrewAI and LlamaIndex. The repository describes itself as: From thought to skill. From signal to structure. The licence is MIT.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit d0f0355. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are python).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Agentsop Map Reduce Fanout loads about 8.1k tokens when it runs. Until then it costs about 149 tokens; SKILL.md has 3,529 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from agentsope/SkillAlchemy at commit d0f0355, republished under its MIT licence (© agentsope). 3,529 words, ~8,137 tokens.
.claude/skills/agentsop-map-reduce-fanout/SKILL.md (or your agent's skills folder).Pattern:
results = reduce(combine, parallel_map(f, L))wherefis one or more LM calls. The only reason to fan out is that latency or throughput matters more than the cost of doing it. The only reason to fan in is that the consumer wants one answer, not N.
Source posture: claims grounded in primary docs and 2026 production write-ups, cited inline with short tags resolved in the citation index.
Activate this skill when any of these is true:
for item in items: result = llm(item) loop
and the items are independent (no item depends on the previous result).asyncio.gather(...), ThreadPoolExecutor(...),
Send(...), Process.hierarchical parallel branches, or
crew.kickoff_for_each(...) and the question is how to use them safely.InvalidUpdateError on a key two parallel
branches write to (see OP-3 cross-link in skill O5 state-reducer).Send-based fan-out is hitting GRAPH_RECURSION_LIMIT or
rate-limit 429s because all N workers fired at once
[aipractitioner/scaling].Do not activate when:
asyncio.wait(..., return_when=FIRST_COMPLETED), not gather.Fan out for latency, fan in for coherence.
The whole protocol is two questions: what runs concurrently? and how do
the answers merge? Everything else — Send, gather, Semaphore,
reducers — is mechanics.
Three load-bearing concepts:
The unit of fan-out is the item, not the call. f(item) may itself
be a multi-step LM workflow (retrieve → rerank → synthesize) — that is
fine. What you parallelise is the per-item function. Don't confuse
"parallel LM calls" with "parallel workflow instances". The latter is
what Send and gather(f(x) for x in L) actually do
[deepwiki/mapreduce].
Concurrency is bounded, never infinite. Every external dependency
(OpenAI/Anthropic API, your vector DB, your KV cache) has at least one
of: RPM limit, TPM limit, connection-pool limit, GPU KV-cache budget.
asyncio.gather(*[call(x) for x in 100_items]) will not call 100
things — it will fire 100, the API will 429 most, and you'll spend the
next 20 minutes in retry-storm hell [newline/asyncio-llm]
[tianpan/structured-concurrency]. The first thing you choose, before
any code, is N_concurrent.
The reducer determines the shape of the answer. concatenate keeps
all evidence (large output, no judgment); summarize collapses
(information loss, smaller output); vote / majority picks one (lossy
but decisive); rank-top-K selects the best few. Pick the reducer
before you write the map — because the map's expected_output shape
is dictated by the reduce.
The Pregel-lineage version of the same point: a fan-out / fan-in is one
superstep. Either it all succeeds and the reduce node runs, or one branch
fails and (in LangGraph) the whole superstep is discarded
[aipractitioner/scaling]. Your code must decide before fan-out which
semantics you want: atomic-or-nothing, or best-effort-with-holes.
Walk this top-down. Each step has a decision gate.
Gate: can f(item_i) run without seeing f(item_j) for any j ≠ i?
If no, stop. You have a sequential or recursive problem masquerading as map-reduce. Use a chain, or model the dependency explicitly (DAG, beam search, etc.). Fan-out will silently drop the cross-talk.
Before any code, multiply:
cost = N · cost_per_item (in $, tokens, AND seconds_wall)
peak_rps = N_concurrent / mean_latency_per_itemThree numbers must fit inside three budgets:
cost_$ ≤ task budgetpeak_rps ≤ min(API RPM/60, vector-DB QPS, GPU concurrency)peak_tps ≤ API TPM / 60 — TPM is the silent killer: 50 parallel calls
each with a 4k-token prompt instantly exceeds most providers' TPM even
if RPM is fine [newline/asyncio-llm].If any budget is tight, fan-out is the wrong tool. Options: batch calls into single API request (embeddings, reranking), reduce N (pre-filter items), or accept sequential.
Default rubric:
| Constraint | Pick |
|---|---|
| API-bound (OpenAI/Anthropic) | min(10, RPM/60 · target_latency_s) — keep at most one "request-second" of headroom |
| Self-hosted vLLM/SGLang | Start at num_kv_blocks / mean_prompt_blocks; for most setups 8–32 |
| Vector DB retrieval | Provider QPS limit / 2 (leave headroom for other paths) |
| Mixed (LM + tool calls) | Constrain by the slowest dependency |
| No instrumentation yet | Start at 5. Always. Then measure. |
N_concurrent is enforced with asyncio.Semaphore(N) for asyncio,
ThreadPoolExecutor(max_workers=N) for sync, max_concurrency config in
LangGraph, max_rpm in CrewAI. Never rely on the framework's defaults.
Four canonical policies — pick one explicitly:
| Policy | When to use | How |
|---|---|---|
| Abort-all | One missing item invalidates the answer (legal review, regulated workflows) | asyncio.gather(*, return_exceptions=False) — first exception cancels siblings. In LangGraph this is the default superstep semantic [aipractitioner/scaling]. |
| Best-effort | Concatenate / summarize use cases — partial is OK | asyncio.gather(*, return_exceptions=True) then filter; or per-task try/except returning a sentinel |
| Retry-then-skip | API flakiness is the main failure | Wrap f with tenacity / backoff: 3 attempts × exponential, then sentinel |
| Quorum | Vote / ensemble — need at least K of N | asyncio.as_completed, collect K, cancel the rest |
Anti-pattern: making this decision implicitly. The single most common
production bug in this pattern is "I assumed gather would skip failures"
or "I assumed one failed branch wouldn't kill the rest" — both are wrong
defaults [aipractitioner/scaling] [newline/asyncio-llm].
| Reducer | Shape change | Cost | When to use |
|---|---|---|---|
concatenate | [A, B, C] → "A\nB\nC" | None | Downstream LM can handle big context; you want full evidence |
summarize (LM call) | [A, B, C] → "abc" | +1 LM call | Output must fit in a final prompt; lossy by design |
vote / majority | [A, B, A] → A | None | Self-consistency / ensemble agreement |
rank-top-K | [(a,0.9), (b,0.4), (c,0.7)] → [a, c] | None | Multi-query retrieval, reranking |
merge-dedupe | overlapping lists → set | Cheap | Multi-query retrieval, multi-source enrichment |
tree-reduce | binary combine(x, y) recursively | log₂(N) LM calls | When pairwise merging is meaningful (summary-of-summaries) |
Choose by asking: what does the next node consume? The reducer is just "adapt the fan-out shape to the consumer's input shape."
Two independent timeouts:
asyncio.wait_for(call, timeout=30) — protects against
one stuck call holding a semaphore slot forever (the canonical "100 items,
99 done in 5s, the whole job blocked 5 min on item 73"). Default 30s.asyncio.wait_for(gather(...), timeout=N · mean + 3σ)
— protects against pathological N. Default 2× the optimistic wall
estimate.In LangGraph, recursion_limit is not a timeout — set both, plus
node-level RetryPolicy [lc-docs/errors]. In CrewAI, max_rpm rate-
limits but does not timeout — wrap kickoff() in asyncio.wait_for.
After the reducer runs, do one cheap LLM-free check:
concatenate → assert len(merged) is within expected bounds; raise if
one branch returned a 50KB blob.vote → log the vote distribution; if it's near-uniform, the items
weren't actually deciding the same question — go back to Step 1.rank-top-K → assert scores are not all identical (failure mode of a
broken reranker).These cheap checks catch the "fan-out succeeded but the answer is garbage" bug class that no exception will surface.
Format: Trigger → Action → Output → Evidence.
asyncio.gather with bounded concurrency (Python baseline)sem = asyncio.Semaphore(N_CONCURRENT)
async def bounded(item):
async with sem:
return await asyncio.wait_for(f(item), timeout=PER_CALL_S)
results = await asyncio.gather(
*[bounded(i) for i in items],
return_exceptions=True, # explicit best-effort
)
ok = [r for r in results if not isinstance(r, Exception)]return_exceptions=True is the failure policy;
wait_for is the per-call timeout.[newline/asyncio-llm], [soumendrak/semaphore],
[superfastpython/gather].asyncio.as_completed for quorum / first-Ktasks = [asyncio.create_task(f(i)) for i in items]
done = []
for fut in asyncio.as_completed(tasks):
try: done.append(await fut)
except Exception: pass
if len(done) >= K:
for t in tasks: t.cancel()
break[instructor/learn-async].Send for dynamic fan-outfrom langgraph.types import Send
def route_fanout(state):
return [Send("worker", {"item": x}) for x in state["items"]]
graph.add_conditional_edges("planner", route_fanout, ["worker"]).compile() time via
compile(...).with_config({"max_concurrency": N}) or
graph.invoke(..., {"max_concurrency": N}).Send is its own trace
segment in LangSmith.[deepwiki/mapreduce], [mlplus/langgraph-mr],
[medium/send-api]. Skill langgraph-sop OP-4 is the canonical entry.O5)Send-spawned workers (or any parallel
branches) write to the same state key. Without a reducer, LangGraph
raises InvalidUpdateError.summaries: Annotated[list[str], operator.add] for
concatenate, add_messages for chat, or a custom reducer for dedupe/top-K.[cheatsheet/gotchas] "Reducers are mandatory, not
optional, for parallel execution"; [lc-docs/persistence].kickoff_for_each (parallel crew invocations)results = crew.kickoff_for_each(inputs=[{"topic": t} for t in topics])
# async variant: kickoff_for_each_asyncCrew(..., max_rpm=30). Wrap calls in
asyncio.wait_for for total wall budget.[crewai-docs/kickoff]. Note: CrewAI's hierarchical
process is not a fan-out primitive — see DC-3.ThreadPoolExecutor for sync LM clientsfrom concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=N_CONCURRENT) as ex:
results = list(ex.map(f, items, timeout=PER_CALL_S))as_completed(futures, timeout=...) for failure isolation. Avoid for
CPU-bound f — use ProcessPoolExecutor or a job queue.concurrent.futures docs.MultiQueryRetriever or QueryFusionRetriever with
num_queries=N; let LlamaIndex parallelise internally. Combine with
RRF (reciprocal rank fusion) as the reducer, not naive concat.llamaindex-sop.summarize(a, b) → c is meaningful.Send rounds; in plain Python,
recursive gather.O(N), latency O(log N).gather)f not the gather:@tenacity.retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, max=10),
retry=retry_if_exception_type((RateLimitError, APITimeoutError)),
)
async def f_retry(item): return await f(item)item_id in the LangSmith /
OTel / Logfire trace. Log len(items), len(ok), len(failed),
wall_time_s on the reduce step. For LangGraph, each Send is
already a distinct trace segment — name the worker node descriptively
(summarize_doc not worker).[swarnendu/best]; LangSmith Send-tracing docs.await asyncio.gather(*[summarize(d) for d in 100_docs]) against the Anthropic API. First 50 fire instantly, the rest
queue inside the client; provider returns 429 for half; tenacity retries
pile on; nothing finishes. Wall time worse than sequential.N_concurrent = TPM / (60 · tokens_per_call) = 200_000 / (60 · 3000) ≈ 1.1 → round to 2 in-flight, not 10.
RPM said 50 — TPM said 2. Bind to the tighter one.100 · 5s / 2 = 250s
— exceeds SLA.20 · 5s / 2 = 50s ✓ under SLA.return_exceptions=True and a 30s per-call wait_for. If
one doc hangs, the others still finish.[newline/asyncio-llm]Send fans out 50 workers, all write state['summaries'], get InvalidUpdateError"Send to spawn one summarizer per
retrieved doc. State has summaries: list[str]. LangGraph crashes on
the first run with InvalidUpdateError: At key 'summaries': Can receive only one value per step. Use an Annotated key to handle multiple values. Skill langgraph-sop flags this [cheatsheet/gotchas].O5 state-reducer: parallel writes require
an explicit reducer.summaries: Annotated[list[dict], operator.add]
where each worker returns [{"doc_id": ..., "summary": ...}].operator.add order (Python list
concat is in completion order); sort by doc_id in the reduce node.doc_id.Send
worker must have a reducer. Pick operator.add for concat,
add_messages for chat, a custom function for dedupe / top-K. Order
is completion order, not Send order — sort in the reduce node if you
need stability. [cheatsheet/gotchas], [deepwiki/mapreduce]retry_if_exception_type(( RateLimitError, APITimeoutError, APIConnectionError)). Permanent
errors fall through immediately.return_exceptions=True): permanent
failures land as exceptions in the result list, tagged with the
input id.failed_records list alongside ok_records
— never lose the failure metadata.len(failed) / N > threshold,
surface a louder failure (e.g., refuse to emit the CSV). Otherwise
emit partial + a sidecar errors file.[aipractitioner/scaling]temperature=0.7, vote on the answer. Three say "A", two say "B".
Coder is about to return mode(answers).confident_if agreement_ratio ≥ 0.8. Below
that, return ("unknown", {"votes": Counter(...)}).summarize_doc returns ~400 tokens. Flat
concat = 80k tokens → exceeds context for the final synthesis step.200 + 20 + 4 + 1 = 225 calls (vs. flat-concat's
impossible 1 call); latency O(log_10 200) ≈ 3 layers.[langchain/map-reduce-chain]Concrete don'ts:
Don't use unbounded asyncio.gather. "100 tasks at once" is not
parallelism — it's a denial-of-service against your own dependencies.
Always wrap in a semaphore, even if you think N is small
[newline/asyncio-llm].
Don't omit per-call timeout. Without wait_for, a single stuck
call holds a semaphore slot forever and silently lowers your effective
concurrency to N−1, then N−2, etc.
Don't ignore the failure policy. Decide before coding: abort,
best-effort, retry-then-skip, or quorum. Implicit defaults are wrong
half the time: gather(...) aborts on first failure (surprise to
most), but LangGraph parallel branches also abort the entire
superstep — not the same level but the same shape of surprise
[aipractitioner/scaling].
Don't Send for fixed-cardinality work. If N is always 3,
static parallel edges are simpler, more readable, and easier to
trace [aipractitioner/scaling]. Send is for runtime-variable N.
Don't write to a state key from parallel branches without a
reducer. This is the canonical InvalidUpdateError
(cross-link skill O5 state-reducer) [cheatsheet/gotchas].
Don't use the same model on every branch when self-consistency is
the goal. Self-consistency requires diversity — sample with
temperature > 0, vary the prompt, or vary the model. Same prompt
Don't fan out a CPU-bound f. asyncio.gather and
ThreadPoolExecutor parallelise I/O, not CPU. CPU-bound f
(heavy local tokenisation, image preprocessing) needs
ProcessPoolExecutor or a job queue.
Don't reduce by "let the next LM call see everything". That's flat-concat dressed up; the context-overflow failure mode is identical. See DC-5: tree-reduce when N is large.
Don't trust the framework's default max_concurrency. LangGraph,
CrewAI, and LlamaIndex all default to "as many as you ask for". Set
the cap explicitly, in code, near the fan-out.
Don't fan out when latency is dominated by network, not compute. A 50ms function fanned 100-wide over a 200ms-RTT network finishes in ~250ms regardless of N — the win evaporates. Profile first.
Hard boundaries — when this skill is the wrong tool:
| Framework | Primitive | Concurrency cap | Failure default | Reduce mechanism |
|---|---|---|---|---|
| asyncio | gather(*coros) / as_completed | Semaphore(N) | return_exceptions=False aborts on first | Post-gather list comprehension; manual |
| threads | ThreadPoolExecutor(max_workers=N) | max_workers arg | Per-future result() raises | as_completed; manual aggregation |
| LangGraph | Send("worker", state) from conditional edge | config={"max_concurrency": N} | Atomic superstep — one fails, all discarded [aipractitioner/scaling] | State reducer (operator.add, add_messages, custom) — mandatory for parallel writes [cheatsheet/gotchas] |
| CrewAI | crew.kickoff_for_each(inputs=[...]), Flow @listen | Crew(max_rpm=N) | Per-kickoff; not atomic | Caller-side merge; or downstream task with context=[...] |
| LlamaIndex | MultiQueryRetriever, QueryFusionRetriever, async_aggregate=True on QueryEngine | Internal; configurable in retriever | Per-retriever | RRF / dedupe / score-merge built into the retriever |
| OpenAI/Anthropic batch APIs | Native batch endpoint | Provider-managed | Per-item | One sync API call returns the list |
Quick chooser:
Send + reducer (OP-3 + OP-4).kickoff_for_each (OP-5); for conditional
routing → CrewAI Flow with parallel @listen branches.The general rule: prefer the lowest abstraction that lets you set concurrency cap, per-call timeout, and failure policy explicitly. If a framework hides any of the three, wrap it.
import asyncio
from typing import Awaitable, Callable, TypeVar
T = TypeVar("T"); R = TypeVar("R")
async def map_reduce(
items: list[T],
f: Callable[[T], Awaitable[R]],
reduce: Callable[[list[R]], R],
*,
n_concurrent: int = 5,
per_call_timeout_s: float = 30.0,
fail_policy: str = "best_effort", # "abort" | "best_effort"
) -> R:
sem = asyncio.Semaphore(n_concurrent)
async def bounded(x: T):
async with sem:
return await asyncio.wait_for(f(x), timeout=per_call_timeout_s)
raw = await asyncio.gather(
*(bounded(x) for x in items),
return_exceptions=(fail_policy == "best_effort"),
)
if fail_policy == "best_effort":
ok = [r for r in raw if not isinstance(r, BaseException)]
failed = [r for r in raw if isinstance(r, BaseException)]
if not ok:
raise RuntimeError(f"all {len(items)} branches failed: {failed[:3]}")
return reduce(ok)
return reduce(raw)Use this when there's no graph framework already in the codebase. Replace
reduce with lambda xs: "\n".join(xs) (concat), Counter(xs).most_common(1)[0][0]
(vote), or a follow-up LM call (summarize). The same three knobs —
n_concurrent, per_call_timeout_s, fail_policy — show up in every
framework variant; this snippet just makes them explicit.
Send referenceimport operator
from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Send
class State(TypedDict):
items: list[str]
summaries: Annotated[list[dict], operator.add] # reducer is mandatory
class WorkerState(TypedDict):
item: str
def fanout(state: State):
return [Send("worker", {"item": x}) for x in state["items"]]
async def worker(s: WorkerState):
# one LM call per item; state writes are merged via the reducer above
text = await llm.ainvoke(f"Summarize: {s['item']}")
return {"summaries": [{"item": s["item"], "summary": text.content}]}
def reduce_node(state: State):
ordered = sorted(state["summaries"], key=lambda d: state["items"].index(d["item"]))
return {"final": "\n".join(d["summary"] for d in ordered)}
g = StateGraph(State)
g.add_node("worker", worker)
g.add_node("reduce", reduce_node)
g.add_conditional_edges(START, fanout, ["worker"])
g.add_edge("worker", "reduce")
g.add_edge("reduce", END)
app = g.compile()
result = await app.ainvoke({"items": docs}, {"max_concurrency": 5})Cross-references: skill langgraph-sop (OP-4 Send, OP-3 reducers),
skill O5 state-reducer (deeper on parallel-write semantics).
[deepwiki/mapreduce] = deepwiki.com/langchain-ai/langchain-academy/7.1-map-reduce-pattern[medium/send-api] = medium.com/@vishy2k5/langgraph-send-api-7aaab56bc6b8[mlplus/langgraph-mr] = machinelearningplus.com/gen-ai/langgraph-map-reduce-parallel-execution/[aipractitioner/scaling] = aipractitioner.substack.com/p/scaling-langgraph-agents-parallelization[cheatsheet/gotchas] = sumanmichael.github.io/langgraph-cheatsheet/cheatsheet/faqs-gotchas/[lc-docs/persistence] = docs.langchain.com/oss/python/langgraph/persistence[lc-docs/errors] = docs.langchain.com/oss/python/langgraph/errors[newline/asyncio-llm] = newline.co/@zaoyang/python-asyncio-for-llm-concurrency-best-practices[soumendrak/semaphore] = soumendrak.com/blog/semaphores-python-async-programming/[superfastpython/gather] = superfastpython.com/asyncio-gather-limit-concurrency/[instructor/learn-async] = python.useinstructor.com/blog/2023/11/13/learn-async/[tianpan/structured-concurrency] = tianpan.co/blog/2026-04-09-structured-concurrency-ai-pipelines-parallel-tool-calls[crewai-docs/kickoff] = docs.crewai.com/en/concepts/crews (kickoff_for_each)[swarnendu/best] = swarnendu.de/blog/langgraph-best-practices/[langchain/map-reduce-chain] = python.langchain.com/docs/versions/migrating_chains/map_reduce_chain/© agentsope, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/agentsop-map-reduce-fanout of agentsope/SkillAlchemy.
Open the folder on GitHubat commit d0f0355
Agentsop Map Reduce Fanout next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Agentsop Map Reduce Fanout this skillagentsope/SkillAlchemy | 436 | — | ~8.1k | Automated safety check: Pass | MIT | |
| Mem0 Platform SDKmem0ai/mem0 | 67k | 1 repos | ~2.2k | Automated safety check: Pass | Apache-2.0 | |
| Cloudbase Agent PythonTencentCloudBase/CloudBase-AI-Toolkit | 1.1k | 2 repos | ~2.9k | Automated safety check: Notes | MIT | |
| Edgeone Makers MigrationTencentEdgeOne/edgeone-makers-tools | 1.9k | 1 repos | ~4.1k | Automated safety check: Pass | MIT | |
| Omnigent Framework Detectionomnigent-ai/omnigent | 11k | — | ~610 | Automated safety check: Pass | Apache-2.0 | |
| Edgeone Makers AgentsTencentEdgeOne/edgeone-makers-tools | 1.9k | 1 repos | ~5.8k | Automated safety check: Notes | MIT |
mem0ai/mem0
Adds persistent memory to AI apps with the Mem0 Python and TypeScript SDKs: store, search, update and delete user memories, with framework integrations.
TencentCloudBase/CloudBase-AI-Toolkit
Build production-ready AI agent backends using the CloudBase Agent Python SDK — create agents with LangGraph/CrewAI/LlamaIndex, serve them via FastAPI with AG-UI protocol streaming +…
TencentEdgeOne/edgeone-makers-tools
Migrate existing AI agent projects (LangChain, LangGraph, OpenAI Agents SDK, Claude Agent SDK, CrewAI) to EdgeOne Makers platform conventions.
omnigent-ai/omnigent
Scans Python agent code for framework imports and recommends the matching Omnigent executor type, or says when the framework is not natively supported yet.
TencentEdgeOne/edgeone-makers-tools
This skill guides building AI agent endpoints on EdgeOne Makers — five framework routes (DeepAgents, LangGraph, CrewAI, OpenAI Agents SDK, Claude Agent SDK), platform-injected context.store /…
UiPath/skills
UiPath Coded Functions — deterministic Python or TypeScript/JavaScript units built with the uip function CLI (new -l py|ts|js, init, serve, run, pack, publish); the functions map in uipath.json…
agentsope/SkillAlchemy
SOP for terminal-based, git-native AI pair programming with Aider (git work-tree + tree-sitter repo-map + edit-format + human-in-loop REPL).
agentsope/SkillAlchemy
Coder-agent working-file budget discipline: keep the editable working set (files you /add into writable context) under ~25k tokens, separate "read" from "edit", delegate breadth to a read-only…
agentsope/SkillAlchemy
Split a multi-call LM workflow by cognitive load, not by accuracy: let one strong model make the few reasoning decisions and a cheap model do the many mechanical executions (Aider architect+editor…
agentsope/SkillAlchemy
SOP for building multi-agent systems with CrewAI — role-based collaboration, sequential/hierarchical processes, Flows, memory, delegation.
agentsope/SkillAlchemy
SOP for building LLM applications on Dify — visual workflow + chatflow + agent + RAG knowledge base + plugin marketplace + observability, self-hostable.
agentsope/SkillAlchemy
Designs multiscale chunking for RAG by embedding small units for retrieval precision and returning larger context for synthesis.
Works with
Categories
Decision protocol for the map-reduce / dynamic fan-out pattern in LM pipelines — "given list L, run f(item) for each item in parallel, then combine". Agentsop Map Reduce Fanout is an agent skill from agentsope/SkillAlchemy. Decision protocol for the map-reduce / dynamic fan-out pattern in LM pipelines — "given list L, run f(item) for each item in parallel, then combine".
Agentsop Map Reduce Fanout fits situations like: tasks that involve Building AI agents.
Run `npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a claude-code`. Or copy the skill folder (skills/agentsop-map-reduce-fanout in agentsope/SkillAlchemy) into .claude/skills/agentsop-map-reduce-fanout in your project. Claude Code loads it when a task matches its description.
Run `npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a codex`. Or copy the skill folder (skills/agentsop-map-reduce-fanout in agentsope/SkillAlchemy) into .agents/skills/agentsop-map-reduce-fanout in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add agentsope/SkillAlchemy --skill agentsop-map-reduce-fanout -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/agentsop-map-reduce-fanout, .gemini/skills/agentsop-map-reduce-fanout, .github/skills/agentsop-map-reduce-fanout and .opencode/skills/agentsop-map-reduce-fanout in your project.
SKILL.md names no scripts, command-line tools or credentials: Agentsop Map Reduce Fanout is instructions for the agent only. Our summary lists: Python 3.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Agentsop Map Reduce Fanout is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.1k tokens (SKILL.md is roughly 33k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Agentsop Map Reduce Fanout: Mem0 Platform SDK (mem0ai/mem0, 67k stars), Cloudbase Agent Python (TencentCloudBase/CloudBase-AI-Toolkit, 1.1k stars), Edgeone Makers Migration (TencentEdgeOne/edgeone-makers-tools, 1.9k stars) and Omnigent Framework Detection (omnigent-ai/omnigent, 11k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
agentsope (a GitHub user) maintains it in agentsope/SkillAlchemy, which has 436 GitHub stars. The repository holds 46 skills in this directory. The repository was last updated on October 9, 2026.
Source: agentsope/SkillAlchemy on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.