专题十二:新 Agent 对话空泡卡住(max_tokens 过小)与改回 8192
本专题记录一次真实故障:播城行政问答智能体在长会话里问「法定假日加班的制度是什么」时,页面出现空回复气泡、显示
3/3、输入框发不出新问题,看起来像「卡死」。实际不是还在生成,而是 已经失败 3 次。失败原因是 Prompt 超过当前
max_tokens=2048。随后按专题十一同一套改库办法,把该 Agent 的max_tokens从2048改回8192。本文按真实排查顺序写:先怎么判断「卡住还是已失败」,再怎么从库表和日志定位报错,再怎么改两张配置表,最后怎么验证、回滚和避免再点发布把值写坏。
运行环境:业务脚本均在 207 固定目录
/opt/workbuddy执行,Python 为/usr/bin/python3。 Windows 开发机仅保留_probe_*调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。
目录
- 一、本文解决什么问题
- 二、和专题十一的关系
- 三、本次环境
- 四、现象
- 五、完整排查思路
- 六、根因
- 七、临时处理:怎么取消卡住并继续问
- 八、正式解法:把 max_tokens 从 2048 改回 8192
- 九、为什么改完不要再点发布
- 十、回滚方法
- 十一、以后再遇到空泡卡住怎么排
- 十二、本次环境实录
- 十三、常见误区
- 十四、结论
一、本文解决什么问题
这次要同时解决两件事:
- 对话页看起来卡住:空气泡、
3/3、不能发新问题。要能判断后台到底还在跑,还是已经失败;失败时从哪看原因、怎么把前端状态清掉。 - 根因修复:该 Agent 的
max_tokens被专题十一改成了2048,长会话 + 知识库检索后输入已经超过这个上限。需要把它改回8192,并且改完后仍然不要点「发布」。
验收标准:
- 库里活动快照和编辑草稿的
model.model_settings.max_tokens都是8192 - 刷新后新开对话,同样的知识库问答能正常出字,不再报
Model token limit (2048) exceeded - 旧的失败会话不用救,直接弃用即可
二、和专题十一的关系
先把两篇专题分开,避免混在一起:
| 专题 | 用户当时看到的现象 | 库里的真实情况 | 处理 |
|---|---|---|---|
| 专题十一 | 控制台把 Max Tokens 改成 2048,点发布后又变回 8192 | max_tokens 仍是 null,运行时回退插件默认 8192 | 改库,把 null 写成 2048 |
| 本专题十二 | 对话空泡、3/3、发不出新问题 | max_tokens 已经是 2048,长 Prompt 超限 | 先确认是 error,再改库把 2048 写成 8192 |
专题十一修的是 UI 不落库。 专题十二修的是 落库成功之后,2048 对带知识库的长会话太小。
两篇用的是同一套改法:
- 表:
agent_config_snapshots+agent_config_drafts - 路径:
config_snapshot -> model -> model_settings -> max_tokens - 工具:PostgreSQL
jsonb_set - 禁忌:改完不要再点「构建 / 发布」
专题十一的原文不要改。那篇记录的是当时把值写成 2048 的过程。本篇是后续故障和改回 8192。
三、本次环境
| 项 | 值 |
|---|---|
| 服务器 | 192.168.110.207 |
| Dify | 1.16.1,入口 http://192.168.110.207 |
| 应用 | 播城行政问答智能体 |
| 应用 ID | 40e95fd3-e7f5-44f2-a03b-dc3e60413540 |
| Agent ID | 01a012e7-bed3-7574-b0d1-a5cc79a0d3de |
| 应用类型 | 新 Agent(mode=agent) |
| 模型 | qwen3.6-27b,provider langgenius/openai/openai |
| 活动快照 ID | 01a0135c-320e-79dc-97a0-94da9b904c13(version=2) |
| 编辑草稿 ID | 01a012e7-bf60-7673-a894-64551d984a23 |
| Postgres 容器 | docker-db_postgres-1 |
| 数据库 | dify,用户 postgres |
改前 max_tokens | 2048 |
改后 max_tokens | 8192 |
| 触发问句 | 「法定假日加班的制度是什么」 |
| 所在会话名 | 「员工打卡管理规定」(长会话,前面已问过上下班、午休、适用范围、请假流程) |
相关容器:
| 容器 | 这次排查时的作用 |
|---|---|
docker-db_postgres-1 | 看 messages.error、改配置 JSON,最关键 |
docker-api-1 | 对话 API 日志,能搜到 token limit |
docker-agent_backend-1 | 新 Agent 运行时日志 |
docker-worker-1 | Celery / Tracing,这次与故障无关,只是排除项 |
下面的 UUID 是本次 207 环境的实录。换 Agent 或重新发布后 ID 可能变,必须先查再改,不要直接抄 ID。
四、现象
用户在应用对话页看到:
- 发出「法定假日加班的制度是什么」后,回复气泡是空的,没有任何文字。
- 气泡旁出现
3/3。 - 输入框发不出新问题,整页像卡住。
- 页面没有弹出红色错误条,所以第一直觉是「模型还在跑、超时、或前端死锁」。
这和「模型正在流式输出但很慢」长得很像,但本质不同:
- 如果还在跑:
messages.status一般是normal以外的进行中状态,error为空,日志还在刷 - 如果已经失败:
messages.status = error,error列有完整英文报错,后台任务已经结束
这次属于第二种。页面没把 error 渲染出来,只留下一个空泡和重试计数 3/3。
五、完整排查思路
导读:对话空泡/
3/3/token limit→ 先 §七 取消卡住,再 §八 改 max_tokens;根因与 专题十一 成对阅读。
| 现象 | 章节 |
|---|---|
| 界面卡住、无正文 | §四现象 → §七临时处理 |
| Langfuse 见 thinking 占满 | §六根因 |
| 改完又变 2048 | 勿点发布;查作业 4 是否改过快照 |
定位时不要先去改配置,也不要先重启容器。按「先排除还在跑,再看失败原因,再把失败原因连回配置」这条线走。
5.0 思路总览
页面空泡 + 发不出新问题
│
▼
① 查 messages:status / error / query / created_at
│
├─ status 仍在处理中,error 为空 → 真的还在跑,看 api / agent_backend 卡在哪
│
└─ status = error,error 有内容 → 已经失败,页面只是没显示
│
▼
② 同一句 query 出现几次? → 本次同一句失败 3 次,对应 UI 的 3/3
│
▼
③ 读 error 原文
│
├─ token limit exceeded → 去核对 Agent 的 max_tokens
├─ timeout / 连接失败 → 去看模型服务、插件、网络
└─ 其它业务报错 → 按报错本文继续
│
▼
④ 查 agent_config_snapshots + agent_config_drafts 的 max_tokens
│
▼
⑤ 对照会话长度:前面多轮问答 + 知识库检索片段,是否可能把 Prompt 顶满
│
▼
⑥ 结论:不是卡住,是 2048 太小;临时刷新新开对话,正式改回 8192为什么先查库不先看 UI:
- 新 Agent 这次 没有把错误画到气泡里
messages.error是服务端落库的最终结果,比截图更可靠3/3只是前端重试次数,本身不解释失败原因
5.1 第一步:查最近消息,判断卡住还是已失败
在 207 上执行:
完整脚本:scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_01.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_01.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec docker-db_postgres-1 psql -U postgres -d dify -c "
SELECT id,
conversation_id,
status,
answer IS NOT NULL AS has_answer,
left(query, 80) AS q,
left(error, 180) AS err,
created_at,
updated_at
FROM messages
ORDER BY created_at DESC
LIMIT 8;
"本次关键观察:
- 最近几条
query都是「法定假日加班的制度是什么」 status是error,不是处理中has_answer为假,对应页面空泡error列已经有完整英文
到这里就可以下第一个结论:
后台已经结束,没有需要取消的正在跑的任务。页面卡住是前端还停在失败态。
5.2 第二步:把 3/3 对上数据库
3/3 不是「第 3 步 / 共 3 步」,也不是知识库检索进度。
它是:同一句话自动或手动重试了 3 次,当前看到的是第 3 次失败。
核对方法:按 query 和 created_at 看最近是不是连续 3 条相同问句,且都是 error。
不要再点气泡旁的 < 3/3 > 来回翻。那只是在 3 条失败记录之间切换,不会让模型重新成功。
5.3 第三步:读 error 原文,不要先猜超时
本次三条失败记录的错误都是同一句:
Model token limit (2048) exceeded before any response was generated.
Increase the `max_tokens` model setting, or simplify the prompt逐句拆:
| 原文 | 含义 |
|---|---|
Model token limit (2048) | 当前这次调用使用的上限就是 2048,不是 8192,也不是模型卡的物理上限 |
exceeded before any response was generated | 请求还没开始生成就已经超限,所以气泡是空的 |
Increase the max_tokens model setting | 官方提示:把模型设置里的 max_tokens 调大 |
or simplify the prompt | 或者缩短输入:新开对话、少带历史、少检索片段 |
这条错误 不是:
- Langfuse 补丁导致
- 模型服务宕机
- 知识库检索失败
- 前端 JS 死循环
知识库检索其实已经做完了。正是检索结果和多轮历史一起塞进 Prompt,才把输入顶过 2048。
5.4 第四步:用日志交叉验证,排除「还卡在某层」
库表已经够定性,日志用来排除其它层。
完整脚本:scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_02.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_02.sh
#!/usr/bin/env bash
set -euo pipefail
docker logs --since 20m docker-api-1 2>&1 | grep -i 'token limit'
docker logs --since 20m docker-agent_backend-1 2>&1 | grep -i 'token limit'
docker logs --since 20m docker-worker-1 2>&1 | grep -iE 'error|timeout|Traceback|token limit' | tail -30本次结果:
api/agent_backend能搜到同样的token limit (2048)worker没有对应的生成失败(worker 负责异步追踪,不负责这次生成)- 没有新的持续刷屏,说明任务没有挂死
如果这三路日志都没有 token limit,而 messages.error 有,仍然以库表为准。 如果库表 status 不是 error、日志还在刷模型请求,才按「真的还在跑」去跟。
5.5 第五步:核对当前 Agent 配置里的 max_tokens
报错里的 (2048) 必须能在配置里对上,否则可能是别的应用、旧快照、或插件默认值。
agent_config_snapshots 没有 app_id 列,不要写成 WHERE app_id = ...。正确路径是 snapshots.agent_id -> agents.id -> agents.app_id。
查活动快照:
SELECT s.id,
s.version,
s.config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' AS max_tokens
FROM agent_config_snapshots s
JOIN agents a ON s.agent_id = a.id
WHERE a.app_id = '40e95fd3-e7f5-44f2-a03b-dc3e60413540'
ORDER BY s.version DESC;本次结果:
id | version | max_tokens
-------------------------------------+---------+-----------
01a0135c-320e-79dc-97a0-94da9b904c13 | 2 | 2048
01a012e7-bed6-7151-ac28-91fcdaab31c3 | 1 |查编辑草稿:
SELECT id,
config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' AS max_tokens
FROM agent_config_drafts
WHERE agent_id = '01a012e7-bed3-7574-b0d1-a5cc79a0d3de'
ORDER BY updated_at DESC NULLS LAST;本次结果:
id | max_tokens
-------------------------------------+-----------
01a012e7-bf60-7673-a894-64551d984a23 | 2048到这里配置和报错对上了:运行时用的就是快照 version 2,值就是 2048。
5.6 第六步:解释为什么「才问一句制度」也会超 2048
用户直觉是:问句很短,怎么会超过 2048?
新 Agent 送进模型的不是用户那一行字,而是整包 Prompt,至少包括:
- 系统提示 / Agent 指令
- 本会话前面多轮问答(上下班、午休、适用范围、请假流程等)
- 本轮知识库检索到的制度原文片段
- 本轮用户问句
专题十一把 max_tokens 写成 2048 之后,新 Agent 会把这个数当成 整段上下文预算,不是「只限制模型再写 2048 个字」。
因此只要:
系统提示 + 历史 + 检索片段 + 本轮问句 >= 2048就会在 生成第 1 个 token 之前 直接失败。表现就是空泡。
这和「回答太长被截断」也不一样。截断至少会先吐字。这次是一个字都没有。
5.7 第七步:排除项清单
排查时明确否定过这些方向,避免后续再绕:
| 怀疑 | 为什么排除 |
|---|---|
| 模型还在生成,需要等 | messages.status=error,日志不再刷 |
| 需要点某个「取消任务」 | 后端已经停了,没有可取消的运行中任务 |
| Langfuse 补丁把请求弄丢 | 报错发生在调用模型之前的 token 校验,Tracing 只是旁路 |
| 知识库挂了 | 能拼出超长 Prompt,说明检索已经发生 |
| UI 把 Max Tokens 显示错了 | 快照和草稿里都是数字 2048,与报错括号一致 |
| 应该重启 Dify | 配置在数据库里,重启不会把 2048 变大 |
六、根因
根因可以收成一句话:
专题十一已经成功把该 Agent 的 max_tokens 从 null 写成 2048;在长会话 + 知识库场景下,2048 不够装完整 Prompt。新 Agent 把 max_tokens 当整段上限,于是在生成前报 Model token limit (2048) exceeded。页面没渲染这条错误,只留下空泡和 3/3,所以看起来像卡住。
分层看:
- 配置层:活动快照 / 草稿的
model.model_settings.max_tokens = 2048 - 运行层:拼好 Prompt 后做 token 校验,输入已经 >= 2048,拒绝生成
- 数据层:写入
messages.status=error和完整error文本 - 展示层:对话 UI 没有把
error展示出来,输入框仍锁在这条失败消息上
所以修法也分两层:
- 展示层 / 会话层:刷新 + 新开对话,立刻能继续问
- 配置层:把
max_tokens改回8192,长会话不再轻易顶满
七、临时处理:怎么「取消卡住」并继续问
后端没有运行中任务,不用 docker restart,也不用杀进程。
- 浏览器刷新对话页(
F5或Ctrl + F5)。 - 点左上角 新对话 / New Chat。
- 不要再在「员工打卡管理规定」这条旧会话里重试同一句。
- 不要点空泡旁的
< 3/3 >。 - 如果刷新后输入框还是发不出去:关掉该浏览器标签,重新打开 Dify 应用。
旧失败记录可以留在库里当证据,不必删 messages。
注意:只做临时处理、不改 max_tokens 的话,新会话的前几轮通常能成功;一旦历史和检索再次把 Prompt 堆过 2048,同样的空泡还会出现。所以还是要把值改大。
八、正式解法:把 max_tokens 从 2048 改回 8192
8.1 为什么还是改库,不走控制台
控制台改 Max Tokens 再点发布,正是专题十一的坑:新 Agent 可能把 max_tokens 写回 null。 null 运行时会回退插件默认 8192,看起来也像「改回去了」,但:
- UI 持久化问题还在
- 下次想改成别的数仍然改不进去
- 和专题十一的口径不一致
因此本专题继续只改数据库,而且 快照和草稿两张表一起改。
| 表 | 谁在读 | 只改一张会怎样 |
|---|---|---|
agent_config_snapshots | 运行时 | 只改草稿:对话仍按 2048 失败 |
agent_config_drafts | 配置页 UI | 只改快照:对话好了,打开抽屉又显示 2048 / 再发布可能覆盖 |
8.2 操作前准备
- 确认你改的是目标应用,不要抄错 UUID。
- 确认当前最新 snapshot / draft 的 ID(发布过就会变)。
- 先备份两份
config_snapshot全文。 - 只用
jsonb_set改max_tokens这一个字段。 - 改完不要点「构建 / 发布」。
Windows 工作区访问 207 时,普通 ssh 容易在密码提示处卡住,可用 Paramiko 脚本;人在 207 本机则直接 docker exec。
8.3 第 1 步:进入数据库
完整脚本:scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_03.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_03.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec -it docker-db_postgres-1 psql -U postgres -d dify8.4 第 2 步:查出当前 snapshot / draft ID
不要假设还是上次那些 ID,先查。
-- 最新活动快照
SELECT s.id, s.version,
s.config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' AS max_tokens
FROM agent_config_snapshots s
JOIN agents a ON s.agent_id = a.id
WHERE a.app_id = '40e95fd3-e7f5-44f2-a03b-dc3e60413540'
ORDER BY s.version DESC;
-- 草稿
SELECT d.id,
d.config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' AS max_tokens
FROM agent_config_drafts d
WHERE d.agent_id = '01a012e7-bed3-7574-b0d1-a5cc79a0d3de'
ORDER BY d.updated_at DESC NULLS LAST;本次查到的最新 ID:
- snapshot:
01a0135c-320e-79dc-97a0-94da9b904c13(version 2,值为 2048) - draft:
01a012e7-bf60-7673-a894-64551d984a23(值为 2048)
如果这里已经是 8192,不要重复更新。如果是 null,说明被发布写坏了,应先按专题十一理解 null 的含义,再决定写成多少。
8.5 第 3 步:备份两份 JSON
在宿主机导出:
完整脚本:scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_04.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_04.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c \
"SELECT config_snapshot FROM agent_config_snapshots WHERE id='01a0135c-320e-79dc-97a0-94da9b904c13'" \
> backup_snap_before_8192.json
docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c \
"SELECT config_snapshot FROM agent_config_drafts WHERE id='01a012e7-bf60-7673-a894-64551d984a23'" \
> backup_draft_before_8192.json本次备份大小约各 2677 字节。备份存在才能放心执行 UPDATE。
8.6 第 4 步:事务里同时改两张表
BEGIN;
UPDATE agent_config_snapshots
SET config_snapshot = jsonb_set(
config_snapshot::jsonb,
'{model,model_settings,max_tokens}',
'8192'::jsonb
)::text
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';
UPDATE agent_config_drafts
SET config_snapshot = jsonb_set(
config_snapshot::jsonb,
'{model,model_settings,max_tokens}',
'8192'::jsonb
)::text
WHERE id = '01a012e7-bf60-7673-a894-64551d984a23';
COMMIT;成功时应看到:
BEGIN
UPDATE 1
UPDATE 1
COMMIT如果出现 UPDATE 0,立刻:
ROLLBACK;然后重新核对 ID。不要在事务外盲跑第二条。
8.7 这些 SQL 在干什么
和专题十一相同,只是目标值从 2048 换成 8192:
| 写法 | 作用 |
|---|---|
config_snapshot::jsonb | 原列是 text,先转 jsonb 才能改路径 |
jsonb_set(...) | 只改一个节点,其它配置保持不动 |
'{model,model_settings,max_tokens}' | JSON 路径三层 |
'8192'::jsonb | 写成 JSON 数字,不要写成 '"8192"' 字符串 |
)::text | 再转回 text 写回原列 |
错误示范:
-- 错:字符串 8192,后续可能当文本而不是数字
'"8192"'::jsonb8.8 第 5 步:数据库验证
SELECT 'snapshot' AS which,
id,
config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' AS max_tokens
FROM agent_config_snapshots
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13'
UNION ALL
SELECT 'draft',
id,
config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens'
FROM agent_config_drafts
WHERE id = '01a012e7-bf60-7673-a894-64551d984a23';正确结果:
which | id | max_tokens
---------+--------------------------------------+-----------
snapshot | 01a0135c-320e-79dc-97a0-94da9b904c13 | 8192
draft | 01a012e7-bf60-7673-a894-64551d984a23 | 8192再看完整 model_settings,确认只动了这一个字段:
SELECT config_snapshot::json -> 'model' -> 'model_settings'
FROM agent_config_snapshots
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';本次实际输出:
{
"stop": null,
"top_p": null,
"max_tokens": 8192,
"temperature": null,
"response_format": null,
"presence_penalty": null,
"frequency_penalty": null
}8.9 第 6 步:控制台和运行验证
- 强制刷新 Dify 控制台(
Ctrl + F5)。 - 打开该 Agent 配置页,看 Max Tokens 是否显示 8192。
- 不要点发布。
- 打开应用对话页,新开对话,再问带知识库的制度类问题。
- 旧的失败会话不用验证,它里面的历史仍然很长,也已经是
error。
运行验证通过的标准:
- 气泡有正文,不是空的
messages.status为成功态(通常是normal)messages.error为空- 日志里不再出现
token limit (2048)
九、为什么改完不要再点发布
新 Agent 的发布动作可能再次把 max_tokens 存成 null。
null 的运行时效果是回退插件默认 8192,短时间看起来「还能用」,但:
- 配置页会再次显示成默认值,你无法确认库里到底是数字还是空
- 下次再想改成 2048 / 4096 仍然写不进去
- 和专题十一同一条坑
正确收尾:
- 改库
- 验证库
- 强刷 UI
- 新开对话测试
不要多做「发布确认」。
十、回滚方法
如果改错 Agent,或需要回到 2048:
10.1 整段 JSON 回滚(最稳)
用改前备份覆盖:
BEGIN;
UPDATE agent_config_snapshots
SET config_snapshot = '这里粘贴 backup_snap_before_8192.json 的完整内容'
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';
UPDATE agent_config_drafts
SET config_snapshot = '这里粘贴 backup_draft_before_8192.json 的完整内容'
WHERE id = '01a012e7-bf60-7673-a894-64551d984a23';
COMMIT;JSON 里的单引号要按 SQL 规则写成 ''。更稳的是脚本读文件再执行。
10.2 只把数字改回 2048
BEGIN;
UPDATE agent_config_snapshots
SET config_snapshot = jsonb_set(
config_snapshot::jsonb,
'{model,model_settings,max_tokens}',
'2048'::jsonb
)::text
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';
UPDATE agent_config_drafts
SET config_snapshot = jsonb_set(
config_snapshot::jsonb,
'{model,model_settings,max_tokens}',
'2048'::jsonb
)::text
WHERE id = '01a012e7-bf60-7673-a894-64551d984a23';
COMMIT;回滚后长会话 + 知识库仍可能再次空泡。这是预期行为。
十一、以后再遇到「空泡卡住」怎么排
把这次流程收成可重复的检查单:
- 先查
messages,不要先重启。 status=error就按已失败处理;先读error原文。- UI 上的
n/n先当成重试计数,去库里数相同query的失败条数。 - 报
token limit (N)时,去快照里核对max_tokens是不是就是 N。 - 查快照必须
JOIN agents,不要找不存在的snapshots.app_id。 - 同时看草稿,避免 UI 和运行时各用各的值。
- 临时:刷新 + 新对话。正式:两张表一起
jsonb_set。 - 改完验证数字类型是 JSON number,然后 不要发布。
如果 error 不是 token limit,不要套用本专题的改库值。只把「先看 messages.error」这一步复用。
十二、本次环境实录
12.1 故障当时
- 会话已有多轮打卡 / 请假类问答
- 新问句:「法定假日加班的制度是什么」
- 同一句连续 3 条
messages.status=error - 错误原文:
Model token limit (2048) exceeded before any response was generated. - 页面空泡 +
3/3+ 无法发送
12.2 改库当时
| 表 | ID | 改前 | 改后 | UPDATE |
|---|---|---|---|---|
agent_config_snapshots | 01a0135c-320e-79dc-97a0-94da9b904c13 | 2048 | 8192 | UPDATE 1 |
agent_config_drafts | 01a012e7-bf60-7673-a894-64551d984a23 | 2048 | 8192 | UPDATE 1 |
备份文件:
backup_snap_before_8192.jsonbackup_draft_before_8192.json
12.3 现成可执行版本(仅限本 Agent、本快照仍在时)
BEGIN;
UPDATE agent_config_snapshots
SET config_snapshot = jsonb_set(
config_snapshot::jsonb,
'{model,model_settings,max_tokens}',
'8192'::jsonb
)::text
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';
UPDATE agent_config_drafts
SET config_snapshot = jsonb_set(
config_snapshot::jsonb,
'{model,model_settings,max_tokens}',
'8192'::jsonb
)::text
WHERE id = '01a012e7-bf60-7673-a894-64551d984a23';
COMMIT;换别的 Agent 时,只替换三个量:snapshot_id、draft_id、目标数字。
十三、常见误区
- 看见空泡就重启 api / agent_backend
这次任务已经结束,重启既看不清 error,也改不了 max_tokens。
- 在旧会话里反复重试
旧会话 Prompt 已经超限,重试只会再写一条 error,3/3 变成更多失败。
- 只改快照或只改草稿
运行时和 UI 会不一致,下一次发布还可能互相覆盖。
- 改完去点发布「让它生效」
可能把 8192 写回 null。
- 把
max_tokens理解成「回答最多写这么长」
在这个新 Agent 实现里,它更像整段上下文预算。输入先占满,回答一个字都没有。
- 以为 2048 对短问句一定够
问句短,但系统提示、历史、知识库片段加起来不短。
- 从 Windows 直接
ssh root@192.168.110.207卡在密码
这是本环境已知问题,改用 Paramiko 或到 207 本机操作。与本次 token 故障无关。
十四、结论
- 空泡 +
3/3+ 发不出新问题,优先查messages.status和messages.error。 - 这次不是卡住,是同一句失败了 3 次,错误为
Model token limit (2048) exceeded before any response was generated. - 2048 来自专题十一的改库结果;长会话 + 知识库后输入超过该上限。
- 临时处理:刷新,新开对话。
- 正式处理:同时把
agent_config_snapshots和agent_config_drafts的max_tokens改成8192。 - 改完验证库,强刷 UI,不要再点发布。
本专题 Python 程序一览
| 文件 | 用途 | |---|---|| 专题十二_01_查失败消息.py | 专题十二 · 步骤 1:查最近 messages,判断空泡是卡住还是已失败。 | | 专题十二_02_查Agent_max_tokens.py | 专题十二 · 步骤 2:核对 Agent 活动快照与草稿 max_tokens(应对上 error 里的数字)。 | | 专题十二_03_备份改前JSON.py | 专题十二 · 步骤 3:改回 8192 前备份快照与草稿 JSON。 | | 专题十二_04_写max_tokens为8192_修改后.py | 专题十二 · 步骤 4 · 修改后:把 max_tokens 从 2048 改回 8192(快照+草稿,勿点发布)。 | | 专题十二_05_验证max_tokens改回.py | 专题十二 · 步骤 5:验证 max_tokens 已改回 8192,并扫 api 日志无 token limit。 |
专题十二_01_查失败消息.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_01_查失败消息.py
完整补丁:patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_01.py
运行/引用:见 patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_01.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十二 · 步骤 1:查最近 messages,判断空泡是卡住还是已失败。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
sql = (
"docker exec %s psql -U postgres -d dify -c "
"\"SELECT id, conversation_id, status, answer IS NOT NULL AS has_answer, "
"left(query, 80) AS q, left(error, 180) AS err, created_at "
"FROM messages ORDER BY created_at DESC LIMIT 8;\"" % C.PG_CONTAINER
)
out, err = d.sh(sql)
print(out or err)
if "token limit" in (out or "").lower():
print("\n[提示] 发现 token limit 报错 —— 继续运行专题十二后续脚本")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十二_02_查Agent_max_tokens.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_02_查Agent_max_tokens.py
完整补丁:patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_02.py
运行/引用:见 patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_02.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十二 · 步骤 2:核对 Agent 活动快照与草稿 max_tokens(应对上 error 里的数字)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
snap_sql = (
"docker exec %s psql -U postgres -d dify -c "
"\"SELECT s.id, s.version, "
"s.config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' AS max_tokens "
"FROM agent_config_snapshots s JOIN agents a ON s.agent_id = a.id "
"WHERE a.app_id = '%s' ORDER BY s.version DESC;\"" % (C.PG_CONTAINER, C.APP_ID)
)
out1, _ = d.sh(snap_sql)
print("=== 活动快照 ===")
print(out1.strip())
draft_sql = (
"docker exec %s psql -U postgres -d dify -c "
"\"SELECT id, config_snapshot::json -> 'model' -> 'model_settings' -> 'max_tokens' "
"AS max_tokens FROM agent_config_drafts WHERE agent_id = '%s' "
"ORDER BY updated_at DESC NULLS LAST;\"" % (C.PG_CONTAINER, C.AGENT_ID)
)
out2, _ = d.sh(draft_sql)
print("\n=== 编辑草稿 ===")
print(out2.strip())
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十二_03_备份改前JSON.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_03_备份改前JSON.py
完整补丁:patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_03.py
运行/引用:见 patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_03.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十二 · 步骤 3:改回 8192 前备份快照与草稿 JSON。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _agent_db as A
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
A.backup_snapshot(d, "agent_config_snapshots", C.SNAP_ID, "snap_before_8192")
A.backup_snapshot(d, "agent_config_drafts", C.DRAFT_ID, "draft_before_8192")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十二_04_写max_tokens为8192_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_04_写max_tokens为8192_修改后.py
完整补丁:patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_04.py
运行/引用:见 patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_04.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十二 · 步骤 4 · 修改后:把 max_tokens 从 2048 改回 8192(快照+草稿,勿点发布)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _agent_db as A
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
sql = "BEGIN;\n"
for table, rid in (
("agent_config_snapshots", C.SNAP_ID),
("agent_config_drafts", C.DRAFT_ID),
):
sql += (
"UPDATE %s SET config_snapshot = jsonb_set("
"config_snapshot::jsonb, '{model,model_settings,max_tokens}', '%s'::jsonb)::text "
"WHERE id='%s';\n" % (table, C.MAX_TOKENS_DEFAULT, rid)
)
sql += "COMMIT;\n"
print(A.psql(d, sql))
A.verify_max_tokens(d, C.SNAP_ID, C.DRAFT_ID)
print("\n[WARN] 改完不要点「构建/发布」;请新开对话验证")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十二_05_验证max_tokens改回.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_05_验证max_tokens改回.py
完整补丁:patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_05.py
运行/引用:见 patches_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_05.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十二 · 步骤 5:验证 max_tokens 已改回 8192,并扫 api 日志无 token limit。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _agent_db as A
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
A.verify_max_tokens(d, C.SNAP_ID, C.DRAFT_ID)
snap = A.read_max_tokens(d, "agent_config_snapshots", C.SNAP_ID)
draft = A.read_max_tokens(d, "agent_config_drafts", C.DRAFT_ID)
ok = str(snap) == str(C.MAX_TOKENS_DEFAULT) and str(draft) == str(C.MAX_TOKENS_DEFAULT)
out, _ = d.sh(
"docker logs --since 30m docker-api-1 2>&1 | grep -i 'token limit' | tail -5"
)
print("\n=== 最近 api token limit 日志 ===")
print(out.strip() or "(无新 token limit 日志)")
print("\n[OK]" if ok else "\n[FAIL]", "期望 snapshot/draft 均为", C.MAX_TOKENS_DEFAULT)
return 0 if ok else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())