DeepAgents.js教程03——全部输出与流式输出

2 阅读5分钟

deepagents.js教程03——全部输出与流式输出

在实际使用中,不同场景对输出形式有不同要求:短任务可以等待完整结果,长对话、复杂任务则需要实时反馈。本节我们详细讲解 Deep Agents 的两种输出模式,以及 streamstreamEvents 两个流式 API 的区别与用法。


一、方式一:invoke 全部输出(一次性返回)

invoke 是前两节我们使用的基础调用方式,也是最简单的输出模式。

1. 核心特点

  • 阻塞式调用:发起请求后,等待 智能体 完全执行结束才返回结果
  • 一次性返回:最终返回完整的对话状态与 AI 最终回答
  • 无中间过程:无法获取 AI 生成进度、工具调用状态等中间信息

2. 适用场景

  • 短平快的简单问答任务
  • 后台自动化脚本、不需要展示中间过程的场景
  • 批量任务、离线处理

3. 代码示例

沿用我们上一节的基础写法,环境与依赖完全复用:

import "dotenv/config";
import { createDeepAgent } from "deepagents";
import { model } from "./00-model.js";

const agent = createDeepAgent({
  model: model,
  systemPrompt: "你是一个简洁友好的中文助手。",
});

async function runInvoke() {
  const userInput = "写一首关于夏天的短诗";
  console.log("用户:", userInput);

  // invoke 一次性返回完整结果
  const result = await agent.invoke({
    messages: [{ role: "user", content: userInput }],
  });

  // 取最后一条消息即为 AI 最终回答
  const answer = result.messages.at(-1).content;
  console.log("\nAI 最终回答:\n", answer);
}

runInvoke().catch(console.error);

4. 优缺点

  • ✅ 优点:用法极简,结果完整,适合脚本与后台任务
  • ❌ 缺点:长任务时用户需要全程等待,无任何反馈,交互体验差

二、方式二:流式输出(实时反馈)

流式输出可以让智能体边生成边返回内容,类似常见的打字机效果,大幅提升长文本、多步任务的交互体验。

Deep Agents 提供了两个不同粒度的流式 API:

  • stream():轻量流式,仅返回最终文本内容块
  • streamEvents():事件流式,可订阅消息、工具调用、子代理等全流程事件

2.1 stream () 轻量流式

stream() 是最简单的流式方法,按块返回 AI 生成的最终文本内容,适合只需要展示对话内容的基础聊天场景。

核心特点
  • 仅返回最终回答的文本片段,不包含工具调用、思考过程等中间信息
  • 用法简单,几行代码即可实现打字机效果
  • 输出粒度为文本块,而非单个字符
适用场景
  • 基础聊天界面、对话机器人
  • 只需要展示最终回答,不需要监控执行过程的场景
代码示例
import "dotenv/config";
import { createDeepAgent } from "deepagents";
import { model } from "./00-model.js";

const agent = createDeepAgent({
  model: model,
  systemPrompt: "你是一个简洁友好的中文助手。",
});

async function runStream() {
  const userInput = "写一首关于夏天的短诗";
  console.log("用户:", userInput);
  console.log("AI:");

  // 调用 stream 方法获取流式输出
  const stream = await agent.stream({
    messages: [{ role: "user", content: userInput }],
  });

  // 遍历流式块,逐段打印实现打字机效果
  for await (const chunk of stream) {
    const text = chunk.messages?.at(-1)?.content || "";
    if (text) process.stdout.write(text);
  }
  console.log("\n");
}

runStream().catch(console.error);

2.2 streamEvents () 事件流式

streamEvents() 是功能更强大的流式 API,它以事件流的形式运行,支持订阅多种「投影」,可以细粒度监控智能体的完整执行过程。

核心特点
  • 多投影订阅:可同时监听消息、工具调用、子代理等多种事件
  • 细粒度控制:可以拿到工具调用的参数、状态、结果,子代理的执行进度等
  • 结构化事件:每个事件都有明确的类型与状态,便于前端渲染与调试
核心投影(Projections)
投影说明
stream.messagesAI 生成的文本消息片段,对应聊天回复内容
stream.toolCalls工具调用全流程:入参、执行状态、返回结果 / 错误
stream.subagents子代理的委派、执行与返回,支持嵌套监听
适用场景
  • 完整的智能体交互界面(需要展示工具调用、思考过程)
  • 调试与排查问题
  • 复杂多步任务、带子代理的任务
  • 需要实时展示执行进度的场景
代码示例

结合「消息流式打印 + 工具调用监控」的完整示例:

import "dotenv/config";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import * as z from "zod";
import { model } from "./00-model.js";

// 定义一个简单的天气工具,用于演示工具调用事件
const getWeather = tool(
  ({ city }) => `${city} 今日晴,气温 22-28℃,微风。`,
  {
    name: "get_weather",
    description: "查询指定城市的实时天气",
    schema: z.object({
      city: z.string().describe("城市名称"),
    }),
  }
);

const agent = createDeepAgent({
  model: model,
  systemPrompt: "你是一个简洁友好的中文助手。",
  tools: [getWeather],
});

async function runStreamEvents() {
  const userInput = "北京今天天气怎么样?";
  console.log("用户:", userInput);
  console.log("AI:");

  // 调用 streamEvents,指定版本 v3
  const stream = await agent.streamEvents(
    { messages: [{ role: "user", content: userInput }] },
    { version: "v3" }
  );

  // 并行订阅多个投影
  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        if (message.reasoning) {
          process.stdout.write("\n💭 思考: ");
          for await (const thinkToken of message.reasoning) {
            if (thinkToken) process.stdout.write(thinkToken);
          }
        }
        if (message.text) {
          process.stdout.write("\n🤖 回答: ");
          for await (const textToken of message.text) {
            if (textToken) {
              process.stdout.write(textToken);
              lastText += textToken;
            }
          }
        }
      }
    })(),
    (async () => {
      for await (const call of stream.toolCalls) {
        console.log(`\n🔧 [工具调用] ${call.name}(${JSON.stringify(call.input)})`);
        const status = await call.status;
        if (status === "finished") {
          console.log(`\n📦 [工具结果] ${await call.output}`);
        } else if (status === "error") {
          console.error(`\n❌ [工具错误] ${await call.error}`);
        }
      }
    })(),
  ]);
  console.log("\n");
}

runStreamEvents().catch(console.error);

注意:stream.messages 中每条消息的 text 属性是一个 Promise,需要 await 后才能拿到文本内容。


三、stream 与 streamEvents 核心区别

对比维度stream()streamEvents()
输出内容仅最终文本消息片段消息、工具调用、子代理等全流程事件
信息粒度粗粒度,只关注最终输出细粒度,可监控每一步执行过程
用法复杂度简单,直接遍历即可稍复杂,需要订阅对应投影
工具调用信息不返回完整返回参数、状态、结果、错误
子代理信息不返回支持嵌套监听子代理执行
适用场景基础聊天、纯文本生成复杂智能体、调试、带工具的交互界面

四、选型建议

  1. 入门、简单对话场景:优先用 invoke,逻辑最简单,心智成本最低
  2. 聊天界面、只需要打字机效果:用 stream() 足够,轻量简洁
  3. 带工具调用、子代理的复杂 智能体:用 streamEvents(),完整掌控执行过程
  4. 调试、排查问题:必用 streamEvents(),可清晰看到每一步执行细节

将代码保存为 03-output-modes.js,在终端执行 node 03-output-modes.js 即可运行测试。