惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cyber Attacks, Cyber Crime and Cyber Security
V
V2EX - 技术
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Schneier on Security
Schneier on Security
SecWiki News
SecWiki News
Security Latest
Security Latest
P
Privacy & Cybersecurity Law Blog
Google Online Security Blog
Google Online Security Blog
N
News | PayPal Newsroom
T
Tor Project blog
T
The Exploit Database - CXSecurity.com
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
L
LINUX DO - 最新话题
C
Cisco Blogs
G
GRAHAM CLULEY
C
Comments on: Blog
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
量子位
S
Secure Thoughts
美团技术团队
E
Exploit-DB.com RSS Feed
PCI Perspectives
PCI Perspectives
Cisco Talos Blog
Cisco Talos Blog
J
Java Code Geeks
The Cloudflare Blog
I
Intezer
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Vulnerabilities – Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
K
Kaspersky official blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
H
Hacker News: Front Page
小众软件
小众软件
博客园 - 【当耐特】
GbyAI
GbyAI
I
InfoQ
M
Microsoft Research Blog - Microsoft Research
爱范儿
爱范儿
雷峰网
雷峰网
罗磊的独立博客
Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学

掘金

从“连接不上”到“交易成功”:我用 @solana/web3.js 在 React 中搞定 Solana 钱包交互的全过程 juejin.cn 海量人群包存储优化:基于 RoaringBitmap 交换格式与 Redis 分片 Bitmap 的实践 juejin.cn juejin.cn 鸿蒙项目首页启动链路与 ArkUI 架构学习总结 如何手写一个 AI Agent 工具调用循环(Tool Loop) Tauri 应用首次上架 App Store 被驳回了 3 次(iOS)和 12 轮(macOS)的经历 juejin.cn Flutter 桌面小组件开发 现代多模态大模型的核心基础:Unified Self-Attention juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn Transformer 原论文怎么训出来的:8 张 P100、12 小时、warmup 4000 步 Hermes 升级后,我的 Telegram 附件突然发不出来了 Transformer 中的前馈网络:那个看似平平无奇的两层 MLP,其实是「记忆」所在 AI Coding开始进入第四个时代,我还没上车呢! 【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (1)---基础 juejin.cn Vibe Coding 全栈实战:章鱼哥解题 01|搭好产品底座与登录链路 juejin.cn 我让 AI 加了一个开关,结果代码走了原本不该走的分支 Manim物理模拟:别自己写欧拉了! 我也该升级了,陪伴了我7年的博客 juejin.cn juejin.cn MCP 高德地图实战:当 AI 学会使用工具,一个协议如何重塑大模型的行动边界 用魔法打败魔法:我让AI替我去面试前端岗,AI面试官给我打了92分,还发了offer juejin.cn juejin.cn juejin.cn juejin.cn Android Input Spy Window Claude Code CLI 命令大全:60 个原生命令一次讲清 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn 关于一个新手小白靠claude帮助下的全栈留言板项目开发 juejin.cn juejin.cn juejin.cn 从本地开发到生产部署:用 Docker Compose 跑通 NestJS、MySQL 与 Milvus AI应用开发七:可以替代 RAG 的技术 juejin.cn 小书匠:一款本地优先、去中心化的全能笔记软件 juejin.cn juejin.cn juejin.cn Shadow实战接入与生产落地:从零搭建到稳定运行 Shadow Transform:编译期的魔法——字节码替换实战 juejin.cn juejin.cn Hermes Agent:一个真正“会成长”的开源 AI Agent,正在改变 AI 自动化玩法 juejin.cn juejin.cn juejin.cn 残差连接:为什么深层网络必须留一条直路 juejin.cn FastAPI 从入门到实战:3 分钟构建高性能异步 API juejin.cn juejin.cn CryptoJS:数据安全的JavaScript加密利器 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn ArkClaw AI 盯盘管家 —— 从手动口令到自动推送,4 套预置定时任务模版一键启用 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn “杀!杀!杀!”、“我最讨厌事后道歉”——骂“杀哥”之前,谁还没当过情绪崩溃的人
Instructor实战:让LLM老老实实返回JSON
92year · 2026-05-15 · via 掘金

Instructor实战:让LLM老老实实返回JSON

你让GPT返回个用户信息JSON,它给你包了三层markdown:

{
  "name": "张三"
}

你要个评分数字,它给你返回"高"。你要个列表,它少了俩字段。

这不是模型不行,是你没用对工具。Instructor就是专门治这个病的——3行代码,保证LLM输出跟你的Pydantic模型一模一样。

为什么需要Instructor

Raw JSON mode有三个硬伤:

1. 格式不可控

# 你期望的
{"score": 0.85}

