Merge pull request 'Map background card failures to CardFailed with L1 body' (#177) from feature/fast-slow-display-u1 into main
ci / vet-and-test (push) Successful in 49s

Reviewed-on: https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/177
This commit was merged in pull request #177.
This commit is contained in:
2026-10-12 12:16:45 +00:00
4 changed files with 73 additions and 21 deletions
+53 -4
View File
@@ -120,18 +120,67 @@ func TestBackgroundCardLifecycleOnFailure(t *testing.T) {
t.Fatalf("cards = %d, want 1", len(cards))
}
card := cards[0]
if card.Status != core.CardDone || card.Tone != "danger" {
t.Fatalf("status = %s tone = %s, want done/danger", card.Status, card.Tone)
if card.Status != core.CardFailed || card.Tone != "danger" {
t.Fatalf("status = %s tone = %s, want failed/danger", card.Status, card.Tone)
}
var md string
statusLabel := ""
for _, n := range card.Body {
if n.Type == core.UIMarkdown {
md = n.Markdown
}
if n.Type == core.UIKV {
for _, kv := range n.KV {
if kv[0] == core.TaskSummaryKVStatus {
statusLabel = kv[1]
}
}
}
}
if !strings.Contains(md, "执行失败") {
t.Fatalf("failure body = %q", md)
}
if statusLabel != core.TaskSummaryStatusFailed {
t.Fatalf("status kv = %q, want %q", statusLabel, core.TaskSummaryStatusFailed)
}
}
func TestBackgroundCardCreateUsesL1SkeletonNotRawTask(t *testing.T) {
runner := &holdRunner{release: make(chan struct{})}
c, _ := newCardSpawnEnv(t, runner)
c.mgr.runner = noopRunner{}
raw := "逐页解析上传材料并对照验收条件" + strings.Repeat("LONGTASK", 40)
if _, err := c.SpawnBackground("agent.sub.general", "深度解析员", raw, ""); err != nil {
t.Fatal(err)
}
waitUntil(t, func() bool {
cards, err := c.mgr.cards.List(c.run.Owner, c.run.ConversationID)
return err == nil && len(cards) == 1
})
cards, err := c.mgr.cards.List(c.run.Owner, c.run.ConversationID)
if err != nil {
t.Fatal(err)
}
card := cards[0]
var md string
for _, n := range card.Body {
if n.Type == core.UIMarkdown {
md = n.Markdown
}
}
if !strings.Contains(md, "执行失败") {
t.Fatalf("failure body = %q", md)
if strings.Contains(md, "LONGTASK") || strings.Contains(md, raw[:32]) {
t.Fatalf("create body still embeds raw task: %q", md)
}
if md != core.TaskSummaryNoFindingsYet {
t.Fatalf("create markdown = %q, want honest placeholder", md)
}
close(runner.release)
waitUntil(t, func() bool {
c.mgr.convMu.Lock()
busy := c.mgr.convActive[c.run.ConversationID]
c.mgr.convMu.Unlock()
return !busy
})
}
// TestBackgroundCardSkippedWithoutInbox:收件箱未启用(cards==nil)时后台派发
+1 -1
View File
@@ -275,7 +275,7 @@ func TestMaterialQueuedWhenFull(t *testing.T) {
return false
}
for _, card := range cards {
if card.Title == materialCardTitle && card.SourceRunID != "" && strings.Contains(cardBodyText(card), "执行中") {
if card.Title == materialCardTitle && card.SourceRunID != "" && strings.Contains(cardBodyText(card), core.TaskSummaryStatusRunning) {
return true
}
}
+18 -15
View File
@@ -1947,13 +1947,14 @@ func (m *Manager) createBackgroundCard(parent core.Run, childID core.ID, name, t
if title == "" {
title = "后台任务"
}
if status == "" {
status = "执行中,完成后自动回投结果"
}
body := []core.UINode{
{Type: core.UIMarkdown, Markdown: truncate(strings.TrimSpace(task), 400)},
{Type: core.UIKV, KV: append(append([][2]string(nil), kv...), [2]string{"通道", "慢通道 · 后台执行"}, [2]string{"状态", status})},
_ = task // 派发说明保留入参兼容;用户可见正文用 L1 骨架,不回填原始指令。
statusLabel := core.TaskSummaryStatusRunning
if status != "" {
statusLabel = status
}
focus := "正在执行:" + title
extra := append(append([][2]string(nil), kv...), [2]string{"通道", "慢通道 · 后台执行"})
body := (core.TaskSummaryView{Focus: focus}).RunningBody(statusLabel, extra)
card, err := m.cards.Create(parent.Owner, core.ActivityCard{
Kind: core.CardKindProgress, Title: title, Status: core.CardActive, Tone: "info",
ConversationID: parent.ConversationID, ProjectID: parent.ProjectID, SourceRunID: childID,
@@ -1976,9 +1977,9 @@ func (m *Manager) promoteQueuedCard(owner string, card core.ActivityCard, childI
if node.Type != core.UIKV {
continue
}
for j, kv := range node.KV {
if kv[0] == "状态" {
card.Body[i].KV[j][1] = "执行中,完成后自动回投结果"
for j, pair := range node.KV {
if pair[0] == core.TaskSummaryKVStatus || pair[0] == "状态" {
card.Body[i].KV[j][1] = core.TaskSummaryStatusRunning
}
}
}
@@ -1988,15 +1989,19 @@ func (m *Manager) promoteQueuedCard(owner string, card core.ActivityCard, childI
}
// finishBackgroundCard 把后台子代理终态写回进度卡:成功=结果摘要(含工件行),
// 失败=错误说明;状态统一置 done,tone 区分成败。kv 原样保留(材料键等持久凭据)。
// 失败=错误说明并置 CardFailed(U1:失败不得显示完成灯)。kv 原样保留。
func (m *Manager) finishBackgroundCard(owner string, card core.ActivityCard, payload core.AgentResultPayload, err error, kv [][2]string) {
if m.cards == nil || card.ID == "" {
return
}
summary := strings.TrimSpace(payload.Summary)
tone := "ok"
status := core.CardDone
statusLabel := core.TaskSummaryStatusDone
if err != nil {
tone = "danger"
status = core.CardFailed
statusLabel = core.TaskSummaryStatusFailed
if summary == "" {
summary = err.Error()
}
@@ -2005,14 +2010,12 @@ func (m *Manager) finishBackgroundCard(owner string, card core.ActivityCard, pay
if summary == "" {
summary = "(无结果摘要)"
}
card.Body = []core.UINode{
{Type: core.UIMarkdown, Markdown: summary},
{Type: core.UIKV, KV: append(append([][2]string(nil), kv...), [2]string{"通道", "慢通道 · 后台执行"}, [2]string{"状态", "已完成"})},
}
extra := append(append([][2]string(nil), kv...), [2]string{"通道", "慢通道 · 后台执行"})
card.Body = (core.TaskSummaryView{OutcomeLine: summary}).FinishedBody(statusLabel, extra)
if uerr := m.cards.Update(owner, card); uerr != nil {
log.Printf("[runmgr] background card update: %v", uerr)
}
if _, serr := m.cards.SetStatus(owner, card.ID, core.CardDone, tone, ""); serr != nil {
if _, serr := m.cards.SetStatus(owner, card.ID, status, tone, ""); serr != nil {
log.Printf("[runmgr] background card status: %v", serr)
}
}