ai agent ---output汇总

一.输出方式介绍

大模型的输出有2种处理方式,1.output parser 2.tool

Output Parser(输出解析器)

Tool(工具/函数调用)

它们是两个不同层级的东西,但经常一起出现在 Agent 的流程中

简单来说:

  • Output Parser = 「模型吐出文本 → 我把它解析成对象」
  • Tool = 「模型说要用某个工具 → 我去执行那个工具 → 把结果喂回模型」

你说的应该是 AI Agent 里的 Output Parser(输出解析器) ​ 和 Tool(工具/函数调用) 。它们是两个不同层级的东西,但经常一起出现在 Agent 的流程中。下面帮你快速理清。


一、核心区别:Parser vs Tool

维度 Output Parser(输出解析器) Tool / Function Calling(工具调用)
本质 把 LLM 返回的原始文本解析成程序可用的结构化数据(dict / Pydantic / 等) LLM 决定调用哪个外部函数,由你的代码执行该函数
谁在执行 你的应用代码对模型输出字符串做解析 你的应用代码执行被调用的函数/API
典型用途 从自然语言中提取结构化信息(如抽取用户画像 JSON) 让 Agent 查天气、搜数据库、调 API、算数等
模型参与方式 模型只负责生成文本,Parser 是后处理 模型生成工具名+参数(tool_call),框架解析并执行
LangChain 对应 JsonOutputParser / PydanticOutputParser @tool 装饰器 / with_structured_output() / Tool Calling

简单来说:

  • Output Parser = 「模型吐出文本 → 我把它解析成对象」
  • Tool = 「模型说要用某个工具 → 我去执行那个工具 → 把结果喂回模型」

二、它们在 Agent 中的关系

在一个典型的 ReAct Agent 循环里,两者是上下游协作的关系:

sql 复制代码
用户提问
  ↓
LLM 思考 → 生成 Tool Call(工具名 + 参数)
  ↓
你的代码解析 Tool Call → 执行对应 Tool 函数
  ↓
Tool 返回原始结果(可能是 JSON / HTML / 字符串)
  ↓
【可选】在 Tool 内部对原始结果做格式化/清洗
  ↓
把结果作为 Observation 送回 LLM
  ↓
LLM 生成最终回答
  • Tool Calling 负责「模型决定做什么动作」
  • Output Parser(或在 Tool 内部对原始结果做格式化/清洗)负责「把模型输出或工具输出变成干净的结构化数据」

三、什么时候选择用JsonOutputParser,StructuredOutputParser,XMLOutputParser什么时候用withStructuredOutput

方式 底层机制 格式约束能力 模型要求 什么时候选它
JsonOutputParser Prompt 引导 + 后解析 ⭐⭐ 弱(靠模型自觉) 任何模型 简单 JSON,模型能力强,快速原型
StructuredOutputParser Prompt 引导 + 后解析 ⭐⭐⭐ 中(带格式说明) 任何模型 需要自定义字段描述、复杂 schema
XMLOutputParser Prompt 引导 + 后解析 ⭐⭐ 弱(XML 嵌套易错) 任何模型 几乎不推荐,除非模型对 XML 特别友好
with_structured_output() 原生 Tool/Function Calling ⭐⭐⭐⭐⭐ 强(模型原生保证) 支持 function calling 的模型 首选,生产环境、复杂 schema

四、总结:

能走 with_structured_output() 就走它(它底层就是 Tool Calling,最稳)。

走不了才用 JsonOutputParser 兜底

StructuredOutputParserXMLOutputParser 基本是历史遗留,新项目不用特意学。

二.案例

1.使用JsonOutputParser

js 复制代码
import "dotenv/config";
import {ChatOpenAI} from "@langchain/openai";
import {JsonOutputParser} from "@langchain/core/output_parsers";
import {HumanMessage} from '@langchain/core/messages';


const model= new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
});

const parser = new JsonOutputParser();
const question = `请你介绍一下爱因斯坦的信息,以json的格式返回,具体包含的字段如下: 
name(姓名),birth(出生日期),death(逝世日期),nationality(国籍),education(教育背景)famous_theory(著名理论) 
${parser.getFormatInstructions()}
`; 

//response.content是一个markdown格式的字符串,包含了爱因斯坦的信息。我们希望将这些信息转换成json格式,以便于后续处理。为此,我们可以使用parser.parse()方法进行转换。
const response = await model.invoke([new HumanMessage(question)]);
console.log("格式化前"+response.content);

