main: 增强 Agent Run 调度可靠性与幂等性
- 默认切换 AgentProvider 为 HttpAgentProvider,增强网络请求的容错和重试机制 - 优化 Run 逻辑,支持多场景去重与并发保护 - 添加 Redis 发布失败的日志记录以提升问题排查效率 - 扩展 OpenAPI 规范,新增 Error 和 Run 状态相关模型 - 增强测试覆盖,验证调度策略和重复请求的幂等性 - 增加数据库索引以优化查询性能 - 更新所有相关文档和配置文件
This commit is contained in:
@@ -3,8 +3,10 @@
|
||||
namespace App\Services;
|
||||
|
||||
use App\Jobs\AgentRunJob;
|
||||
use App\Models\ChatSession;
|
||||
use App\Models\Message;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RunDispatcher
|
||||
@@ -25,36 +27,50 @@ class RunDispatcher
|
||||
throw (new ModelNotFoundException())->setModel(Message::class, [$triggerMessageId]);
|
||||
}
|
||||
|
||||
$existingForTrigger = Message::query()
|
||||
->where('session_id', $sessionId)
|
||||
->where('type', 'run.status')
|
||||
->where('payload->trigger_message_id', $triggerMessageId)
|
||||
->orderByDesc('seq')
|
||||
->first();
|
||||
$shouldDispatch = false;
|
||||
|
||||
if ($existingForTrigger && ($existingForTrigger->payload['run_id'] ?? null)) {
|
||||
return $existingForTrigger->payload['run_id'];
|
||||
$runId = DB::transaction(function () use ($sessionId, $triggerMessageId, &$shouldDispatch) {
|
||||
ChatSession::query()
|
||||
->whereKey($sessionId)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
|
||||
$latestStatus = Message::query()
|
||||
->where('session_id', $sessionId)
|
||||
->where('type', 'run.status')
|
||||
->orderByDesc('seq')
|
||||
->first();
|
||||
|
||||
if ($latestStatus && ($latestStatus->payload['status'] ?? null) === 'RUNNING' && ($latestStatus->payload['run_id'] ?? null)) {
|
||||
logger('existing run found', ['sessionId' => $sessionId, 'runId' => $latestStatus->payload['run_id']]);
|
||||
return $latestStatus->payload['run_id'];
|
||||
}
|
||||
|
||||
$candidateRunId = (string) Str::uuid();
|
||||
$wasDeduped = null;
|
||||
|
||||
$statusMessage = $this->outputSink->appendRunStatus($sessionId, $candidateRunId, 'RUNNING', [
|
||||
'trigger_message_id' => $triggerMessageId,
|
||||
'dedupe_key' => 'run:trigger:'.$triggerMessageId,
|
||||
], $wasDeduped);
|
||||
|
||||
$finalRunId = $statusMessage->payload['run_id'] ?? $candidateRunId;
|
||||
|
||||
if ($wasDeduped) {
|
||||
logger('existing run found', ['sessionId' => $sessionId, 'runId' => $finalRunId]);
|
||||
return $finalRunId;
|
||||
}
|
||||
|
||||
$shouldDispatch = true;
|
||||
|
||||
return $finalRunId;
|
||||
});
|
||||
|
||||
if ($shouldDispatch) {
|
||||
logger('dispatching run', ['sessionId' => $sessionId, 'runId' => $runId]);
|
||||
dispatch(new AgentRunJob($sessionId, $runId));
|
||||
}
|
||||
|
||||
$latestStatus = Message::query()
|
||||
->where('session_id', $sessionId)
|
||||
->where('type', 'run.status')
|
||||
->orderByDesc('seq')
|
||||
->first();
|
||||
|
||||
if ($latestStatus && ($latestStatus->payload['status'] ?? null) === 'RUNNING' && ($latestStatus->payload['run_id'] ?? null)) {
|
||||
return $latestStatus->payload['run_id'];
|
||||
}
|
||||
|
||||
$runId = (string) Str::uuid();
|
||||
|
||||
$this->outputSink->appendRunStatus($sessionId, $runId, 'RUNNING', [
|
||||
'trigger_message_id' => $triggerMessageId,
|
||||
'dedupe_key' => 'run:trigger:'.$triggerMessageId,
|
||||
]);
|
||||
|
||||
dispatch(new AgentRunJob($sessionId, $runId));
|
||||
|
||||
return $runId;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user