微信扫码
与创始人交个朋友
我要投稿
最新消息!开源 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()) })) });
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
// 对象定义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 })) });
// 添加自定义验证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") });
// 基础配置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);
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; },"用户名已存在" ) });
// 定义可复用的类型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+中大型企业
2025-02-02
快速搭建专业AI知识库的开源工具:Ragflow
2025-02-01
刚刚,OpenAI发布o3-mini,可免费使用、3大推理模式
2025-02-01
奥特曼:在开源AI上,我们错了!DeepSeek让OpenAI优势不再,下一个是GPT-5
2025-02-01
回应DeepSeek抄袭的质疑,DeepSeek和OpenAI ChatGPT的比较
2025-01-31
吴恩达评DeepSeek:中国 AI 崛起,开源模型重塑行业格局
2025-01-30
我让DeepSeek自己谈它和GPT的区别,大白话版笑死我了
2025-01-29
使用DeepSeek必备的10个技巧
2025-01-29
DeepSeek R1,本地部署才是王道!支持WebUI
2025-01-01
2024-07-25
2024-05-06
2025-01-21
2024-08-13
2024-06-12
2024-09-20
2024-07-11
2024-07-20
2024-12-26
2025-01-22
2025-01-16
2024-12-24
2024-12-20
2024-12-19
2024-11-22
2024-11-19
2024-11-13