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"프로젝트로 돌아가기