//使用parser的目的是为了将response.content转换成json格式,这样就可以直接使用result变量来访问解析后的数据了。
const result = await parser.parse(response.content);
console.log("格式化后"+JSON.stringify(result, null, 2));

JsonOutputParser把带有md格式的字符串,转化成了我们想要的json形式。这个json自动使用prompt提示语里面的字段。

2.使用StructuredOutputParser

js 复制代码
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import {StructuredOutputParser} from '@langchain/core/output_parsers';
import {HumanMessage} from '@langchain/core/messages';

const model= new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
});

const parser = StructuredOutputParser.fromNamesAndDescriptions({
  name: '姓名',
  birth: '出生日期',
  death: '逝世日期',
  nationality: '国籍',
  main_work: '主要成就',
});

const question = `请你介绍一下爱因斯坦的信息, ${parser.getFormatInstructions()}`;

const response = await model.invoke([new HumanMessage(question)]);
console.log("格式化前"+response.content);

const output = await parser.parse(response.content);
console.log("格式化后"+JSON.stringify(output, null, 2));

fromNamesAndDescriptions 指定生成格式

3.StructuredOutputParser+zod

js 复制代码
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { StructuredOutputParser } from '@langchain/core/output_parsers';
import { HumanMessage } from '@langchain/core/messages';
import { z } from 'zod';

const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
});

const schema = z.object({
  name: z.string().describe('姓名'),
  birth: z
    .string()
    .describe('出生日期,格式为 YYYY-MM-DD'),
  death: z
    .string()
    .optional()
    .describe('逝世日期,格式为 YYYY-MM-DD,如果仍在世则不要输出此字段'),
  nationality: z.string().describe('国籍'),
  main_work: z
    .array(z.string())
    .describe('主要成就列表,必须是字符串数组,如 ["相对论", "光电效应"]'),
});

const parser = StructuredOutputParser.fromZodSchema(schema);

const question = `请你介绍一下爱因斯坦的信息。\n\n${parser.getFormatInstructions()}`;

const response = await model.invoke([new HumanMessage(question)]);
console.log('格式化前:', response.content);

const output = await parser.parse(response.content);
console.log('格式化后:', JSON.stringify(output, null, 2));

4.model用绑定json格式的tool形式处理结果

js 复制代码
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';
import { z } from 'zod';

const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
});

const schema = z.object({
  name: z.string().describe('姓名'),
  birth: z
    .string()
    .describe('出生日期,格式为 YYYY-MM-DD'),
  death: z
    .string()
    .optional()
    .describe('逝世日期,格式为 YYYY-MM-DD,如果仍在世则不要输出此字段'),
  nationality: z.string().describe('国籍'),
  main_work: z
    .array(z.string())
    .describe('主要成就列表,必须是字符串数组,如 ["相对论", "光电效应"]'),
});

const modelWithTool = model.bindTools([  {
    name: "extract_info",
    description: "从输入的文本中提取信息,并返回一个 JSON 对象。",
    schema: schema,
  }]
);


const question = `请你介绍一下爱因斯坦的信息。`;

const response = await modelWithTool.invoke([new HumanMessage(question)]);

console.log('格式化前:', response.tool_calls[0].args);
console.log("格式化以后",JSON.stringify(response.tool_calls[0].args, null, 2))

5.withStructuredOutput

js 复制代码
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { z } from 'zod';

// 初始化模型,增加 timeout 配置 (例如设置为 60秒/60000毫秒)
const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
  timeout: 60000, // ✅ 关键修复:将超时时间延长至 60 秒
  maxRetries: 3,  // ✅ 可选:开启内部自动重试次数
});

// 修复 Zod Schema
const schema = z.object({
  name: z.string().describe('人物的中文名称,必须返回"爱因斯坦"'),
  birth: z
    .string()
    .describe('出生日期,格式为 YYYY-MM-DD'),
  death: z
    .string()
    .nullable()
    .describe('逝世日期,格式为 YYYY-MM-DD,如果仍在世则输出 null'),
  nationality: z.string().describe('国籍'),
  main_work: z
    .array(z.string())
    .nullable()
    .describe('主要成就列表,必须是字符串数组,如 ["相对论", "光电效应"]'),
});

