Somnia 백서

Somnia

원문

bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 54쪽 중 50쪽

agents/base-agents/llm-parse-website) — Extract structured data from websites # Quickstart This section covers how to invoke Somnia Agents—calling their methods and receiving results. ## How Agent Invocation Works Agent invocations are **asynchronous**. When you invoke an agent: 1. Your request is sent to the Somnia network 2. Multiple validator nodes execute the agent independently 3. Validators reach consensus on the result 4. The result is returned (and optionally delivered to a callback) {% @mermaid/diagram content="sequenceDiagram participant You participant Platform as Platform Contract participant Network as Validator Network ``` You->>Platform: Invoke agent + fee Platform->>Network: Distribute to validators Network->>Network: Execute & reach consensus Network->>Platform: Return result Platform->>You: Deliver result (callback or response)" %} ``` This decentralized execution ensures that results are trustworthy and auditable. ## Network & Tokens Somnia Agents are live on both **Somnia Mainnet** and **Somnia Testnet**. Pick the network that matches your deployment target: | Property | Mainnet | Testnet | | ----------------------- | -------------------------------------------- | -------------------------------------------- | | Network Name | Somnia Mainnet | Somnia Testnet | | Chain ID | `5031` | `50312` | | RPC URL | `https://api.infra.mainnet.somnia.network` | `https://api.infra.testnet.somnia.network` | | Currency | SOMI | STT | | `SomniaAgents` Contract | `0x5E5205CF39E766118C01636bED000A54D93163E6` | `0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776` | Get testnet STT from the [Somnia Faucet](https://testnet.somnia.network). For mainnet, acquire SOMI through the usual channels. ## Explore Agents The easiest way to get started is the Agent Explorer web app: | Network | URL | | ------- | ---------------------------------------------------------------------------------- | | Testnet | [**https://agents.testnet.somnia.network**](https://agents.testnet.somnia.network) | | Mainnet | [**https://agents.somnia.network**](https://agents.somnia.network) | Here you can: * Browse all available agents * View agent methods and their parameters * Invoke agents directly from the browser * View execution receipts * Generate code snippets for Solidity and JavaScript ## Invocation Methods There are two ways to invoke agents: | Method | Use Case | Guide | | ------------ | ----------------------------------------------- | --------------------------------------------------------------- | | **Web App** | Exploration, testing, one-off calls | This page | | **Solidity** | Smart contract integration, on-chain automation | [Invoking from Solidity](/agents/invoking-agents/from-solidity) | All agent invocations go through the Solidity platform contract — including calls made from the web app. ## Next Steps * [Invoking from Solidity](/agents/invoking-agents/from-solidity) — Integrate agents into smart contracts * [Receipts](broken://pages/mMhGrKhRzYkIc9MyZqDZ) — Understand execution receipts * [Gas Fees](/agents/invoking-agents/gas-fees) — Learn about invocation costs # Invoking from Solidity This guide shows how to invoke Somnia Agents from smart contracts. Your contracts can call agents and receive responses via callbacks. ## Generate Code from the Web App The easiest way to get started is to use the code generator: 1. Visit <https://agents.somnia.network> 2. Select an agent from the list 3. Click on a method to expand it 4. Select the "Solidity" tab to see generated code for that method 5. Copy the generated code into your project The generator creates a complete contract with: * Platform interface definitions * Agent method encoding * Callback handling * Proper error handling > **Tip:** The "TypeScript" tab generates code for invoking agents from JavaScript/TypeScript — it sends transactions to the platform contract and listens for response events. ## How It Works {% @mermaid/diagram content="sequenceDiagram participant Contract as Your Contract participant Platform as SomniaAgents Contract participant Committee participant Validators as Validator Network ``` Contract->>Platform: createRequest{value: deposit}(agentId, callback, selector, payload) Platform-->>Validators: RequestCreated(..., perAgentBudget, subcommittee) Validators->>Platform: submitResponse (result, receipt, success, executionCost) Platform-->>Validators: gas refund (operations reserve) Platform->>Platform: Reach consensus Platform->>Contract: handleResponse(requestId, responses[], status, details) Platform->>Committee: deposit(subcommittee, [median × subSize]) Platform->>Contract: Send rebate (unused budget) Platform-->>Contract: RequestFinalized event" %} ``` 1. Your contract calls `createRequest()` with the agent ID, encoded payload, and a deposit. 2. The platform splits the deposit into an **operations reserve** (`minPerAgentDeposit × subSize`, funds gas refunds / callback / upkeep) and an **agent reward pot** (the rest). `perAgentBudget = rewardPot / subSize` is emitted in `RequestCreated` so runners know their cap. 3. Elected validators execute the agent and submit responses; each submission is gas-refunded from the operations reserve. 4. Once consensus is reached, the platform calls your callback, then pays every elected subcommittee member the **median** of the reported `executionCost` values via the committee. 5. Any unused funds are rebated to the requester. See [Gas Fees](/agents/invoking-agents/gas-fees) for a full fund-flow diagram and details on sizing `msg.value`. ## Platform Interface ```solidity enum ConsensusType { Majority, Threshold } enum ResponseStatus { None, // 0 - Default zero value (uninitialized storage) Pending, // 1 - Awaiting responses Success, // 2 - Consensus reached normally Failed, // 3 - Validators reported failure TimedOut // 4 - Request timed out } struct Response { address validator; bytes result; ResponseStatus status; uint256 receipt; uint256 timestamp; uint256 executionCost; } struct Request { uint256 id; address requester; address callbackAddress; bytes4 callbackSelector; address[] subcommittee; Response[] responses; uint256 responseCount; uint256 failureCount; uint256 threshold; uint256 createdAt; uint256 deadline; ResponseStatus status; ConsensusType consensusType; uint256 remainingBudget; // escrow remaining at any point in the lifecycle uint256 perAgentBudget; // max each elected member can claim (set at creation) } interface IAgentRequester { // Events event RequestCreated(uint256 indexed requestId, uint256 indexed agentId, uint256 perAgentBudget, bytes payload, address[] subcommittee); event RequestFinalized(uint256 indexed requestId, ResponseStatus status); event SubcommitteePaid(uint256 indexed requestId, uint256 totalPaid, uint256 perMember); event CommitteeDepositFailed(uint256 indexed requestId, uint256 attemptedAmount); // Request Creation (payable - sends budget upfront) function createRequest( uint256 agentId, address callbackAddress, bytes4 callbackSelector, bytes calldata payload ) external payable returns (uint256 requestId); function createAdvancedRequest( uint256 agentId, address callbackAddress, bytes4 callbackSelector, bytes calldata payload, uint256 subcommitteeSize, uint256 threshold, ConsensusType consensusType, uint256 timeout ) external payable returns (uint256 requestId); // Query Functions function getRequest(uint256 requestId) external view returns (Request memory); function hasRequest(uint256 requestId) external view returns (bool); // Deposit helpers — query the exact msg.value required function getRequestDeposit() external view returns (uint256); function getAdvancedRequestDeposit(uint256 subcommitteeSize) external view returns (uint256); } interface IAgentRequesterHandler { function handleResponse( uint256 requestId, Response[] memory responses, ResponseStatus status, Request memory details ) external; } ``` **`SomniaAgents` / `AgentRequester` Addresses:** | Network | Address | | -------------------------- | -------------------------------------------- | | Mainnet (chain ID `5031`) | `0x5E5205CF39E766118C01636bED000A54D93163E6` | | Testnet (chain ID `50312`) | `0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776` | The examples below use the testnet address; swap in the mainnet address for production. ## Basic Example ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; enum ConsensusType { Majority, Threshold } enum ResponseStatus { None, // 0 - Default zero value (uninitialized storage) Pending, // 1 - Awaiting responses Success, // 2 - Consensus reached normally Failed, // 3 - Validators reported failure TimedOut // 4 - Request timed out } struct Response { address validator; bytes result; ResponseStatus status; uint256 receipt; uint256 timestamp; uint256 executionCost; } struct Request { uint256 id; address requester; address callbackAddress; bytes4 callbackSelector; address[] subcommittee; Response[] responses; uint256 responseCount; uint256 failureCount; uint256 threshold; uint256 createdAt; uint256 deadline; ResponseStatus status; ConsensusType consensusType; uint256 remainingBudget; // escrow remaining at any point in the lifecycle uint256 perAgentBudget; // max each elected member can claim (set at creation) } interface IAgentRequester { function createRequest( uint256 agentId, address callbackAddress, bytes4 callbackSelector, bytes calldata payload ) external payable returns (uint256 requestId); function getRequestDeposit() external view returns (uint256); } interface IJsonApiAgent { function fetchUint(string calldata url, string calldata selector, uint8 decimals) external returns (uint256); } contract PriceOracle { IAgentRequester public platform = IAgentRequester(0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776); uint256 public constant JSON_API_AGENT_ID = 12345678901234567890; // Replace with actual agent ID from the web app uint256 public constant SUBCOMMITTEE_SIZE = 3; // matches the platform default uint256 public constant JSON_FETCH_COST_PER_AGENT = 0.03 ether; // see Gas Fees → Current Per-Agent Prices uint256 public latestPrice; mapping(uint256 => bool) public pendingRequests; event PriceRequested(uint256 indexed requestId); event PriceReceived(uint256 indexed requestId, uint256 price); function requestBitcoinPrice() external payable returns (uint256 requestId) { // Encode the agent function call bytes memory payload = abi.encodeWithSelector( IJsonApiAgent.fetchUint.selector, "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd", "bitcoin.usd", uint8(8) ); // Safe deposit: contract floor + per-agent execution reward. // Floor only would satisfy the contract but runners would skip the // request because perAgentBudget < scheduledExecutionCost. uint256 reserve = platform.getRequestDeposit(); // = minPerAgentDeposit × subSize uint256 reward = JSON_FETCH_COST_PER_AGENT * SUBCOMMITTEE_SIZE; uint256 deposit = reserve + reward; requestId = platform.createRequest{value: deposit}( JSON_API_AGENT_ID, address(this), this.handleResponse.selector, payload ); pendingRequests[requestId] = true; emit PriceRequested(requestId); } // Callback function - called by the platform when consensus is reached function handleResponse( uint256 requestId, Response[] memory responses, ResponseStatus status, Request memory details ) external { require(msg.sender == address(platform), "Only platform can call"); require(pendingRequests[requestId], "Unknown request"); delete pendingRequests[requestId]; if (status == ResponseStatus.Success && responses.length > 0) { latestPrice = abi.decode(responses[0].result, (uint256)); emit PriceReceived(requestId, latestPrice); } } // Allow receiving rebates receive() external payable {} } ``` ## Callback Function Signature Your callback function must match this signature: ```solidity function handleResponse( uint256 requestId, Response[] memory responses, ResponseStatus status, Request memory details ) external; ``` * `requestId`: The ID returned from `createRequest()` * `responses`: Array of `Response` structs from validators (each contains `.result` bytes, `.status`, `.validator`, etc.) * `status`: The overall request status — one of `Success` (2), `Failed` (3), or `TimedOut` (4) * `details`: The full `Request` struct with metadata (subcommittee, threshold, deadline, etc.) You can name the function anything, but the parameter types must match. The selector you pass to `createRequest` must match your function. ### Response Status | Status | Value | Meaning | | ---------- | ----- | ------------------------------------------------------- | | `None` | 0 | Default zero value (uninitialized storage) | | `Pending` | 1 | Awaiting responses | | `Success` | 2 | Validators reached consensus | | `Failed` | 3 | Validators reported failure (success became impossible) | | `TimedOut` | 4 | Request timed out before consensus was reached | ## Decoding Responses The `responses` array contains `Response` structs from validators. Each response has a `.result` field with ABI-encoded bytes and a `.status` field. The callback receives **all** responses submitted up to finalization. For `Majority` consensus, at least `threshold` responses will have identical `.result` bytes (the consensus value). For `Threshold` consensus, results may differ. ```solidity // Decode from the first successful response if (status == ResponseStatus.Success && responses.length > 0) { // Single uint256 uint256 value = abi.decode(responses[0].result, (uint256)); // Single string string memory text = abi.decode(responses[0].result, (string)); // Multiple values (uint256 value, string memory message) = abi.decode(responses[0].result, (uint256, string)); } // Iterate all responses (useful for Threshold consensus) for (uint256 i = 0; i < responses.length; i++) { if (responses[i].status == ResponseStatus.Success) { uint256 value = abi.decode(responses[i].result, (uint256)); // Process each validator's result... } } ``` ## Multiple Callbacks Use different callbacks for different response types: ```solidity contract MultiAgentConsumer { IAgentRequester public platform = IAgentRequester(0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776); // Request with uint256 response function requestPrice() external payable { bytes memory payload = abi.encodeWithSelector(...); platform.createRequest{value: msg.value}( PRICE_AGENT_ID, address(this), this.handlePriceResponse.selector, payload ); } // Request with string response function requestText() external payable { bytes memory payload = abi.encodeWithSelector(...); platform.createRequest{value: msg.value}( TEXT_AGENT_ID, address(this), this.handleTextResponse.selector, payload ); } function handlePriceResponse( uint256 requestId, Response[] memory responses, ResponseStatus status, Request memory details ) external { require(msg.sender == address(platform), "Only platform"); if (status == ResponseStatus.Success && responses.length > 0) { uint256 price = abi.decode(responses[0].result, (uint256)); // Process price... } } function handleTextResponse( uint256 requestId, Response[] memory responses, ResponseStatus status, Request memory details ) external { require(msg.sender == address(platform), "Only platform"); if (status == ResponseStatus.Success && responses.length > 0) { string memory text = abi.decode(responses[0].result, (string)); // Process text... } } receive() external payable {} } ``` ## Error Handling Handle all response statuses gracefully: ```solidity function handleResponse( uint256 requestId, Response[] memory responses, ResponseStatus status, Request memory details ) external { require(msg.sender == address(platform), "Only platform"); if (status == ResponseStatus.Failed) { emit AgentFailed(requestId); // Validators reported execution failure return; } if (status == ResponseStatus.TimedOut) { emit AgentTimedOut(requestId); // Request timed out - maybe retry return; } // Success if (responses.length == 0) { emit EmptyResponse(requestId); return; } // Decode and process response... } ``` ## Security Considerations 1. **Always verify the caller**: Check that `msg.sender` is the platform contract 2. **Track pending requests**: Use a mapping to validate incoming callbacks 3. **Handle all statuses**: Check `status` — requests can succeed, fail, or time out 4. **Validate responses length**: Check `responses.length` before decoding 5. **Accept rebates**: Implement `receive()` so your contract can receive rebated funds ## Next Steps * [Gas Fees](/agents/invoking-agents/gas-fees) — Understand costs # Receipts Every agent invocation produces an **execution receipt** — a per-validator log of what happened during execution. Receipts provide transparency and auditability for agent operations. {% hint style="info" %} **Note:** Receipts are currently stored on centralized infrastructure (a public Google Cloud Storage bucket). We plan to migrate to decentralized storage in the future to make receipts tamper-proof and permanently available. {% endhint %} ## Understanding Receipts ### Consensus vs. Receipts It's important to understand the distinction: * **Result**: The final output of the agent is what validators reach **consensus** on. All nodes must agree on this value for it to be accepted. * **Receipt**: The execution steps are **subjective** and may vary slightly between nodes. Receipts are for transparency and debugging, not consensus. This means: * You can trust the **result** completely — it's been verified by multiple validators. * Each receipt shows **what one node did** to compute that result. * Different nodes may have different receipts (timing, network paths) but still agree on the final result. ### One request → one receipt per subcommittee member A single agent request is processed by every member of its subcommittee (default size **3**, configurable via `createAdvancedRequest`). Each member produces its own receipt, so a default request yields **3 receipts**. The receipt service exposes them as a manifest keyed by `(contractAddress, requestId)`. ## Viewing Receipts in the UI ### From the Web App 1. Visit the Agent Explorer for the network you used — [https://agents.somnia.network](https://agents.somnia.network/) (mainnet) or [https://agents.testnet.somnia.network](https://agents.testnet.somnia.network/) (testnet). 2. Invoke an agent. 3. After execution completes, click **View Receipt**. 4. Explore each step of the execution. The UI displays receipts in a readable format, showing: * Per-validator execution timelines * External calls made (URLs, responses) * Data transformations * Timing information ### From Monitoring Browse recent invocations or search by request ID at: ``` https://agents.somnia.network/monitoring ``` Useful when you don't have the receipt URL handy or want to scan recent activity. ### From Receipt URL If you already know the request ID, navigate directly to: ``` https://agents.somnia.network/receipts/<requestId> ``` Replace `<requestId>` with the numeric request ID returned by `createRequest`. ## Fetching Receipts Programmatically Fetching is a **two-step flow**: 1. Hit the receipts service with the platform `contractAddress` and `requestId` — you get back a manifest of public Google Cloud Storage URLs. 2. Fetch each URL directly to read the per-validator receipt JSON. | Network | Receipts service base URL | Platform contract | | ------- | --------------------------------------------- | -------------------------------------------- | | Mainnet | `https://receipts.mainnet.agents.somnia.host` | `0x5E5205CF39E766118C01636bED000A54D93163E6` | | Testnet | `https://receipts.testnet.agents.somnia.host` | `0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776` | ```javascript const baseUrl = 'https://receipts.
프로젝트로 돌아가기