📎 配套脚本附件正文未内嵌 · 共 12 个 · 点开可在线查看
📚 另需公共库(正文未重复内嵌,点名字直接看):_agent_cfg.py、_bootstrap.py、_config.py、_plugin_util.py
📂 查看全部 761 个脚本附件 →
⚠️ 脚本内含明文凭据(与本页正文一致),请勿在公网环境直接运行。
Dify · 专题十二

新 Agent 对话空泡卡住
max_tokens 过小与改回 8192

先判断卡住还是已失败,再从 messages.error 定位 token limit。步骤未改,代码块已换成完整程序。

专题十二:新 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。


目录

  1. 一、本文解决什么问题
  2. 二、和专题十一的关系
  3. 三、本次环境
  4. 四、现象
  5. 五、完整排查思路
  6. 六、根因
  7. 七、临时处理:怎么取消卡住并继续问
  8. 八、正式解法:把 max_tokens 从 2048 改回 8192
  9. 九、为什么改完不要再点发布
  10. 十、回滚方法
  11. 十一、以后再遇到空泡卡住怎么排
  12. 十二、本次环境实录
  13. 十三、常见误区
  14. 十四、结论

一、本文解决什么问题

这次要同时解决两件事:

  1. 对话页看起来卡住:空气泡、3/3、不能发新问题。要能判断后台到底还在跑,还是已经失败;失败时从哪看原因、怎么把前端状态清掉。
  2. 根因修复:该 Agent 的 max_tokens 被专题十一改成了 2048,长会话 + 知识库检索后输入已经超过这个上限。需要把它改回 8192,并且改完后仍然不要点「发布」。

验收标准:


二、和专题十一的关系

先把两篇专题分开,避免混在一起:

专题用户当时看到的现象库里的真实情况处理
专题十一控制台把 Max Tokens 改成 2048,点发布后又变回 8192max_tokens 仍是 null,运行时回退插件默认 8192改库,把 null 写成 2048
本专题十二对话空泡、3/3、发不出新问题max_tokens 已经是 2048,长 Prompt 超限先确认是 error,再改库把 2048 写成 8192

专题十一修的是 UI 不落库。 专题十二修的是 落库成功之后,2048 对带知识库的长会话太小。

两篇用的是同一套改法:

专题十一的原文不要改。那篇记录的是当时把值写成 2048 的过程。本篇是后续故障和改回 8192。


三、本次环境

项值
服务器192.168.110.207
Dify1.16.1,入口 http://192.168.110.207
应用播城行政问答智能体
应用 ID40e95fd3-e7f5-44f2-a03b-dc3e60413540
Agent ID01a012e7-bed3-7574-b0d1-a5cc79a0d3de
应用类型新 Agent(mode=agent)
模型qwen3.6-27b,provider langgenius/openai/openai
活动快照 ID01a0135c-320e-79dc-97a0-94da9b904c13(version=2)
编辑草稿 ID01a012e7-bf60-7673-a894-64551d984a23
Postgres 容器docker-db_postgres-1
数据库dify,用户 postgres
改前 max_tokens2048
改后 max_tokens8192
触发问句「法定假日加班的制度是什么」
所在会话名「员工打卡管理规定」(长会话,前面已问过上下班、午休、适用范围、请假流程)

相关容器:

容器这次排查时的作用
docker-db_postgres-1看 messages.error、改配置 JSON,最关键
docker-api-1对话 API 日志,能搜到 token limit
docker-agent_backend-1新 Agent 运行时日志
docker-worker-1Celery / Tracing,这次与故障无关,只是排除项

下面的 UUID 是本次 207 环境的实录。换 Agent 或重新发布后 ID 可能变,必须先查再改,不要直接抄 ID。


四、现象

用户在应用对话页看到:

  1. 发出「法定假日加班的制度是什么」后,回复气泡是空的,没有任何文字。
  2. 气泡旁出现 3/3。
  3. 输入框发不出新问题,整页像卡住。
  4. 页面没有弹出红色错误条,所以第一直觉是「模型还在跑、超时、或前端死锁」。

这和「模型正在流式输出但很慢」长得很像,但本质不同:

这次属于第二种。页面没把 error 渲染出来,只留下一个空泡和重试计数 3/3。


五、完整排查思路

导读:对话空泡/3/3/token limit → 先 §七 取消卡住,再 §八 改 max_tokens;根因与 专题十一 成对阅读。

现象章节
界面卡住、无正文§四现象 → §七临时处理
Langfuse 见 thinking 占满§六根因
改完又变 2048勿点发布;查作业 4 是否改过快照

定位时不要先去改配置,也不要先重启容器。按「先排除还在跑,再看失败原因,再把失败原因连回配置」这条线走。

5.0 思路总览

text
页面空泡 + 发不出新问题
        │
        ▼
① 查 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:

5.1 第一步:查最近消息,判断卡住还是已失败

在 207 上执行:

完整脚本:scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_01.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_01.sh

