cd /news/ai-agents/prolog-swarms-beam-like-concurrency-… · home › topics › ai-agents › article
[ARTICLE · art-150770] src=deepclause.substack.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

“Prolog Swarms” - BEAM-like Concurrency in DeepClause

A developer added BEAM-style actor concurrency to DeepClause, a Prolog-based agent framework, using Node worker threads and SWI Prolog engines to let processes run independently, share no state, and communicate only via messages. The update introduces new DML language features, including a swarm predicate for running parallel agents and send/receive primitives for inter-process messaging, enabling multi-agent systems with parallel tool calling and sub-agents.

by read6 min views2 publishedOct 15, 2026
“Prolog Swarms” - BEAM-like Concurrency in DeepClause
Image: Deepclause (auto-discovered)

tldr; DeepClause now supports concurrency and can be used to build highly concurrent multi-agent systems.

(Written by a human being)

There is some folklore out there that the first version of Erlang was created in Prolog. I guess that this must have subconsciously influenced me when I started to seriously think about adding some form of concurrency to DeepClause and DML.

DeepClause is a side-project of mine, which I have been - against better judgement - working on for almost two years now. At its core is the DeepClause Meta Language (DML), which is designed to occupy that strange middle ground between pure markdown on the one end, and custom implemented orchestration code on the other. It is based on Prolog and its runtime is implemented using TypeScript combined with SWI Prolog’s WASM build.

Over the past few weeks, I’ve been working on a big refactor, making the DML REPL the core piece of DeepClause. The DML REPL combines a traditional REPL for Prolog with an LLM Agent. With this we can directly run code or let the agent explore the loaded module, freely modify it and successively improve it by itself, thereby adjusting to specific tasks, models, or domains. The sum of all predicates (just think of functions) loaded into the REPL thus becomes a harness that can be exported, shared and reused (Read the REPL post for more about this!). This can be considered a generalization of the more “traditional” combination of markdown based skills and Python/JS/Bash code used in most coding agents.

Now, the REPL did greatly help with the development of DeepClause, but at some point during the development of an Agent framework, concurrency becomes an obvious feature to implement, not only needed for implementing parallel tool calling, but also sub-agents, or full blown “burn through your subscription in 2 min.” multi agent frameworks and Huggingface-breaking agent swarms.

Since everything is based on Prolog, I felt it was an interesting experiment to let Deepseek v4.1 help me implement a BEAM-style actor-driven approach to concurrency in DeepClause.

On a super high level, this meant:

  • Introducing an abstract process model where processes run independently from each other, share no state and communicate only via messages.
  • Implementing the processes using node worker threads
  • Giving each thread a copy of the current Harness (=REPL module) state and run the code inside the thread.
  • Building a mechanism to allow for process to send and receive messages, as well as wait for messages. To achieve the latter, one can make use of “Prolog-engines” , i.e. co-routines for Prolog that provide primitives similar to async/await in Python or JS.
  • Introducing new language features into DML so that we get a nice programming model for all this.

The resulting new language features feel natural and idiomatic:

Example 1: Running a swarm of agents

% A simple one turn agent
brief(Topic, Brief) :-
    system("You are an expert summarizer."),
    task("Summarise {Topic} in two sentences for a technical audience.", string(Brief)).

survey(Topics, Briefs) :-
    % Start 4 actors that each run the brief/2 predicate
    swarm(brief, Topics, Results, 4),
    % Collect all successful results 
    findall(B, member(ok(B), Results), Briefs).

Example 2: Sending and waiting for messages

worker(Topic, Parent) :-
    task("Summarise {Topic} in one sentence.", string(Summary)),
    send(Parent, done(Topic, Summary)),
    answer(Summary).

spawn_worker(Me, Topic, Pid) :- spawn(worker(Topic, Me), Pid).

fan_out(Topics, Results) :-
    self(Me),
    maplist(spawn_worker(Me), Topics, _Pids),
    collect(Topics, Results).

%% Selective receive: wait for *this* topic and leave the others queued. Workers
%% finish out of order, and receive/2 would consume the wrong message and fail.
collect([], []).
collect([Topic|Topics], [Topic-Summary|Rest]) :-
    receive_match(done(Topic, Summary), _, 300000),
    collect(Topics, Rest).

Example 3: Service Pattern

joke(A,B) :-
    prompt("Tell a short, clean joke about {A}. Return only the joke text, 
            with no preamble.",B).

% Service loop: answer a request(From, Ref, joke) 
% with reply(Ref, Joke); `stop` ends it. joke_loop(+Topic).
joke_loop(A) :-
    receive(B),
    (B=request(C,D,joke) 
   ->joke(A,E),
     send(C,reply(D,E)),
     joke_loop(A)

    ;B==stop->true

    ;joke_loop(A)).

% Start a jokes service under a registered Name. start_joke_service(+Name, +Topic)
% Ex: start_joke_service("jokes", "programming").
start_joke_service(A,B) :-
    serve(A,joke_loop(B)).

Now what does this all have to do with (Sub-)Agents?

Since the REPL comes with all the above primitives we can directly ask our REPL: “Please run a sub-agent to do X…”. The REPL will then execute the spawn predicate to start a process that does what ever we asked the agent to do. A spawn will always return control back to the REPL/Agent, who can then use some built-monitoring functions for regularly checking up on our sub-agent and see if it’s finished.

We may also ask the REPL Agent something like “Please create a service process that works like a scheduled cronjob and checks websites X,Y,Z every five minutes, summarizes content A,B,C on it and sends me an email”.

Going further, we can define tools that trigger messages to other processes. So, for example, a main agent that has these tools could retrieve a message, run an agent loop which triggers tool calls, which - in turn - trigger messages to other running processes (=a routing mechanism). Stuff like this allows us to build entirely event driven, highly dynamic multi-agent systems with complex topologies.

To maintain some sort of idea what’s currently, the REPL also has some special slash commands, such as e.g. “/ps [—watch] [—logs] “ to follow the output of running processes, or “/send [message]” and “/stop [pid]” for sending messages to a process or killing it altogether.

Does that really work?

Well, so far it seems to, yes. And there are a at least two features that support this claim:

  1. Every process gets a snapshot of the current code in the REPL session and runs only this code. It cannot change any code in the main REPL session and changes to the code in the main session module do not affect running processes. If the code running inside a process is defined in the main session, we can modify the code definition of the process, start it again and we will have two processes running different version of the code.
  2. “Let it crash”: It is assumed that processes will crash and that’s ok. We can monitor process state, check for errors, fix the code and re-run. Without ever restarting the entire system.
  3. All running processes can be observed, killed and inspected live.

Where to next? Good question! Besides having had a lot of fun with this (I would’ve never dreamed of building something like this before roughly 9 months ago), I hope that DeepClause will become useful for some in one way or the other. If you build something interesting with it, please let me know!

── more in #ai-agents 4 stories · sorted by recency
── more on @deepclause 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/prolog-swarms-beam-l…] indexed:0 read:6min 2026-10-15 · —