Skip to content

大模型默认输出自然语言,但经常需要结构化 JSON。两种方式:Output Parser(在 prompt 加格式说明 + 解析响应)和 Tool Call(绑定工具 schema,模型返回结构化 args)。推荐用 withStructuredOutput(schema),它底层自动选最可靠的方案(大多用 tool call)。两个不适合的场景:流式打印(需要 Output Parser 的 stream + parse)和非 JSON 格式(XML/YAML 用 XMLOutputParser)。Tool Call 的流式可以通过 tool_call_chunks 实现逐字打印。

结构化大模型输出:Output Parser 还是 Tool?

大模型默认输出自然语言,但很多场景需要结构化 JSON。两种方式实现:Output Parser 和 Tool Call。

方式一:Output Parser

原理:在 prompt 里加入格式说明,然后解析响应。

JsonOutputParser

最简单,解析 JSON 格式。

js
import { JsonOutputParser } from '@langchain/core/output_parsers';

const parser = new JsonOutputParser();

// prompt 里加上格式说明
const question = `请介绍爱因斯坦的信息,以 JSON 格式返回。
${parser.getFormatInstructions()}`;

const response = await model.invoke(question);
const result = await parser.parse(response.content);

StructuredOutputParser

可以定义具体字段和描述。

js
import { StructuredOutputParser } from '@langchain/core/output_parsers';

// 方式 1:用字段名和描述
const parser = StructuredOutputParser.fromNamesAndDescriptions({
  name: '姓名',
  birth_year: '出生年份',
  nationality: '国籍',
  famous_theory: '著名理论',
});

// 方式 2:用 Zod schema(支持复杂嵌套结构)
const schema = z.object({
  name: z.string().describe('姓名'),
  birth_year: z.number().describe('出生年份'),
  awards: z.array(z.object({
    name: z.string().describe('奖项名称'),
    year: z.number().describe('获奖年份'),
  })).describe('获得的奖项'),
});
const parser = StructuredOutputParser.fromZodSchema(schema);

XMLOutputParser

非 JSON 格式,用专门的 parser。

js
import { XMLOutputParser } from '@langchain/core/output_parsers';

const parser = new XMLOutputParser();
const question = `请提取人物信息。\n${parser.getFormatInstructions()}`;

const response = await model.invoke(question);
const result = await parser.parse(response.content);

方式二:Tool Call

不定义 tool 的实现,只定义 schema,模型返回结构化 args。

js
const scientistSchema = z.object({
  name: z.string().describe('科学家的全名'),
  birth_year: z.number().describe('出生年份'),
  nationality: z.string().describe('国籍'),
  fields: z.array(z.string()).describe('研究领域列表'),
});

const modelWithTool = model.bindTools([{
  name: 'extract_scientist_info',
  description: '提取和结构化科学家的详细信息',
  schema: scientistSchema,
}]);

const response = await modelWithTool.invoke('介绍一下爱因斯坦');
const result = response.tool_calls[0].args;  // 结构化数据

推荐:withStructuredOutput

底层自动选最可靠的方案(大多用 tool call),直接返回解析好的对象。

js
const schema = z.object({
  name: z.string().describe('姓名'),
  birth_year: z.number().describe('出生年份'),
  nationality: z.string().describe('国籍'),
  fields: z.array(z.string()).describe('研究领域'),
});

const structuredModel = model.withStructuredOutput(schema);
const result = await structuredModel.invoke('介绍一下爱因斯坦');
// result 已经是解析好的对象,不需要再 parse

两个不适合 withStructuredOutput 的场景

1. 流式打印

withStructuredOutput 的流式是等全部完成再返回,不能逐字打印。

用 Output Parser 实现流式:

js
const stream = await model.stream(`${prompt}\n${parser.getFormatInstructions()}`);

let fullContent = '';
for await (const chunk of stream) {
  process.stdout.write(chunk.content);  // 逐字打印
  fullContent += chunk.content;
}
const result = await parser.parse(fullContent);  // 最后解析

用 Tool Call 也能流式(通过 tool_call_chunks):

js
const stream = await modelWithTool.stream(prompt);

for await (const chunk of stream) {
  if (chunk.tool_call_chunks?.length > 0) {
    process.stdout.write(chunk.tool_call_chunks[0].args);  // 逐字打印参数
  }
}

2. 非 JSON 格式(XML / YAML)

Tool Call 只能返回 JSON,XML/YAML 需要用 Output Parser。

三种方式对比

text
Output Parser:prompt 加格式说明 + parse 响应 → 灵活,支持 XML/YAML/JSON
Tool Call:绑定工具 schema,模型返回 args → 可靠,模型训练保证
withStructuredOutput:自动选最优方案 → 推荐,但不支持流式打印和非 JSON

要点

  • 结构化输出的本质 — 在 prompt 加格式说明,或利用 tool call 的参数约束
  • 推荐 withStructuredOutput — 底层自动选最优方案,直接返回解析好的对象
  • Output Parser 适合流式场景stream() 逐字打印 + 最后 parse()
  • Output Parser 适合非 JSON 格式 — XML/YAML 等用专门的 parser
  • Tool Call 的流式 — 通过 tool_call_chunks 实现逐字打印效果
  • Zod 描述复杂结构 — 和 Tool 的参数描述一样,用 zod schema 定义