协议不强绑
后端返回什么事件,就映射什么事件。Agentdown 不要求你为了前端再设计一套统一后端协议。
Agentdown 的重点不是“把 markdown 显示出来”,而是把这一整段链路收敛起来:
raw packet / SSE -> protocol -> bridge -> assembler -> runtime -> Agent UI它特别适合下面这类页面:
MarkdownRenderer 先渲染静态或一次性 markdown 内容RunSurface 把 runtime 里的 block 渲染成聊天界面defineEventProtocol() 适配任意自定义后端事件define*ToolComponents() 按工具名挂接自定义卡片define*EventComponents() 在特定 SSE 事件到来时直接渲染组件| 框架 | 入口 | 适合什么场景 |
|---|---|---|
| Agno | useAgnoChatSession() / createAgnoAdapter() | 聊天页面优先用 useAgnoChatSession(),更底层场景再往 adapter / protocol 走 |
| LangChain | useLangChainChatSession() / createLangChainAdapter() | 想直接消费 astream_events(),并快速接到聊天界面 |
| AutoGen | useAutoGenChatSession() / createAutoGenAdapter() | 想消费官方 run_stream() 事件,并快速接到聊天界面 |
| CrewAI | useCrewAIChatSession() / createCrewAIAdapter() | 想消费官方 SSE chunk 和最终 CrewOutput |
它们都遵循同一个原则:
如果你做的是标准聊天页面,优先用每个框架自己的 use*ChatSession()。 useAgentChat() 更适合你在项目里继续包一层统一抽象,或者接你自己的 framework driver。
MarkdownRenderer。defineEventProtocol() 接入。builtinComponents、renderers 和 messageShells。Agentdown 从一开始就假设你会遇到这些问题:
所以它默认提供:
textSlabCharsvirtualizegroupWindowlazyMount@telemetry详情见 性能优化。
import {
// `cmd` is the helper layer used by protocols to create runtime commands.
cmd,
// Runtime stores nodes, blocks, intents, and history.
createAgentRuntime,
// Bridge wires protocol mapping and stream assembly together.
createBridge,
// Markdown assembler stabilizes streaming markdown blocks.
createMarkdownAssembler,
// The lightest way to map event-based packets.
defineEventProtocol
} from 'agentdown';
type Packet =
| { event: 'RunContent'; text: string }
| { event: 'RunCompleted' }
| { event: 'ToolCall'; id: string; name: string }
| { event: 'ToolCompleted'; id: string; name: string; content: Record<string, unknown> };
// Create one runtime for the whole conversation/session.
const runtime = createAgentRuntime();
// Map backend packets into runtime commands.
const protocol = defineEventProtocol<Packet>({
// Stream assistant text into a markdown block.
RunContent: (event) => [
cmd.content.open({
streamId: 'stream:assistant',
slot: 'main'
}),
cmd.content.append('stream:assistant', event.text)
],
// Close the stream when this answer is done.
RunCompleted: () => cmd.content.close('stream:assistant'),
// Insert a tool block when the tool starts.
ToolCall: (event, context) =>
cmd.tool.start({
id: event.id,
title: event.name,
at: context.now()
}),
// Update that same tool block with the final payload.
ToolCompleted: (event, context) =>
cmd.tool.finish({
id: event.id,
title: event.name,
result: event.content,
at: context.now()
})
});
// Bridge is where protocol mapping and stream assembly actually run.
const bridge = createBridge({
runtime,
protocol,
assemblers: {
markdown: createMarkdownAssembler()
}
});