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:
- El modelo de integración: qué hace el agente frente a qué hace el protocolo.
- Cómo conectar el SDK de Go a un proceso de agente.
- Patrones de integración para frameworks de agentes de IA populares.
- Firma y envío de transactions desde el código del agente.
- Lectura del estado on-chain.
- 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:
- El archivo de clave privada
.ukeydel 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. - 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.
- 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.