[ERC8436] 다수의 AI 에이전트가 투표를 통해 작업 승인 결과를 결정하는 메커니즘
요약
ERC8436은 다수의 AI 에이전트가 작업 결과물에 대해 투표하고 판단하는 과정을 온체인 표준으로 정의합니다. 이는 단일 판정자의 편향성을 줄이고, 누가 투표할지(참여자)와 최종적으로 자산 상태를 변경할 권한을 가진 주체(consumer)를 분리하여 신뢰성을 높입니다. 이 표준은 필요 표 도달 여부, 이의 제기 횟수 등 모든 규칙을 사전에 고정하는 'Decision Policy'를 통해 투명하게 작동합니다.
핵심 포인트
- AI 에이전트 결과물 평가에 대한 온체인 표준(ERC8436) 제시
- 투표 참여자와 자산 변경 권한(consumer)을 분리하여 신뢰성 확보
- 필요 표 도달 여부, 이의 제기 횟수 등 모든 규칙을 사전에 고정 (Decision Policy)
- 미달 시 자동 처리 방지: 'NoDecision' 상태를 명확히 기록
처음 뵙겠습니다.
『DApps 개발 입문』이라는 책이나 여러 기사를 쓰고 있는 karude ne입니다.
아래에서도 정보를 발신하고 있으니, 흥미로운 글이 있다면 꼭 읽어봐 주세요!
AI 에이전트가 만든 결과물을 다른 AI 에이전트에게 평가시키는 장면이 늘어나고 있습니다.
하지만 한 명의 판정자에게 맡기면, 그 판정자의 정지나 편향이 작업 수용 결과에 영향을 미치게 됩니다.
ERC8436은 다수의 주체에 의한 투표, 판단 불가 결과, 이의 제기, 업무 처리 반영을 하나의 온체인 절차로 기록하는 표준입니다.
누가 투표할 수 있는지와 누가 자산이나 작업 상태를 변경할 수 있는지를 분리합니다.
2026년 10월 16일 기준으로 Draft입니다.
사양, 전체 API, 공식 참고 구현, 공식 테스트 벡터, 도입 시의 안전성을 설명합니다.
소프트웨어 개발을 의뢰하고 AI 에이전트로부터 결과물이 제출된 장면을 가정해 봅시다.
발주자는 한 명의 평가자가 아니라 기술적인 측면을 확인할 5명의 AI 에이전트에게 수용 판단을 맡기고 싶어 합니다.
3명 이상이 승인하면 결과물을 수용하고, 3명 이상이 반대하면 거부합니다.
단순히 투표 결과만 기록한다고 해서, 발주자의 작업 컨트랙트가 투표 결과에 따르는 것은 아닙니다. 평가자의 평판이나 보유 토큰은 투표 자격의 근거가 될 수는 있지만, 발주자의 자산이나 작업 상태를 변경할 권한이 되지는 않기 때문입니다.
필요표에 도달하지 못한 경우를 승인 또는 거부로 해석하면, 같은 투표에서 구현마다 다른 결과가 생길 수 있습니다.
그래서 ERC8436은 참여자, 필요표, 제한 시간, 이의 제기 횟수를 투표 전에 고정합니다.
이 고정된 투표 규칙을 Decision Policy라고 부릅니다.
같은 결과물에 대해 같은 질문을 고정된 규칙으로 판단하는 일련의 기록이 Issue입니다.
투표 결과를 업무 처리에 반영할 것을 사전에 약속하는 컨트랙트 또는 계정을 consumer라고 부릅니다.
consumer는 승인 시 처리, 거부 시 처리, 필요표에 도달하지 못했을 때의 처리를 투표 전에 고정합니다.
찬성도 반대도 아닌 결과는 NoDecision으로 남고, consumer가 사전에 지정한 기본 처리로 전달됩니다.
투표에서는 각 참여자가 같은 Issue, 같은 round, 같은 판단 그룹에 단 한 표를 제출합니다.
필요표가 모이거나 투표 시간이 종료되면, 해당 그룹의 판단이 결정됩니다.
여러 그룹을 사용할 경우에는 기술적인 측면과 경제적인 측면 모두가 찬성했을 때만 승인하는 구성이나, 거부권 Body가 approve ballot를 했을 때 전체를 중단시키는 구성도 만들 수 있습니다.
첫 번째 판단에 이의 제기가 있으면 새로운 round를 시작합니다.
이의 제기 후에 필요표에 도달하지 못했을 경우, 이전 판단을 남길지, 아니면 전체를 NoDecision으로 할지도 투표 전 규칙에 포함할 수 있습니다.
최종 결과가 확정된 후에만, consumer가 자신의 권한으로 결과물의 수용 또는 거부를 실행합니다.
5명 중 3명의 승인을 요구하는 경우, 같은 결과물과 질문을 5명에게 전달합니다. 각 참여자는 자신이 확인한 내용에 기반하여 찬성 또는 반대를 제출합니다.
3표의 찬성이 모이면 Yes이고, 3표의 반성이 모이면 No입니다.
기한까지 찬성이 2표, 반대가 2표, 미투표가 1명이라면, 어느 조건에도 도달하지 못합니다.
ERC8436은 찬반 어느 쪽의 필요표에도 도달하지 못한 투표를 NoDecision으로 기록합니다.
미달을 자동으로 거부로 변환하지 않기 때문에, 발주자는 투표 시작 전에 NoDecision일 경우 기존 기한 처리로 되돌리겠다고 결정할 수 있습니다.
투표 참여자는 판단을 내리지만, 작업 보수나 결과물의 상태를 변경하지 않습니다. 작업 컨트랙트를 조작할 수 있는 consumer만이 최종 결과를 업무 처리에 반영합니다.
consumer는 Issue를 직접 만들거나, 사전 수용 조건을 등록하거나, 제3자가 만든 Issue를 투표 전에 승인하는 세 가지 방법 중 하나로 약속을 고정합니다.
이 투표 전의 약속을 Consumer commitment라고 부릅니다.
약속이 성립된 Issue는 Binding입니다.
약속이 없는 Issue는 Advisory로서 판단 기록은 남길 수 있지만, 업무 처리를 구속하지 않습니다.
발주자 측의 adapter는 작업 컨트랙트로부터 결과물, 기한, 승인 시와 거부 시의 처리를 읽어 Issue를 만듭니다.
Issue Registry는 고정된 투표 규칙과 consumer
첫 번째 round의 약속을 확인하고 시작합니다.
참가자 투표, 필요 표 판정, 이의 제기를 거쳐 최종 결과를 저장합니다.
최종 결과가 Yes이면 adapter가 작업 컨트랙트의 수락 함수를 호출하고, No이면 거부 함수를 호출합니다.
호출에 실패하더라도 투표 결과는 변하지 않습니다.
consumer는 실패를 실행 기록에 남기고, 작업 컨트랙트의 조건이 허용하는 범위 내에서 재시도합니다.
여러 서명을 모으는 메커니즘이나 토큰 보유자가 제안에 투표하는 메커니즘은 이미 존재합니다.
하지만 구체적인 결과물과 질문, 결과에 따라 행동하는 consumer, 필요 표에 도달하지 못한 결과, 이의 제기, 업무 처리의 성공 여부까지 모두 같은 기록으로 연결하기 위해서는 추가적인 약속이 필요합니다.
평가자 한 명만 지정하는 작업 컨트랙트에서는 평가자의 중단이 곧바로 처리 중단을 의미하게 됩니다.
여러 투표자를 배치하더라도, 투표자와 업무 실행자의 권한을 분리하지 않으면 평가 자격을 가진 주체가 발주자의 자산까지 움직일 수 있는 설계가 됩니다.
ERC8436은 판단을 만드는 절차와 판단을 실행하는 권한을 분리합니다.
투표 규칙과 consumer의 약속을 먼저 고정하고, 투표 후에 편리한 결과만 채택할 여지 또한 줄입니다.
| 방식 | 질문 및 대상 | NoDecision | 다중 그룹 | 이의 제기 | 업무 처리와의 연결 |
|---|---|---|---|---|---|
multisig | 호출 내용에 임베드 | 개별 설계 | 단일 서명자 집합 | 개별 설계 | 서명이 성립되면 직접 실행 |
governor | 제안마다 보유 | quorum 미달로 처리 | 보통 하나의 유권자 집합 | 개별 설계 | 제안의 실행에 연결 |
단일 evaluator | 작업마다 보유 | evaluator 응답 규칙에 따름 | 없음 | 개별 설계 | 작업 컨트랙트가 결과를 읽음 |
| ERC8436 | Issue에 고정 | 독립적인 공식 결과 | Body 별로 분리 | Policy에 고정 | consumer가 자신의 권한으로 실행 |
ERC8436이 필요한 경우는 단순 투표 수뿐만 아니라 어떤 질문을, 누구의 판단으로, 어떤 업무에 반영할지를 재현하고 싶을 때입니다.
투표 자격은 고정된 참가자 목록이나 특정 보고자로 Policy에 들어갑니다.
반면, 업무 실행 권한은 consumer가 이미 가진 권한입니다.
투표 참가자가 높은 평가를 가지고 있더라도, consumer의 약속 없이는 보상이나 작업 상태를 변경할 수 없습니다.
이러한 분리를 통해 참가자 선정 방식을 미래의 다른 사양에 위임할 수 있습니다.
신원, 평판, 이해관계 배제를 추가하더라도, consumer의 권한 모델을 변경할 필요가 없습니다.
투표에서는 찬성에도 반대에도 도달하지 않은 채 기한이 만료되는 경우가 있을 수 있습니다.
단일 보고자가 기한까지 응답하지 않을 수도 있습니다.
거부권을 가진 그룹이 거부도 허용도 명시하지 않는 경우도 있습니다.
ERC8436은 이를 NoDecision의 이유로 구분합니다.
consumer는 NoDecision 시의 처리를 투표 전에 고정하기 때문에, 결과를 보고서 승인 처리인지 거부 처리인지를 선택할 수 없습니다.
Issue Registry는 투표와 이의 제기로부터 최종 결과를 만듭니다.
외부 컨트랙트의 자산이나 작업 상태는 변경하지 않습니다.
consumer는 최종 결과를 읽고, 자신이 조작할 수 있는 컨트랙트에 승인 또는 거부를 보냅니다.
외부 호출이 실패했을 경우, 최종 결과를 되돌리지 않고 Failed 실행 기록을 남깁니다.
판단의 확정과 업무 처리의 성공을 별개로 감사(audit)할 수 있습니다.
ERC8436은 Policy Registry와 Issue Registry를 동시에 배포하는 구성을 정합니다.
Policy Registry는 변경 불가능한 투표 규칙을 저장하고, Issue Registry는 구체적인 판단 건, 투표 라운드, 최종 결과를 저장합니다.
ACDFTypes 라이브러리는 Policy, Issue, round, 결과에서 공유하는 Enum 타입과 구조체를 정의합니다.
enum BodyKind { ROSTER_KOFN, AUTHORIZED_SUBMITTER }
BodyKind는 하나의 판단 그룹이 고정 명부의 투표로 결정할지, 고정된 보고자가 결과를 제출할지를 나타냅니다.
enum Acceptance { ON_CHAIN_TALLY, SIGNED_BALLOTS, AUTHORIZED_SUBMITTER }
Acceptance는 Body가 판단 자료를 받아들이는 방식을 나타냅니다.
enum Combinator { BODY, ALL, ANY, KOFM, VETO }
Combinator는 Body 또는 자식 Node의 4가지 판단을 합성하는 방법을 나타냅니다.
enum VetoSilence { PASS_THROUGH, REQUIRE_CLEARANCE }
VetoSilence는 거부권 Body가 기한까지 결론을 내리지 못한 경우의 처리 방식입니다.
거부권 Body에서 approve ballot에 의한 Yes는 veto를 의미하며, No는 명시적인 clearance를 의미합니다.
PASS_THROUGH는 침묵 시 target의 결과를 통과시키고, REQUIRE_CLEARANCE는 명시적인 No가 없기 때문에 NoDecision으로 처리합니다.
enum AppealStanding { ANYONE, CONSUMER_OR_FILER }
AppealStanding은 이의 제기를 시작할 수 있는 주체를 나타냅니다.
ANYONE은 임의의 address를 허용하며, CONSUMER_OR_FILER는 consumer 또는 Issue 생성자만을 허용합니다.
enum AppealMode { PRESERVE_UNLESS_OVERTURNED, REQUIRE_FRESH_DECISION }
AppealMode는 appeal round가 NoDecision이 되었을 때 이전 round의 판단을 남길지 여부를 나타냅니다.
PRESERVE_UNLESS_OVERTURNED는 가장 최근의 Yes 또는 No를 채택하고, REQUIRE_FRESH_DECISION은 Issue 전체를 NoDecision으로 만듭니다.
| 값 | appeal 결과 | Final 결과 | 예 |
|---|---|---|---|
PRESERVE_UNLESS_OVERTURNED | 신 round가 NoDecision | 직전의 Yes 또는 No를 채택한다 | round 1의 Yes를 남긴다 |
REQUIRE_FRESH_DECISION | 신 round가 NoDecision | Issue 전체를 NoDecision으로 만든다 | 재심에서 필요표에 도달하지 못해 effect를 선택하지 않는다 |
struct BodySpec {
BodyKind kind;
Acceptance acceptance;
...
}
BodySpec 구조체는 하나의 판단 그룹의 참가자, 입력 방식, 임계값(threshold), 기한을 요약합니다.
Policy 생성자가 PolicySpec에 포함시키고, Policy Registry가 저장하며, Issue Registry의 Body 판정 및 bodyOf 함수가 참조합니다.
필드
| 필드 | 상세 |
|---|---|
kind | 명부 투표 또는 고정 보고자(fixed reporter)를 선택한다. |
acceptance | 온체인 투표, 서명 투표, 보고자 제출을 선택한다. |
members | zero가 아닌 중복되지 않는 참가자입니다. 보고자 방식에서는 1건입니다. |
k | 명부 투표에서 필요한 찬성표 수입니다. |
window | round 시작부터 입력을 받는 초(seconds) 수입니다. |
struct Node {
Combinator op;
uint32 body;
...
}
Node 구조체는 Body 결과를 어떤 논리로 합성할지 나타냅니다.
nodes[0]가 최종 판단을 반환하는 root입니다.
Policy 생성자가 tree를 포함시키고, Policy Registry가 저장하며, Issue Registry의 nodeStatus 함수가 root까지 평가합니다.
필드
| 필드 | 상세 |
|---|---|
op | Body 참조, ALL, ANY, K-of-M, VETO를 선택한다. |
body | BODY에서 참조하는 Body 번호입니다. |
k | KOFM에서 필요한 Yes의 수입니다. |
target | VETO에서 보호하는 Node 번호입니다. |
vetoBody | 거부권을 가진 Body 번호입니다. |
silence | 거부권 Body가 침묵했을 때의 처리입니다. |
children | ALL, ANY, K-of-M에서 참조하는 자식 Node 번호입니다. |
struct PolicySpec {
bytes32 family;
uint32 version;
...
}
PolicySpec 구조체는 투표 자격, 임계값, 합성(aggregation), 시간, 이의 제기(appeal), 수용 조건(acceptance condition)을 하나로 요약합니다.
모든 필드의 ABI encoding으로부터 policyId를 계산할 수 있으므로, 설명용 JSON과 실행 규칙 간의 불일치를 감지할 수 있습니다.
Policy 생성자가 registerPolicy 함수에 전달하고, Policy Registry가 변경 불가능한 값으로 저장하며, Issue Registry와 각 read 함수가 참조합니다.
필드
| 그룹 | 필드 | 의미 |
|---|---|---|
| 버전 (Version) | family, version, previous, updateAuthority | 직전 Policy에 대한 참조, 증가하는 버전 번호, 공개 주체입니다. |
| 판단 (Judgment) | bodies, nodes | 참여자, 임계값(threshold), 합성된 tree입니다. |
appeal | maxAppeals, appealWindow, appealable, appealStanding, appealMode | 이의 제기 횟수, 시간, 주체, 채택 규칙입니다. |
| 시간 (Time) | maxTotalDuration, ackWindow | 절차 전체와 사후 승인(post-acknowledgement)의 상한선입니다. |
| 보조 (Advisory) | allowAdvisory, descriptorHash | Advisory 시작 여부와 오프체인(off-chain) 설명의 hash입니다. |
enum ProcedureState { None, Filed, Deciding, Provisional, Final, Withdrawn }```
`ProcedureState`는 `Issue`가 접수부터 종료까지 어느 단계에 있는지를 저장합니다.
| 값 | 저장 상태 | 허용되는 처리 |
|---|---|---|
| `None` | `Issue`가 존재하지 않음 | 후속 작업을 거부합니다. |
| `Filed` | 접수됨, 투표 시작 전 | 승인(Approval), `Advisory` 시작, 만료, 취소를 받을 수 있습니다. |
| `Deciding` | `round`가 진행 중 | 투표, `Body` 결과 제출, `settle`을 받을 수 있습니다. |
| `Provisional` | `round` 결과가 결정되어 이의 제기 대기 중 | `appeal` 또는 `finalize`를 받을 수 있습니다. |
| `Final` | 최종 결과가 저장됨 | `consumer`가 결과를 실행할 수 있습니다. |
| `Withdrawn` | 투표 전에 취소됨 | 터미널(terminal) 상태로서 후속 작업을 거부합니다. |
enum OutcomeType { None, Decided, NoDecision }```
OutcomeType은 Final 시점에 Yes 또는 No가 결정되었는지, 판단을 내릴 수 없었는지를 나타냅니다.
None일 때는 effect를 실행하지 않으며, Decided일 때는 outcomeYes에 해당하는 effect를, NoDecision일 때는 disposition을 선택합니다.
| 값 | 저장 상태 | 후속 판단 | 예시 |
|---|---|---|
| None | Final 이전 또는 결과 미저장 | effect를 실행하지 않습니다. | 진행 중인 Deciding의 Issue
| Decided | Yes 또는 No가 Final에 저장됨 | outcomeYes로부터 effect를 선택합니다. | Yes라면 accept를 호출합니다. |
| NoDecision | 판단 불가 상태가 Final에 저장됨 | 사전의 disposition을 사용합니다. | quorum 부족으로 기본 처리로 돌아갑니다. |
enum AcceptanceMode { CONSUMER_FILED, STANDING_ACCEPTANCE, POST_ACK }```
`AcceptanceMode`는 `consumer`가 결과에 따르겠다는 약속을 투표 전에 고정하는 방법입니다.
| 값 | 약속 생성 작업 | `admission` |
|---|---|---|
| `CONSUMER_FILED` | `consumer` 자신이 `Issue`를 만듭니다. | 즉시 `Binding`으로 시작합니다. |
| `STANDING_ACCEPTANCE` | `consumer`가 사전 조건을 등록합니다. | 조건 일치 시 `Binding`으로 시작합니다. |
| `POST_ACK` | `filer`가 만들고, `consumer`가 기한 내에 승인합니다. | 승인 후에 `Binding`으로 시작합니다. |
enum EffectClass { Advisory, Binding }```
EffectClass는 최종 결과가 consumer의 사전 약속을 가지는지 여부를 나타냅니다.
Advisory는 판단 기록만 남기고, Binding은 확정된 effects와 obligation을 가집니다.
| 값 | 저장 상태 | 후속 판단 | 예시 |
|---|---|---|---|
Advisory | consumer commitment 없음 | enactment을 기록하지 않음 | 미승인 Issue를 참고 판단으로 시작함 |
Binding | consumer, effects, obligation이 확정됨 | Final-Decided 이후에 effect를 선택할 수 있음 | Yes로 작업을 수락함 |
enum NodeStatus { Pending, Yes, No, NoDecision }
NodeStatus는 Body 또는 합성 Node의 현재 사치 판단입니다.
예시로, K-of-N 필요표에 도달하면 Yes, blocking threshold에 도달하면 No, 기한 전까지 어느 쪽에도 도달하지 못하면 Pending을 반환합니다.
Pending은 입력에 따라 변하는 상태이며, Yes와 No는 실질 판단이고, NoDecision은 기한 내에 판단 조건이 성립되지 않은 상태입니다.
enum Reason { NONE, QUORUM_NOT_MET, ... }
Reason은 NoDecision, 거부권, 미승인이 발생한 이유를 저장합니다.
| 값 | 발생 조건 |
|---|---|
NONE | 특별한 실패 이유가 없음 |
QUORUM_NOT_MET | 명부 투표가 필요표에 도달하지 않음 |
SUBMITTER_SILENT | 확정 보고자가 기한까지 제출하지 않음 |
VETOED | 거부권 Body가 거부함 |
NO_CLEARANCE | 명시 허가가 필요한 거부권 Body가 침묵함 |
NOT_REACHED | 하위 Node에서 합성 결과를 만들 수 없음 |
TOTAL_TIMEOUT | hard deadline까지 round가 끝나지 않음 |
NO_ACCEPTANCE | POST_ACK가 승인되지 않음 |
enum EnactmentStatus { NotEnacted, Enacted, Failed }
EnactmentStatus는 consumer가 Final 결과를 업무 계약에 반영할 수 있었는지 나타냅니다.
Failed는 나중에 Enacted로 업데이트될 수 있지만, Enacted는 terminal입니다.
| 값 | 저장 상태 | 후속 판단 | 예시 |
|---|---|---|---|
NotEnacted | 실행 보고 없음 | 실행 또는 재시행을 기다림 | Final-Decided 직후 |
Failed | target call 실패를 보고함 | 조건이 돌아오면 재시도 가능 | 기한 조건으로 accept가 실패함 |
Enacted | effect 성공을 보고함 | 후속 업데이트를 거부함 | accept 완료를 기록함 |
struct Subject { uint256 chainId; address target; ... }
Subject 구조체는 어떤 chain의 어느 target에 무엇을 평가할지 확정합니다.
filer 또는 adapter가 만들고, Issue Registry가 Issue 내에 저장하며, getIssue 함수와 consumer adapter가 대조합니다.
필드
| 필드 | 상세 |
|---|---|
chainId | 평가 대상이 속하는 chain입니다. |
target | 작업 계약 또는 대상 계약입니다. |
id | task, submission, token의 식별 번호입니다. |
dataHash | 평가할 구체적인 내용을 확정하는 hash입니다. |
struct IssueInput { bytes32 policyId; AcceptanceMode mode; ... }
IssueInput
구조체는 filer가 Issue를 생성할 때 전달하는 값을 모아 놓습니다.
file 함수가 acceptance mode 별로 값을 검증하고, admission은 Issue Registry의 Issue에 고정되며, 이는 getIssue 함수에서 읽을 수 있습니다.
필드
| 그룹 | 필드 | 의미 |
|---|---|---|
| 규칙 | policyId, mode, acceptanceId | 적용되는 Policy와 consumer의 약속입니다. |
| 주체와 대상 | consumer, subject, question | 실행 주체, 평가 대상, 질문입니다. |
| 결과 처리 | effectYes, effectNo, disposition | Yes, No, NoDecision 시의 처리입니다. |
| 의무와 기한 | obligationId, consumerDeadline | 병렬 Issue를 방지하는 범위와 consumer 측 기한입니다. |
struct StandingAcceptanceInput {
bytes32 policyId;
address subjectTarget;
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기