bash
#!/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;
"

本次关键观察:

到这里就可以下第一个结论:

后台已经结束,没有需要取消的正在跑的任务。页面卡住是前端还停在失败态。

5.2 第二步:把 3/3 对上数据库

3/3 不是「第 3 步 / 共 3 步」,也不是知识库检索进度。

它是:同一句话自动或手动重试了 3 次,当前看到的是第 3 次失败。

核对方法:按 query 和 created_at 看最近是不是连续 3 条相同问句,且都是 error。

不要再点气泡旁的 < 3/3 > 来回翻。那只是在 3 条失败记录之间切换,不会让模型重新成功。

5.3 第三步:读 error 原文,不要先猜超时

本次三条失败记录的错误都是同一句:

text
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或者缩短输入:新开对话、少带历史、少检索片段

这条错误 不是:

知识库检索其实已经做完了。正是检索结果和多轮历史一起塞进 Prompt,才把输入顶过 2048。

5.4 第四步:用日志交叉验证,排除「还卡在某层」

库表已经够定性,日志用来排除其它层。

完整脚本:scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_02.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十二_max_tokens过小导致空泡卡住/专题十二_内联_02.sh

bash
#!/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

本次结果:

如果这三路日志都没有 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。

查活动快照:

sql
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;

本次结果:

text
id                                   | version | max_tokens
-------------------------------------+---------+-----------
01a0135c-320e-79dc-97a0-94da9b904c13 |       2 | 2048
01a012e7-bed6-7151-ac28-91fcdaab31c3 |       1 |

查编辑草稿:

sql
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;

本次结果:

text
id                                   | max_tokens
-------------------------------------+-----------
01a012e7-bf60-7673-a894-64551d984a23 | 2048

到这里配置和报错对上了:运行时用的就是快照 version 2,值就是 2048。

5.6 第六步:解释为什么「才问一句制度」也会超 2048

用户直觉是:问句很短,怎么会超过 2048?

新 Agent 送进模型的不是用户那一行字,而是整包 Prompt,至少包括:

  1. 系统提示 / Agent 指令
  2. 本会话前面多轮问答(上下班、午休、适用范围、请假流程等)
  3. 本轮知识库检索到的制度原文片段
  4. 本轮用户问句

专题十一把 max_tokens 写成 2048 之后,新 Agent 会把这个数当成 整段上下文预算,不是「只限制模型再写 2048 个字」。

因此只要:

text
系统提示 + 历史 + 检索片段 + 本轮问句  >= 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,所以看起来像卡住。

分层看:

  1. 配置层:活动快照 / 草稿的 model.model_settings.max_tokens = 2048
  2. 运行层:拼好 Prompt 后做 token 校验,输入已经 >= 2048,拒绝生成
  3. 数据层:写入 messages.status=error 和完整 error 文本
  4. 展示层:对话 UI 没有把 error 展示出来,输入框仍锁在这条失败消息上

所以修法也分两层:


七、临时处理:怎么「取消卡住」并继续问

后端没有运行中任务,不用 docker restart,也不用杀进程。

  1. 浏览器刷新对话页(F5 或 Ctrl + F5)。
  2. 点左上角 新对话 / New Chat。
  3. 不要再在「员工打卡管理规定」这条旧会话里重试同一句。
  4. 不要点空泡旁的 < 3/3 >。
  5. 如果刷新后输入框还是发不出去:关掉该浏览器标签,重新打开 Dify 应用。

旧失败记录可以留在库里当证据,不必删 messages。

注意:只做临时处理、不改 max_tokens 的话,新会话的前几轮通常能成功;一旦历史和检索再次把 Prompt 堆过 2048,同样的空泡还会出现。所以还是要把值改大。


八、正式解法:把 max_tokens 从 2048 改回 8192

8.1 为什么还是改库,不走控制台

控制台改 Max Tokens 再点发布,正是专题十一的坑:新 Agent 可能把 max_tokens 写回 null。 null 运行时会回退插件默认 8192,看起来也像「改回去了」,但:

因此本专题继续只改数据库,而且 快照和草稿两张表一起改。

表谁在读只改一张会怎样
agent_config_snapshots运行时只改草稿:对话仍按 2048 失败
agent_config_drafts配置页 UI只改快照:对话好了,打开抽屉又显示 2048 / 再发布可能覆盖

8.2 操作前准备

  1. 确认你改的是目标应用,不要抄错 UUID。
  2. 确认当前最新 snapshot / draft 的 ID(发布过就会变)。
  3. 先备份两份 config_snapshot 全文。
  4. 只用 jsonb_set 改 max_tokens 这一个字段。
  5. 改完不要点「构建 / 发布」。

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

bash
#!/usr/bin/env bash
set -euo pipefail

docker exec -it docker-db_postgres-1 psql -U postgres -d dify

8.4 第 2 步:查出当前 snapshot / draft ID

不要假设还是上次那些 ID,先查。

