跳转到主要内容
@ag-kit/ui-react 包提供了一个轻量级的React钩子和TypeScript类型,用于构建支持消息流式传输、带可选UI的工具调用以及Human-in-the-loop中断的Agent聊天界面。

安装

npm install @ag-kit/ui-react zod
  • 同级依赖zod(v3.25+ 或 v4)
  • 使用 fetch + 服务器发送事件(SSE)。您的服务器端点必须流式传输与AG‑Kit兼容的事件。

快速开始

import React, { useState } from "react";
import { useChat } from "@ag-kit/ui-react";

export default function App() {
  const { uiMessages, sendMessage, streaming } = useChat({
    url: "/send-message", // 您的Agent服务器端点(SSE)
  });

  const [input, setInput] = useState("");

  return (
    <div>
      <div>
        {uiMessages.map((m, i) => (
          <div key={i}>
            {m.role === "user" && m.parts.map((p, idx) => p.type === "text" ? <p key={idx}>{p.text}</p> : null)}
            {m.role === "assistant" && m.parts.map((p, idx) => {
              if (p.type === "text") return <p key={idx}>{p.text}</p>;
              if (p.type === "tool-call") return (
                <div key={idx}>
                  {p.render ? p.render() : <pre>{p.arguments}</pre>}
                </div>
              );
              if (p.type === "interrupt" && p.render) return <div key={idx}>{p.render()}</div>;
              return null;
            })}
          </div>
        ))}
      </div>

      <form onSubmit={(e) => { e.preventDefault(); sendMessage(input); setInput(""); }}>
        <input value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入..." />
        <button type="submit" disabled={streaming}>发送</button>
      </form>
    </div>
  );
}

应用程序接口

useChat

用于聊天状态、流式传输、工具调用和中断的React钩子。
function useChat(options?: {
  url?: string;                  // 默认:"/send-message"
  tools?: ReadonlyArray<Tool<z.ZodTypeAny>>;
  threadId?: string;             // 默认:生成的UUID
  interrupt?: InterruptWithResume;
}): {
  messages: Array<ClientMessage>; // 来自服务器的低级消息流
  setMessages: (
    next: Array<ClientMessage> | ((prev: Array<ClientMessage>) => Array<ClientMessage>)
  ) => void;
  uiMessages: Array<UIMessage>;  // 适合UI显示的消息部分
  uiToolCalls: Array<{
    id: string;
    name: string;
    arguments: string;
    result?: string;
    state: ToolStreamingState;
    errorText?: string;
  }>;
  sendMessage: (arg?:
    | string
    | { toolCallId: string; content: string }
    | { interruptId: string; payload: unknown }
  ) => Promise<void>;
  loading: boolean;
  streaming: boolean;
}
  • sendMessage形式
    • sendMessage("text"):发送用户消息
    • sendMessage({ toolCallId, content }):提交工具结果消息
    • sendMessage({ interruptId, payload }):通过携带有效载荷从中断恢复

工具

使用zod创建强类型工具。工具可以声明为三种能力之一:handlerrenderrenderAndWaitForResponse
import { z } from "zod";
import { tool } from "@ag-kit/ui-react";

// 1) 处理器工具:当输入可用时自动运行
const add = tool({
  name: "add",
  description: "相加两个数字",
  parameters: z.object({ a: z.number(), b: z.number() }),
  handler: ({ a, b }) => a + b,
});

// 2) 仅渲染工具:渲染UI以显示(无自动处理器)
const show = tool({
  name: "show",
  description: "显示有效载荷",
  parameters: z.object({ data: z.string() }),
  render: ({ input }) => <div>有效载荷: {input.data}</div>,
});

// 3) 渲染并等待:渲染UI并提交结果回Agent
const confirm = tool({
  name: "confirm",
  description: "请求确认",
  parameters: z.object({ prompt: z.string() }),
  renderAndWaitForResponse: ({ input, submitToolResult }) => (
    <button onClick={() => submitToolResult?.("confirmed")}>确认: {input.prompt}</button>
  ),
});
将工具传递给useChat({ tools: [...] })。当服务器发出与提供的工具名称匹配的工具调用时:
  • 如果工具具有handler,它会在输入流式传输完成时自动运行,并将其结果作为工具消息发送。
  • 如果工具具有render,它将出现在uiMessages中,并带有可选的render()函数用于自定义UI。
  • 如果工具具有renderAndWaitForResponse,则提供submitToolResult回调以返回字符串结果,该结果将发送回Agent。

在UI中渲染工具调用

// 在您的消息渲染器内部
if (part.type === "tool-call") {
  return (
    <div>
      {/* 优先使用工具提供的UI(如果可用) */}
      {part.render ? part.render() : (
        <details>
          <summary>工具: {part.name}</summary>
          <pre>参数: {part.arguments}</pre>
          {part.result && <pre>结果: {part.result}</pre>}
        </details>
      )}
    </div>
  );
}

客户端工具调用注册与执行模式

  • 自动运行(handler):提供带有handler的工具。它将在input-available时自动执行并继续下一轮。
  • 自处理运行(renderAndWaitForResponse):提供renderAndWaitForResponse并调用submitToolResult("...")发送结果。
  • 手动提交(仅UI工具):对于没有处理器的工具,使用sendMessage({ toolCallId, content })手动提交结果。
// 为没有处理器的工具手动提交示例
import { useChat } from "@ag-kit/ui-react";

const { uiMessages, sendMessage } = useChat({ /* tools: [...] */ });

// ... 在渲染中
if (part.type === "tool-call") {
  return (
    <div>
      {part.render ? part.render() : <pre>{part.arguments}</pre>}
      <button
        onClick={() => {
          // 内容必须是字符串;发送结构化数据时使用JSON.stringify
          sendMessage({ toolCallId: part.id, content: JSON.stringify({ ok: true }) });
        }}
      >
        提交结果
      </button>
    </div>
  );
}
您还可以检查uiToolCalls以显示进度/状态:
const { uiToolCalls } = useChat();
return (
  <ul>
    {uiToolCalls.map(tc => (
      <li key={tc.id}>{tc.name}: {tc.state}</li>
    ))}
  </ul>
);

中断

通过interrupt选项为Human-in-the-loop审批/输入提供自定义渲染器。
import { useChat } from "@ag-kit/ui-react";

const chat = useChat({
  interrupt: {
    renderWithResume: ({ interrupt, resume }) => (
      <div>
        <p>{interrupt.reason}</p>
        <button onClick={() => resume({ approved: true })}>批准</button>
      </div>
    ),
  },
});
当中断到达时,uiMessages包含一个type: "interrupt"的部分。如果提供了renderWithResume UI,调用resume(payload)将继续对话。

类型

这些TypeScript类型被导出用于构建UI:
// 工具
type ToolWithInputSchema<TSchema extends z.ZodTypeAny> = { /* name, description, parameters */ };
type ToolWithHandler<TSchema extends z.ZodTypeAny> = ToolWithInputSchema<TSchema> & { handler(input): unknown };
type ToolWithRender<TSchema extends z.ZodTypeAny> = ToolWithInputSchema<TSchema> & { render(props): React.ReactNode };
type ToolWithRenderAndWaitFor