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

@@ -138,11 +138,122 @@ func cloudAgentToolResult(runID string, state *cloudAgentRuntime, call cloudAgen
}
state.event(runID, kind, payload)
state.Canonical.Messages = append(state.Canonical.Messages, map[string]any{"role": "tool", "tool_call_id": call.ID, "content": string(raw)})
if err == nil {
cloudAgentCompactSupersededReadResult(state, call)
}
state.CallIndex++
state.Approval = nil
return exhausted
}
// cloudAgentCompactSupersededReadResult bounds repeated snapshots without
// changing the transcript shape. A newer read with the same cache key makes an
// older canvas projection stale; keep the old tool message for provider pairing,
// but remove rereadable bodies while retaining hashes and execution facts.
func cloudAgentCompactSupersededReadResult(state *cloudAgentRuntime, current cloudAgentCall) {
if state == nil || !cloudAgentReadToolCacheable(current.Function.Name) {
return
}
if current.Function.Name != "canvas_get_state" && current.Function.Name != "canvas_read_storyboard" && current.Function.Name != "director_scene_read" {
return
}
currentKey := cloudAgentReadCacheKey(current)
protected := make(map[string]bool, len(state.Calls))
for _, call := range state.Calls {
protected[call.ID] = true
}
callByID := make(map[string]cloudAgentCall)
for _, message := range state.Canonical.Messages {
if stringValue(message["role"]) != "assistant" {
continue
}
for _, rawCall := range canonicalAgentToolCalls(message["tool_calls"]) {
var call cloudAgentCall
encoded, err := json.Marshal(rawCall)
if err != nil || json.Unmarshal(encoded, &call) != nil || call.ID == "" {
continue
}
callByID[call.ID] = call
}
}
for _, message := range state.Canonical.Messages {
if stringValue(message["role"]) != "tool" {
continue
}
callID := stringValue(message["tool_call_id"])
if callID == "" || callID == current.ID || protected[callID] {
continue
}
prior, ok := callByID[callID]
if !ok || prior.Function.Name != current.Function.Name || cloudAgentReadCacheKey(prior) != currentKey {
continue
}
if compacted := cloudAgentCompactSupersededReadBody(stringValue(message["content"]), current.Function.Name); compacted != "" {
message["content"] = compacted
}
}
}
func cloudAgentCompactSupersededReadBody(content, toolName string) string {
var result map[string]any
if content == "" || json.Unmarshal([]byte(content), &result) != nil {
return ""
}
removed := false
switch toolName {
case "canvas_get_state":
if nodes, ok := result["nodes"].([]any); ok {
facts := make([]map[string]any, 0, len(nodes))
for _, value := range nodes {
node, ok := value.(map[string]any)
if !ok {
continue
}
fact := map[string]any{"nodeId": node["id"]}
for _, key := range []string{"generation", "generationDraft", "outputReference"} {
if value, exists := node[key]; exists {
fact[key] = value
}
}
if len(fact) > 1 {
facts = append(facts, fact)
}
}
if len(facts) > 0 {
result["observedFacts"] = facts
}
delete(result, "nodes")
removed = true
}
if _, exists := result["connections"]; exists {
delete(result, "connections")
removed = true
}
case "canvas_read_storyboard":
if _, exists := result["rows"]; exists {
delete(result, "rows")
removed = true
}
case "director_scene_read":
for _, key := range []string{"content", "shots", "scenes"} {
if _, exists := result[key]; exists {
delete(result, key)
removed = true
}
}
}
if !removed {
return ""
}
result["contextCompacted"] = true
result["guidance"] = "较新的同参读取已替代这份旧快照;需要正文时重新读取,不要依据旧快照重复提交写入。"
encoded, err := json.Marshal(result)
if err != nil || len(encoded) >= len(content) {
return ""
}
return string(encoded)
}
// cloudAgentReadResultInContext reports whether the full cached result is still
// present in the canonical transcript. Context compaction deliberately removes old
// tool bodies, so a replay must restore the body once instead of returning a receipt