AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


Ollama 0.5.1重磅更新:支持结构化输出,让 AI 回复更精准
发布日期:2024-12-25 19:19:08 浏览次数: 1819 来源:字节笔记本

最新消息!开源 AI 工具 Ollama 新增了结构化输出支持。这意味着我们可以使用预定义的格式来约束 AI 的输出,让回复更加精确和可控。让我们通过一些实际的代码示例来看看这个强大的新功能。

直接从数据中提取结构化的内容并分析

使用 Ollama 进行数据提取非常简单。下面是一个提取宠物信息的示例:


这里使用国产最强开源模型qwen2.5

Qwen2.5是阿里巴巴通义千问团队于2024年9月发布的最新开源大语言模型系列,涵盖从0.5B到72B参数规模的模型,包括基础版本和指令微调版本。


/**
 * 数据提取测试
 * 演示如何从自然语言文本中提取结构化数据
 */import { Ollama } from "ollama";import { z } from "zod";import { zodToJsonSchema } from "zod-to-json-schema";const config = {baseUrl: "http://192.168.1.180:8000",model: "qwen2.5:14b",
};// 定义宠物信息的数据结构const Pet = z.object({name: z.string().describe("宠物名字"),animal: z.string().describe("动物类型"),age: z.number().describe("年龄"),color: z.string().nullable().describe("毛色"),favorite_toy: z.string().nullable().describe("最喜欢的玩具"),
});// 定义宠物列表的数据结构const PetList = z.object({pets: z.array(Pet).describe("宠物列表"),
});async function extractPetsInfo() {const ollama = new Ollama({host: config.baseUrl,
});try {console.log("\n=== 宠物信息提取测试 ===");const response = await ollama.chat({model: config.model,messages: [
{role: "user",content: `
我家有三只宠物。
一只叫小白的猫咪,今年3岁,是白色的,最喜欢玩逗猫棒。
还有一只2岁的橘猫叫胖虎,特别喜欢玩毛线球。
最后是一只叫旺财的狗,5岁了,是棕色的,最喜欢玩飞盘。
`,
},
],format: zodToJsonSchema(PetList),
});// 解析返回的数据const petInfo = PetList.parse(JSON.parse(response.message.content));// 打印原始 JSON 数据console.log("\n提取的原始数据:");console.log(JSON.stringify(petInfo, null, 2));// 格式化输出每只宠物的信息console.log("\n=== 宠物信息汇总 ===");
petInfo.pets.forEach((pet, index) => {console.log(`\n${index + 1}. ${pet.name}:`);console.log(` - 类型: ${pet.animal}`);console.log(` - 年龄: ${pet.age}岁`);console.log(` - 毛色: ${pet.color || "未知"}`);console.log(` - 最爱玩具: ${pet.favorite_toy || "未知"}`);
});// 统计信息console.log("\n=== 统计信息 ===");const animalCount = petInfo.pets.reduce((acc, pet) => {
acc[pet.animal] = (acc[pet.animal] || 0) + 1;return acc;
}, {});console.log("宠物类型统计:");Object.entries(animalCount).forEach(([animal, count]) => {console.log(`- ${animal}: ${count}只`);
});const avgAge =
petInfo.pets.reduce((sum, pet) => sum + pet.age, 0) / petInfo.pets.length;console.log(`平均年龄: ${avgAge.toFixed(1)}岁`);
} catch (error) {console.error("数据提取失败:", error.message);if (error.response) {console.error("API 响应:", error.response);
}
}
}

提取结果如下图所示:

图片分析能力

Ollama 还支持图片内容分析,可以返回结构化的图片描述

这里使用 Llama 3.2-Vision作为图片识别模型

Llama 3.2-Vision是Meta公司于2024年9月发布的多模态大语言模型,具备处理文本和图像的能力。该模型提供11B和90B两个版本,支持128,000个tokens的上下文长度。在文本处理方面,Llama 3.2-Vision支持多种语言,包括英语、德语、法语、意大利语、葡萄牙语、印地语、西班牙语和泰语。然而,在图像与文本结合的应用中,目前仅支持英语。


先上图

再上代码

