feat(agent): 云端 Agent 运行时 - 完善 Pi 桥接、上下文压力与临时故障重试

This commit is contained in:
ddcat666
2026-10-04 02:20:30 +08:00
parent 0030f4da9b
commit 76ca0fd247
32 changed files with 1663 additions and 1485 deletions

View File

@@ -2,10 +2,12 @@ package app
import (
"context"
"fmt"
"strings"
"testing"
"infinite-canvas/backend/internal/model"
"infinite-canvas/backend/internal/repository"
)
// 线上复现:recall_lessons 经运行时工具桥执行后,交回模型的 tool 消息是 "null",
@@ -19,16 +21,46 @@ func TestCloudAgentRuntimeToolResultIsNeverNull(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct{ name, args string }{
cases := []struct{ name, args string }{
{"recall_lessons", `{"topic":"用户称呼偏好"}`},
{"recall_lessons", `{}`},
{"canvas_list_node_types", `{}`},
{"canvas_get_state", `{}`},
} {
}
calls := make([]cloudAgentCall, 0, len(cases))
for index, tc := range cases {
var call cloudAgentCall
call.ID = "call-" + tc.name + "-" + tc.args
call.ID = fmt.Sprintf("tool-result-%d", index)
call.Function.Name = tc.name
call.Function.Arguments = tc.args
calls = append(calls, call)
}
// executeCloudAgentRuntimeTool is intentionally strict: a bridge result is
// accepted only when the model declaration has already been checkpointed.
// Build that same durable boundary here instead of bypassing the contract by
// calling the executor with an arbitrary call.
persisted, err := s.repo.CloudAgent("user", run.ID)
if err != nil {
t.Fatal(err)
}
state, err := cloudAgentDecode(persisted)
if err != nil {
t.Fatal(err)
}
state.Calls = calls
state.CallIndex = 0
state.Canonical.Messages = append(state.Canonical.Messages, map[string]any{
"role": "assistant", "content": "", "tool_calls": calls,
})
if err := s.repo.MutateCloudAgent("user", run.ID, persisted.Revision, func(current *model.CloudAgentExecution, _ *repository.Repository) error {
return cloudAgentSave(current, &state)
}); err != nil {
t.Fatalf("persist model tool calls: %v", err)
}
for index, tc := range cases {
call := calls[index]
result, err := s.executeCloudAgentRuntimeTool(context.Background(), "user", run.ID, call)
if err != nil {
t.Fatalf("%s(%s) bridge error: %v", tc.name, tc.args, err)
@@ -38,5 +70,16 @@ func TestCloudAgentRuntimeToolResultIsNeverNull(t *testing.T) {
if strings.TrimSpace(content) == "" || content == "null" {
t.Fatalf("%s(%s) returned empty tool content: %#v", tc.name, tc.args, result)
}
// The bridge can retry after losing its response. The second request must
// replay the committed tool message, not execute the next model call.
replayed, err := s.executeCloudAgentRuntimeTool(context.Background(), "user", run.ID, call)
if err != nil {
t.Fatalf("%s(%s) replay error: %v", tc.name, tc.args, err)
}
replayedPayload, _ := replayed.(map[string]any)
if replayedPayload["content"] != content {
t.Fatalf("%s(%s) replay changed committed content: first=%q replay=%#v", tc.name, tc.args, content, replayed)
}
}
}