Recipes

암호화폐 신원, A2A 메시징, x402 USDC 결제를 갖춘 연구 에이전트를 Bindufy하세요

README 링크에서 발견 · 2 days ago

Software & DevOps

Agno 리서치 에이전트를 DID와 A2A 프로토콜로 온라인에 연결하기 위해 래핑합니다

What they did

Agno 리서치 에이전트(OpenAI 모델과 DuckDuckGo 검색 도구)를 이미 작동하는 상태로 보유하고 있었고, identity와 payment 배관을 직접 구현하지 않고도 다른 에이전트들이 이 에이전트에 접근할 수 있기를 원했다. DID 라이브러리, OAuth 플로우, payment 미들웨어, A2A 호환 HTTP 레이어를 각각 통합하는 대신, `uv add bindu`로 Bindu를 설치하고 핸들러를 단순한 `(messages) -> response` 함수로 유지한 뒤 에이전트를 `bindufy()`에 전달했다. 이 단일 호출로 에이전트는 자체 암호화 identity, 다른 에이전트가 주소를 지정할 수 있게 해주는 A2A 프로토콜 지원, 그리고 작업을 수행하기 전에 EVM 체인에서 USDC를 요구할 수 있는 x402 payment 게이팅과 함께 온라인 상태가 되었다. LLM provider 키(OPENROUTER_API_KEY, OPENAI_API_KEY, 또는 MINIMAX_API_KEY)를 설정하고 스크립트를 실행한 다음, Bindu inbox walkthrough를 사용해 에이전트 간의 서명된 JSON-RPC 트래픽을 관찰하고, identity를 인라인으로 검증하고, 마치 이메일 스레드인 것처럼 swarm에 답장했다. 핸들러 내부의 프레임워크는 계속 교체 가능하게 유지되었다 — 여기서는 Agno였지만, LangChain, CrewAI, 또는 커스텀 코드도 동일한 방식으로 작동할 것이다.

How it went

에이전트는 단 한 번의 함수 호출로 로컬 스크립트에서 네트워크 주소 지정이 가능하고, 신원을 지니며, 결제 가능한 엔드포인트로 전환되었고, 맞춤형 DID나 결제 코드는 전혀 필요 없었다. 인박스 뷰는 에이전트 간 트래픽을 읽을 수 있게 만들어 주었다: 서명된 메시지, 인라인 서명 검증, 스레드 형식의 답장. 완성도 수준은 동작하는 프로토타입이다 — 퀵스타트는 짧고 핸들러 계약은 최소한이므로, 결과물은 강화된 프로덕션 서비스라기보다 살아 있는 에이전트다.

Worth knowing

Python 3.12+와 uv, 그리고 최소 하나의 LLM 제공자에 대한 API 키가 필요합니다. 결제 설정(EVM 체인, USDC 수신 주소, 가격 책정)은 퀵스타트에 명시되어 있지 않으며 문서를 참고해야 합니다. 에이전트를 공개적으로 노출하기 전에 아이덴티티 키와 각종 시크릿은 별도로 처리해야 합니다. 인박스 둘러보기는 데모용 표면이며 프로덕션 모니터링 스택이 아닙니다. 다국어 SDK(TypeScript, Kotlin)는 gRPC 코어를 공유하지만 여기서는 실행해 보지 않았습니다.

Wiring

순서: 설치(`uv add bindu`, 또는 Bindu 자체를 해킹하려면 클론 후 `uv sync --dev`) → LLM 제공자 키 설정 → 에이전트 정의(예: instructions, OpenAIChat 모델, DuckDuckGoTools를 갖춘 Agno Agent) → 핸들러를 `(messages) -> response`로 정의 → identity/skill 설정과 함께 `bindufy(agent, ...)` 호출 → 스크립트 실행. 그러면 Bindu가 HTTP/A2A 계층을 세우고, 에이전트의 DID 기반 암호화 identity를 생성 및 관리하며, 핸들러가 실행되기 전에 호출이 EVM 체인상의 USDC를 요구할 수 있도록 x402 결제 미들웨어를 삽입한다. 다른 에이전트들은 서명된 JSON-RPC를 사용해 A2A로 이를 호출하며, 서명은 인라인으로 검증된다. Python, TypeScript, Kotlin SDK는 동일한 gRPC 코어를 공유하므로 프로토콜과 identity 동작이 언어 전반에서 일관된다. inbox UI는 에이전트 간 스레드를 관찰하고 답장하기 위한 관찰 표면이다.

Try it yourself

# Making Your Agent Network-Addressable with Bindu

## 1. Verify Python 3.12+ and uv, add bindu

```bash
python --version   # must be >= 3.12
uv --version       # install via: curl -LsSf https://astral.sh/uv/install.sh | sh
```

Add the dependency (adjust to your project layout):

```bash
uv add bindu
```

If you're on a `pyproject.toml`-based project, this updates it and the lockfile. For a plain `requirements.txt` setup, add `bindu` to it and run `uv pip install -r requirements.txt`.

