テーブル、チャート、フォーム、差分比較など、リッチでインタラクティブなコンポーネントをインラインでレンダリングするエージェント — テキストだけではありません。フロントエンドコードは不要です。
従来のチャットボットUIはすべてをテキストバブルとしてレンダリングします。しかし、エージェントはクエリ結果、バリデーションレポート、デプロイメントステータス、比較差分といった構造化データを生成します。これらすべてをmarkdownとしてレンダリングするのは貧しい体験です。
Hailaの生成的UIは、ツールが出力の表示方法を宣言的に制御できるようにします。データベースクエリはテーブルとしてレンダリングされます。バリデーション結果はステータスカードとしてレンダリングされます。デプロイメントパイプラインはプログレストラッカーとしてレンダリングされます。すべてチャット内にインラインで、フロントエンドコードを書くことなく実現します。
ツールは ui モジュールを使用してUIコンポーネントを返します。ランタイムはすべての結果に対して2つの処理を行います:
どちらのパスも犠牲にしません。ユーザーは美しいテーブルを見て、エージェントは構造化データを受け取ります。
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)
}1行でエージェントの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ワークフローは自動的にWeb 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 Webコンポーネントを 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クライアントも不要。インストールすべきコンポーネントライブラリも不要。1つの .haira ファイル、1つのバイナリ、完全なUI。