golemcloud/golem/golem-call-another-agent-go

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Calling one Go agent from another via typed RPC. Use when the user asks to invoke, call, or communicate with another agent, do agent-to-agent RPC, or await a result from another agent in a Go Golem project.

Install

npx vskill@latest install golemcloud/golem/golem-call-another-agent-go

SKILL.md

Calling Another Agent (RPC) in Go

Overview

Agents talk to each other through the callee's definition package. You get a typed client from Agent.Get(id) and call a method descriptor on it. The caller imports only the callee's definition package (its ID, its method descriptors) — never its impl — so the two implementations never form a Go import cycle.

Calls are durable: results are recorded in the oplog and replayed after a restart rather than re-issued.

Steps

  1. Import the callee's definition package (e.g. myapp/agents/ledger).
  2. Get a client for a specific instance: ledger.Agent.Get(ledger.ID{Region: "eu"}).
  3. Call a method descriptor on the client: ledger.Record.MustCall(client, ledger.RecordIn{Amount: 10}), or Call to get (Out, error) back.
  4. If the method returns a golem.Result, unwrap it (.MustOk() or inspect it).

Synchronous call

go
package impl
import (
"myapp/agents/ledger"
"myapp/agents/rpccaller"
"github.com/golemcloud/golem/sdks/go/golem"
)
type state struct{}
var agent = rpccaller.Agent.Implement(func(rpccaller.ID) *state { return &state{} })
func init() {
agent.Handle(rpccaller.Call, func(_ *golem.Context[state], in rpccaller.CallIn) int64 {
// Get a client for the ledger instance identified by region.
c := ledger.Agent.Get(ledger.ID{Region: in.Region})
// MustCall returns the method's Out; here Out is a golem.Result, unwrapped with MustOk.
return ledger.Record.MustCall(c, ledger.RecordIn{Amount: in.Amount}).MustOk()
})
}

Call and MustCall block until the callee returns. Because a single agent processes invocations sequentially, a synchronous self-call (calling back into the same instance) would deadlock — use Trigger/Schedule for self-messaging (see golem-recurring-task-go).

Plain vs Result returns

MustCall returns the method's declared output type directly. Only unwrap when that type is a golem.Result — don't append .MustOk() to a plain return:

go
// greeter.Hello returns a plain string → use it directly
greeting := greeter.Hello.MustCall(c, greeter.HelloIn{Name: "alice"})
// ledger.Record returns golem.Result[int64, string] → unwrap
total := ledger.Record.MustCall(c, ledger.RecordIn{Amount: 10}).MustOk()

Handling a failed call

Call returns (Out, error). The error is a *golem.RemoteCallError when the callee failed, could not be reached after the runtime's retries, or returned a value that does not decode as Out. Its Kind classifies the failure, and a remote custom error stays reachable through errors.As:

go
total, err := ledger.Record.Call(c, ledger.RecordIn{Amount: 10})
if err != nil {
var remote *golem.RemoteCallError
if errors.As(err, &remote) && remote.Kind == golem.RemoteNotFound {
return "the ledger is not deployed"
}
panic(err)
}

MustCall panics with the same error instead. A panic in a method traps the component, as in Rust: the calling invocation fails without retry.

Asynchronous call

Start the call, do other work, then await:

go
fut := ledger.Record.CallAsync(c, ledger.RecordIn{Amount: in.Amount}) // *golem.Future[Out]
// ... other work ...
total := fut.MustGet().MustOk() // MustGet() blocks for the result; Get() returns (Out, error)

Launch several and await them to fan out work across other agents.

Unwrapping a golem.Result

When a method's output is golem.Result[Ok, Err]:

go
res := ledger.Record.MustCall(c, in) // golem.Result[int64, string]
switch {
case res.IsErr():
// handle the domain error value
msg := res.Err()
_ = msg
default:
total := res.Ok()
_ = total
}
// Or fail loud (panic on Err, which fails the calling agent):
total := ledger.Record.MustCall(c, in).MustOk()

