useStream React Hook 内置了对深度智能体流式传输的支持。它能自动跟踪子智能体的生命周期,将子智能体消息与主对话分离,并提供丰富的 API 用于构建多智能体界面。
深度智能体的主要特性:
子智能体跟踪 — 自动管理每个子智能体的生命周期(待定、运行中、已完成、错误)
消息过滤 — 将子智能体消息与主对话流分离
工具调用可见性 — 访问子智能体执行过程中的工具调用和结果
状态重建 — 页面重新加载时从线程历史中恢复子智能体状态
在您的 React 应用中安装 LangGraph SDK 以使用 useStream Hook:
基本用法
要从带有子智能体的深度智能体进行流式传输,请为 useStream 配置 filterSubagentMessages,并在提交时传入 streamSubgraphs: true:
import { useStream } from "@langchain/langgraph-sdk/react" ;
import type { agent } from "./agent" ;
function DeepAgentChat () {
const stream = useStream < typeof agent > ( {
assistantId : "deep-agent" ,
apiUrl : "http://localhost:2024" ,
filterSubagentMessages : true , // Keep subagent messages separate
} ) ;
const handleSubmit = ( message : string ) => {
stream . submit (
{ messages : [ { content : message , type : "human" } ] },
{ streamSubgraphs : true } // Enable subagent streaming
) ;
};
return (
< div >
{ /* Main conversation messages (subagent messages filtered out) */ }
{ stream . messages . map ( ( message , idx ) => (
< div key = { message . id ?? idx } >
{ message . type } : { message . content }
</ div >
)) }
{ /* Subagent progress */ }
{ stream . activeSubagents . length > 0 && (
< div >
< h3 > Active subagents: </ h3 >
{ stream . activeSubagents . map ( ( subagent ) => (
< SubagentCard key = { subagent . id } subagent = { subagent } />
)) }
</ div >
) }
{ stream . isLoading && < div > Loading... </ div > }
</ div >
) ;
}
除了标准 useStream 参数 之外,深度智能体流式传输还支持: 为 true 时,子智能体消息将从主 stream.messages 数组中排除。请改为通过 stream.subagents.get(id).messages 访问子智能体消息,以保持主对话的整洁。
subagentToolNames
string[]
default: "['task']"
派生子智能体的工具名称。默认情况下,深度智能体使用 task 工具将工作委托给子智能体。仅当您自定义了工具名称时才需要修改此项。
除了标准返回值 之外,深度智能体流式传输还提供: subagents
Map<string, SubagentStream>
所有子智能体的映射,以工具调用 ID 为键。每个子智能体包含其消息、状态、工具调用和结果。
当前正在运行的子智能体数组(状态为 "pending" 或 "running")。
getSubagent
(toolCallId: string) => SubagentStream | undefined
通过工具调用 ID 获取特定子智能体。
getSubagentsByMessage
(messageId: string) => SubagentStream[]
获取由特定 AI 消息触发的所有子智能体。适用于将子智能体与触发它们的消息关联起来。
getSubagentsByType
(type: string) => SubagentStream[]
按 subagent_type(例如 "researcher"、"writer")筛选子智能体。
子智能体流接口
stream.subagents 映射中的每个子智能体都暴露了类似流的接口:
interface SubagentStream {
// Identity
id : string ; // Tool call ID
toolCall : { // Original task tool call
subagent_type : string ;
description : string ;
};
// Lifecycle
status : " pending " | " running " | " complete " | " error " ;
startedAt : Date | null ;
completedAt : Date | null ;
isLoading : boolean ;
// Content
messages : Message[] ; // Subagent's messages
values : Record < string , any > ; // Subagent's state
result : string | null ; // Final result
error : string | null ; // Error message
// Tool calls
toolCalls : ToolCallWithResult[] ;
getToolCalls : ( message : Message ) => ToolCallWithResult[] ;
// Hierarchy
depth : number ; // Nesting depth (0 for top-level subagents)
parentId : string | null ; // Parent subagent ID (for nested subagents)
}
渲染子智能体流
子智能体卡片
构建展示每个子智能体流式内容、状态和进度的卡片:
import { AIMessage } from "langchain" ;
import { useStream , type SubagentStream } from "@langchain/langgraph-sdk/react" ;
import type { Message } from "@langchain/langgraph-sdk" ;
import type { agent } from "./agent" ;
function SubagentCard ({ subagent } : { subagent : SubagentStream < typeof agent > }) {
const content = getStreamingContent (subagent . messages) ;
return (
< div className = "border rounded-lg p-4" >
{ /* Header */ }
< div className = "flex items-center gap-2 mb-2" >
< StatusIcon status = { subagent . status } />
< span className = "font-medium" > { subagent . toolCall . subagent_type } </ span >
< span className = "text-sm text-gray-500" >
{ subagent . toolCall . description }
</ span >
</ div >
{ /* Streaming content */ }
{ content && (
< div className = "prose text-sm mt-2" >
{ content }
</ div >
) }
{ /* Result */ }
{ subagent . status === "complete" && subagent . result && (
< div className = "mt-2 p-2 bg-green-50 rounded text-sm" >
{ subagent . result }
</ div >
) }
{ /* Error */ }
{ subagent . status === "error" && subagent . error && (
< div className = "mt-2 p-2 bg-red-50 rounded text-sm text-red-700" >
{ subagent . error }
</ div >
) }
</ div >
) ;
}
function StatusIcon ({ status } : { status : string }) {
switch (status) {
case "pending" :
return < span className = "text-gray-400" > ⏳ </ span > ;
case "running" :
return < span className = "animate-spin" > ⚙️ </ span > ;
case "complete" :
return < span className = "text-green-500" > ✓ </ span > ;
case "error" :
return < span className = "text-red-500" > ✗ </ span > ;
default :
return null ;
}
}
/** Extract text content from subagent messages */
function getStreamingContent ( messages : Message[] ) : string {
return messages
. filter ( ( m ) => m . type === "ai" )
. map ( ( m ) => {
if ( typeof m . content === "string" ) return m . content ;
if (Array . isArray (m . content)) {
return m . content
. filter ( ( c ) : c is { type : "text" ; text: string } =>
c . type === "text" && "text" in c
)
. map ( ( c ) => c . text)
. join ( "" ) ;
}
return "" ;
} )
. join ( "" ) ;
}
将子智能体映射到消息
使用 getSubagentsByMessage 将子智能体卡片与触发它们的 AI 消息关联:
import { useMemo } from "react" ;
import { useStream } from "@langchain/langgraph-sdk/react" ;
import type { agent } from "./agent" ;
function DeepAgentChat () {
const stream = useStream < typeof agent > ( {
assistantId : "deep-agent" ,
apiUrl : "http://localhost:2024" ,
filterSubagentMessages : true ,
} ) ;
// Map subagents to the human message that triggered them
const subagentsByMessage = useMemo ( () => {
const result = new Map () ;
const messages = stream . messages ;
for ( let i = 0 ; i < messages . length ; i ++ ) {
if (messages[i] . type !== "human" ) continue ;
// The next message should be the AI message with task tool calls
const next = messages[i + 1 ] ;
if ( ! next || next . type !== "ai" || ! next . id) continue ;
const subagents = stream . getSubagentsByMessage (next . id) ;
if (subagents . length > 0 ) {
result . set (messages[i] . id , subagents) ;
}
}
return result ;
}, [stream . messages , stream . subagents]) ;
return (
< div >
{ stream . messages . map ( ( message , idx ) => (
< div key = { message . id ?? idx } >
< MessageBubble message = { message } />
{ /* Show subagent pipeline after the human message that triggered it */ }
{ message . type === "human" && subagentsByMessage . has (message . id) && (
< SubagentPipeline
subagents = { subagentsByMessage . get (message . id) ! }
isLoading = { stream . isLoading }
/>
) }
</ div >
)) }
</ div >
) ;
}
带进度条的子智能体流水线
展示进度条和子智能体卡片网格:
function SubagentPipeline ({
subagents ,
isLoading ,
} : {
subagents : SubagentStream[] ;
isLoading : boolean ;
}) {
const completed = subagents . filter (
( s ) => s . status === "complete" || s . status === "error"
) . length ;
const allDone = completed === subagents . length ;
return (
< div className = "my-4 space-y-3" >
{ /* Progress header */ }
< div className = "flex items-center justify-between text-sm" >
< span className = "font-medium" >
Subagents ( { completed } / { subagents . length } )
</ span >
{ allDone && isLoading && (
< span className = "text-blue-500 animate-pulse" >
Synthesizing results...
</ span >
) }
</ div >
{ /* Progress bar */ }
< div className = "h-1.5 bg-gray-200 rounded-full overflow-hidden" >
< div
className = "h-full bg-blue-500 transition-all duration-300"
style = {{ width : ` ${ ( completed / subagents . length) * 100 } %` }}
/>
</ div >
{ /* Subagent cards */ }
< div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3" >
{ subagents . map ( ( subagent ) => (
< SubagentCard key = { subagent . id } subagent = { subagent } />
)) }
</ div >
</ div >
) ;
}
渲染工具调用
使用 toolCalls 属性展示子智能体执行过程中的工具调用和结果:
function SubagentWithTools ({ subagent } : { subagent : SubagentStream }) {
return (
< div className = "border rounded-lg p-4" >
< div className = "flex items-center gap-2 mb-3" >
< StatusIcon status = { subagent . status } />
< span className = "font-medium" > { subagent . toolCall . subagent_type } </ span >
{ subagent . toolCalls . length > 0 && (
< span className = "text-xs bg-gray-100 px-2 py-0.5 rounded-full" >
{ subagent . toolCalls . length } tool calls
</ span >
) }
</ div >
{ /* Tool calls */ }
{ subagent . toolCalls . map ( ( tc ) => (
< div key = { tc . call . id } className = "mb-2 p-2 bg-gray-50 rounded text-sm" >
< div className = "flex items-center gap-2" >
< span className = "font-mono text-xs" > { tc . call . name } </ span >
{ tc . result !== undefined ? (
< span className = "text-green-600 text-xs" > completed </ span >
) : (
< span className = "text-yellow-600 text-xs animate-pulse" >
running...
</ span >
) }
</ div >
{ /* Tool arguments */ }
< pre className = "text-xs text-gray-600 mt-1 overflow-x-auto" >
{ JSON . stringify (tc . call . args , null , 2 ) }
</ pre >
{ /* Tool result */ }
{ tc . result !== undefined && (
< div className = "mt-1 pt-1 border-t text-xs" >
{ typeof tc . result === "string"
? tc . result . slice ( 0 , 200 )
: JSON . stringify (tc . result , null , 2 ) }
</ div >
) }
</ div >
)) }
{ /* Streaming content */ }
< div className = "mt-2 prose text-sm" >
{ getStreamingContent (subagent . messages) }
</ div >
</ div >
) ;
}
线程持久化
跨页面重新加载持久化线程 ID,以便用户可以继续之前的深度智能体对话:
import { useCallback , useState , useEffect } from "react" ;
import { useStream } from "@langchain/langgraph-sdk/react" ;
import type { agent } from "./agent" ;
function useThreadIdParam () {
const [ threadId , setThreadId ] = useState < string | null > ( () => {
const params = new URLSearchParams (window . location . search) ;
return params . get ( "threadId" ) ;
} ) ;
const updateThreadId = useCallback ( ( id : string ) => {
setThreadId (id) ;
const url = new URL (window . location . href) ;
url . searchParams . set ( "threadId" , id) ;
window . history . replaceState ( {}, "" , url . toString ()) ;
}, []) ;
return [threadId , updateThreadId] as const ;
}
function PersistentDeepAgentChat () {
const [ threadId , onThreadId ] = useThreadIdParam () ;
const stream = useStream < typeof agent > ( {
assistantId : "deep-agent" ,
apiUrl : "http://localhost:2024" ,
filterSubagentMessages : true ,
threadId ,
onThreadId ,
reconnectOnMount : true , // Auto-resume stream after page reload
} ) ;
return (
< div >
{ stream . messages . map ( ( message , idx ) => (
< div key = { message . id ?? idx } >
{ message . type } : { message . content }
</ div >
)) }
{ /* Subagents are reconstructed from thread history on reload */ }
{ [ ... stream . subagents . values ()] . map ( ( subagent ) => (
< SubagentCard key = { subagent . id } subagent = { subagent } />
)) }
</ div >
) ;
}
页面重新加载时,useStream 会从线程历史中重建子智能体状态。已完成的子智能体将以最终状态和结果恢复,用户可查看包含子智能体工作的完整对话历史。
类型安全
Python 类型安全方面,访问流状态时请使用类型注解:
from deepagents import create_deep_agent
agent = create_deep_agent (
system_prompt = "You are a helpful assistant" ,
subagents = [ ... ],
)
# Type hints are inferred from the agent definition
result = agent . invoke (
{ "messages" : [{ "role" : "user" , "content" : "Hello" }]}
)
# result["messages"] is typed as list of messages
完整示例
有关结合以上所有模式的完整可运行实现,请参阅 LangGraph.js 代码库中的以下示例:
深度智能体示例 具有网格布局、流式内容、进度跟踪和综合检测功能的并行子智能体示例。
带工具调用的深度智能体 工具调用可见性、线程持久化、可展开的子智能体卡片以及页面重新加载后的自动重连。
相关资源
通过 MCP 将这些文档连接 到 Claude、VSCode 等工具,获取实时答案。