/**
 * 图片内容分析测试
 * 演示如何使用 AI 模型分析图片并返回结构化数据
 */import { Ollama } from "ollama";import { z } from "zod";import { zodToJsonSchema } from "zod-to-json-schema";import fs from "fs/promises";const config = {baseUrl: "http://192.168.1.180:8000",model: "llama3.2-vision",
};// 定义物体信息的数据结构const Object = z.object({name: z.string().describe("物体名称"),confidence: z.number().min(0).max(1).describe("置信度"),attributes: z.string().describe("物体属性描述"),
});// 定义图片描述的数据结构const ImageDescription = z.object({summary: z.string().describe("图片整体描述"),objects: z.array(Object).describe("检测到的物体列表"),scene: z.string().describe("场景描述"),colors: z.array(z.string()).describe("主要颜色列表"),time_of_day: z
.enum(["Morning", "Afternoon", "Evening", "Night"])
.describe("拍摄时间"),setting: z.enum(["Indoor", "Outdoor", "Unknown"]).describe("环境设置"),text_content: z.string().nullable().describe("图片中的��字内容"),
});async function analyzeImage(imagePath) {const ollama = new Ollama({host: config.baseUrl,
});try {// 读取图片文件const imageBuffer = await fs.readFile(imagePath);const base64Image = imageBuffer.toString("base64");console.log("\n=== 图片内容分析测试 ===");const response = await ollama.chat({model: config.model,messages: [
{role: "user",content:"请分析这张图片,描述你看到的内容,包括物体、场景、颜色和任何可以检测到的文字。",images: [base64Image],
},
],format: zodToJsonSchema(ImageDescription),options: {temperature: 0, // 设置为0以获得更确定性的输出},
});// 解析返回的数据const imageInfo = ImageDescription.parse(JSON.parse(response.message.content)
);// 打印原始 JSON 数据console.log("\n=== 原始 JSON 数据 ===");console.log(JSON.stringify(imageInfo, null, 2));// 打印格式化的分析结果console.log("\n=== 格式化分析结果 ===");console.log("\n【整体描述】");console.log(imageInfo.summary);console.log("\n【场景信息】");console.log(`场景类型: ${imageInfo.scene}`);console.log(`环境设置: ${getChineseSettingName(imageInfo.setting)}`);console.log(`拍摄时间: ${getChineseTimeName(imageInfo.time_of_day)}`);console.log("\n【检测到的物体】");
imageInfo.objects.forEach((obj, index) => {console.log(`\n${index + 1}. ${obj.name}`);console.log(` 置信度: ${(obj.confidence * 100).toFixed(1)}%`);console.log(` 属性: ${obj.attributes}`);
});console.log("\n【主要颜色】");
imageInfo.colors.forEach((color) => console.log(`- ${color}`));if (imageInfo.text_content) {console.log("\n【检测到的文字】");console.log(imageInfo.text_content);
}
} catch (error) {console.error("图片分析失败:", error.message);if (error.response) {console.error("API 响应:", error.response);
}
}
}// 环境设置的中文映射function getChineseSettingName(setting) {const settingMap = {Indoor: "室内",Outdoor: "室外",Unknown: "未知",
};return settingMap[setting] || setting;
}// 时间段的中文映射function getChineseTimeName(time) {const timeMap = {Morning: "早晨",Afternoon: "下午",Evening: "傍晚",Night: "夜晚",
};return timeMap[time] || time;
}// 使用示例const imagePath = "/Users/SCR-20241019-marq.png";analyzeImage(imagePath).catch(console.error);

返回结果如下图:

简历信息提取

结构化输出在处理文档信息时也很有用:

const Resume = z.object({basicInfo: z.object({name: z.string().describe("姓名"),age: z.number().describe("年龄"),email: z.string().email().describe("电子邮箱"),phone: z.string().describe("电话号码")
}),education: z.array(z.object({school: z.string(),degree: z.string(),major: z.string(),period: z.object({start: z.string(),end: z.string()
})
})),workExperience: z.array(z.object({company: z.string(),position: z.string(),period: z.object({start: z.string(),end: z.string()
}),responsibilities: z.array(z.string()),achievements: z.array(z.string())
}))
});

实用技巧

  • 使用 Zod 来定义清晰的数据结构
  • 为每个字段添加描述,帮助模型理解
  • 设置 temperature 为 0,使输出更稳定
  • 合理使用可空字段(nullable)增加灵活性

应用场景

  • 自动化数据提取和整理
  • 智能文档解析
  • 图片内容分析
  • 结构化知识提取
  • API 响应格式化


扩展阅读:Zod 使用详解

1. 基础类型定义

import { z } from "zod";// 基础类型const stringSchema = z.string();const numberSchema = z.number();const booleanSchema = z.boolean();const nullSchema = z.null();// 可选和可空const optionalString = z.string().optional(); // string | undefinedconst nullableString = z.string().nullable(); // string | null

2. 复杂对象定义

// 对象定义const UserSchema = z.object({id: z.number(),name: z.string().min(2),email: z.string().email(),age: z.number().min(0).max(120),role: z.enum(["admin", "user", "guest"]),tags: z.array(z.string())
});// 嵌套对象const PostSchema = z.object({title: z.string(),content: z.string(),author: UserSchema,comments: z.array(z.object({text: z.string(),user: UserSchema
}))
});

3. 类型转换和验证

// 添加自定义验证const PhoneSchema = z.string().refine((val) => /^1[3-9]\d{9}$/.test(val),"请输入有效的手机号");// 类型转换const NumberFromString = z.string().transform((val) => parseInt(val));// 默认值const ConfigSchema = z.object({port: z.number().default(3000),host: z.string().default("localhost")
});

4. 继承和合并

// 基础配置const BaseConfig = z.object({version: z.string(),name: z.string()
});// 扩展配置const FullConfig = BaseConfig.extend({description: z.string(),isEnabled: z.boolean()
});// 合并多个 Schemaconst MergedSchema = z.intersection(SchemaA,SchemaB);

5. 实战应用技巧

  • 添加字段描述
const ProductSchema = z.object({name: z.string().describe("产品名称"),price: z.number().describe("产品价格(元)"),stock: z.number().describe("库存数量"),category: z.enum(["电子", "服装", "食品"]).describe("产品类别")
});
  • 错误处理
try {const result = ProductSchema.parse(data);
} catch (error) {if (error instanceof z.ZodError) {
error.errors.forEach(err => {console.log(`${err.path.join('.')}: ${err.message}`);
});
}
}
  • 异步验证
const AsyncSchema = z.object({username: z.string().refine(async (val) => {// 检查用户名是否可用const exists = await checkUserExists(val);return !exists;
},"用户名已存在"
)
});

6. 最佳实践

  • 类型复用
// 定义可复用的类型const DateRangeSchema = z.object({start: z.string(),end: z.string()
});// 在其他 Schema 中复用const EventSchema = z.object({title: z.string(),period: DateRangeSchema,location: z.string()
});
  • 性能优化
// 预编译 Schemaconst compiledSchema = z.object({/*...*/}).compile();// 重复使用已编译的 Schemafunction validateData(data) {return compiledSchema.safeParse(data);

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询