mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
cc8c945893
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
142 lines
6.6 KiB
Plaintext
142 lines
6.6 KiB
Plaintext
CopilotKit is built on the [AG-UI protocol](https://ag-ui.com) — a lightweight, event-based standard that defines how AI agents communicate with user-facing applications over Server-Sent Events (SSE).
|
|
|
|
Everything in CopilotKit — messages, state updates, tool calls, and more — flows through AG-UI events. Understanding this layer helps you debug, extend, and build on top of CopilotKit more effectively.
|
|
|
|
## Accessing Your Agent with `useAgent`
|
|
|
|
The `useAgent` hook is your primary interface to the AG-UI agent powering your copilot. It returns an [`AbstractAgent`](https://github.com/ag-ui-protocol/ag-ui/blob/main/typescript/packages/client/src/agents/abstract-agent.ts) from the AG-UI client library — the same base type that all AG-UI agents implement.
|
|
|
|
```tsx
|
|
import { useAgent } from "@copilotkit/react-core";
|
|
|
|
function MyComponent() {
|
|
const { agent } = useAgent();
|
|
|
|
// agent.messages - conversation history
|
|
// agent.state - current agent state
|
|
// agent.isRunning - whether the agent is currently running
|
|
}
|
|
```
|
|
|
|
If you have multiple agents, pass the `agentId` to select one:
|
|
|
|
```tsx
|
|
const { agent } = useAgent({ agentId: "research-agent" });
|
|
```
|
|
|
|
The returned `agent` is a standard AG-UI `AbstractAgent`. You can subscribe to its events, read its state, and interact with it using the same interface defined by the [AG-UI specification](https://docs.ag-ui.com).
|
|
|
|
### Subscribing to AG-UI Events
|
|
|
|
Every agent exposes a `subscribe` method that lets you listen for specific AG-UI events as they stream in. Each callback receives the event and the current agent state:
|
|
|
|
```tsx
|
|
import { useAgent } from "@copilotkit/react-core";
|
|
import { useEffect } from "react";
|
|
|
|
function MyComponent() {
|
|
const { agent } = useAgent();
|
|
|
|
useEffect(() => {
|
|
const subscription = agent.subscribe({
|
|
// Called on every event
|
|
onEvent({ event, agent }) {
|
|
console.log("Event:", event.type, event);
|
|
},
|
|
|
|
// Text message streaming
|
|
onTextMessageContentEvent({ event, textMessageBuffer, agent }) {
|
|
console.log("Streaming text:", textMessageBuffer);
|
|
},
|
|
|
|
// Tool calls
|
|
onToolCallEndEvent({ event, toolCallName, toolCallArgs, agent }) {
|
|
console.log("Tool called:", toolCallName, toolCallArgs);
|
|
},
|
|
|
|
// State updates
|
|
onStateSnapshotEvent({ event, agent }) {
|
|
console.log("State snapshot:", agent.state);
|
|
},
|
|
|
|
// High-level lifecycle
|
|
onMessagesChanged({ agent }) {
|
|
console.log("Messages updated:", agent.messages);
|
|
},
|
|
onStateChanged({ agent }) {
|
|
console.log("State changed:", agent.state);
|
|
},
|
|
});
|
|
|
|
return () => subscription.unsubscribe();
|
|
}, [agent]);
|
|
}
|
|
```
|
|
|
|
The full list of subscribable events maps directly to the [AG-UI event types](https://docs.ag-ui.com/concepts/events):
|
|
|
|
| Event | Callback | Description |
|
|
| --- | --- | --- |
|
|
| Run lifecycle | `onRunStartedEvent`, `onRunFinishedEvent`, `onRunErrorEvent` | Agent run start, completion, and errors |
|
|
| Steps | `onStepStartedEvent`, `onStepFinishedEvent` | Individual step boundaries within a run |
|
|
| Text messages | `onTextMessageStartEvent`, `onTextMessageContentEvent`, `onTextMessageEndEvent` | Streaming text content from the agent |
|
|
| Tool calls | `onToolCallStartEvent`, `onToolCallArgsEvent`, `onToolCallEndEvent`, `onToolCallResultEvent` | Tool invocation lifecycle |
|
|
| State | `onStateSnapshotEvent`, `onStateDeltaEvent` | Full state snapshots and incremental deltas |
|
|
| Messages | `onMessagesSnapshotEvent` | Full message list snapshots |
|
|
| Custom | `onCustomEvent`, `onRawEvent` | Custom and raw events for extensibility |
|
|
| High-level | `onMessagesChanged`, `onStateChanged` | Aggregate notifications after any message or state mutation |
|
|
|
|
## The Proxy Pattern
|
|
|
|
When you use CopilotKit with a runtime, your frontend never talks directly to your agent. Instead, CopilotKit creates a **proxy agent** on the frontend that forwards requests through the Copilot Runtime.
|
|
|
|
On startup, CopilotKit calls the runtime's `/info` endpoint to discover which agents are available. Each agent is wrapped in a `ProxiedCopilotRuntimeAgent` — a thin client that extends AG-UI's [`HttpAgent`](https://github.com/ag-ui-protocol/ag-ui/blob/main/typescript/packages/client/src/agents/http-agent.ts). From your component's perspective, this proxy behaves identically to a local AG-UI agent: same `AbstractAgent` interface, same subscribe API, same properties. But under the hood, every `run` call is an HTTP request to your server, and every response is an SSE stream of AG-UI events flowing back.
|
|
|
|
```tsx title="What your component sees"
|
|
const { agent } = useAgent(); // Returns an AbstractAgent
|
|
agent.messages; // Read messages
|
|
agent.state; // Read state
|
|
agent.subscribe({ ... }); // Subscribe to events
|
|
```
|
|
|
|
```tsx title="What actually happens"
|
|
// useAgent() → AgentRegistry checks /info → wraps each agent in ProxiedCopilotRuntimeAgent
|
|
// agent.runAgent() → HTTP POST to runtime → runtime routes to your agent → SSE stream back
|
|
```
|
|
|
|
This indirection is what enables the runtime to provide authentication, middleware, agent routing, and ecosystem features like [threads](/premium/threads) and [observability](/premium/observability) — without changing how you interact with agents on the frontend.
|
|
|
|
## How Agents Slot into the Runtime
|
|
|
|
On the server side, the `CopilotRuntime` accepts a map of AG-UI `AbstractAgent` instances. Each agent framework provides its own implementation, but they all extend the same base type:
|
|
|
|
```ts title="app/api/copilotkit/route.ts"
|
|
import { CopilotRuntime, copilotRuntimeNextJSAppRouterEndpoint } from "@copilotkit/runtime";
|
|
import { HttpAgent } from "@ag-ui/client";
|
|
|
|
const runtime = new CopilotRuntime({
|
|
agents: {
|
|
"my-agent": new HttpAgent({
|
|
url: "https://my-agent-server.example.com",
|
|
}),
|
|
},
|
|
});
|
|
|
|
export const POST = async (req: NextRequest) => {
|
|
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
|
|
runtime,
|
|
endpoint: "/api/copilotkit",
|
|
});
|
|
return handleRequest(req);
|
|
};
|
|
```
|
|
|
|
When a request comes in:
|
|
|
|
1. The runtime resolves the target agent by ID
|
|
2. It clones the agent (for thread safety) and sets messages, state, and thread context from the request
|
|
3. The `AgentRunner` executes the agent, which produces a stream of AG-UI `BaseEvent`s
|
|
4. Events are encoded as SSE and streamed back to the frontend proxy
|
|
|
|
Because every agent is an `AbstractAgent`, you can register any AG-UI-compatible agent — whether it's an `HttpAgent` pointing at a remote server, a framework-specific adapter, or a custom implementation — and the runtime handles routing, middleware, and delivery uniformly.
|