大语言模型(LLM)输出机制(方便自己查阅)

随手记一下,一些自己对AI的疑惑

我想了解claude的续写机制,就是对话会存在不完整的数据那么llm是如何知道数据不完整的并完成续写,于是就和AI进行了相关问题的对话,记录一下对话知识点,方便自己查阅

问题一:关于llm是如何知道数据不完整的并完成续写的问题

一、背景:Token 输出限制

LLM(包括 Claude)有两个关键长度限制:

限制类型 说明
Context Window 模型能"看到"的最大 token 数(输入+输出)
Max Output Tokens 单次响应最多能生成的 token 数

当模型生成的内容超过 max_output_tokens 时,响应会被强制截断,返回一个不完整的结果。

二、如何判断输出不完整?

API 响应中有一个关键字段:stop_reason / finish_reason

json 复制代码
{
  "content": "...(truncated content)...",
  "stop_reason": "max_tokens"   // ← 关键判断依据
}
  • stop_reason: "end_turn" → 模型正常结束,输出完整
  • stop_reason: "max_tokens" → 被截断,输出不完整

Claude Code(或任何使用 Claude API 的客户端)通过检查这个字段来判断是否需要继续请求。

三、如何累加输出完整结果?

这是一种叫做 Continuation / Multi-turn Completion 的技术,核心原理如下:

第一轮

text 复制代码
[System Prompt]
[User Message: "帮我生成完整代码"]
[Assistant: "```python\ndef foo():\n    ..." ]  ← 被截断,stop_reason=max_tokens

检测到截断后,第二轮请求构造方式:

text 复制代码
[System Prompt]
[User Message: "帮我生成完整代码"]
[Assistant: "```python\ndef foo():\n    ..."]   ← 第一轮的截断输出(作为历史)
[User: "continue" 或空提示,或特殊系统指令]    ← 触发继续
[Assistant: "    # 继续上文...\n    return bar"] ← 新一轮输出

关键机制:

  1. 保留上下文 :将前几轮的所有输出拼接进新请求的 messages 历史中
  2. 续写提示 :在 user 消息中加入 \"Continue exactly where you left off\" 或类似指令,让模型从截断处无缝续写
  3. 循环直到完整 :循环检测 stop_reason,直到收到 end_turn
  4. 结果拼接 :将每一轮的 content 字符串首尾拼接,得到完整输出

伪代码示意:

python 复制代码
messages = [{"role": "user", "content": user_input}]
full_response = ""
while True:
    response = claude_api.call(messages=messages, max_tokens=4096)
    full_response += response.content  # 累加输出    
    if response.stop_reason == "end_turn":
        break  # 输出完整,退出循环 
    elif response.stop_reason == "max_tokens":
        # 将截断的输出加入历史,继续请求
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": "Continue exactly where you left off."})
return full_response  # 返回完整拼接结果

四、额外细节:Streaming 模式下的处理

Claude Code 通常使用 Streaming(流式输出),此时:

  • 每个 token 实时推送给用户(所以你能看到逐字输出效果)
  • 流结束时同样会携带 finish_reason 标志
  • 客户端在流结束时检查该标志,决定是否发起续写请求
  • 续写请求的新 token 流会无缝接在上一次输出之后,用户感知上是连续的

五、总结

问题 答案
如何判断不完整 检查 API 响应的 stop_reason == "max_tokens"
如何累加完整输出 将截断内容作为 assistant 历史消息,附加 continue 指令,循环请求直到 stop_reason == "end_turn",逐轮拼接 content
用户是否感知多轮 通常无感知,客户端自动处理并流式拼接展示

问题二:llm中max_output_tokens的大小限制问题

关键认知纠正:max_output_tokens 是"单次限制",不是"总量限制"

text 复制代码
错误理解:整个对话的总输出 ≤ max_output_tokens
正确理解:每一轮请求的输出 ≤ max_output_tokens

每一次 API 调用都是独立的,max_output_tokens 重置,互不影响。

用具体数字理解

假设 max_output_tokens = 1000 tokens,需要生成 2500 tokens 的完整代码:

text 复制代码
┌─────────────────────────────────────────────────────┐
│ 第 1 轮 API 调用                                      │
│   输入: user prompt                                   │
│   输出: token 1 ~ 1000  ← 到达上限,强制截断          │
│   stop_reason: "max_tokens"                          │
└─────────────────────────────────────────────────────┘
           ↓ 检测到截断,发起第 2 轮
