Add files via upload

This commit is contained in:
公明
2026-03-23 21:56:05 +08:00
committed by GitHub
parent c59fcbf5f2
commit fdc36a041e
3 changed files with 77 additions and 2 deletions

View File

@@ -44,11 +44,20 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) {
c.Header("X-Accel-Buffering", "no")
// 用于在 sendEvent 中判断是否为用户主动停止导致的取消。
// 注意baseCtx 会在后面创建;该变量用于闭包提前捕获引用。
var baseCtx context.Context
clientDisconnected := false
sendEvent := func(eventType, message string, data interface{}) {
if clientDisconnected {
return
}
// 用户主动停止时Eino 可能仍会并发上报 eventType=="error"。
// 为避免 UI 看到“取消错误 + cancelled 文案”两条回复,这里直接丢弃取消对应的 error。
if eventType == "error" && baseCtx != nil && errors.Is(context.Cause(baseCtx), ErrTaskCancelled) {
return
}
select {
case <-c.Request.Context().Done():
clientDisconnected = true
@@ -135,7 +144,6 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) {
)
if runErr != nil {
h.logger.Error("Eino DeepAgent 执行失败", zap.Error(runErr))
cause := context.Cause(baseCtx)
if errors.Is(cause, ErrTaskCancelled) {
taskStatus = "cancelled"
@@ -153,6 +161,7 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) {
return
}
h.logger.Error("Eino DeepAgent 执行失败", zap.Error(runErr))
taskStatus = "failed"
h.tasks.UpdateTaskStatus(conversationID, taskStatus)
errMsg := "执行失败: " + runErr.Error()

View File

@@ -0,0 +1,62 @@
package multiagent
import (
"context"
"strings"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/components/tool"
)
// noNestedTaskMiddleware 禁止在已经处于 task(sub-agent) 执行链中再次调用 task
// 避免子代理再次委派子代理造成的无限委派/递归。
//
// 通过在 ctx 中设置临时标记来实现嵌套检测:外层 task 调用会先标记 ctx
// 子代理内再调用 task 时会命中该标记并拒绝。
type noNestedTaskMiddleware struct {
adk.BaseChatModelAgentMiddleware
}
type nestedTaskCtxKey struct{}
func newNoNestedTaskMiddleware() adk.ChatModelAgentMiddleware {
return &noNestedTaskMiddleware{}
}
func (m *noNestedTaskMiddleware) WrapInvokableToolCall(
ctx context.Context,
endpoint adk.InvokableToolCallEndpoint,
tCtx *adk.ToolContext,
) (adk.InvokableToolCallEndpoint, error) {
if tCtx == nil || strings.TrimSpace(tCtx.Name) == "" {
return endpoint, nil
}
// Deep 内置 task 工具名固定为 "task";为兼容可能的大小写/空白,仅做不区分大小写匹配。
if !strings.EqualFold(strings.TrimSpace(tCtx.Name), "task") {
return endpoint, nil
}
// 已在 task 执行链中:拒绝继续委派,直接报错让上层快速终止。
if ctx != nil {
if v, ok := ctx.Value(nestedTaskCtxKey{}).(bool); ok && v {
return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
// Important: return a tool result text (not an error) to avoid hard-stopping the whole multi-agent run.
// The nested task is still prevented from spawning another sub-agent, so recursion is avoided.
_ = argumentsInJSON
_ = opts
return "Nested task delegation is forbidden (already inside a sub-agent delegation chain) to avoid infinite delegation. Please continue the work using the current agent's tools.", nil
}, nil
}
}
// 标记当前 task 调用链,确保子代理内的再次 task 调用能检测到嵌套。
return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
ctx2 := ctx
if ctx2 == nil {
ctx2 = context.Background()
}
ctx2 = context.WithValue(ctx2, nestedTaskCtxKey{}, true)
return endpoint(ctx2, argumentsInJSON, opts...)
}, nil
}

View File

@@ -252,7 +252,11 @@ func RunDeepAgent(
WithoutGeneralSubAgent: ma.WithoutGeneralSubAgent,
WithoutWriteTodos: ma.WithoutWriteTodos,
MaxIteration: deepMaxIter,
Handlers: []adk.ChatModelAgentMiddleware{mainSumMw},
// 防止 sub-agent 再调用 task再委派 sub-agent形成无限委派链。
Handlers: []adk.ChatModelAgentMiddleware{
newNoNestedTaskMiddleware(),
mainSumMw,
},
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: mainTools,