refactor(dispatcher): compose 翻默认 —— 补平三缺口后退役 graph.go 在望
EINO_COMPOSE 默认改为开(留 =0 逃生舱回退权威 graph.go)。翻默认前补平 compose 路径三个会静默吃掉治理能力的缺口: - HITL 审批:execDSLNode 无 approval 分支 → 审批节点被当未识别跳过、下游照跑; 补 case 调 approvalNode。 - 忠实度评测:executeGraph 把 refs 硬写 nil → 评测静默失效;runComposeGraph 改签名回传 refsOf(b)。 - 终态传播:rejected/fatalErr 不传播也不阻断下游 → 任务误判 done-空;加节点 lambda 入口守卫 + branch cond 守卫 + 终态上抛 errRejected/fatalErr,并让 runComposeConversation 模型失败置 fatalErr(对齐 graph.go 的 break 语义)。 新增等价测试:审批拒绝停下游、refs 回流。go test ./... 全绿。 soak 无回归后即可物理退役 graph.go。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,11 @@ package eino
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sundynix/sundynix-dispatcher/internal/harness"
|
||||
"github.com/sundynix/sundynix-dispatcher/internal/llm"
|
||||
@@ -34,13 +38,70 @@ func runBoth(t *testing.T, graph string) (interp, comp string) {
|
||||
t.Fatalf("runGraph: %v", err)
|
||||
}
|
||||
o2 := &Orchestrator{pool: echoLLM(), breaker: harness.NewCircuitBreaker(), sink: &fakeSink{}}
|
||||
a2, err := o2.runComposeGraph(context.Background(), task, &execTracer{})
|
||||
a2, _, err := o2.runComposeGraph(context.Background(), task, &execTracer{})
|
||||
if err != nil {
|
||||
t.Fatalf("runComposeGraph: %v", err)
|
||||
}
|
||||
return a1, a2
|
||||
}
|
||||
|
||||
// rejectWaiter 是恒拒绝的 HITL 审批替身(fail-safe 路径回归用)。
|
||||
type rejectWaiter struct{ note string }
|
||||
|
||||
func (r *rejectWaiter) WaitApproval(context.Context, string, time.Duration) (*contract.ApprovalDecision, error) {
|
||||
return &contract.ApprovalDecision{Approved: false, Note: r.note}, nil
|
||||
}
|
||||
|
||||
// TestComposeApprovalRejectStopsDownstream 钉死 compose 路径的 HITL 审批:
|
||||
// 审批拒绝须置 errRejected、产出拒绝语,且下游 agent 绝不执行(否则等于审批形同虚设)。
|
||||
// 这是翻默认前最高危的回归点——execDSLNode 一度没有 approval 分支会把审批节点当未识别跳过。
|
||||
func TestComposeApprovalRejectStopsDownstream(t *testing.T) {
|
||||
graph := `{"version":"1","nodes":[
|
||||
{"id":"in","kind":"input","config":{"text":"hi"}},
|
||||
{"id":"ap","kind":"approval","config":{"title":"上线审批"}},
|
||||
{"id":"a","kind":"agent","config":{"system":"机密操作"}}
|
||||
],"edges":[
|
||||
{"source":"in","target":"ap"},{"source":"ap","target":"a"}
|
||||
]}`
|
||||
o := &Orchestrator{pool: echoLLM(), breaker: harness.NewCircuitBreaker(),
|
||||
sink: &fakeSink{}, approval: &rejectWaiter{note: "不批"}}
|
||||
ans, refs, err := o.runComposeGraph(context.Background(),
|
||||
&contract.Task{ID: "t_ap", Graph: []byte(graph)}, &execTracer{})
|
||||
if !errors.Is(err, errRejected) {
|
||||
t.Fatalf("审批拒绝应返回 errRejected,got %v", err)
|
||||
}
|
||||
if !strings.Contains(ans, "已被拒绝") {
|
||||
t.Fatalf("应产出拒绝语,got %q", ans)
|
||||
}
|
||||
if strings.Contains(ans, "ANS:") {
|
||||
t.Fatalf("审批拒绝后下游 agent 不应执行,但成稿含 agent 产出: %q", ans)
|
||||
}
|
||||
if refs != nil {
|
||||
t.Fatalf("拒绝终态 refs 应为 nil,got %v", refs)
|
||||
}
|
||||
}
|
||||
|
||||
// TestComposeReturnsRefs 钉死 compose 路径回传检索来源——曾被硬写成 nil,导致忠实度评测静默失效。
|
||||
// 与 runGraph 同图对照:两路径都应回流含检索片段的 refs(喂 grounded judge)。
|
||||
func TestComposeReturnsRefs(t *testing.T) {
|
||||
g := `{"nodes":[
|
||||
{"id":"i","kind":"input","config":{"text":"介绍杭州西湖"}},
|
||||
{"id":"r","kind":"retriever","config":{"kb":"travel"}},
|
||||
{"id":"a","kind":"agent","config":{"system":"你是导游"}}
|
||||
],"edges":[{"source":"i","target":"r"},{"source":"r","target":"a"}]}`
|
||||
ll := &fakeLLM{ready: true, stream: func(m []llm.ChatMessage) string { return m[0].Content }}
|
||||
o := newOrch(ll, kbTool(nil), &fakeSink{}, &fakeExec{})
|
||||
tk := &contract.Task{ID: "t_refs", Graph: json.RawMessage(g), Meta: map[string]any{contract.MetaUserID: "u42"}}
|
||||
|
||||
_, refs, err := o.runComposeGraph(context.Background(), tk, &execTracer{})
|
||||
if err != nil {
|
||||
t.Fatalf("runComposeGraph: %v", err)
|
||||
}
|
||||
if len(refs) == 0 || !strings.Contains(strings.Join(refs, ""), ragSnippet) {
|
||||
t.Fatalf("compose 路径应回流含检索片段的 refs,got %v", refs)
|
||||
}
|
||||
}
|
||||
|
||||
// TestComposeEquivalentLinear 多节点线性图:input→memory→agent→output,两路径成稿应逐字一致。
|
||||
func TestComposeEquivalentLinear(t *testing.T) {
|
||||
graph := `{"version":"1","nodes":[
|
||||
|
||||
Reference in New Issue
Block a user