必须先清洗再解析,不能直接 json_decode()——cot输出含自然语言、中文标点、换行、零宽字符及markdown块,需清除不可见字符、统一换行、优先提取json块,再容错解析并校验字段,流式响应中需累积平衡括号,最后过滤可信度。

必须先清洗再解析,不能直接 json_decode()——大模型的思考链(CoT)输出不是标准 JSON,夹杂自然语言、中文标点、换行、零宽字符甚至 Markdown 包裹块,硬解析必然失败。
清洗干扰内容再提取 JSON 块
CoT 常见格式是:一段推理描述 + ```json{"reasoning":"第一步..."} ``` 或直接混在文字里。重点不是“有没有 JSON”,而是“怎么干净地捞出来”:
- 用
preg_replace('/[x{200B}-x{200D}x{FEFF}]/u', '', $raw)清除零宽空格、BOM 等不可见字符 - 统一换行:
str_replace([" ", " "], " ", $raw),避免正则跨行匹配失效 - 优先提取 Markdown 的
```json ... ```块:preg_match('/```jsons*([sS]*?)s*```/', $raw, $m),取$m[1] - 若无包裹块,用正则粗略定位:
preg_match('/{(?:[^{}]|(?R))*}/', $raw, $m)(需 PCRE 递归支持),或保守匹配首尾大括号内的最长闭合结构
容错解析 JSON 字符串
即使提取出 JSON 片段,也可能因引号不闭合、中文冒号、未转义双引号而解析失败。不能放弃,要降级处理:
- 首选:
json_decode($cleaned, true, 512, JSON_THROW_ON_ERROR),捕获JsonException进入修复流程 - 修复常见引号问题:用
preg_replace('/"(reasoning|thought|steps)":s*"/', '"$1":"', $cleaned)补齐缺失引号(仅限单层) - 处理字段内含未转义双引号(如
"他说:"这个数很大""):先跳过引号内部,再替换外部引号,例如用mb_ereg_replace('"([^"\\]*(?:\\.[^"\\]*)*)"', '" "', $cleaned) - 解析后务必检查键是否存在:
isset($data['reasoning']) && is_string($data['reasoning']),避免空值或类型错乱
流式响应中累积并平衡括号
对接 SSE(text/event-stream)时,CoT 数据是分段到达的,可能中途断在 JSON 中间。不能每段都尝试解析:
- 维护一个字符串缓冲区,持续追加
data:后的内容 - 每次追加后,统计左括号
{和右括号}数量,仅当两者相等且大于 0 时才尝试解析 - 解析成功后清空缓冲区;失败则继续等待下一段——防止把半截 JSON 当完整数据处理
- 设超时或长度上限(如累计超 8KB 仍未平衡),主动丢弃并告警,防卡死
业务层过滤 CoT 内容可信度
解析出来的 reasoning 字段只是文本,不代表逻辑正确。需结合业务规则做可信判断:
- 检查是否含明显矛盾语句(如同时出现“大于100”和“小于50”)
- 验证步骤数与结论匹配度:若
reasoning只有 1 步,但声称“经三步推导”,可标记低可信 - 对关键数值型推理,提取数字并用
bcadd()等 BCMath 函数复核计算过程(避免浮点误差干扰判断) - 若用于决策场景,建议保留原始 CoT 文本存档,而非仅存最终答案——便于后续审计与模型调优
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











