표, 차트, 폼, 차이 비교 등 풍부한 인터랙티브 컴포넌트를 인라인으로 렌더링하는 에이전트 — 텍스트만이 아닙니다. 프론트엔드 코드가 필요 없습니다.
기존 챗봇 UI는 모든 것을 텍스트 말풍선으로 렌더링합니다. 하지만 에이전트는 구조화된 데이터 — 쿼리 결과, 유효성 검사 보고서, 배포 상태, 비교 차이 — 를 생성합니다. 이 모든 것을 마크다운으로 렌더링하는 것은 좋지 않은 경험입니다.
Haira의 생성형 UI는 도구가 출력이 표시되는 방식을 선언적으로 제어할 수 있게 합니다. 데이터베이스 쿼리는 표로 렌더링됩니다. 유효성 검사 결과는 상태 카드로 렌더링됩니다. 배포 파이프라인은 진행 상황 추적기로 렌더링됩니다. 모두 채팅 인라인으로, 프론트엔드 코드 작성 없이.
도구는 ui 모듈을 사용하여 UI 컴포넌트를 반환합니다. 런타임은 모든 결과에 대해 두 가지 작업을 수행합니다:
어느 경로도 희생되지 않습니다. 사용자는 아름다운 표를 보고, 에이전트는 구조화된 데이터를 봅니다.
import "ui"
tool query_database(query: string) -> any {
"""Executes a SQL query and displays results."""
rows, err = postgres.query(db, query)
if err != nil {
return ui.status_card("error", "Query Failed", conv.to_string(err))
}
return ui.table("Query Results", headers, rows)
}한 줄로 에이전트에 UI를 활성화합니다:
agent DataExplorer {
provider: OpenAI
tools: [query_database, search_data]
ui: ui
memory: conversation(max_turns: 30)
}import "ui"
ui.status_card("success", "Deploy Complete", "All 3 services updated")
ui.table("Results", ["Name", "Email"], [["Alice", "a@co"], ["Bob", "b@co"]])
ui.key_value("Server Info", {"Region": "us-east-1", "Status": "healthy"})
ui.chart("bar", "Revenue", ["Q1", "Q2", "Q3", "Q4"], [dataset])
ui.confirm("Delete this record?", "Yes, delete", "Cancel")
ui.group(
ui.status_card("success", "Query Complete", "42 rows"),
ui.table("Results", headers, rows)
)모든 Haira 워크플로는 자동으로 웹 UI를 갖습니다. 워크플로를 정의하면 Haira가 폼을 생성합니다:
@webui(title: "File Summarizer", description: "Upload a file and get an AI summary")
@post("/summarize")
workflow Summarize(document: file, context: string) -> { summary: string } {
content, err = io.read_file(document)
if err != nil { return { summary: "Failed to read file." } }
reply, err = Summarizer.ask("Summarize: ${content}")
if err != nil { return { summary: "AI error." } }
return { summary: reply }
}@webui 데코레이터는 제목과 설명을 설정합니다. file 파라미터는 업로드 입력으로 렌더링됩니다. 스트리밍 워크플로 (-> stream)는 전체 채팅 인터페이스를 갖습니다.
스트리밍 워크플로는 가장 풍부한 경험을 제공합니다 — 실시간 토큰 스트리밍, 도구 실행 카드, 인라인 UI 컴포넌트:
@webhook("/chat")
workflow Chat(message: string, session_id: string) -> stream {
return Assistant.stream(message, session: session_id)
}
fn main() {
http.Server([Chat]).listen(8080)
}채팅 UI는 ARP 프로토콜을 통해 통신합니다 — WebSocket 또는 SSE를 통해 텍스트 델타, 도구 수명 주기 이벤트, 풍부한 컴포넌트 렌더링을 처리합니다.
도메인별 요구 사항을 위해 TypeScript 웹 컴포넌트를 components/ 디렉토리에 추가합니다:
export class HairaGanttChart extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: "open" });
}
setProps(props) {
// Render your custom UI
}
}
export default {
tag: "haira-gantt-chart",
component: HairaGanttChart,
};커스텀 컴포넌트는 CSS 커스텀 속성을 통해 Haira의 테마를 상속하며 채팅 메시지가 되는 haira-action 이벤트를 발송할 수 있습니다. 컴파일러가 빌드 시 이를 자동으로 발견하고, 번들링하고, 포함시킵니다.
ui.* 함수를 통해 타입화된 데이터 반환별도의 프론트엔드 저장소 없음. 유지할 API 클라이언트 없음. 설치할 컴포넌트 라이브러리 없음. 하나의 .haira 파일, 하나의 바이너리, 완전한 UI.