Saltar al contenido principal

Integración de Agentes de IA Externos

Descripción General​

Cualquier proceso autónomo que pueda invocar endpoints HTTP o ejecutar código Go puede integrarse como agente en ULedger. El SDK de Go (github.com/ULedgerInc/golang-sdk) es la interfaz de integración principal. El node también expone una API REST HTTP directa para los entornos en los que el SDK de Go no es la mejor opción.

Esta guía cubre:

  1. El modelo de integración: qué hace el agente frente a qué hace el protocolo.
  2. Cómo conectar el SDK de Go a un proceso de agente.
  3. Patrones de integración para frameworks de agentes de IA populares.
  4. Firma y envío de transactions desde el código del agente.
  5. Lectura del estado on-chain.
  6. Monitoreo y confirmación de receipts.

El Modelo de Integración​

En ULedger, el rol del agente es limitado y está bien definido:

External AI System (LLM, agent framework, custom code)
│
│ "I want to invoke council-voting:submitWork with vote=1"
│
▼
ULedger Go SDK (or raw HTTP)
│
│ 1. Load agent wallet private key (.ukey file)
│ 2. Build transaction payload
│ 3. Sign with agent wallet private key
│ 4. POST to any node endpoint
│ 5. Poll for receipt
│ 6. Return transaction ID and block height
│
▼
ULedger Chain
│
│ Validate capabilities, execute, commit, Helix-proof
│
▼
On-chain record: "agent-wallet-X submitted vote=1 at block 12345"

El sistema de IA decide qué hacer. El SDK se encarga de cómo crear y enviar la transaction firmada. El protocolo determina si la transaction está autorizada y la registra de forma permanente.


Configuración​

Para el recorrido completo de obtención de un agente (generar, registrar, crear la entity, vincular la billetera, adjuntar capabilities) se puede consultar Obtención de una Billetera del Agente.

Puntos de control clave de la integración antes de escribir cualquier código de agente:

  1. El archivo de clave privada .ukey del agente debe ser accesible para el proceso del agente en tiempo de ejecución. Se debe usar un gestor de secretos o un volumen montado de forma segura, no variables de entorno ni el control de versiones.
  2. Las capabilities deben ser adjuntadas por la billetera padre antes de que el agente pueda actuar. El agente no puede otorgarse permisos a sí mismo. Se puede consultar Capabilities para la referencia completa de campos.
  3. El agente necesita: la ruta del archivo .ukey, las URLs de endpoint de los nodes, el ID de blockchain y la(s) dirección(es) del contract que está autorizado a invocar.

Para el ciclo de vida completo, incluidas la administración y la terminación, se puede consultar El Ciclo de Vida de la Billetera del Agente.


Integración con el SDK de Go​

Importación​

import (
"github.com/ULedgerInc/golang-sdk/pkg/wallet"
"github.com/ULedgerInc/golang-sdk/pkg/transaction"
"github.com/ULedgerInc/golang-sdk/pkg/txconfirm"
)

Cargar la Billetera del Agente​

agentWallet, err := wallet.LoadFromFile("./wallets/agent.ukey", passphrase)
if err != nil {
return fmt.Errorf("load agent wallet: %w", err)
}

Enviar una Invocación de Contract​

// Build the invoke transaction
tx := transaction.InvokeContract{
ContractAddress: contractAddress,
FunctionName: "submitWork",
Args: []string{proposalID, "1", "AI analysis complete: recommend approval"},
GasLimit: 100_000,
}

// Sign and submit
receipt, err := transaction.SignAndSubmit(ctx, agentWallet, nodeEndpoints, blockchainID, tx)
if err != nil {
return fmt.Errorf("submit: %w", err)
}

// Poll for confirmation (from a different node for cross-node consistency)
confirmed, err := txconfirm.Poll(ctx, receipt.TxID, alternateNodeEndpoint, txconfirm.PollConfig{
IntervalSeconds: 1,
TimeoutSeconds: 80,
})

Leer el Estado del Contract​

state, err := sdk.ReadContractState(ctx, nodeEndpoint, blockchainID, contractAddress, key)

Enviar una Data Transaction​

dataTx := transaction.DataTx{
Payload: []byte(`{"event":"research_complete","result":"positive","confidence":0.92}`),
}
receipt, err := transaction.SignAndSubmit(ctx, agentWallet, nodeEndpoints, blockchainID, dataTx)

Integración mediante API HTTP (Agentes que no usan Go)​

Para los agentes escritos en Python, TypeScript u otros lenguajes, está disponible la API HTTP directa.

Enviar una Transaction​

POST http://localhost:8080/chains/{chainId}/transactions
Content-Type: application/json

{
"type": "INVOKE_SMART_CONTRACT",
"sender": "<agent-wallet-address>",
"signature": "<hex-encoded-signature>",
"payload": {
"contractAddress": "<contract-address>",
"function": "submitWork",
"args": ["<proposalId>", "1", "AI analysis: positive"],
"gasLimit": 100000
}
}

La firma es sign(SHA256(transaction-payload-bytes), agentPrivateKey) utilizando el algoritmo de clave de la billetera.

Ejemplo en Python (secp256k1)​

import hashlib
import requests
from coincurve import PrivateKey

