Predictive state updates
Stream in-progress agent state updates to the frontend.
This video shows the result of npx copilotkit@latest init with the
implementation section applied to it!
What is this?#
A CrewAI Flow's state updates discontinuosly; only across function transitions in the flow. But even a single function in the flow often takes many seconds to run and contain sub-steps of interest to the user.
Agent-native applications reflect to the end-user what the agent is doing as continuously possible.
CopilotKit enables this through its concept of predictive state updates.
When should I use this?#
You can use this when you want to provide the user with feedback about what your agent is doing, specifically to:
- Keep users engaged by avoiding long loading indicators
- Build trust by demonstrating what the agent is working on
- Enable agent steering - allowing users to course-correct the agent if needed
Important Note#
When a function in your CrewAI flow finishes executing, its returned state becomes the single source of truth. While intermediate state updates are great for real-time feedback, any changes you want to persist must be explicitly included in the function's final returned state. Otherwise, they will be overwritten when the function completes.
Implementation#
Install the CopilotKit SDK#
Any LangGraph agent can be used with CopilotKit. However, creating deep agentic experiences with CopilotKit requires our LangGraph SDK.
uv add copilotkitpoetry add copilotkitpip install copilotkit --extra-index-url https://copilotkit.gateway.scarf.sh/simple/conda install copilotkit -c copilotkit-channelnpm npm install @copilotkit/sdk-js
Define the state#
We'll be defining a observed_steps field in the state, which will be updated as the agent writes different sections of the report.
from copilotkit.crewai import CopilotKitState
from typing import Literal
class AgentState(CopilotKitState):
observed_steps: list[str] # Array of completed stepsEmit the intermediate state#
Observe the predictions#
These predictions will be emitted as the agent runs, allowing you to track its progress before the final state is determined.
// ...
const YourMainContent = () => {
// Get access to both predicted and final states
const { agent } = useAgent({ agentId: "sample_agent" });
// Add a state renderer to observe predictions
useAgent({
agentId: "sample_agent",
render: ({ state }) => {
if (!state.observed_steps?.length) return null;
return (
<div>
<h3>Current Progress:</h3>
<ul>
{state.observed_steps.map((step, i) => (
<li key={i}>{step}</li>
))}
</ul>
</div>
);
},
});
return (
<div>
<h1>Agent Progress</h1>
{agent.state?.observed_steps?.length > 0 && (
<div>
<h3>Final Steps:</h3>
<ul>
{agent.state.observed_steps.map((step, i) => (
<li key={i}>{step}</li>
))}
</ul>
</div>
)}
</div>
)
}Give it a try!#
Now you'll notice that the state predictions are emitted as the agent makes progress, giving you insight into its work before the final state is determined. You can apply this pattern to any long-running task in your agent.
