添加新的工具功能和测试覆盖:

- 注册 `LsTool` 和 `BashTool` 工具,支持目录操作和命令执行
- 增强工具调用逻辑,添加日志记录以提升调试能力
- 增加 `ToolRegistry` 和 `RunLoop` 的增量累积与排序优化
- 完善单元测试覆盖新工具的执行与行为验证
This commit is contained in:
2025-12-23 17:26:27 +08:00
parent 78875ec3eb
commit 71226c255b
11 changed files with 721 additions and 25 deletions

View File

@@ -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 [