validateMessageHistory 验证消息历史格式是否正确 DeepSeek 等 API 要求:每个包含 tool_calls 的 assistant 消息后必须紧跟对应的 tool_result 消息 注意:__parse_error__ 标记的消息不在这里处理,而是在 convertMessages 中替换为有效 JSON
(messages []types.Message)
| 1621 | // DeepSeek 等 API 要求:每个包含 tool_calls 的 assistant 消息后必须紧跟对应的 tool_result 消息 |
| 1622 | // 注意:__parse_error__ 标记的消息不在这里处理,而是在 convertMessages 中替换为有效 JSON |
| 1623 | func (a *Agent) validateMessageHistory(messages []types.Message) bool { |
| 1624 | for i, msg := range messages { |
| 1625 | // 检查是否有 tool_calls,并收集所有 tool_call IDs |
| 1626 | var toolCallIDs []string |
| 1627 | hasToolCalls := false |
| 1628 | for _, block := range msg.ContentBlocks { |
| 1629 | if toolUse, ok := block.(*types.ToolUseBlock); ok { |
| 1630 | hasToolCalls = true |
| 1631 | toolCallIDs = append(toolCallIDs, toolUse.ID) |
| 1632 | // 注意:不再检查 __parse_error__,让 convertMessages 处理 |
| 1633 | } |
| 1634 | } |
| 1635 | |
| 1636 | if hasToolCalls { |
| 1637 | // 如果是最后一条消息,无效(tool_calls 必须有 response) |
| 1638 | if i+1 >= len(messages) { |
| 1639 | return false |
| 1640 | } |
| 1641 | |
| 1642 | // 检查紧接着的下一条消息是否包含 tool_result |
| 1643 | nextMsg := messages[i+1] |
| 1644 | var toolResultIDs []string |
| 1645 | hasToolResult := false |
| 1646 | for _, block := range nextMsg.ContentBlocks { |
| 1647 | if toolResult, ok := block.(*types.ToolResultBlock); ok { |
| 1648 | hasToolResult = true |
| 1649 | toolResultIDs = append(toolResultIDs, toolResult.ToolUseID) |
| 1650 | } |
| 1651 | } |
| 1652 | |
| 1653 | if !hasToolResult { |
| 1654 | return false |
| 1655 | } |
| 1656 | |
| 1657 | // 验证每个 tool_call ID 都有对应的 tool_result |
| 1658 | for _, toolCallID := range toolCallIDs { |
| 1659 | found := slices.Contains(toolResultIDs, toolCallID) |
| 1660 | if !found { |
| 1661 | return false |
| 1662 | } |
| 1663 | } |
| 1664 | } |
| 1665 | } |
| 1666 | return true |
| 1667 | } |
| 1668 | |
| 1669 | // removeIncompleteToolCalls 移除所有不完整的 tool_call 序列 |
| 1670 | // 策略:找到第一个不完整的 tool_call,截断到该位置之前 |