Import-cycle rule (important)

  • impl packages never import each other. All cross-agent references go through the callee's definition package, which imports only the SDK.
  • This keeps the import graph a DAG no matter how cyclic the call graph is (A↔B mutual calls are fine as long as each impl imports the other's def, not its impl).

Calling an agent in another component

The definition package only exists inside the component that defines the agent. To call an agent that lives in another component — in any language — use the guest client golem build generates for it.

Declare the dependency on the calling component in golem.yaml:

yaml
components:
example:caller:
dir: caller
templates: go
dependencies:
agents:
- example:weather/WeatherAgent

golem build then generates golem-temp/bridge-sdk/go/internal/weather-agent-guest-client, a Go module named golem.local/bridge/weather-agent-guest-client. Point the calling component's go.mod at it:

require golem.local/bridge/weather-agent-guest-client v0.0.0
replace golem.local/bridge/weather-agent-guest-client => ../golem-temp/bridge-sdk/go/internal/weather-agent-guest-client

Import it and call through the typed client:

go
import weather "golem.local/bridge/weather-agent-guest-client"
client, err := weather.GetWeatherAgent(weather.WeatherAgentID{City: "London"})
if err != nil {
return err.Error() // the weather component is not deployed
}
forecast, err := client.Forecast(3) // (Out, error)
forecast = client.MustForecast(3) // panics on a failed call
receipt := client.MustTriggerForecast(3) // enqueue, don't wait
pending := client.ForecastAsync(3) // *golem.Future[Out]
  • Get<Agent> and NewPhantom<Agent> return an error when the host cannot resolve the target, as the callee lives in another component that may not be deployed. MustGet<Agent> and MustNewPhantom<Agent> panic instead.
  • Each method has the forms of a same-component MethodDef: <M> (MethodDef.Call: (Out, error), only error without output), Trigger<M>, Schedule<M>(at, …), each with a Must form, and <M>Async (CallAsync). A failed call returns the SDK's own *golem.RemoteCallError, exactly as a same-component call does. Methods that take or return a stream have no trigger or schedule form.
  • The .local module path can never be fetched from a module proxy, so a missing replace fails the build rather than silently resolving something else.
  • The client's types are the callee's, generated from its schema: records become structs, variants sealed interfaces with one type per case, enums integer constants with a String() method.
  • Methods that take or return streams are included, with each stream spelled golem.AgentStream[T] (see golem-streaming-agent-go).
  • Get<Agent> and NewPhantom<Agent> take golem.ClientOpts. When the callee declares local configuration, the client also has a <Agent>Config struct, with one values.Option field per config path (["limits", "max-items"] → LimitsMaxItems), and a With<Agent>Config option. Only the fields you set are sent; the rest keep their provisioned values:
go
counter, err := counters.GetCounterAgent(counters.CounterAgentID{Name: "fast"},
counters.WithCounterAgentConfig(counters.CounterAgentConfig{Step: golem.Some[uint32](5)}))

Key Constraints

  • You call Method.Call(client, in) (not client.Call(method, in)): the method descriptor carries the input/output types. Same for Trigger, Schedule, CallAsync and the Must forms.
  • The client comes from the definition (Def.Agent.Get(id)); the ID is encoded from the ID type, so the caller does not need the callee locally registered.
  • Don't hold a *golem.Future across invocations — await it (Get() / MustGet()) within the same handler.
Related Skills
SkillWhen to Load
golem-fire-and-forget-goEnqueue a call without awaiting a result (Trigger)
golem-recurring-task-goAn agent messages itself / schedules future work
golem-multi-instance-agent-goAddress a specific instance (or a phantom) by ID
golem-add-agent-goDefine the agent being called
golem-agent-reflection-goCall agents without their Go definition (runtime discovery, remote declarations, agent ids)

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/golemcloud/golem/golem-call-another-agent-go/badge)](https://verified-skill.com/skills/golemcloud/golem/golem-call-another-agent-go)

Something wrong with this skill or its page? Tell us.