# AI agent: từng bước trực tiếp, phê duyệt và thông báo

> Hiển thị từng bước agent làm trong lúc chạy, giữ các lệnh gọi công cụ rủi ro trong hộp duyệt trực tiếp cho đến khi có người duyệt, và gửi kết quả đến trình duyệt và điện thoại từ agent, MCP client và các luồng không cần code.

Source: https://hibira.io/vi/use-cases/ai-agents

Một AI agent chạy nền suốt một phút mà màn hình không hiện gì trông như đã treo, còn một agent tự hoàn tiền đơn hàng mà không hỏi ai thì còn tệ hơn. Hibira cho agent của bạn dùng chung các kênh thời gian thực với phần còn lại của ứng dụng. Mỗi bước hiện ra ngay khi xong, lệnh gọi công cụ rủi ro chờ trong hộp duyệt trực tiếp cho đến khi có người duyệt, và kết quả đến đúng người trên trình duyệt hoặc điện thoại.

## Người dùng nhận được gì

- Từng bước agent làm ("Đã tra cứu đơn hàng", "Đã soạn câu trả lời") hiện ra ngay khi xong, trên mọi tab đang mở.
- Yêu cầu duyệt xuất hiện trong hộp duyệt ngay lập tức, kèm những người duyệt khác đang xem, nên hai người không quyết cùng một khoản hoàn tiền.
- Thông báo đẩy khi agent xong việc hoặc cần một quyết định mà không ai đang mở trang.

