更新于

LLM logprobs 在生产场景的实用价值


在日常调用 LLM API 的过程中,大多数开发者只关注模型输出的文本内容。但 OpenAI、Anthropic、Google 等主流 API 还提供了一个常被忽视的参数——logprobs(对数概率)。开启它之后,模型不只给你一个答案,还会附上”它有多确定”的数值信号。

本文从生产场景出发,系统梳理 logprobs 的含义、获取方式,以及在置信度过滤、分类任务、RAG 质量控制等真实业务中的落地方法。

一、logprobs 是什么,能拿到什么

基本概念

语言模型的本质是一个下一个 token 的概率分布。每次生成时,模型对词表里每个 token 计算一个 softmax 概率。logprobs 就是把这些概率取 log 后返回给你。

取 log 有两个好处:

  1. 数值更稳定(避免极小概率的精度丢失)
  2. 多个 token 的概率可以直接相加(而非连乘),方便计算整句/整词的概率

OpenAI API 示例

from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "这段情绪是正面还是负面?\n文本:今天天气真好,心情棒极了"}],
    logprobs=True,
    top_logprobs=5,  # 返回每个位置 top-5 候选 token
    max_tokens=5,
)

for token_info in response.choices[0].logprobs.content:
    print(f"token: {token_info.token!r:15}  logprob: {token_info.logprob:.4f}  "
          f"prob: {2.718 ** token_info.logprob:.4f}")
    for top in token_info.top_logprobs:
        print(f"  候选: {top.token!r:15} logprob={top.logprob:.4f}")

典型输出:

token: '正'            logprob: -0.0023  prob: 0.9977
  候选: '正'           logprob=-0.0023
  候选: '负'           logprob=-6.1201
  候选: '中'           logprob=-9.3402
  候选: '积'           logprob=-12.0000
  候选: '好'           logprob=-13.5000

prob ≈ 0.998 意味着模型对”正面”答案几乎没有疑问;如果 prob 只有 0.6,就该引起警惕。

二、置信度评估:让模型输出可信度分数

为什么需要置信度

LLM 有一个著名缺陷:它们会自信地给出错误答案。仅凭输出文本无法判断模型是在”确信地说”还是”蒙”。logprobs 提供了一个客观的量化信号。

将 logprob 转为置信度

import math

def get_answer_confidence(response) -> float:
    """计算第一个生成 token 的概率作为置信度代理"""
    if not response.choices[0].logprobs:
        return None
    first_token = response.choices[0].logprobs.content[0]
    return math.exp(first_token.logprob)

def get_sequence_confidence(response) -> float:
    """计算整个输出序列的平均 logprob(越接近 0 越自信)"""
    tokens = response.choices[0].logprobs.content
    if not tokens:
        return None
    avg_logprob = sum(t.logprob for t in tokens) / len(tokens)
    return math.exp(avg_logprob)

可操作结论:在实际系统中,建议同时记录两个指标——首 token 概率(适合分类)和序列平均概率(适合生成任务)。设置阈值时先用测试集调参,不要拍脑袋。

置信度过滤流水线

def confident_answer_or_fallback(question: str, threshold: float = 0.85) -> dict:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": question}],
        logprobs=True,
        max_tokens=100,
    )
    
    confidence = get_sequence_confidence(response)
    answer = response.choices[0].message.content
    
    if confidence >= threshold:
        return {"answer": answer, "confidence": confidence, "status": "confident"}
    else:
        # 降级:转人工 / 换更强模型 / 要求补充信息
        return {"answer": answer, "confidence": confidence, "status": "uncertain", 
                "action": "escalate"}

三、分类任务:logprobs 比 Few-shot 更稳

传统分类的问题

用 LLM 做分类时,常见做法是让模型输出”正面”/“负面”/“中性”。但有时模型会输出”这段文本是正面的”或”情感偏向于积极”——格式不稳定,需要额外解析。

用 logprobs 做确定性分类

def classify_sentiment(text: str) -> dict:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "只输出一个词:正面、负面、中性"},
            {"role": "user", "content": f"情感分类:{text}"},
        ],
        logprobs=True,
        top_logprobs=5,
        max_tokens=3,
    )
    
    # 提取 top-5 候选,找目标标签
    first_position = response.choices[0].logprobs.content[0].top_logprobs
    candidates = {item.token: math.exp(item.logprob) for item in first_position}
    
    # 目标类别
    target_labels = {"正面", "负面", "中性", "正", "负", "中"}
    label_probs = {k: v for k, v in candidates.items() if k in target_labels}
    
    if label_probs:
        best_label = max(label_probs, key=label_probs.get)
        return {"label": best_label, "probability": label_probs[best_label], 
                "all_candidates": candidates}
    
    # 兜底:用模型输出文本
    return {"label": response.choices[0].message.content.strip(), 
            "probability": None, "all_candidates": candidates}

分类任务 logprobs 对比表

方法输出稳定性可信度量化边界案例处理
直接输出标签(无 logprobs)中(格式漂移)靠提示词
用 logprobs 提取候选概率高(直接读概率)有(精确数值)低置信度可标记
Function Calling / JSON mode靠参数约束