## 2. Locate your agent entrypoint

Your handler should have the signature `(messages) -> response`. Common shapes:

- **Agno**: `agent.run(messages)` or a thin wrapper around it
- **LangChain**: a `Runnable` or a function calling `chain.invoke(messages)`
- **Custom**: any callable taking a list of messages and returning a response

Identify the exact callable and the module path, e.g. `my_agent.handler:run`. You will not modify it.

## 3. Wrap with `bindufy` (handler unchanged)

Create a new file, e.g. `serve.py`, at the repo root:

```python
# serve.py
import os
from bindu.penguin.bindufy import bindufy
from my_agent.handler import run  # <-- your existing handler, unchanged

bindufy(
    handler=run,
    name="my-agent",
    description="My existing agent, now reachable over A2A.",
    skills=[
        {
            "id": "chat",
            "name": "Chat",
            "description": "General-purpose conversational responses.",
        },
    ],
    # x402 payment gating (see step 5)
    payment={
        "address": os.environ["X402_RECEIVING_ADDRESS"],
        "price": os.environ["X402_PRICE"],
        "chain": "base",  # EVM chain used for settlement
    },
)
```

`bindufy` wraps your handler as-is; it does not touch internal logic. It exposes the agent over A2A and advertises the name/description/skills in the agent card so other agents can discover it.

## 4. Agent config for discovery

The `name`, `description`, and `skills` passed to `bindufy` are what other agents see when they fetch your agent card over A2A. Keep skill `id`s stable — they're the contract other agents reference. If you prefer config-as-data, move these into a `agent_config.yaml` and load it before calling `bindufy`; the fields are the same.

## 5. x402 payment gating

Read the receiving address and price from env vars (never hardcode):

```bash
export X402_RECEIVING_ADDRESS=0xYourReceivingAddress
export X402_PRICE="0.01"
```

**EVM chain used:** Base (mainnet). If you need a different chain, change `"chain": "base"` in `serve.py` to the target chain identifier supported by your x402 facilitator (e.g. `base-sepolia` for testnet). Document the chosen chain in your README so callers know where to settle payment.

## 6. LLM provider key from env

Ensure your agent reads its key from the environment. In your existing handler (or a small config module it already imports), the key should come from:

```python
import os

api_key = (
    os.environ.get("OPENROUTER_API_KEY")
    or os.environ.get("OPENAI_API_KEY")
    or os.environ.get("MINIMAX_API_KEY")
)
if not api_key:
    raise RuntimeError("No LLM provider key set (OPENROUTER_API_KEY / OPENAI_API_KEY / MINIMAX_API_KEY)")
```

If your handler already does this, leave it alone. If it currently hardcodes a key, replace the literal with the env lookup above — that's the only change, and it doesn't alter logic.

## 7. Smoke test

```python
# tests/test_smoke.py
import os
import pytest
from bindu.client import A2AClient  # adjust import to your bindu version

@pytest.mark.asyncio
async def test_agent_responds_over_a2a():
    # Assumes the agent is running locally (see README) at this URL.
    client = A2AClient(base_url=os.environ.get("AGENT_URL", "http://localhost:8000"))

    response = await client.send_message(
        messages=[{"role": "user", "content": "Hello, are you there?"}],
    )

    assert response is not None
    assert response.get("signature"), "expected a signed response"
    assert response.get("content"), "expected non-empty content"
```

Run it against a locally started agent:

```bash
uv run python serve.py &          # start agent
uv run pytest tests/test_smoke.py # send sample A2A message, assert signed response
```

If your bindu version exposes a different client helper, swap `A2AClient` for the equivalent — the assertion (signed, non-empty response) stays the same.

## 8. README section

```markdown
## Running locally

1. Set required env vars:
   ```bash
   export OPENROUTER_API_KEY=...        # or OPENAI_API_KEY / MINIMAX_API_KEY
   export X402_RECEIVING_ADDRESS=0x...
   export X402_PRICE=0.01
   ```
2. Start the agent:
   ```bash
   uv run python serve.py
   ```
3. The agent is now reachable over A2A at `http://localhost:8000`. Other agents can discover it via its agent card (name, description, skills).

## Viewing traffic in the Bindu inbox

Open the Bindu inbox UI (default `http://localhost:8000/inbox`) to see incoming A2A messages, responses, and payment-gated requests in real time. Use it to confirm your agent is receiving and answering messages as expected.

## Payments

Requests are gated by x402. Settlement happens on **Base** (mainnet). Set `X402_RECEIVING_ADDRESS` and `X402_PRICE` before starting the agent.
```

---

**Summary of changes:** added `bindu` as a dependency; created `serve.py` that wraps your existing handler with `bindufy` (handler untouched); configured name/description/skills for A2A discovery; wired x402 payment via env vars with Base as the documented EVM chain; ensured the LLM key is read from env; added a smoke test asserting a signed A2A response; and documented local run + Bindu inbox in the README. Your agent's internal logic is unchanged.

Source

English

Tools in this stack