# GPT给你的
```json
{"score": "很高", "confidence": "非常确定"}
\```

2. 字段缺失/多余 模型经常自己发挥,加字段或者漏字段,下游代码直接炸。

3. 没有重试机制 验证失败了就失败了,你得手写重试逻辑。

Instructor的解法很直接:用Pydantic定义schema,自动验证,失败了自动重试,直到拿到合法数据或者超过重试次数。

5分钟上手

pip install instructor openai pydantic

最简单的例子——提取人物信息:

import instructor
from openai import OpenAI
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int
    occupation: str

client = instructor.from_openai(OpenAI())

person = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "Extract: John is 30, works as an engineer"}
    ],
    response_model=Person,
    max_retries=3
)

print(person.name)  # John
print(person.age)   # 30

工作流程:

  1. 定义Pydantic模型
  2. from_openai()包装客户端
  3. response_model参数
  4. 拿到的直接是验证过的Python对象

验证失败?自动重试。字段类型错了?自动重试。直到成功或者达到max_retries

复杂场景:嵌套结构+自定义验证

真实业务不会这么简单。来个工单分类系统:

from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
from enum import Enum

class Priority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"

class Tag(BaseModel):
    name: str = Field(..., min_length=2, max_length=20)
    confidence: float = Field(..., ge=0.0, le=1.0)

class Ticket(BaseModel):
    title: str = Field(..., min_length=10)
    priority: Priority
    tags: List[Tag] = Field(..., min_items=1, max_items=5)
    estimated_hours: Optional[float] = Field(None, gt=0, le=100)
    
    @field_validator('estimated_hours')
    @classmethod
    def validate_hours(cls, v):
        if v is not None and v % 0.5 != 0:
            raise ValueError('工时必须是0.5的倍数')
        return v

ticket = client.chat.completions.create(
    model="gpt-4o",
    messages=[{
        "role": "user",
        "content": "分类工单:生产环境Redis崩了,影响所有用户登录"
    }],
    response_model=Ticket,
    max_retries=3
)

print(ticket.priority)  # Priority.CRITICAL
print(ticket.tags[0].name)  # redis
print(ticket.estimated_hours)  # 2.0 (符合0.5倍数规则)

这里用了:

  • 枚举类型(Priority)
  • 嵌套对象(List[Tag])
  • 字段约束(min_length、ge/le范围)
  • 自定义验证器(工时必须0.5倍数)

任何一个不符合,Instructor会把验证错误信息塞回prompt,让模型重新生成。

流式输出:边生成边处理

大列表或者复杂对象,等模型全生成完太慢。Instructor支持Partial streaming:

from instructor import Partial

class UserList(BaseModel):
    users: List[Person]

stream = client.chat.completions.create_partial(
    model="gpt-4o",
    messages=[{
        "role": "user",
        "content": "提取:张三30岁工程师,李四25岁设计师,王五35岁产品"
    }],
    response_model=Partial[UserList],
    stream=True
)

for partial_result in stream:
    if partial_result.users:
        print(f"已提取 {len(partial_result.users)} 个用户")
        for user in partial_result.users:
            if user.name:  # Partial对象字段可能None
                print(f"  - {user.name}")

输出:

已提取 1 个用户
  - 张三
已提取 2 个用户
  - 张三
  - 李四
已提取 3 个用户
  - 张三
  - 李四
  - 王五

Partial[T]会把模型里的所有字段标记为Optional,你可以实时处理部分结果,不用等全部生成完。

注意:streaming模式下不能用@field_validator,因为中间状态可能不满足验证规则。

多provider支持:一套代码跑遍所有模型

Instructor 2026版支持15+家provider,API完全一样:

# OpenAI
client = instructor.from_openai(OpenAI())

# Anthropic Claude
from anthropic import Anthropic
client = instructor.from_anthropic(Anthropic())

# Google Gemini
import google.generativeai as genai
client = instructor.from_gemini(genai.GenerativeModel("gemini-2.0-flash-001"))

# 本地模型 (Ollama)
from openai import OpenAI
client = instructor.from_openai(
    OpenAI(base_url="http://localhost:11434/v1", api_key="ollama"),
    mode=instructor.Mode.JSON
)

切provider只需要改初始化代码,response_model和业务逻辑完全不变。

本地模型建议用Mode.JSON而不是Mode.TOOLS,因为开源模型的function calling能力参差不齐。

三个踩坑记录

1. 重试次数别设太高

# ❌ 这样写token消耗爆炸
response = client.create(
    response_model=ComplexModel,
    max_retries=10  # 验证失败会重试10次,每次都扣token
)

# ✅ 3次够了,验证失败说明prompt有问题
max_retries=3

每次重试都会把错误信息加到prompt里再调一次API。复杂模型验证失败10次,token消耗是正常的3-5倍。

2. 嵌套太深会超context

class DeepNested(BaseModel):
    level1: List['DeepNested']  # 递归嵌套

# 模型生成超深结构时,验证错误信息会非常长
# 重试时prompt可能超出context window

解决:限制嵌套深度,或者用max_items约束列表长度。

3. streaming不支持同步validators

class User(BaseModel):
    email: str
    
    @field_validator('email')
    @classmethod
    def validate_email(cls, v):
        # ❌ 在 create_partial(stream=True) 里不会执行
        if '@' not in v:
            raise ValueError('Invalid email')
        return v

streaming模式返回的是Partial对象,验证器会被跳过。需要在最终结果上再验证一次。

Instructor vs PydanticAI:什么时候该换

Instructor适合单次提取任务

  • 从文本提取结构化数据
  • 分类、打标签
  • 格式转换

PydanticAI适合多轮agent交互

  • 需要调用工具(搜索、数据库查询)
  • 多步骤推理
  • 需要记录执行历史和可观测性

两者都是Pydantic团队官方产品,Instructor专注schema-first提取,PydanticAI是完整的agent runtime。

简单记:只要JSON就用Instructor,要干活就上PydanticAI

一张清单

生产环境用Instructor之前,确认这5点:

  • max_retries设成2-3,不要超过5
  • 嵌套层级 ≤ 3层
  • List字段都加了max_items限制
  • streaming场景没用field_validator
  • 算过token成本(重试会double消耗)

完整代码:python.useinstructor.com/