feat(dispatcher): 熔断降级真三态状态机(弃用空桩)+ 单测
CircuitBreaker 此前是空桩(Allow 恒 true、Report 空操作),dispatcher 调 LLM/工具 无任何失败保护——今天就撞上 DeepSeek 流连接累积把报告卡死。改为真实三态熔断: - Closed:正常放行;连续失败达阈值(默认5) → Open。 - Open:快速拒绝;冷却(默认10s)到点 → HalfOpen 放行少量探测(默认1)。 - HalfOpen:探测成功 → Closed 恢复;探测失败 → 重新 Open。 - sync.Mutex 并发安全(多任务 goroutine 共享);时钟可注入便于确定性测试。 orchestrator.Handle:熔断开启时不再静默丢弃任务,改为回流"服务繁忙"提示 + CompleteStream 收尾,让客户端解阻不挂死。 测试(含 -race):达阈值断开、成功清零、半开恢复、探测失败重断、并发安全 —— 全过。 PROGRESS.md 勾掉熔断项。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -47,13 +47,18 @@ func NewOrchestrator(pool *llm.Pool, breaker *harness.CircuitBreaker, sink Token
|
||||
|
||||
// Handle 消费一个任务:按 DSL 编译 Eino 图并执行,把 Token 流回流到 sundynix.streams.<id>。
|
||||
func (o *Orchestrator) Handle(ctx context.Context, t *contract.Task) error {
|
||||
if !o.breaker.Allow() {
|
||||
log.Printf("[eino] circuit open, drop task %s", t.ID)
|
||||
return nil
|
||||
}
|
||||
tr := o.tracer(t.ID)
|
||||
defer tr.done()
|
||||
|
||||
// 熔断开启:快速拒绝,但要让客户端解阻(回流提示 + 收尾流),不静默丢弃。
|
||||
if !o.breaker.Allow() {
|
||||
log.Printf("[eino] 熔断开启,拒绝任务 %s", t.ID)
|
||||
tr.info("task", "system", "服务熔断", "后端连续失败,暂时拒绝新任务,请稍后重试")
|
||||
_ = o.sink.PublishToken(t.ID, []byte("⚠️ 服务繁忙(已触发熔断保护),请稍后重试。"))
|
||||
_ = o.sink.CompleteStream(t.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 报告生成走专用多步编排(规划→分章并行检索撰写→汇聚→渲染 Word),而非通用对话图。
|
||||
if intent, _ := t.Meta[contract.MetaIntent].(string); intent == contract.IntentReport {
|
||||
return o.handleReport(ctx, t, tr)
|
||||
|
||||
Reference in New Issue
Block a user