Skip to content
AgentField
Coordination

Service Discovery

Find agents, reasoners, and skills at runtime through the control plane discovery API.

Runtime service discovery — agents find each other

Find every agent, reasoner, and skill in your fleet -- at runtime, not at deploy time.

Hard-coded service addresses break the moment you scale past a handful of agents. AgentField's discovery API lets any agent query the control plane for live capabilities, filtered by tags, health status, or name patterns. Build orchestrators that discover what is available, introspect input schemas, and dynamically decide which agents to call -- all without a single hardcoded address.

@app.reasoner(tags=["orchestrator"])
async def dynamic_router(question: str) -> dict:
    # 1. Discover active agents with input schemas — zero hardcoded addresses
    caps = app.discover(
        tags=["public"],
        health_status="active",
        include_input_schema=True,
        format="json",
    )
    print(f"{caps.json.total_agents} agents online, {len(caps.json.capabilities)} capabilities")

    # 2. Feed discovered capabilities + schemas into an LLM — it picks the best agent
    result = await app.ai(
        system=(
            "You are a routing agent. Pick the best tool for the user's question.\n"
            "Available capabilities:\n" + caps.raw
        ),
        user=question,
        tools="discover",  # built-in: turns discovered capabilities into callable tools
    )

    # 3. The LLM chose an agent and called it — no static routing table needed
    return result

# New agents register at startup → the router discovers them automatically.
# No config changes, no redeployment, no service mesh.
agent.reasoner('dynamicRouter', async (ctx) => {
  // 1. Discover active agents with input schemas — zero hardcoded addresses
  const caps = await agent.discover({
    tags: ['public'],
    healthStatus: 'active',
    includeInputSchema: true,
    format: 'json',
  });
  console.log(`${caps.json?.totalAgents} agents online`);

  // 2. Feed discovered capabilities + schemas into an LLM — it picks the best agent
  const result = await ctx.ai(ctx.input.question, {
    system: `You are a routing agent. Pick the best tool.\nAvailable:\n${caps.raw}`,
  });

  // 3. The LLM chose an agent and called it — no static routing table needed
  return result;
});

// New agents register at startup → the router discovers them automatically.
a.RegisterReasoner("dynamic_router", func(ctx context.Context, input map[string]any) (any, error) {
    // 1. Discover active agents with input schemas — zero hardcoded addresses
    caps, err := a.Discover(ctx,
        agent.WithTags([]string{"public"}),
        agent.WithHealthStatus("active"),
        agent.WithDiscoveryInputSchema(true),
        agent.WithFormat("json"),
    )
    if err != nil {
        return nil, err
    }
    fmt.Printf("%d agents online, %d capabilities\n",
        caps.JSON.TotalAgents, len(caps.JSON.Capabilities))

    // 2. Use compact format in LLM context for dynamic tool selection
    // 3. Route to the best agent based on discovered capabilities
    return map[string]any{
        "available_agents": caps.JSON.TotalAgents,
        "capabilities":     caps.JSON.Capabilities,
    }, nil
})

// New agents register at startup → the router discovers them automatically.

What just happened

  • The agent queried the live control plane registry instead of relying on hardcoded targets
  • Discovery results included enough metadata to drive dynamic routing decisions
  • New active agents could appear in routing immediately after registration

Example discovery summary:

{
  "discovered_at": "2026-03-23T12:00:00Z",
  "total_agents": 2,
  "total_reasoners": 2,
  "total_skills": 0,
  "pagination": { "limit": 100, "offset": 0, "has_more": false },
  "capabilities": [
    {
      "agent_id": "weather-agent",
      "base_url": "http://weather-agent:9000",
      "version": "1.0.0",
      "health_status": "active",
      "deployment_type": "sidecar",
      "last_heartbeat": "2026-03-23T12:00:00Z",
      "reasoners": [
        { "id": "forecast", "tags": ["public", "weather"], "invocation_target": "weather-agent:forecast" }
      ],
      "skills": []
    },
    {
      "agent_id": "translator",
      "base_url": "http://translator:9000",
      "version": "1.0.0",
      "health_status": "active",
      "deployment_type": "sidecar",
      "last_heartbeat": "2026-03-23T12:00:00Z",
      "reasoners": [
        { "id": "translate", "tags": ["public", "translation"], "invocation_target": "translator:translate" }
      ],
      "skills": []
    }
  ]
}