에이전트에게 HTML을 제공하지 마세요
Source: Dev.to
TL;DR — AI에게 블랙 박스가 아닌 화이트 박스를 제공하라
대부분의 AI 에이전트는 블랙 박스 방식으로 웹 앱과 상호작용합니다: DOM 덤프나 스크린샷을 소비하고, 클릭할 대상을 추측합니다. HTML은 기계용으로 설계된 것이 아닙니다. AI 입장에서 DOM은 비즈니스 로직이 희미하게 숨어 있는 잡음에 불과합니다.
이 에세이는 화이트 박스 접근법을 주장합니다: 시맨틱 상태 레이어를 노출하여 애플리케이션의 구조, 규칙, 상태 및 유효한 전이를 직접 드러냅니다. 이는 UI를 대체하는 것이 아니라 전통적인 사용자 인터페이스와 함께 인텔리전스 인터페이스 (II) 를 제공하는 것입니다.

직접 사용해 보기 → Manifesto Playground
Source: …
1. 블랙 박스: 현재 AI + 웹 앱 상황
오늘날 대부분의 팀이 웹 앱에 “AI를 추가”하는 방식은 다음과 같습니다:
- LangChain, AutoGPT, 혹은 브라우저 자동화 사용
- Playwright 또는 Puppeteer 구동
- DOM이나 스크린샷을 모델에 투입
- 무엇을 클릭해야 할지 모델이 알아내길 기대
이것이 블랙 박스 접근 방식입니다. 에이전트는 렌더링된 화면만 보고 나머지는 모두 추론해야 합니다.
DOM 덤프의 문제점은?
<div class="product-name">
<label>Product Name</label>
<input type="text" name="name" />
<span class="error">This field is required.</span>
</div>
에이전트 입장에서 보면:
| 문제 | 영향 |
|---|---|
| 토큰 낭비 | 토큰의 90 %가 클래스 이름과 래퍼에 사용됨 |
| 제약 조건 누락 | 필수 입력인가? 최대 길이는 얼마인가? |
| 의존성 없음 | 이 필드가 다른 필드에 의존하고 있는가? |
| 인과 관계 부재 | 제출 버튼이 비활성화되어 있는데 — 왜 그런가? |
에이전트는 추측을 강요받습니다. CSS 리팩터링만으로도 모든 것이 깨지고, 레이아웃 변경만으로도 모델이 혼란스러워집니다. 논리는 절대 노출되지 않았으며, 오직 시각적 투영만이 제공되었습니다.
시그널 90 %.
2. White Box: 애플리케이션의 두뇌 노출
대안은 White Box 프로토콜입니다. HTML을 보여주는 대신, 엔진은 Semantic Snapshot — 에이전트가 직접 읽을 수 있는 애플리케이션 내부 상태의 구조화된 표현 — 을 노출합니다.
{
"topology": {
"viewId": "product-create",
"mode": "create",
"sections": [
{ "id": "basic", "title": "Basic Info", "fields": ["name", "productType"] },
{ "id": "shipping", "title": "Shipping", "fields": ["shippingWeight"] }
]
},
"state": {
"form": { "isValid": false, "isDirty": false },
"fields": {
"name": {
"value": "",
"meta": { "valid": false, "hidden": false, "disabled": false, "errors": ["Required"] }
},
"productType": {
"value": "PHYSICAL",
"meta": { "valid": true, "hidden": false, "disabled": false, "errors": [] }
},
"shippingWeight": {
"value": null,
"meta": { "valid": true, "hidden": false, "disabled": false, "errors": [] }
}
}
},
"constraints": {
"name": { "required": true, "minLength": 2, "maxLength": 100 },
"shippingWeight": { "min": 0, "max": 2000, "dependsOn": ["productType"] }
},
"interactions": [
{ "id": "updateField:name", "intent": "updateField", "target": "name", "available": true },
{ "id": "updateField:productType", "intent": "updateField", "target": "productType", "available": true },
{ "id": "submit", "intent": "submit", "available": false, "reason": "Name is required" }
]
}
이제 에이전트는 다음을 갖게 됩니다:
- Topology – 화면 구조, 섹션, 필드 계층
- State – 현재 값, 유효성, 가시성, 필드별 오류
- Constraints – 필수 여부, 최소/최대값, 의존 관계
- Interactions – 어떤 행동이 가능한지와 왜 일부가 차단되는지
추측이 필요 없습니다. 추론이 필요 없습니다. 에이전트는 애플리케이션의 두뇌를 직접 읽습니다.
3. 실제 사용 사례: “주를 어디서 선택하나요?”
🎮 실제 동작 보기: Manifesto Playground – 필드 값을 변경하고 의미론적 상태가 실시간으로 업데이트되는 모습을 확인해 보세요.
시나리오
사용자: “날짜 선택기는 보이는데, 주는 어디서 선택하나요?”
AI 챗봇: “주 선택기는 frequency를 ‘Weekly’로 설정했을 때만 나타납니다. 현재는 ‘Daily’로 설정돼 있네요. 대신 바꿔드릴까요?”
이를 위해 AI가 알아야 할 내용:
weekSelector라는 필드가 존재하며 현재는 숨겨져 있다frequency === 'WEEKLY'일 때 보이게 된다- 현재
frequency값은'DAILY'이다
DOM‑만으로는 이 정보를 신뢰성 있게 제공할 수 없지만, 의미론적 스냅샷을 사용하면 가능합니다:
{
"fields": {
"frequency": {
"value": "DAILY",
"meta": { "hidden": false }
},
"weekSelector": {
"value": null,
"meta": { "hidden": true },
"visibleWhen": "frequency === 'WEEKLY'"
}
}
}
AI는 이를 읽고 추론 없이 필드가 숨겨진 정확한 이유와 어떤 조건에서 나타나는지를 알 수 있습니다.
4. 프로토콜 루프
Manifesto는 엔진과 AI 에이전트 사이에 지속적인 피드백 루프를 구현합니다:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ [Context Injection] → [Reasoning] → [Action Dispatch] → [Delta] │
│ ▲ │ │
│ └─────────────── Continuous Snapshots ────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
단계별 설명
- 컨텍스트 주입 – 엔진은 토폴로지, 상태, 제약 조건 및 상호작용을 포함한 시맨틱 스냅샷을 내보냅니다.
- 추론 – 에이전트는 스냅샷을 기반으로 다음 행동을 계획합니다.
- 액션 디스패치 – 에이전트는 원시 DOM 이벤트 대신 추상 인텐트(예:
updateField,submit,reset,validate)를 호출합니다. - 델타 피드백 – 엔진은 단순히 “성공”이 아니라 무엇이 변했는지를 반환합니다. 차이는 인과관계를 보여줍니다(예: “X를 변경했더니 Y가 숨겨졌다”).
- 루프는 업데이트된 스냅샷으로 반복되어 에이전트에게 “클릭하고 기대”가 아닌 예측 가능하고 구조화된 피드백을 제공합니다.
5. API: 탐색 및 실행
Manifesto는 이 프로토콜을 @manifesto-io/ai를 통해 노출합니다.
탐색 모드 – “여기서 무엇을 할 수 있나요?”
import { createInteroperabilitySession } from '@manifesto-io/ai'
const session = createInteroperabilitySession({
runtime, // FormRuntime instance
viewSchema, // View definition
entitySchema, // Entity definition
})
// Get the current semantic snapshot
const snapshot = session.snapshot()
// snapshot.interactions tells the agent:
// - submit: available = false, reason = "Name is required"
여기서 에이전트는 사용 가능한 상호작용을 조회하고, 필드를 업데이트하며, 폼을 제출하고, 증분 차이를 받아올 수 있습니다. 모두 화이트‑박스 의미론적 표현에 의해 구동됩니다.