DEV Community

Philip Stayetski
Philip Stayetski

Posted on

Connecting a Python Agent to a Go Agent: A Cross-Language A2A Tutorial

You have a Python agent doing data extraction and a Go agent handling real-time inference. They need to talk to each other — but one is async Python, the other is compiled Go, and neither has the other's SDK. How do you bridge the gap in a way that's fast, secure, and doesn't require both teams to agree on a single language?

Cross-language agent-to-agent communication is a recurring friction point in multi-agent systems. This tutorial walks through the actual options, then shows a concrete worked example using a wire-format approach that lets a Python agent and a Go agent exchange messages without either team leaving their ecosystem.

The Core Problem: SDKs Differ, the Network Doesn't

When you write two agents in different languages, they can't import each other's libraries. A Python agent using asyncio can't call into a Go agent's goroutine-based runtime directly. What they need is a shared wire protocol — a format and transport that both sides implement independently.

The usual approaches are:

  • HTTP/REST: both sides speak it, but you're exposing a port and dealing with request/response semantics rather than persistent agent-to-agent messaging
  • gRPC: works cross-language, but you need to share and regenerate .proto files, and both agents need public or tunneled connectivity
  • Message queue (Redis, NATS): adds a broker dependency between your agents
  • Overlay network with SDKs: each language gets a native SDK that speaks the same encrypted wire protocol — agents get virtual addresses and talk directly

The last option is worth a closer look because it treats the network as the common ground instead of adding infrastructure between the agents.

Option 1: HTTP with a Shared Schema

The simplest cross-language setup: one agent exposes an HTTP endpoint, the other calls it.

# Python agent — client side
import httpx

response = httpx.post("http://go-agent:8080/infer", json={"input": "data"})
result = response.json()
Enter fullscreen mode Exit fullscreen mode
// Go agent — server side
http.HandleFunc("/infer", func(w http.ResponseWriter, r *http.Request) {
    var req Request
    json.NewDecoder(r.Body).Decode(&req)
    result := runInference(req.Input)
    json.NewEncoder(w).Encode(result)
})
http.ListenAndServe(":8080", nil)
Enter fullscreen mode Exit fullscreen mode

This works in a controlled environment where both agents can reach each other's IP. It breaks when:

  • Agents are behind NAT or on different networks
  • You want bidirectional streaming (not just request/response)
  • You need persistent messaging (the Go agent needs to push updates to the Python agent without being polled)

Option 2: Overlay Network with Language SDKs

An overlay network gives each agent a permanent virtual address, then handles transport (encrypted tunnels, NAT traversal) so the agent code just talks to a peer address. Both Python and Go agents install a small daemon and use their respective SDKs to send and receive messages.

Pilot Protocol is one example — it ships native Go (the daemon itself is written in Go with zero external dependencies) and a Python SDK on PyPI. Both speak the same wire format under the hood: X25519 key exchange, AES-GCM encrypted UDP tunnels.

The Wire Protocol at a Glance

Both SDKs use the same message envelope:

virtual_address → virtual_address
encrypted payload (AES-GCM)
per-peer handshake verification
Enter fullscreen mode Exit fullscreen mode

The Python SDK and Go SDK both serialize to this format over the overlay. Neither side needs to know the other's language — they just send structured JSON messages to a peer address.

Worked Example: Python Talks to Go

Let's wire up a Python agent that needs to query a Go agent for inference results.

Step 1 — Install the overlay on both machines:

# Same command on both machines
curl -fsSL https://pilotprotocol.network/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Both agents now have a permanent virtual address. They each get an address like N:NNNN.HHHH.LLLL.

Step 2 — Python agent sends a message (using the Go SDK? No — the Python SDK):

# Python agent — pilot_agent.py
from pilotprotocol import PilotAgent

async def main():
    agent = await PilotAgent.create()

    # The Go agent's virtual address (shared out-of-band or via discovery)
    go_peer = "430:ABCD.1234.5678"

    # Approve the Go agent's handshake (mutual trust)
    await agent.trust(go_peer)

    # Send a structured JSON message — exactly what you'd POST to HTTP
    response = await agent.send(
        go_peer,
        {"type": "inference_request", "payload": {"input": "customer query"}}
    )

    print(f"Go agent replied: {response}")
Enter fullscreen mode Exit fullscreen mode

Step 3 — Go agent receives and replies:

// Go agent — pilot_agent.go
package main

import (
    "github.com/pilot-protocol/go-sdk"
)

func main() {
    agent := pilot.NewAgent()

    // Listen for messages from any trusted peer
    agent.OnMessage(func(msg pilot.Message) {
        // msg.From is the Python agent's virtual address
        // msg.Data is the JSON payload

        var req Request
        json.Unmarshal(msg.Data, &req)

        result := runInference(req.Payload.Input)

        agent.Reply(msg.From, result)
    })

    agent.Listen()
}
Enter fullscreen mode Exit fullscreen mode

No HTTP server. No port exposed. No REST client. Both agents just send and receive messages over the overlay. The Python SDK wraps the same encrypted tunnel that the Go daemon uses — they're talking the same wire format without either team writing protocol code.

Step 4 — Reciprocate: Go sends a message to Python

Bidirectional communication is symmetric — the Go agent can initiate:

// Go agent sends a proactive message to the Python agent
agent.Send("430:WXYZ.9012.3456", pilot.Message{
    Type: "data_ready",
    Data: json.RawMessage(`{"dataset": "processed", "rows": 1500}`),
})
Enter fullscreen mode Exit fullscreen mode

And the Python agent receives it the same way it sent — through the overlay, not via a webhook endpoint it had to expose:

@agent.on_message
async def handle_message(msg):
    print(f"Received from Go agent: {msg.data}")
Enter fullscreen mode Exit fullscreen mode

What Cross-Language A2A Actually Needs

Looking at the worked example, the common requirements for Python-Go agent communication are:

  • A shared address space — both agents need to find each other without coordinating IPs
  • A common message envelope — structured JSON works; both languages parse it natively
  • Encrypted transport by default — you're sending potentially sensitive data between agents
  • No broker dependency — the agents talk directly, not through a queue you have to operate
  • Bidirectional from the start — either agent can initiate, no polling required

An overlay network with SDKs in both target languages provides all of these without either team learning the other's stack. The Python agent writes Python. The Go agent writes Go. The overlay handles the fact that they talk different languages by providing a shared networking layer under both.

The install and setup are the same whether you're connecting two Python agents, two Go agents, or one of each — which is the point. Cross-language A2A is a networking problem, not a code problem, once you stop trying to share libraries and start sharing a wire format.

Top comments (0)