┌─────────────────────────────────────────────────────┐
│ 第 2 轮 API 调用                                      │
│   输入: user prompt + 前1000tokens(历史) + continue   │
│   输出: token 1001 ~ 2000  ← 再次到达上限,截断       │
│   stop_reason: "max_tokens"                          │
└─────────────────────────────────────────────────────┘
           ↓ 检测到截断,发起第 3 轮
┌─────────────────────────────────────────────────────┐
│ 第 3 轮 API 调用                                      │
│   输入: user prompt + 前2000tokens(历史) + continue   │
│   输出: token 2001 ~ 2500  ← 自然结束                 │
│   stop_reason: "end_turn"  ✓                         │
└─────────────────────────────────────────────────────┘
客户端拼接: [1~1000] + [1001~2000] + [2001~2500] = 完整 2500 tokens

为什么模型能"续写"而不是重新生成?

这是最核心的问题,依赖两个机制:

1. 消息历史作为上下文

每轮请求都把前面所有输出作为 assistant 历史传入,模型"看到"自己之前写了什么,自然从断点继续,不会重复或跳跃。

2."Continue" 指令约束行为

text 复制代码
user: "Continue exactly where you left off, do not repeat anything."

这个指令明确告诉模型:不要重新开始,从上次结尾接着写。

代价:输入 token 会越来越大

这里有一个真实的工程 tradeoff:

text 复制代码
第 1 轮:  input=200,  output=1000,  total=1200
第 2 轮:  input=1200, output=1000,  total=2200  ← 输入包含第1轮全部输出
第 3 轮:  input=2200, output=500,   total=2700  ← 输入越来越大
  • 每轮的 output tokens 独立计算,不累加 ✓
  • 但每轮的 input tokens 会累积增长 ← 真正的成本和瓶颈

所以最终的约束不是 max_output_tokens,而是:

text 复制代码
Context Window = 所有历史输入 + 当前输出 ≤ 上限(如 200K tokens)

一旦历史输入 + 新输出超过 Context Window,才会真正无法继续。

总结一句话

max_output_tokens 限制的是每次 API 调用的单次输出,多轮续写中每轮都重新计算,输出上限每次重置。代价是输入侧的 token 会逐轮累积增长,真正的天花板是整个 Context Window 的大小。

问题三:确认是否是llm限制了输出大小,而客户端会根据stop_reason自行拼接

模型只管单次输出,"完整结果"是客户端工程行为,不是模型能力。

确认你的理解

text 复制代码
模型层:  每次输出 ≤ max_output_tokens,超出就截断,附上 stop_reason
         ↑ 模型本身对此无感知,它只是"被切断了"

客户端层: 检测 stop_reason == "max_tokens"
          → 重新组装 messages 历史
          → 发起下一轮请求
          → 拼接所有轮次的输出
          → 呈现给用户一个"完整"的结果
         ↑ 这是 Cursor / Claude Code 等工具自己实现的工程逻辑

所以本质上

角色 做了什么
Claude 模型 无状态,每次独立响应,不知道自己被续写了多少轮
Claude Code / Cursor 有状态,维护完整上下文,负责检测截断、续写、拼接、展示

你看到的"完整输出",是客户端对多次 API 调用结果的透明封装,模型对此毫不知情。

相关推荐
宇擎智脑科技2 小时前
Claude Code 源码分析(一):多 Agent 协调器架构 —— 一个工业级 Coordinator-Worker 模式的完整实现
人工智能·agent·claude code
李元豪2 小时前
3分分类计算差值
人工智能·分类·数据挖掘
云烟成雨TD2 小时前
Spring AI 1.x 系列【22】深度拆解 ToolCallbackProvider 生命周期与调用链路
java·人工智能·spring
萌>__<新2 小时前
AI聊天助手-测试报告
人工智能·python
KC2702 小时前
OpenAkita 深度解析:开源多Agent协作框架的实战指南
人工智能·aigc·ai编程
元拓数智2 小时前
基于数据关系映射的企业AI系统权限最小化落地方法
人工智能
柠萌f2 小时前
从“尝鲜”到“落地”:易元AI真实商家案例拆解(美妆/服饰/3C)
人工智能
人工智能AI技术2 小时前
阿里云发布Qwen3.5-Omni,全模态大战开启
人工智能
用户446594547872 小时前
用 React 写 CLI 是什么体验?—— Ink 框架深度解析与实战
人工智能