<p>必须先清洗再解析大模型的CoT输出:清除零宽字符、统一换行、提取json块;用JSON解析器容错处理,降级修复引号问题;流式响应需累积至括号平衡再解析;CoT内容可信度需业务规则过滤。</p>

大模型返回的思考链(CoT)不是标准 JSON,直接 json_decode() 会失败——必须先清洗再解析。
识别 CoT 输出中的非 JSON 干扰段
主流大模型(如 Qwen、GLM、本地 Llama)在开启 think 或 reasoning 模式时,常以自然语言描述推理过程,夹杂换行、缩进、中文标点、甚至“答:”“所以:”等引导词。这些内容会破坏 JSON 结构,导致 json_last_error() 返回 JSON_ERROR_SYNTAX。
典型干扰片段:
{"reasoning": "第一步:提取价格……
第二步:比较数值大小……
所以最终答案是:42"}注意:"所以最终答案是:42" 中的中文冒号、换行符、多余空格,都可能让部分解析器拒绝处理。
从零搭建飞书机器人。支持 MiniMax/MiMo 等模型、工具调用(搜索/天气/百科/记忆)、Skill 架构。一站式交付可上线运行的飞书群聊 bot。基础版本,后续可自行升级能力
立即学习“PHP免费学习笔记(深入)”;
- 用
preg_replace('/[x{200B}-x{200D}x{FEFF}]/u', '', $raw)清除零宽字符(常见于 API 响应中不可见的 BOM 或分隔符) - 用
str_replace([" ", " "], " ", $raw)统一行尾,避免 Windows/Linux 换行差异影响正则匹配 - 若响应含 Markdown 风格的 ```json``` 包裹块,优先用
preg_match('/```jsons*([sS]*?)s*```/', $raw, $m)提取内部纯 JSON 字符串
安全提取 reasoning 字段并保留原始语义
不要用 substr() 或 strpos() 硬切字符串——CoT 内容可能含嵌套引号、转义反斜杠或意外的 "reasoning": 字样(比如在用户输入里)。必须依赖 JSON 解析器本身的容错能力。
- 先尝试
json_decode($cleaned, true, 512, JSON_THROW_ON_ERROR),捕获JsonException后进入降级流程 - 降级时用
json_decode(preg_replace('/"(reasoning|thought|steps)":s*"/', '"$1":"', $cleaned), true)修复常见引号不闭合问题(仅限单层结构) - 若字段值含未转义双引号(如
"reasoning": "他说:"这个数很大""),需预处理:用mb_ereg_replace('"([^"\\]*(?:\\.[^"\\]*)*)"', '""', $cleaned)跳过引号内内容再替换外部引号
处理流式响应(SSE)中的分段 CoT 数据
当使用 text/event-stream 接收流式 CoT(如 Ollama、vLLM 的 /chat/completions 流式接口),data: 行可能截断在 JSON 字段中间,导致每帧单独解析失败。
- 累积
data:行到出现完整"}"且括号层级为 0 时再解析(用计数器跟踪{和}差值) - 不要依赖
json_decode($line, true)单行解析——90% 的流式 CoT 帧是不完整的 JSON 片段 - 示例判断逻辑:
$depth += substr_count($line, '{') - substr_count($line, '}'); if ($depth === 0 && !empty($buffer)) { json_decode($buffer, true); $buffer = ''; }
真正难的不是解析,而是判断哪一段是「有效推理」、哪一段是模型胡说——CoT 字段内容可信度无法靠 PHP 校验,得靠业务规则过滤,比如检查是否包含数字计算步骤、是否引用了输入中的关键词。别把 json_decode() 当成信任入口。


