sql
-- 最新活动快照
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:

如果这里已经是 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

bash
#!/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 步:事务里同时改两张表

sql
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;

成功时应看到:

text
BEGIN
UPDATE 1
UPDATE 1
COMMIT

如果出现 UPDATE 0,立刻:

sql
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 写回原列

错误示范:

sql
-- 错:字符串 8192,后续可能当文本而不是数字
'"8192"'::jsonb

8.8 第 5 步:数据库验证

sql
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';

正确结果:

text
which    | id                                   | max_tokens
---------+--------------------------------------+-----------
snapshot | 01a0135c-320e-79dc-97a0-94da9b904c13 | 8192
draft    | 01a012e7-bf60-7673-a894-64551d984a23 | 8192

再看完整 model_settings,确认只动了这一个字段:

sql
SELECT config_snapshot::json -> 'model' -> 'model_settings'
FROM agent_config_snapshots
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';

本次实际输出:

json
{
  "stop": null,
  "top_p": null,
  "max_tokens": 8192,
  "temperature": null,
  "response_format": null,
  "presence_penalty": null,
  "frequency_penalty": null
}

8.9 第 6 步:控制台和运行验证

  1. 强制刷新 Dify 控制台(Ctrl + F5)。
  2. 打开该 Agent 配置页,看 Max Tokens 是否显示 8192。
  3. 不要点发布。
  4. 打开应用对话页,新开对话,再问带知识库的制度类问题。
  5. 旧的失败会话不用验证,它里面的历史仍然很长,也已经是 error。

运行验证通过的标准:


九、为什么改完不要再点发布

新 Agent 的发布动作可能再次把 max_tokens 存成 null。

null 的运行时效果是回退插件默认 8192,短时间看起来「还能用」,但:

正确收尾:

  1. 改库
  2. 验证库
  3. 强刷 UI
  4. 新开对话测试

不要多做「发布确认」。


十、回滚方法

如果改错 Agent,或需要回到 2048:

10.1 整段 JSON 回滚(最稳)

用改前备份覆盖:

sql
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

sql
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;

回滚后长会话 + 知识库仍可能再次空泡。这是预期行为。


十一、以后再遇到「空泡卡住」怎么排

把这次流程收成可重复的检查单:

  1. 先查 messages,不要先重启。
  2. status=error 就按已失败处理;先读 error 原文。
  3. UI 上的 n/n 先当成重试计数,去库里数相同 query 的失败条数。
  4. 报 token limit (N) 时,去快照里核对 max_tokens 是不是就是 N。
  5. 查快照必须 JOIN agents,不要找不存在的 snapshots.app_id。
  6. 同时看草稿,避免 UI 和运行时各用各的值。
  7. 临时:刷新 + 新对话。正式:两张表一起 jsonb_set。
  8. 改完验证数字类型是 JSON number,然后 不要发布。

如果 error 不是 token limit,不要套用本专题的改库值。只把「先看 messages.error」这一步复用。


十二、本次环境实录

12.1 故障当时

12.2 改库当时

表ID改前改后UPDATE
agent_config_snapshots01a0135c-320e-79dc-97a0-94da9b904c1320488192UPDATE 1
agent_config_drafts01a012e7-bf60-7673-a894-64551d984a2320488192UPDATE 1

备份文件:

12.3 现成可执行版本(仅限本 Agent、本快照仍在时)

sql
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、目标数字。


十三、常见误区

  1. 看见空泡就重启 api / agent_backend

这次任务已经结束,重启既看不清 error,也改不了 max_tokens。

  1. 在旧会话里反复重试

旧会话 Prompt 已经超限,重试只会再写一条 error,3/3 变成更多失败。

  1. 只改快照或只改草稿

运行时和 UI 会不一致,下一次发布还可能互相覆盖。

  1. 改完去点发布「让它生效」

可能把 8192 写回 null。

  1. 把 max_tokens 理解成「回答最多写这么长」

在这个新 Agent 实现里,它更像整段上下文预算。输入先占满,回答一个字都没有。

  1. 以为 2048 对短问句一定够

问句短,但系统提示、历史、知识库片段加起来不短。

  1. 从 Windows 直接 ssh root@192.168.110.207 卡在密码

这是本环境已知问题,改用 Paramiko 或到 207 本机操作。与本次 token 故障无关。


十四、结论

  1. 空泡 + 3/3 + 发不出新问题,优先查 messages.status 和 messages.error。
  2. 这次不是卡住,是同一句失败了 3 次,错误为 Model token limit (2048) exceeded before any response was generated.
  3. 2048 来自专题十一的改库结果;长会话 + 知识库后输入超过该上限。
  4. 临时处理:刷新,新开对话。
  5. 正式处理:同时把 agent_config_snapshots 和 agent_config_drafts 的 max_tokens 改成 8192。
  6. 改完验证库,强刷 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

python
#!/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

python
#!/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

python
#!/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

python
#!/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

python
#!/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())