def submit_vote(node_url, chain_id, contract_addr, private_key_hex, proposal_id, vote):
payload = {
"contractAddress": contract_addr,
"function": "submitWork",
"args": [proposal_id, str(vote), "AI vote"],
"gasLimit": 100000,
}
payload_bytes = json.dumps(payload, separators=(',', ':')).encode()
msg_hash = hashlib.sha256(payload_bytes).digest()

privkey = PrivateKey(bytes.fromhex(private_key_hex))
sig = privkey.sign(msg_hash, hasher=None)

tx = {
"type": "INVOKE_SMART_CONTRACT",
"sender": derive_address(privkey.public_key.format(compressed=True).hex()),
"signature": sig.hex(),
"payload": payload,
}

resp = requests.post(f"{node_url}/chains/{chain_id}/transactions", json=tx)
resp.raise_for_status()
return resp.json()["txId"]

Para ML-DSA-87 (post-cuántico), la firma utiliza la implementación mldsa87 de Cloudflare CIRCL; se recomienda usar el SDK de Go para esto, ya que pocas bibliotecas de Python implementan ML-DSA-87 por el momento.


Patrones de Integración con Frameworks​

LangChain (Python)​

Se envuelven las operaciones del SDK como herramientas de LangChain:

from langchain.tools import tool

@tool
def submit_on_chain_vote(proposal_id: str, vote: int, reasoning: str) -> str:
"""Submit a vote on a governance proposal on ULedger. Vote: 1=yes, 2=no, 3=abstain."""
tx_id = uledger_client.invoke_contract(
contract=COUNCIL_CONTRACT,
function="submitWork",
args=[proposal_id, str(vote), reasoning],
)
return f"Vote submitted. Transaction ID: {tx_id}"

@tool
def read_proposal_status(proposal_id: str) -> str:
"""Read the current status of a governance proposal."""
status = uledger_client.read_state(COUNCIL_CONTRACT, f"proposal:{proposal_id}:status")
return status

El LLM decide cuándo llamar a estas herramientas. El cliente de ULedger se encarga de la firma. Cada llamada queda registrada on-chain.

CrewAI (Python)​

Se asignan las herramientas de ULedger a los agentes:

from crewai import Agent, Task

voting_agent = Agent(
role="Governance Analyst",
goal="Review proposals and submit votes based on organizational policy",
tools=[submit_on_chain_vote, read_proposal_status, read_proposal_details],
wallet_key_path="./wallets/crew-analyst.ukey",
)

vote_task = Task(
description="Review proposal {proposal_id} and submit a vote with reasoning.",
agent=voting_agent,
)

Bucle de Orquestación Personalizado​

Para sistemas completamente autónomos sin un framework:

import time

def agent_loop(node_url, chain_id, contract_addr, wallet):
while True:
# Check for pending proposals
proposals = fetch_active_proposals(node_url, chain_id, contract_addr)

for proposal in proposals:
if not already_voted(proposal["id"], wallet.address):
# AI decision logic
analysis = run_analysis(proposal["description"])
vote = 1 if analysis["recommendation"] == "approve" else 2

# Submit on-chain
tx_id = wallet.invoke_contract(
contract_addr, "submitWork",
[proposal["id"], str(vote), analysis["reasoning"]]
)
log(f"Voted {vote} on proposal {proposal['id']}: tx={tx_id}")

time.sleep(10) # Poll interval

Monitoreo de la Actividad de la Billetera del Agente​

Consultar el Estado de la Billetera​

GET /chains/{chainId}/wallets/{agentWalletAddress}

Devuelve el estado actual de la billetera: habilitada/deshabilitada, capabilities, afiliación a la entity.

Consultar el Estado de las Capabilities​

GET /chains/{chainId}/wallets/{agentWalletAddress}/capabilities/status

Devuelve el estado de cada capability: active, expiring_soon, exhausted, expired.

Suscribirse a Eventos del Contract (WebSocket)​

WS /chains/{chainId}/events?contract={contractAddress}

Se reciben en tiempo real los eventos emitidos por el contract, lo cual es útil para activar respuestas del agente cuando otros agentes o personas realizan acciones.

Consultar el Historial de Transactions​

GET /chains/{chainId}/transactions?sender={agentWalletAddress}&limit=100

Historial on-chain completo de todas las transactions enviadas por la billetera del agente.


Flujo de Renovación de Capabilities​

Se recomienda configurar un proceso de renovación de capabilities para evitar que los agentes queden fuera de línea cuando las capabilities expiren:

def check_and_renew_capabilities(parent_wallet, agent_wallet_addr, node_url, chain_id):
status = fetch_capability_status(node_url, chain_id, agent_wallet_addr)

for cap in status["capabilities"]:
if cap["status"] in ("expiring_soon", "exhausted"):
# Parent wallet renews capability
parent_wallet.alter_wallet(agent_wallet_addr, new_capabilities=[
{
"action": cap["action"],
"resource": cap["resource"],
"limits": {"maxInvocations": 1000},
"notAfter": current_block + 50000,
}
])
log(f"Renewed capability {cap['action']} for agent {agent_wallet_addr}")

Se recomienda ejecutar esto como una tarea programada. La clave de la billetera padre no necesita estar en poder del agente; se recomienda mantenerla en una ubicación separada y más segura.