Somnia 백서

Somnia

원문

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

nt Contract as Your Contract participant Agent as LLM Agent participant LLM as Language Model participant MCP as MCP Server ``` Contract->>Agent: inferToolsChat(messages, mcpServerUrls, [], 5, false) Agent->>MCP: Discover available tools MCP-->>Agent: Tool definitions Agent->>LLM: Prompt + tool definitions LLM-->>Agent: tool_call: getWeather("Tokyo") Agent->>MCP: Execute getWeather("Tokyo") MCP-->>Agent: {"temp": "22°C", "condition": "sunny"} Agent->>LLM: Tool result LLM-->>Agent: "The weather in Tokyo is 22°C and sunny" Agent-->>Contract: ("stop", "The weather in Tokyo is 22°C and sunny", [], [], [], [])" %} ``` ### On-Chain Tools (Yield & Resume) On-chain tools are yielded back to the caller as ABI-encoded calldata. The caller executes them, then resumes the conversation with the results. {% @mermaid/diagram content="sequenceDiagram participant Contract as Your Contract participant Agent as LLM Agent participant LLM as Language Model participant DEX as On-Chain DEX ``` Contract->>Agent: inferToolsChat(messages, [], onchainTools, 5, false) Agent->>LLM: Prompt + tool definitions LLM-->>Agent: tool_call: swap(0xA0b8..., 1000) Agent-->>Contract: ("tool_calls", "", state, [callId], [0xd004f0f7...]) Note over Contract: Execute the on-chain call Contract->>DEX: swap(0xA0b8..., 1000) DEX-->>Contract: "success" Note over Contract: Resume with tool result Contract->>Agent: inferToolsChat(updatedState + toolResult, [], onchainTools, 5, false) Agent->>LLM: Conversation + tool result LLM-->>Agent: "Swapped 1000 USDC successfully" Agent-->>Contract: ("stop", "Swapped 1000 USDC successfully", [], [], [], [])" %} ``` ### Resuming After On-Chain Tool Calls When `finishReason == "tool_calls"`, the caller should: 1. Execute each on-chain call using the calldata in `pendingToolCalls` 2. Append the results to the conversation state: * For each pending call, add `role: "tool"` with a JSON message `{"tool_call_id": pendingToolCallIds[i], "content": "result string"}` 3. Call `inferToolsChat` again with the updated conversation ## Deterministic Execution Because the models run deterministically across all validating nodes, consensus can be achieved on the output, making AI results trustworthy for on-chain use. {% @mermaid/diagram content="flowchart LR subgraph Input P\[Prompt Text] end ``` subgraph Execution["Deterministic Execution"] N1[Node 1 - LLM] N2[Node 2 - LLM] N3[Node 3 - LLM] end subgraph Consensus C{Same Output?} end subgraph Output R[Verified Response] end P --> N1 & N2 & N3 N1 & N2 & N3 --> C C -->|Yes| R" %} ``` ## Example Use Cases * Analyzing text content for moderation decisions * Generating summaries of on-chain data * Making classification decisions (sentiment, category, etc.) * Creating dynamic NFT descriptions or game narratives * Extracting numeric scores or ratings from text * **Agentic DeFi**: LLM decides which swaps/transfers to execute and returns calldata * **AI oracles with tool use**: LLM fetches external data via MCP servers before responding ## Usage Examples ### Simple Inference ```javascript const abi = [{ type: 'function', name: 'inferString', inputs: [ { name: 'prompt', type: 'string' }, { name: 'system', type: 'string' }, { name: 'chainOfThought', type: 'bool' }, { name: 'allowedValues', type: 'string[]' } ], outputs: [{ name: 'response', type: 'string' }] }]; // Content moderation example — constrain output to "safe" or "unsafe" const calldata = encodeFunctionData({ abi, functionName: 'inferString', args: [ 'Analyze the following text: "Check out this amazing new product!"', 'You are a content moderation assistant.', false, // chainOfThought ['safe', 'unsafe'] // allowedValues ] }); ``` ### Numeric Inference ```javascript const abi = [{ type: 'function', name: 'inferNumber', inputs: [ { name: 'prompt', type: 'string' }, { name: 'system', type: 'string' }, { name: 'minValue', type: 'int256' }, { name: 'maxValue', type: 'int256' }, { name: 'chainOfThought', type: 'bool' } ], outputs: [{ name: 'response', type: 'int256' }] }]; // Sentiment score from 1-10 const calldata = encodeFunctionData({ abi, functionName: 'inferNumber', args: [ 'Rate the sentiment of this review: "Absolutely loved it, best purchase ever!"', 'You are a sentiment analysis assistant. Rate sentiment from 1 (very negative) to 10 (very positive).', 1, // minValue 10, // maxValue false // chainOfThought ] }); ``` ### Conversational Example ```javascript const abi = [{ type: 'function', name: 'inferChat', inputs: [ { name: 'roles', type: 'string[]' }, { name: 'messages', type: 'string[]' }, { name: 'chainOfThought', type: 'bool' } ], outputs: [{ name: 'response', type: 'string' }] }]; const calldata = encodeFunctionData({ abi, functionName: 'inferChat', args: [ ['system', 'user', 'assistant', 'user'], [ 'You are a helpful coding assistant.', 'How do I reverse a string in JavaScript?', 'You can use: str.split("").reverse().join("")', 'Can you explain how that works step by step?' ], false // chainOfThought ] }); ``` ### MCP Tool Use (Solidity) The LLM automatically discovers and calls tools from the MCP server, gets the result, and incorporates it into its response. ```solidity interface ILLMAgent { struct OnchainTool { string signature; string description; } function inferToolsChat( string[] calldata roles, string[] calldata messages, string[] calldata mcpServerUrls, OnchainTool[] calldata onchainTools, uint256 maxIterations, bool chainOfThought ) external returns ( string memory finishReason, string memory response, string[] memory updatedRoles, string[] memory updatedMessages, string[] memory pendingToolCallIds, bytes[] memory pendingToolCalls ); } // Provide MCP server URLs — the agent discovers available tools automatically string[] memory mcpServerUrls = new string[](1); mcpServerUrls[0] = "http://weather-service:80/"; ILLMAgent.OnchainTool[] memory onchainTools = new ILLMAgent.OnchainTool[](0); string[] memory roles = new string[](2); roles[0] = "system"; roles[1] = "user"; string[] memory messages = new string[](2); messages[0] = "You are a weather assistant. Use available tools when asked about weather."; messages[1] = "What is the weather in Tokyo?"; bytes memory payload = abi.encodeWithSelector( ILLMAgent.inferToolsChat.selector, roles, messages, mcpServerUrls, onchainTools, 5, false ); ``` ### On-Chain Tool Use with Yield & Resume (Solidity) The LLM returns calldata for on-chain tools. Your contract executes them and resumes the conversation. ```solidity // Define on-chain tools ILLMAgent.OnchainTool[] memory onchainTools = new ILLMAgent.OnchainTool[](1); onchainTools[0] = ILLMAgent.OnchainTool( "swap(address token, uint256 amount)", "Swap tokens on the DEX" ); // First call — LLM decides to use swap tool ( string memory finishReason, string memory response, string[] memory updatedRoles, string[] memory updatedMessages, string[] memory pendingToolCallIds, bytes[] memory pendingToolCalls ) = llmAgent.inferToolsChat(roles, messages, mcpServerUrls, onchainTools, 5, false); if (keccak256(bytes(finishReason)) == keccak256("tool_calls")) { // Execute each pending on-chain call for (uint i = 0; i < pendingToolCalls.length; i++) { // pendingToolCalls[i] is calldata: swap(0xA0b8..., 1000) (bool success, bytes memory result) = dexContract.call(pendingToolCalls[i]); // Append tool result to conversation for resumption // ... add ("tool", '{"tool_call_id":"...","content":"success"}') to updatedRoles/updatedMessages } // Resume — call inferToolsChat again with updated conversation // The LLM will see the tool results and produce a final response } ``` # LLM Parse Website Search a domain (or directly scrape a URL) and extract structured data using AI. Unlike the JSON API Request agent, this agent can handle HTML pages and extract human-readable content. Sites are visited using a real web browser, making it suitable for pages that require JavaScript rendering. ## Overview This agent is designed for on-chain callers that need trustless, auditable web extraction: * Find relevant pages via search, or scrape explicit URL * Fetch HTML and generate markdown * Build a context window across sources * Run a structured LLM extraction * Return a typed, ABI-encoded response ## Example Use Cases * Scraping price information from e-commerce sites * Extracting news headlines for on-chain curation * Monitoring website content for changes * Building oracles for data not available via APIs ## How It Works {% @mermaid/diagram content="flowchart TB subgraph Request\["Caller"] A\[baseUrl + prompt + schema] end subgraph Search\["Search Layer"] B{resolveUrl?} C\[Search service] end subgraph Fetch\["Fetch + Convert"] D\[Fetch pages] E\[HTML] F\[Markdown] end subgraph LLM\["Structured Extraction"] G\[Context builder] H\[LLM structured output] end subgraph Response\["Response"] I\[Result + Receipt] end A --> B B -->|true| C B -->|false| D C --> D --> E --> F --> G --> H --> I" %} ### Flow Stages (Receipt) The agent emits receipt steps for auditability. Each step includes timing, inputs, and outputs. 1. request: Input parameters and schema 2. search: URL discovery (skipped when resolveUrl=false) 3. scrape: Fetch HTML artifacts 4. sanitise: Markdown conversion and context assembly 5. extract: LLM structured extraction ## ABI Functions The ABI exposes the following functions: ### ExtractString Extract a single string field, optionally choosing from a list of options. Inputs <table><thead><tr><th width="197.21875">Name</th><th>Type</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td><code>key</code></td><td><code>string</code></td><td><code>best_drama</code></td><td>Field name to extract</td></tr><tr><td><code>description</code></td><td><code>string</code></td><td><code>Title of the film that won Best Motion Picture - Drama.</code></td><td>Field description for the LLM</td></tr><tr><td><code>options</code></td><td><code>string[]</code></td><td><code>[]</code></td><td>Literal options; pass an empty array when you do not want to constrain the output</td></tr><tr><td><code>prompt</code></td><td><code>string</code></td><td><code>Best Picture winners at the 2026 Golden Globe Awards</code></td><td>Natural language extraction prompt, also used as search term</td></tr><tr><td><code>url</code></td><td><code>string</code></td><td><code>goldenglobes.com</code></td><td>URL, either base URL or direct</td></tr><tr><td><code>resolveUrl</code></td><td><code>bool</code></td><td><code>true</code></td><td>Search domain vs. scrape direct URLs</td></tr><tr><td><code>numPages</code></td><td><code>uint8</code></td><td><code>3</code></td><td>Max pages to fetch, if resolveUrl is off, value is capped at 1</td></tr><tr><td><code>confidenceThreshold</code></td><td><code>uint8</code></td><td><code>70</code></td><td>Minimum extraction confidence (0–100) required to return a result; below it the field is treated as not answerable</td></tr></tbody></table> Output * output (string) ### ExtractANumber Extract a single numeric field (type fixed to number), optionally bounded by min/max. Inputs <table><thead><tr><th width="192.55859375">Name</th><th>Type</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td><code>key</code></td><td><code>string</code></td><td><code>senegal_goals</code></td><td>Field name to extract</td></tr><tr><td><code>description</code></td><td><code>string</code></td><td><code>Number of goals scored by Senegal in the 18/1/26 AFCON final vs Morocco.</code></td><td>Field description for the LLM</td></tr><tr><td><code>min</code></td><td><code>uint256</code></td><td><code>0</code></td><td>Minimum bound (set both min and max to 0 to disable bounds)</td></tr><tr><td><code>max</code></td><td><code>uint256</code></td><td><code>0</code></td><td>Maximum bound</td></tr><tr><td><code>prompt</code></td><td><code>string</code></td><td><code>Africa Cup of Nations final score: number of goals for Senegal on 18/1/26 against Morocco.</code></td><td>Natural language extraction prompt, also used as search term</td></tr><tr><td><code>url</code></td><td><code>string</code></td><td><code>espn.com</code></td><td>URL, either base URL or direct</td></tr><tr><td><code>resolveUrl</code></td><td><code>bool</code></td><td><code>true</code></td><td>Search domain vs. scrape direct URLs</td></tr><tr><td><code>numPages</code></td><td><code>uint8</code></td><td><code>3</code></td><td>Max pages to fetch, if resolveUrl is off, value is capped at 1</td></tr><tr><td><code>confidenceThreshold</code></td><td><code>uint8</code></td><td><code>70</code></td><td>Minimum extraction confidence (0–100) required to return a result; below it the field is treated as not answerable</td></tr></tbody></table> Output * output (uint256) Notes * Values are coerced to integers; negative values are clamped to 0. * If bounds are provided, they must be within JS safe integer range. ## JavaScript Example ```javascript const abi = [ { type: 'function', name: 'ExtractANumber', inputs: [ { name: 'key', type: 'string' }, { name: 'description', type: 'string' }, { name: 'min', type: 'uint256' }, { name: 'max', type: 'uint256' }, { name: 'prompt', type: 'string' }, { name: 'url', type: 'string' }, { name: 'resolveUrl', type: 'bool' }, { name: 'numPages', type: 'uint8' }, { name: 'confidenceThreshold', type: 'uint8' } ], outputs: [{ name: 'output', type: 'uint256' }] }, { type: 'function', name: 'ExtractString', inputs: [ { name: 'key', type: 'string' }, { name: 'description', type: 'string' }, { name: 'options', type: 'string[]' }, { name: 'prompt', type: 'string' }, { name: 'url', type: 'string' }, { name: 'resolveUrl', type: 'bool' }, { name: 'numPages', type: 'uint8' }, { name: 'confidenceThreshold', type: 'uint8' } ], outputs: [{ name: 'output', type: 'string' }] } ]; const numberCalldata = encodeFunctionData({ abi, functionName: 'ExtractANumber', args: [ 'senegal_goals', 'Number of goals scored by Senegal in the 18/1/26 AFCON final vs Morocco.', 0, 0, 'Africa Cup of Nations final score: number of goals for Senegal on 18/1/26 against Morocco.', 'espn.com', true, 3, 70 ] }); const stringCalldata = encodeFunctionData({ abi, functionName: 'ExtractString', args: [ 'best_drama', 'Title of the film that won Best Motion Picture - Drama.', [], 'Best Picture winners at the 2026 Golden Globe Awards', 'goldenglobes.com', true, 3, 70 ] }); ``` ## Solidity 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; uint256 perAgentBudget; } interface IAgentRequester { function createRequest( uint256 agentId, address callbackAddress, bytes4 callbackSelector, bytes calldata payload ) external payable returns (uint256 requestId); function getRequestDeposit() external view returns (uint256); } // Agent interface (for .selector and type safety) interface IParseWebsiteAgent { function ExtractANumber( string memory key, string memory description, uint256 min, uint256 max, string memory prompt, string memory url, bool resolveUrl, uint8 numPages, uint8 confidenceThreshold ) external returns (uint256); function ExtractString( string memory key, string memory description, string[] calldata options, string memory prompt, string memory url, bool resolveUrl, uint8 numPages, uint8 confidenceThreshold ) external returns (string memory); } contract MyContract { // testnet: 0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776 // mainnet: 0x5E5205CF39E766118C01636bED000A54D93163E6 IAgentRequester public platform = IAgentRequester(0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776); uint256 constant AGENT_ID = 12875401142070969085; uint256 constant SUBCOMMITTEE_SIZE = 3; // platform default uint256 constant LLM_PARSE_WEBSITE_COST_PER_AGENT = 0.10 ether; // see Gas Fees → Current Per-Agent Prices mapping(uint256 => address) public requestSenders; event AgentResponseReceived( uint256 indexed requestId, ResponseStatus status, string output ); function invokeExtractString( string calldata key, string calldata description, string[] calldata options, string calldata prompt, string calldata url, bool resolveUrl, uint8 numPages, uint8 confidenceThreshold ) external payable returns (uint256 requestId) { bytes memory payload = abi.encodeWithSelector( IParseWebsiteAgent.ExtractString.selector, key, description, options, prompt, url, resolveUrl, numPages, confidenceThreshold ); // Safe deposit: contract floor + per-agent execution reward. uint256 reserve = platform.getRequestDeposit(); uint256 reward = LLM_PARSE_WEBSITE_COST_PER_AGENT * SUBCOMMITTEE_SIZE; uint256 deposit = reserve + reward; requestId = platform.createRequest{value: deposit}( AGENT_ID, address(this), this.handleResponse.selector, payload ); requestSenders[requestId] = msg.sender; } function invokeExtractANumber( string calldata key, string calldata description, uint256 min, uint256 max, string calldata prompt, string calldata url, bool resolveUrl, uint8 numPages, uint8 confidenceThreshold ) external payable returns (uint256 requestId) { bytes memory payload = abi.encodeWithSelector( IParseWebsiteAgent.ExtractANumber.selector, key, description, min, max, prompt, url, resolveUrl, numPages, confidenceThreshold ); // Safe deposit: contract floor + per-agent execution reward. uint256 reserve = platform.getRequestDeposit(); uint256 reward = LLM_PARSE_WEBSITE_COST_PER_AGENT * SUBCOMMITTEE_SIZE; uint256 deposit = reserve + reward; requestId = platform.createRequest{value: deposit}( AGENT_ID, address(this), this.handleResponse.selector, payload ); requestSenders[requestId] = msg.sender; } function getSenegalGoals() external payable returns (uint256 requestId) { return this.invokeExtractANumber{value: msg.value}( "senegal_goals", "Number of goals scored by Senegal in the 18/1/26 AFCON final vs Morocco.", 0, 0, "Africa Cup of Nations final score: number of goals for Senegal on 18/1/26 against Morocco.", "espn.com", true, 3, 70 ); } function getBestDrama() external payable returns (uint256 requestId) { string[] memory options = new string[](0); return this.invokeExtractString{value: msg.value}( "best_drama", "Title of the film that won Best Motion Picture - Drama.", options, "Best Picture winners at the 2026 Golden Globe Awards", "goldenglobes.com", true, 3, 70 ); } // 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"); if (status == ResponseStatus.Success && responses.length > 0) { string memory output = abi.decode(responses[0].result, (string)); emit AgentResponseReceived(requestId, status, output); } else { emit AgentResponseReceived(requestId, status, ""); } } // Allow receiving rebates receive() external payable {} } ``` ## Structured Output Schema Internally calls to the LLM are encoded into an output schema as a JSON object with: ```json { "type": "struct"
프로젝트로 돌아가기