const structuredOutputParser = model.withStructuredOutput(schema);
const question = `请你介绍一下爱因斯坦的信息,请用中文回复。`;

  try {
    const response = await structuredOutputParser.invoke(question);
    console.log('格式化以后', JSON.stringify(response, null, 2));

  } catch (error) {
    console.error('发生未知错误:', error);
  }

6.stream --流式输出

js 复制代码
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';

const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
  timeout: 60000,
  maxRetries: 3,
});

const question = `请你介绍一下爱因斯坦的信息`;


  const stream = await model.stream(question);  // ✅ 注意变量名 steam(避免和 chunk 混)
  let full = '';

  for await (const chunk of stream) {
    const content = chunk.content;  // ✅ LangChain chunk 用 .content,不是 .text
    if (typeof content === 'string') {
      full += content;
      process.stdout.write(content);
    }
  }

  console.log('\n===== 完整内容 =====');
  console.log(full);

7.stream 流式输出+withStructuredOutput格式化流式输出

js 复制代码
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { z } from 'zod';

const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL,
  },
  timeout: 60000,
  maxRetries: 3,
});

// 1. 定义 Schema(建议字段全用 string,避免数字解析失败)
const schema = z.object({
  name: z.string().describe('姓名'),
  birth_year: z.string().describe('出生年份'),
  death_year: z.string().nullable().describe('去世年份,在世则输出 null'),
  nationality: z.string().describe('国籍'),
  famous_works: z.array(z.string()).describe('著名作品或成就列表'),
});

// 2. 普通模型用于流式输出(不强制 JSON 模式,体验流畅不报错)
const baseModel = model;
// 3. 结构化模型用于最终提取(只走一次性调用 invoke)
const structuredModel = model.withStructuredOutput(schema);

const prompt = `请你详细介绍爱因斯坦的信息。`;

async function runStableStreamAndExtract() {
  // ========== 第一阶段:流式输出给用户看 ==========
  const stream = await baseModel.stream(prompt);
  let fullText = '';

  console.log('🎬 开始流式接收...\n');
  for await (const chunk of stream) {
    const content = chunk.content;
    if (typeof content === 'string') {
      process.stdout.write(content);//实时输出
      fullText += content;
    }
  }
  console.log('\n\n✅ 流式接收完毕,开始结构化提取...\n');

  // ========== 第二阶段:对完整文本进行结构化提取 ==========
  try {
    // 将刚才的完整文本作为上下文,让模型提取结构化数据
    const extractPrompt = `请根据以下文本,提取出结构化的 JSON 信息:\n\n${fullText}。要求:完全按照文件回答问题,不要胡编乱造`;
    const result = await structuredModel.invoke(extractPrompt);
    
    console.log('🎯 最终结构化对象:');
    console.log(JSON.stringify(result, null, 2));
  } catch (e) {
    console.error('❌ 结构化提取失败:', e.message);
  }
}

runStableStreamAndExtract();

process.stdout.write(content);//实时输出

await baseModel.stream(prompt);//流式输出

model.withStructuredOutput(schema);--json格式化

const result = await structuredModel.invoke(extractPrompt);---结构化

此时的流式输出里面你可以使用jsonoutputparser,structuredOutputParser,也可以用tool,都可以达到格式化数据的效果。

相关推荐
4311媒体网1 小时前
帝国CMS网站自定义搭建技巧
服务器·开发语言·前端
JunjunZ1 小时前
Vue 3 文件预览的通用设计与实现
前端·vue.js
CappuccinoRose1 小时前
ES6+ 核心语法糖
前端·es6·解构·可选链·空值合并
两只羊ovo1 小时前
Promise.all 并发请求实战:从串行 await 到双段并行
前端
七牛开发者1 小时前
解读 Cordis:dsh 一切皆插件背后的运行时设计
前端·javascript·后端
BigTopOne1 小时前
ArLiveLite 学习计划
前端
慧都小项2 小时前
夜间Web回归跑到一半停了?TestComplete 15.83提升批量测试稳定性
前端·测试工具·数据挖掘·回归·汽车·web
两只羊ovo2 小时前
Agent ≠ 聊天机器人:从一问一答到 ReAct 循环,到底差在哪?
前端
摇曳的精灵2 小时前
前端缓存优化
前端·缓存·缓存优化