fix: 修复文本解析兜底正则不匹配模型实际输出格式

模型实际输出格式:
  Action: write_file
  Action Input: {"path": "测试.md", "content": ""}

原正则用 [\r\n]+ 强制要求换行,且 Action: 后的冒号和
Action Input: 的冒号处理冲突导致匹配失败。
改为 [\r\n\s]* 灵活匹配空白/换行。
This commit is contained in:
thzxx
2026-04-18 08:21:08 +08:00
parent 445695c6e1
commit 4e158e8783
+7 -22
View File
@@ -104,10 +104,13 @@ const VALID_TOOL_NAMES = new Set([
function parseToolCallsFromText(content: string): ToolCall[] { function parseToolCallsFromText(content: string): ToolCall[] {
const calls: ToolCall[] = []; const calls: ToolCall[] = [];
// 匹配模式1: **Action:** tool_name **Action Input:** {json} // 匹配Action: tool_name(可选加粗标记)
// 匹配模式2: Action: tool_name Action Input: {json} // 然后紧跟 Action Input: {json}
// 同时支持 Action 和 Action Input 写在同一段或多行 // 支持格式:
const actionRegex = /\*?\*?Action:?\*?\*?\s*([\w]+)\s*[\r\n]+\s*\*?\*?Action\s*Input:?\*?\*?\s*(\{[\s\S]*?\})/gi; // Action: write_file Action: write_file
// Action Input: {...} **Action Input:** {...}
// 也支持写在同一段落的情况
const actionRegex = /\*{0,2}Action:?\*{0,2}\s*(\w+)\s+[\r\n\s]*\*{0,2}Action\s*Input:?\*{0,2}\s*(\{[\s\S]*?\})/gi;
let match; let match;
while ((match = actionRegex.exec(content)) !== null) { while ((match = actionRegex.exec(content)) !== null) {
@@ -127,7 +130,6 @@ function parseToolCallsFromText(content: string): ToolCall[] {
} catch { } catch {
// JSON 解析失败,尝试修复常见问题 // JSON 解析失败,尝试修复常见问题
try { try {
// 处理单引号、尾逗号等
let fixed = argsStr let fixed = argsStr
.replace(/'/g, '"') .replace(/'/g, '"')
.replace(/,\s*}/g, '}') .replace(/,\s*}/g, '}')
@@ -146,23 +148,6 @@ function parseToolCallsFromText(content: string): ToolCall[] {
} }
} }
// 备选模式:Action 和 Action Input 写在一行或用不同分隔
if (calls.length === 0) {
const singleLineRegex = /Action:\s*(\w+)\s*.*?Action\s*Input:\s*(\{[^}]+\})/gi;
while ((match = singleLineRegex.exec(content)) !== null) {
const toolName = match[1].trim();
const argsStr = match[2].trim();
if (!VALID_TOOL_NAMES.has(toolName)) continue;
try {
const args = JSON.parse(argsStr);
calls.push({
type: 'function',
function: { name: toolName, arguments: args }
});
} catch { /* skip */ }
}
}
if (calls.length > 0) { if (calls.length > 0) {
logInfo(`文本解析兜底: 从回复中提取到 ${calls.length} 个工具调用`, calls.map(c => c.function.name).join(', ')); logInfo(`文本解析兜底: 从回复中提取到 ${calls.length} 个工具调用`, calls.map(c => c.function.name).join(', '));
} }