Documentation Index
Fetch the complete documentation index at: https://nvd-54.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
当协调器智能体生成专业子智能体(研究员、分析师、撰写者)时,您需要将协调器的消息与每个子智能体的流式输出分开渲染。在 useStream 中设置 filterSubagentMessages: true 可以干净地分离这两个流,然后使用 getSubagentsByMessage 将每个子智能体的进度卡片附加到触发它的协调器消息上。
为什么要过滤子智能体消息
不过滤时,每个子智能体产生的每个 Token 都会交错出现在协调器的消息流中,使其难以阅读。设置 filterSubagentMessages: true 后:
stream.messages 仅包含协调器的消息
- 每个子智能体的内容可通过
stream.subagents 和 stream.getSubagentsByMessage 访问
- UI 保持整洁:协调器的推理与专家的工作分开
这种分离让您可以在一个地方渲染协调器的消息,并将每个子智能体的进度卡片精确地附加到生成它的协调器消息下方。
设置 useStream
始终设置 filterSubagentMessages: true。这会从主消息流中移除子智能体的 Token,以便您可以独立渲染协调器的消息和子智能体的输出。
导入您的智能体并将 typeof myAgent 作为类型参数传递给 useStream,以获得对状态值的类型安全访问:
import type { myAgent } from "./agent";
import { useStream } from "@langchain/react";
const AGENT_URL = "http://localhost:2024";
export function DeepAgentChat() {
const stream = useStream<typeof myAgent>({
apiUrl: AGENT_URL,
assistantId: "deep_agent_subagent_cards",
filterSubagentMessages: true,
});
return (
<div>
{stream.messages.map((msg) => (
<MessageWithSubagents
key={msg.id}
message={msg}
subagents={stream.getSubagentsByMessage(msg.id)}
/>
))}
</div>
);
}
启用子图流式输出提交
提交消息时,启用子图流式输出并设置适当的递归限制。深度智能体工作流通常涉及多层嵌套子图,因此较高的递归限制可防止过早终止:
stream.submit(
{ messages: [{ type: "human", content: text }] },
{ streamSubgraphs: true }
);
深度智能体设置了 10,000 的默认递归限制,对大多数多专家设置来说已足够。如需要可通过 config.recursion_limit 覆盖。
SubagentStreamInterface
每个子智能体暴露一个 SubagentStreamInterface,包含关于子智能体任务、状态和计时的元数据:
interface SubagentStreamInterface {
id: string;
status: "pending" | "running" | "complete" | "error";
messages: BaseMessage[];
result: string | undefined;
toolCall: {
id: string;
name: string;
args: {
description: string;
subagent_type: string;
[key: string]: unknown;
};
};
startedAt: number | undefined;
completedAt: number | undefined;
}
| 属性 | 描述 |
|---|
id | 此子智能体实例的唯一标识符 |
status | 生命周期状态:pending → running → complete 或 error |
messages | 子智能体自己的消息流,实时更新 |
result | 最终输出文本,仅在 status 为 "complete" 时可用 |
toolCall | 生成此子智能体的工具调用,包括任务元数据 |
toolCall.args.description | 协调器分配给此子智能体的任务描述 |
toolCall.args.subagent_type | 专家的类型或名称(例如 "researcher"、"analyst") |
startedAt | 子智能体开始执行的时间戳 |
completedAt | 子智能体完成的时间戳 |
将子智能体链接到消息
getSubagentsByMessage 方法返回由特定 AI 消息生成的子智能体。这让您可以直接在触发它们的协调器消息下方渲染子智能体卡片:
const turnSubagents = stream.getSubagentsByMessage(msg.id);
这返回一个 SubagentStreamInterface 对象数组。如果消息没有生成任何子智能体,则返回空数组。
构建 SubagentCard
每个子智能体卡片显示专家名称、任务描述、流式输出内容或最终结果,以及计时信息:
import { AIMessage } from "@langchain/core/messages";
function SubagentCard({
subagent,
}: {
subagent: SubagentStreamInterface;
}) {
const [expanded, setExpanded] = useState(true);
const title =
subagent.toolCall?.args?.subagent_type ?? `Agent ${subagent.id}`;
const description = subagent.toolCall?.args?.description ?? "";
const lastAIMessage = subagent.messages
.filter(AIMessage.isInstance)
.at(-1);
const displayContent =
subagent.status === "complete"
? subagent.result
: typeof lastAIMessage?.content === "string"
? lastAIMessage.content
: "";
const elapsed = getElapsedTime(subagent.startedAt, subagent.completedAt);
return (
<div className="rounded-lg border bg-white shadow-sm">
<button
onClick={() => setExpanded(!expanded)}
className="flex w-full items-center justify-between p-4"
>
<div className="flex items-center gap-3">
<StatusIcon status={subagent.status} />
<div>
<h4 className="font-semibold capitalize">{title}</h4>
<p className="text-xs text-gray-500">{description}</p>
</div>
</div>
<div className="flex items-center gap-2">
{elapsed && (
<span className="text-xs text-gray-400">{elapsed}</span>
)}
<StatusBadge status={subagent.status} />
</div>
</button>
{expanded && displayContent && (
<div className="border-t px-4 py-3">
<div className="prose prose-sm max-w-none line-clamp-6">
{displayContent}
{subagent.status === "running" && (
<span className="inline-block h-4 w-1 animate-pulse bg-blue-500" />
)}
</div>
</div>
)}
</div>
);
}
function getElapsedTime(
startedAt: number | undefined,
completedAt: number | undefined
): string | null {
if (!startedAt) return null;
const end = completedAt ?? Date.now();
const seconds = Math.round((end - startedAt) / 1000);
if (seconds < 60) return `${seconds}s`;
return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
}
状态图标和徽章
一致的视觉指示器帮助用户快速解析子智能体状态:
function StatusIcon({ status }: { status: SubagentStreamInterface["status"] }) {
switch (status) {
case "pending":
return <span className="text-gray-400">○</span>;
case "running":
return <span className="animate-spin text-blue-500">◉</span>;
case "complete":
return <span className="text-green-500">✓</span>;
case "error":
return <span className="text-red-500">✕</span>;
}
}
function StatusBadge({ status }: { status: SubagentStreamInterface["status"] }) {
const styles = {
pending: "bg-gray-100 text-gray-600",
running: "bg-blue-100 text-blue-700",
complete: "bg-green-100 text-green-700",
error: "bg-red-100 text-red-700",
};
return (
<span className={`rounded-full px-2 py-0.5 text-xs font-medium ${styles[status]}`}>
{status}
</span>
);
}
进度跟踪
显示进度条和计数器,让用户知道有多少子智能体已完成:
function SubagentProgress({
subagents,
}: {
subagents: SubagentStreamInterface[];
}) {
const completed = subagents.filter((s) => s.status === "complete").length;
const total = subagents.length;
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0;
return (
<div className="space-y-1">
<div className="flex items-center justify-between text-xs text-gray-500">
<span>子智能体进度</span>
<span>
{completed}/{total} 已完成
</span>
</div>
<div className="h-2 overflow-hidden rounded-full bg-gray-200">
<div
className="h-full rounded-full bg-blue-500 transition-all duration-300"
style={{ width: `${percentage}%` }}
/>
</div>
</div>
);
}
渲染带子智能体卡片的消息
关键布局模式是渲染每个协调器消息,如果该消息生成了子智能体,则紧接其下方渲染其卡片:
function MessageWithSubagents({
message,
subagents,
}: {
message: BaseMessage;
subagents: SubagentStreamInterface[];
}) {
if (message.type === "human") {
return <HumanMessage content={message.content} />;
}
return (
<div className="space-y-3">
{message.content && (
<div className="prose prose-sm max-w-none">
{message.content}
</div>
)}
{subagents.length > 0 && (
<div className="ml-4 space-y-3 border-l-2 border-blue-200 pl-4">
<SubagentProgress subagents={subagents} />
{subagents.map((subagent) => (
<SubagentCard key={subagent.id} subagent={subagent} />
))}
</div>
)}
</div>
);
}
综合指示器
所有子智能体完成后,协调器需要时间将其结果综合成最终响应。在此阶段显示清晰的指示器:
function SynthesisIndicator({
subagents,
isLoading,
}: {
subagents: SubagentStreamInterface[];
isLoading: boolean;
}) {
const allComplete =
subagents.length > 0 &&
subagents.every((s) => s.status === "complete" || s.status === "error");
if (!allComplete || !isLoading) return null;
return (
<div className="flex items-center gap-2 rounded-lg bg-purple-50 px-4 py-2 text-sm text-purple-700">
<span className="animate-spin">⟳</span>
正在综合 {subagents.length} 个子智能体{subagents.length !== 1 ? "" : ""}的结果...
</div>
);
}
对于复杂的多专家工作流,综合阶段可能需要几秒钟。清晰的”正在综合结果…”指示器可防止用户认为智能体已停滞。
调试未过滤的输出
开发过程中,您可以临时设置 filterSubagentMessages: false 以查看主消息流中所有子智能体的原始交错输出。这对验证子智能体 Token 是否正确流动很有用,但不应在生产 UI 中使用。
使用场景
当您的智能体工作流涉及以下情况时,深度智能体子智能体卡片是正确的选择:
- 深度研究——协调器派遣研究员调查问题的不同方面,然后综合他们的发现
- 多专家分析——领域专家(法律、财务、技术)各自贡献其视角
- 复杂任务分解——规划器将大任务拆分为子任务并分配给专业工作者
- 代码审查流水线——不同的智能体分别处理安全审查、风格检查、性能分析和文档审查
访问完整的子智能体映射
除了按消息查找外,您还可以通过 stream.subagents 一次性访问所有子智能体:
const allSubagents = [...stream.subagents.values()];
const running = allSubagents.filter((s) => s.status === "running");
const completed = allSubagents.filter((s) => s.status === "complete");
const errors = allSubagents.filter((s) => s.status === "error");
这对于构建全局进度指示器或仪表板很有用,可以汇总所有子智能体活动,无论是哪条协调器消息生成了它们。
最佳实践
- 始终设置
filterSubagentMessages: true。未过滤的流会产生协调器和子智能体 Token 的不可读交错。
- 显示任务描述。
toolCall.args.description 字段告诉用户每个子智能体被要求做什么。始终醒目地显示。
- 使用可折叠卡片。在有 5 个以上子智能体的工作流中,自动折叠已完成的卡片,以便用户专注于活跃的工作。
- 显示计时数据。展示每个子智能体花费的时间有助于用户理解性能特征并识别瓶颈。
- 设置适当的递归限制。嵌套子图的深度智能体工作流需要比默认 25 更高的限制。从 100 开始。
- 按子智能体处理错误。一个子智能体失败不应使整个 UI 崩溃。在该子智能体的卡片中显示错误,同时其他智能体继续运行。