feat(agent): 云端 Agent 运行时 - 完善 Pi 桥接、上下文压力与临时故障重试
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user