Welcome to the new Golem Cloud Docs! 👋
Forking Agents

Forking Agents

Explanation

Golem agents are single threaded. To achieve parallel execution, it is possible to spawn child agents and communicate with them using RPC, as described on the Agent to Agent communication page.

A simpler way is to use the fork API. The fork API consists of a single host function, defined as the following:

declare module 'golem:api/host@1.1.7' {
  // ...
 
  /**
   * Indicates which agent the code is running on after `fork`
   */
  export type ForkResult = "original" | "forked";
 
  /**
   * Forks the current agent at the current execution point.
   * The new agent gets the same base agent ID but with a new unique phantom ID.
   * The phantom ID of the forked agent is returned in fork-result on both sides.
   * The newly created agent continues running from the same point,
   * but the return value is going to be different in this agent and the forked agent.
   */
  export function fork(): ForkResult;
}

Usage

Using this fork function from a component that was created from Golem's built-in templates is straightforward because access to the Golem specific host functions is already set up.

The following code snippet demonstrates calling fork and continuing on two different parallel branches based on its result value:

import { BaseAgent, agent } from '@golemcloud/golem-ts-sdk';
import { fork, type ForkResult } from "golem:api/host@1.1.7"
 
@agent()
class ExampleAgent extends BaseAgent {
  name: string;
 
  constructor(name: string, mode: "root" | "fork") {
    super();
    this.name = name;
  }
 
  run() {
    switch (fork(`example-agent("${this.name}",fork)`)) {
      case "original": {
        // ...
        break;
      }
      case "forked": {
        // ...
        break;
      }
    }
  }
}

Join

Once the agent has been forked, the two agent instances are running simultaneously and independently. Golem promises provide a way to resynchronize the two agents.

The high level idea is the following:

  • The original agent creates a Golem promise and stores the resulting PromiseId in a variable.
  • Then forks the new agent. Both the new and the old agents have the promise id.
  • When the new agent is done with the work it was forked for, it completes the promise and includes some arbitrary payload it wants to send back to the original agent.
  • The original agent, after doing some additional work in parallel to the forked one, can suspend its execution to wait for the promise to be completed. When the promise is completed, the original agent resumes execution and receives the payload sent by the forked agent.

The following code snippet demonstrates this pattern:

import { BaseAgent, agent } from '@golemcloud/golem-ts-sdk';
import { awaitPromise, completePromise, createPromise, PromiseId, fork, type ForkResult } from "golem:api/host@1.1.7"
 
@agent()
class ExampleAgent extends BaseAgent {
  name: string;
 
  constructor(name: string, mode: "root" | "fork") {
    super();
    this.name = name;
  }
 
  async run() {
    const promiseId: PromiseId = createPromise();
 
    switch (fork()) {
      case "original": {
        const localResult = ...; // ... do some more work;
 
        const rawForkResult: UInt8Array = await awaitPromise(promiseId);
        const forkResult = JSON.parse(new TextDecoder().decode(rawForkResult));
 
        // Merge localResult and forkResult and continue running
        break;
      }
      case "forked": {
        const result = ...; // do some work in parallel to the original agent
 
        const rawResult: UInt8Array = new TextEncoder().encode(JSON.stringify(result));
        completePromise(promiseId, rawResult);
 
        // Stop execution
        break;
      }
    }
  }
}
⚠️

Note that Golem Promises are NOT JavaScript Promises, and the API to create, complete and await them is currently blocking.