添加新的工具功能和测试覆盖:
- 注册 `LsTool` 和 `BashTool` 工具,支持目录操作和命令执行 - 增强工具调用逻辑,添加日志记录以提升调试能力 - 增加 `ToolRegistry` 和 `RunLoop` 的增量累积与排序优化 - 完善单元测试覆盖新工具的执行与行为验证
This commit is contained in:
@@ -70,6 +70,8 @@ class RunLoop
|
||||
$toolCallCount
|
||||
);
|
||||
|
||||
logger('agent provider iteration', [$iterationResult]);
|
||||
|
||||
// 2.3 处理失败或取消
|
||||
if ($iterationResult['should_exit']) {
|
||||
return;
|
||||
@@ -261,7 +263,7 @@ class RunLoop
|
||||
$streamState = $iterationResult['stream_state'];
|
||||
$latencyMs = $iterationResult['latency_ms'];
|
||||
$updatedToolCount = $iterationResult['updated_tool_count'];
|
||||
|
||||
logger('agent tool calls', [$streamState, $latencyMs, $updatedToolCount]);
|
||||
// 1. 检查工具调用数量是否超限
|
||||
if ($updatedToolCount > $this->maxToolCalls) {
|
||||
$this->appendProviderFailure(
|
||||
@@ -605,47 +607,55 @@ class RunLoop
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具增量收集:同一个 tool_call_id 可能多次分片返回,此处拼接参数与名称。
|
||||
* 工具增量收集:同一个 tool call 通过 index 关联,多次分片返回时拼接参数与名称。
|
||||
*
|
||||
* @param array<string, array<string, mixed>> $buffer
|
||||
* @param array<string, int> $order
|
||||
* OpenAI 流式 API 中,tool call 的第一个 chunk 包含 id、name、index,
|
||||
* 后续 chunks 只包含 arguments 增量和 index(无 id)。
|
||||
* 因此必须使用 index 作为 buffer 的 key 来正确累积。
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $buffer 以 index 为 key 的缓冲区
|
||||
* @param array<int, int> $order 记录 index 出现顺序
|
||||
* @param array<int, array<string, mixed>> $toolCalls
|
||||
*/
|
||||
private function accumulateToolCalls(array &$buffer, array &$order, array $toolCalls): void
|
||||
{
|
||||
foreach ($toolCalls as $call) {
|
||||
$id = is_string($call['id'] ?? null) && $call['id'] !== ''
|
||||
? $call['id']
|
||||
: md5(json_encode($call));
|
||||
// 使用 index 作为主键(OpenAI 流式 API 的标准做法)
|
||||
$index = is_int($call['index'] ?? null) ? (int) $call['index'] : 0;
|
||||
|
||||
$index = is_int($call['index'] ?? null) ? (int) $call['index'] : count($order);
|
||||
|
||||
if (! isset($buffer[$id])) {
|
||||
$buffer[$id] = [
|
||||
'id' => $id,
|
||||
if (! isset($buffer[$index])) {
|
||||
$buffer[$index] = [
|
||||
'id' => $call['id'] ?? null,
|
||||
'name' => $call['name'] ?? null,
|
||||
'arguments' => '',
|
||||
'index' => $index,
|
||||
];
|
||||
$order[$id] = $index;
|
||||
$order[$index] = count($order);
|
||||
}
|
||||
|
||||
// 更新 id(第一个 chunk 才有)
|
||||
if (isset($call['id']) && is_string($call['id']) && $call['id'] !== '') {
|
||||
$buffer[$index]['id'] = $call['id'];
|
||||
}
|
||||
|
||||
// 更新 name(第一个 chunk 才有)
|
||||
if (isset($call['name']) && is_string($call['name']) && $call['name'] !== '') {
|
||||
$buffer[$id]['name'] = $call['name'];
|
||||
$buffer[$index]['name'] = $call['name'];
|
||||
}
|
||||
|
||||
// 累积 arguments
|
||||
$arguments = $call['arguments'] ?? '';
|
||||
if (is_string($arguments) && $arguments !== '') {
|
||||
$buffer[$id]['arguments'] .= $arguments;
|
||||
$buffer[$index]['arguments'] .= $arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存的 tool.call 增量整理为最终列表(保持 provider 给出的顺序)。
|
||||
* 将缓存的 tool.call 增量整理为最终列表(按 index 排序)。
|
||||
*
|
||||
* @param array<string, array<string, mixed>> $buffer
|
||||
* @param array<string, int> $order
|
||||
* @param array<int, array<string, mixed>> $buffer 以 index 为 key 的缓冲区
|
||||
* @param array<int, int> $order 记录 index 出现顺序
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function finalizeToolCalls(array $buffer, array $order, ?string $doneReason): array
|
||||
@@ -654,12 +664,8 @@ class RunLoop
|
||||
return [];
|
||||
}
|
||||
|
||||
uasort($buffer, function ($a, $b) use ($order) {
|
||||
$orderA = $order[$a['id']] ?? ($a['index'] ?? 0);
|
||||
$orderB = $order[$b['id']] ?? ($b['index'] ?? 0);
|
||||
|
||||
return $orderA <=> $orderB;
|
||||
});
|
||||
// 按 index 排序
|
||||
ksort($buffer);
|
||||
|
||||
return array_values(array_map(function (array $call) use ($doneReason) {
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user