feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)
llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、 builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、 编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。 产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt) 留在 AutoGo,宿主接入方式见 MIGRATION.md。
This commit is contained in:
commit
d5dac5a6fc
140 files changed
+26497
No files matched your search
@@ -0,0 +1,98 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.gopher.ink/mirrors2/AutoZero/llm"
|
||||
"git.gopher.ink/mirrors2/AutoZero/tools"
|
||||
)
|
||||
|
||||
// TestSteerDeliveredAtStepBoundary:插话必须在工具结果回填之后、下一次模型调用
|
||||
// 之前落成普通 user 消息;模型下一步就能看到它。
|
||||
func TestSteerDeliveredAtStepBoundary(t *testing.T) {
|
||||
calls := 0
|
||||
prov := &scriptedProvider{fn: func(call int, _ string, _ []llm.Message, _ []llm.ToolDef) (llm.Message, error) {
|
||||
calls++
|
||||
if call == 1 {
|
||||
return llm.Message{Role: llm.RoleAssistant, ToolCalls: []llm.ToolCall{
|
||||
{ID: "t1", Name: "noop", Input: "{}"},
|
||||
}}, nil
|
||||
}
|
||||
return llm.Message{Role: llm.RoleAssistant, Content: "done"}, nil
|
||||
}}
|
||||
noop := &tools.Tool{Name: "noop", Run: func(context.Context, string) (tools.Result, error) {
|
||||
return tools.TextResult("ok"), nil
|
||||
}}
|
||||
a, sess := newTestAgent(t, noop)
|
||||
a.Provider = prov
|
||||
|
||||
pending := []SteerMessage{{Text: "also fix the typo"}}
|
||||
a.Opt.Steer = func() []SteerMessage {
|
||||
out := pending
|
||||
pending = nil
|
||||
return out
|
||||
}
|
||||
var steered []SteerMessage
|
||||
a.Opt.OnSteer = func(m SteerMessage) { steered = append(steered, m) }
|
||||
|
||||
if err := a.Run(context.Background(), "do it"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 2 {
|
||||
t.Fatalf("model calls = %d, want 2", calls)
|
||||
}
|
||||
if len(steered) != 1 || steered[0].Text != "also fix the typo" {
|
||||
t.Fatalf("OnSteer = %+v", steered)
|
||||
}
|
||||
steerAt, toolAt := -1, -1
|
||||
for i, m := range sess.Messages {
|
||||
if m.Role == llm.RoleUser && m.Content == "also fix the typo" {
|
||||
steerAt = i
|
||||
}
|
||||
if m.Role == llm.RoleTool {
|
||||
toolAt = i
|
||||
}
|
||||
}
|
||||
if steerAt < 0 || toolAt < 0 || steerAt < toolAt {
|
||||
t.Fatalf("steer must land after the tool result (tool=%d steer=%d): %+v", toolAt, steerAt, sess.Messages)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSteerContinuesAnOtherwiseCompletedTurn:模型已经说完了,但宿主还有插话——
|
||||
// 不能直接收尾,要把插话落盘再跑一步。这保证"回合结束才发"的旧排队语义也能
|
||||
// 在同一回合内被消费,而不是等下一次用户手动发送。
|
||||
func TestSteerContinuesAnOtherwiseCompletedTurn(t *testing.T) {
|
||||
calls := 0
|
||||
prov := &scriptedProvider{fn: func(call int, _ string, _ []llm.Message, _ []llm.ToolDef) (llm.Message, error) {
|
||||
calls++
|
||||
if call == 1 {
|
||||
return llm.Message{Role: llm.RoleAssistant, Content: "first done"}, nil
|
||||
}
|
||||
return llm.Message{Role: llm.RoleAssistant, Content: "second done"}, nil
|
||||
}}
|
||||
a, sess := newTestAgent(t)
|
||||
a.Provider = prov
|
||||
|
||||
pending := []SteerMessage{{Text: "one more thing"}}
|
||||
a.Opt.Steer = func() []SteerMessage {
|
||||
out := pending
|
||||
pending = nil
|
||||
return out
|
||||
}
|
||||
|
||||
if err := a.Run(context.Background(), "start"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 2 {
|
||||
t.Fatalf("model calls = %d, want 2 (the steer must not be dropped)", calls)
|
||||
}
|
||||
if len(sess.Messages) < 4 {
|
||||
t.Fatalf("messages = %+v", sess.Messages)
|
||||
}
|
||||
last := sess.Messages[len(sess.Messages)-1]
|
||||
prev := sess.Messages[len(sess.Messages)-2]
|
||||
if prev.Role != llm.RoleUser || prev.Content != "one more thing" || last.Content != "second done" {
|
||||
t.Fatalf("steer was not answered in the same turn: %+v", sess.Messages)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user