可操作结论:分类标签数 ≤ 10、且标签是单 token 的场景,logprobs 方法是最轻量且可解释的选择。标签是多 token 的(如”非常满意”)需要对多个 token 概率求和,复杂度上升。

四、RAG 系统:用 logprobs 过滤幻觉答案

问题背景

RAG(检索增强生成)系统的核心痛点是:检索到的文档和问题相关性不够时,模型往往会”编”出听起来合理的答案(幻觉)。

logprobs 无法完全消除幻觉,但可以帮你识别模型自己也不确定的答案,在置信度低时触发重检索或拒答。

RAG 置信度过滤实现

def rag_with_confidence_filter(query: str, retrieved_docs: list[str], 
                                min_confidence: float = 0.70) -> dict:
    context = "\n\n".join(retrieved_docs)
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": 
             "根据给定文档回答问题。如果文档中没有足够信息,输出'信息不足'。"},
            {"role": "user", "content": f"文档:\n{context}\n\n问题:{query}"},
        ],
        logprobs=True,
        max_tokens=200,
    )
    
    answer = response.choices[0].message.content
    confidence = get_sequence_confidence(response)
    
    # 模型主动说信息不足
    if "信息不足" in answer:
        return {"answer": None, "reason": "model_flagged", "confidence": confidence}
    
    # 置信度太低
    if confidence < min_confidence:
        return {"answer": None, "reason": "low_confidence", "confidence": confidence,
                "draft_answer": answer}  # 保留草稿供人工审核
    
    return {"answer": answer, "confidence": confidence, "reason": "ok"}

结合来源归因

更进一步,可以分别计算每条检索文档作为 context 时的输出置信度,选择置信度最高的 context:

def select_best_context(query: str, docs: list[str]) -> tuple[str, float]:
    best_doc, best_conf = None, -1
    for doc in docs:
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "根据文档回答,文档不相关则输出NA"},
                {"role": "user", "content": f"文档:{doc}\n\n问题:{query}"},
            ],
            logprobs=True, max_tokens=50,
        )
        conf = get_sequence_confidence(resp)
        if conf > best_conf:
            best_conf = conf
            best_doc = doc
    return best_doc, best_conf

可操作结论:对于高风险 RAG 场景(法律、医疗、金融),建议设置双重阈值:confident_threshold=0.85 直接返回;0.65~0.85 加 disclaimer;< 0.65 触发人工审核或拒答。

五、A/B 测试与模型评估

用 logprobs 量化模型”确定性”

在做模型选型时,除了看准确率,还可以把 logprobs 作为模型”自信度”的维度:

def compare_model_confidence(prompt: str, models: list[str]) -> dict:
    results = {}
    for model in models:
        resp = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            logprobs=True,
            max_tokens=100,
        )
        results[model] = {
            "answer": resp.choices[0].message.content,
            "avg_confidence": get_sequence_confidence(resp),
            "min_token_prob": min(
                math.exp(t.logprob) for t in resp.choices[0].logprobs.content
            ),
        }
    return results

min_token_prob 尤其有用——它反映模型在整个生成过程中最不确定的那一步,往往就是答案出错的位置。

不同模型置信度特征(经验参考)

特征小模型(如 gpt-4o-mini)大模型(如 gpt-4o)
平均序列置信度偏低(约 0.65–0.75)偏高(约 0.75–0.90)
错误时置信度可能仍高(过度自信)错误时更多低置信
置信度区分度较好

注:以上数据为业界经验估算,实际因任务而异,建议在自己的测试集上验证。

六、生产部署注意事项

成本与延迟

开启 logprobs(尤其是 top_logprobs=5)会增加响应体积,但不会增加 token 计算成本(logprobs 是采样过程的副产品)。延迟增加通常在 5–15ms 之间,可忽略不计。

边界情况

  1. 多 token 词:中文词语通常被拆成多个 token(如”正面”可能是”正”+“面”)。判断置信度时需对所有组成 token 取平均或最小值。
  2. 温度影响:高温度(temperature > 1.0)会拉平概率分布,导致 logprobs 普遍偏低,不要用绝对阈值,应用相对排名。
  3. 函数调用模式:OpenAI 的 function calling 输出中,logprobs 指向参数 token,解读方式不同,需单独处理。

记录到可观测系统

import json

def log_inference_with_probs(response, query: str, metadata: dict):
    log_entry = {
        "query": query,
        "answer": response.choices[0].message.content,
        "avg_confidence": get_sequence_confidence(response),
        "model": response.model,
        "usage": response.usage.model_dump(),
        **metadata,
    }
    # 发送到 Langfuse / Datadog / 自建日志系统
    print(json.dumps(log_entry, ensure_ascii=False))

将置信度指标持续记录到监控系统,可以建立置信度随时间的基线,及早发现模型行为漂移。

七、相关阅读

想在生产环境稳定使用 logprobs 功能,YoTradeApi 提供兼容 OpenAI 格式的 API 中转服务,完整支持 logprobs 参数,无需修改现有代码即可切换。