![Bên trái là lượt chạy của agent, bên phải là hộp thư phê duyệt: các bước hiện ra ngay khi xong, khoản hoàn tiền chờ người duyệt, và sau khi bấm Phê duyệt agent tiếp tục đến hết.](https://hibira.io/portal/articles/demo-ai-agents-vi.mp4)

## Dùng những gì

- **Kênh riêng tư** cho mỗi lần chạy, như `private-agent-run-42`, để gửi các bước và kết quả.
- **[@hibira-io/approvals](https://github.com/hibira-io/approvals)** cho hộp duyệt: helper phía máy chủ cho Vercel AI SDK, Mastra và LangGraph (JavaScript và Python), cùng component React và Vue.
- **Kênh presence**, để người duyệt thấy còn ai đang mở cùng yêu cầu.
- Các công cụ `publish_event`, `send_push` và `request_approval` của **[máy chủ MCP](https://hibira.io/vi/mcp-server)**, cho agent chạy trong Claude, Cursor hoặc MCP client khác.
- **Thông báo đẩy** qua API tương thích Beams.

```text
flow: Các bước của agent đến với người dùng, còn lệnh gọi công cụ rủi ro chờ người duyệt
== Các bước và kết quả
Agent của bạn|một sự kiện cho mỗi bước --`trigger()`--> *Hibira --WebSocket--> Trang của người dùng|danh sách bước trực tiếp
== Lệnh gọi công cụ cần duyệt
Agent của bạn|tạm dừng lần chạy --`approvals.request()`--> *Hibira --WebSocket--> Hộp duyệt của người duyệt|duyệt, sửa hoặc từ chối
```

## Bước 1: phát từng bước từ agent

Với Vercel AI SDK, `onStepFinish` chạy sau mỗi bước. Chỉ gửi tên công cụ, không gửi đầu vào, để sự kiện nhỏ gọn và dữ liệu nhạy cảm không đi qua socket:

```js
import { generateText, stepCountIs } from 'ai';

const channel = `private-agent-run-${run.id}`;
const result = await generateText({
  model,
  tools,
  messages,
  stopWhen: stepCountIs(10),
  onStepFinish: async ({ toolCalls }) => {
    if (toolCalls.length) {
      await pusher.trigger(channel, 'step', { tools: toolCalls.map((c) => c.toolName) });
    }
  },
});
await saveRun(run.id, result.text);
await pusher.trigger(channel, 'done', { id: run.id });
```

Trong trình duyệt, hãy đăng ký kênh trước khi lần chạy bắt đầu và hiển thị nhãn cho từng công cụ. Route cấp quyền kênh của bạn chỉ nên cho phép `private-agent-run-42` với người dùng đã bắt đầu lần chạy 42:

```js
const channel = pusher.subscribe(`private-agent-run-${runId}`);
channel.bind('step', ({ tools }) => tools.forEach((t) => steps.add(LABELS[t] ?? t)));
channel.bind('done', async ({ id }) => showResult(await fetch(`/runs/${id}`).then((r) => r.json())));
```

Các framework agent khác cũng làm như vậy: gọi `trigger()` ở chỗ một bước kết thúc.

## Bước 2: hỏi người trước khi chạy công cụ rủi ro

Đánh dấu công cụ bằng `needsApproval`. Khi lần chạy dừng ở công cụ đó, hãy tạo một yêu cầu: người duyệt nhận sự kiện `approval` trên `private-hibira-approvals.support.inbox` ngay lập tức.

```js
import { tool } from 'ai';
import { createApprovals } from '@hibira-io/approvals';
import { approvalCalls } from '@hibira-io/approvals/ai-sdk';

export const approvals = createApprovals({ pusher, scope: 'support', resume });

const tools = {
  refund_order: tool({ inputSchema, needsApproval: true, execute: refund }),
};

const calls = approvalCalls(result);
if (calls.length) {
  await approvals.request({ agent: 'Support agent', title: 'Refund order #1042', calls, metadata: { chatId } });
}
```

Người duyệt quyết định trong component hộp duyệt. Component gọi backend của bạn qua các route của `approvals.handler()`, không bao giờ qua sự kiện client, và `resume(request)` của bạn trả câu trả lời cho agent đúng một lần, kể cả khi hai người duyệt cùng bấm Gửi:

```jsx
import { ApprovalInbox, createApprovalsClient } from '@hibira-io/approvals/react';
import '@hibira-io/approvals/styles.css';

const client = createApprovalsClient({ endpoint: '/api/approvals', pusher, scope: 'support' });

export function ApprovalsPage({ me }) {
  return <ApprovalInbox client={client} me={me.id} />;
}
```

Cùng gói này có helper cho Mastra (`requireApproval` và `suspend()`) và LangGraph (`interrupt()`). Với Python, `pip install "hibira-approvals[pusher,fastapi]"` cho LangGraph Python tạo cùng loại yêu cầu và có router FastAPI cho hộp duyệt. Dùng Laravel? [Filament AI approvals](https://hibira.io/vi/guides/filament-approvals) đặt hộp duyệt vào panel Filament của bạn cho agent xây bằng Laravel AI SDK.

## Bước 3: agent trong Claude, Cursor và các MCP client khác

Agent chạy trong một MCP client dùng được Hibira mà bạn không phải viết code. Thêm [máy chủ MCP](https://hibira.io/vi/mcp-server) và agent có thể:

- gửi tiến độ và kết quả đến kênh của bạn bằng `publish_event`, để dashboard hoặc trình duyệt của đồng đội theo dõi công việc,
- gửi thông báo đẩy đến một interest hoặc người dùng bằng `send_push` khi xong,
- hỏi người bằng `request_approval`: công cụ trả về liên kết đến một trang chúng tôi lưu trữ, nơi họ chọn câu trả lời, và `get_approval` chờ kết quả. Truyền thêm một kênh thì yêu cầu cũng hiện trong hộp duyệt ở trên.

```sh
claude mcp add --transport http hibira https://hibira.io/mcp
```

## Bước 4: luồng không cần code

Các công cụ tự động hóa cũng gửi được cùng những sự kiện này. Một luồng có thể phát một bước hoặc kết quả lên kênh, gửi thông báo đẩy, hoặc bắt đầu khi có chuyện xảy ra trên một kênh:

- [n8n](https://hibira.io/vi/guides/n8n), node Hibira cũng dùng được làm công cụ cho AI agent của n8n
- [Make](https://hibira.io/vi/guides/make)
- [Node-RED](https://hibira.io/vi/guides/node-red)
- [Activepieces](https://hibira.io/vi/guides/activepieces)

## Mẹo

- Giữ sự kiện nhỏ gọn: gửi ID và nhãn ngắn, rồi tải toàn bộ kết quả từ máy chủ của bạn. Giới hạn kích thước tùy gói, xem [bảng giá](https://hibira.io/vi/pricing).
- Lưu mọi bước và yêu cầu vào cơ sở dữ liệu, để trang mở sau vẫn thấy toàn bộ lần chạy.
- Với agent viết câu trả lời từng token, xem [Tiến độ cho tác vụ dài và stream câu trả lời AI](https://hibira.io/vi/use-cases/job-progress).
- Yêu cầu duyệt không mang đầu vào của công cụ qua socket. Hộp duyệt tải nó từ backend của bạn, nơi kiểm tra người dùng có quyền duyệt hay không.
- Khi không có người duyệt nào trực tuyến, dùng tùy chọn `notify` của `createApprovals()` để gửi thông báo đẩy cho người duyệt theo [hướng dẫn thông báo đẩy](https://hibira.io/vi/guides/push-notifications).
