mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-28 01:03:50 +08:00
feat(activity): 丢弃记录日志合并为单条 error,带 reason/summary/累计数
- 把「丢弃该记录」与「FK 诊断」合并成一条 error 级日志(含"丢弃"关键字, 后台只筛 error 也能看到完整信息,不再漏掉 info 级诊断行) - reason 分类:fk_violation(23503) / unique_violation(23505) / pg_error(code) / write_error - 带 summary 预览(截断去换行)+ 该任务累计丢弃条数,便于判断丢的是哪条、丢弃规模 - 新增 dropReason/bumpDrop/preview 辅助与 dropCnt 计数
This commit is contained in:
1 parent
ea833ad992
commit
95aba396c2
1 file changed
+55
-11
+55
-11
@@ -8,6 +8,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/Autumn-27/artex/agent"
|
||||
@@ -25,6 +26,44 @@ func isFKViolation(err error) bool {
|
||||
return errors.As(err, &pgErr) && pgErr.Code == "23503"
|
||||
}
|
||||
|
||||
// dropReason classifies why an activity write was dropped, so the log can be
|
||||
// grouped/analysed by cause rather than by raw error text.
|
||||
func dropReason(err error) string {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) {
|
||||
switch pgErr.Code {
|
||||
case "23503":
|
||||
return "fk_violation(23503,父exploration不存在)"
|
||||
case "23505":
|
||||
return "unique_violation(23505)"
|
||||
default:
|
||||
return "pg_error(" + pgErr.Code + ")"
|
||||
}
|
||||
}
|
||||
return "write_error"
|
||||
}
|
||||
|
||||
// bumpDrop increments and returns the running count of dropped (unpersistable)
|
||||
// activity records for a task. Concurrent planner + worker emits race here, so the
|
||||
// counter is an atomic behind sync.Map. The count in the log shows loss scale at a
|
||||
// glance instead of forcing a grep-and-count.
|
||||
func (e *Engine) bumpDrop(taskID string) int64 {
|
||||
v, _ := e.dropCnt.LoadOrStore(taskID, new(int64))
|
||||
return atomic.AddInt64(v.(*int64), 1)
|
||||
}
|
||||
|
||||
// preview collapses newlines and trims s to a short rune-safe snippet for one-line
|
||||
// log output (avoids dumping a multi-KB summary/detail into the log).
|
||||
func preview(s string, n int) string {
|
||||
s = strings.ReplaceAll(s, "\r", " ")
|
||||
s = strings.ReplaceAll(s, "\n", " ")
|
||||
r := []rune(s)
|
||||
if len(r) > n {
|
||||
return string(r[:n]) + "…"
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// model_error(provider/API 故障:LLM 层瞬时重试耗尽,或流已开始后中途断流)
|
||||
// 收场的 work 不是「试过没做完」也不是真失败,而是外部抖动。默认把它当永久
|
||||
// blocked 会白丢一条意图,所以这里对该终态额外重跑几次,每次之间退避一下,给
|
||||
@@ -53,6 +92,7 @@ type Engine struct {
|
||||
started sync.Map // taskID -> bool, so Run is idempotent per task
|
||||
lastAct sync.Map // taskID -> int64 unix, last planner/worker activity (heartbeat)
|
||||
paused sync.Map // taskID -> bool, user-paused (planner + workers idle but loops alive)
|
||||
dropCnt sync.Map // taskID -> *int64, running count of dropped (unpersistable) activity records
|
||||
|
||||
// per-task execution context: each planner.Plan / worker.Execute runs under it,
|
||||
// so pausing can CANCEL an in-flight run (not just skip the next one). Recreated
|
||||
@@ -279,22 +319,26 @@ func (e *Engine) emitActivity(t *Task, r db.Activity) {
|
||||
id, err := e.appendActivity(t, r)
|
||||
if err != nil {
|
||||
// NO LONGER SILENT: dropping a record breaks command↔result pairing in the
|
||||
// trace — a tool_use whose tool_result was lost shows as "执行中" forever.
|
||||
log.Printf("[activity] task %s 重试后仍失败、丢弃该记录 (kind=%s tool=%s tuid=%s): %v",
|
||||
t.ID, r.Kind, r.Tool, r.ToolUseID, err)
|
||||
// On the FK-parent failure (23503) probe the live DB so the next occurrence
|
||||
// records WHY the exploration is unreachable instead of just that it is.
|
||||
// trace — a tool_use whose tool_result was lost shows as "执行中" forever, and
|
||||
// a lost 'result'/'round' record leaves the session with no summary ("无总结").
|
||||
// Everything needed to分析根因 goes into ONE error-level line: reason class,
|
||||
// summary preview, running drop count for this task, and — on the FK case — a
|
||||
// live probe of WHY the parent exploration is unreachable.
|
||||
n := e.bumpDrop(t.ID)
|
||||
diag := ""
|
||||
// On the FK-parent failure (23503) probe the live DB so the log records WHY the
|
||||
// exploration is unreachable (row gone / wrong expID) instead of just that it is.
|
||||
if isFKViolation(err) {
|
||||
storeID := t.Store.ID()
|
||||
exists, refs, maxID, dErr := e.m.pg.ExplorationDiag(storeID)
|
||||
if dErr != nil {
|
||||
log.Printf("[activity] task %s FK 诊断查询失败 (store.expID=%d task.ExpID=%d): %v",
|
||||
t.ID, storeID, t.ExpID, dErr)
|
||||
if exists, refs, maxID, dErr := e.m.pg.ExplorationDiag(storeID); dErr != nil {
|
||||
diag = fmt.Sprintf(" | FK诊断查询失败(store.expID=%d task.ExpID=%d): %v", storeID, t.ExpID, dErr)
|
||||
} else {
|
||||
log.Printf("[activity] task %s FK 诊断: store.expID=%d task.ExpID=%d exploration存在=%v 引用它的task数=%d 当前MAX(exploration.id)=%d",
|
||||
t.ID, storeID, t.ExpID, exists, refs, maxID)
|
||||
diag = fmt.Sprintf(" | FK诊断: store.expID=%d task.ExpID=%d exploration存在=%v 引用它的task数=%d MAX(exploration.id)=%d",
|
||||
storeID, t.ExpID, exists, refs, maxID)
|
||||
}
|
||||
}
|
||||
log.Printf("[activity] task %s 丢弃活动记录(该任务累计第 %d 条) worker=%s kind=%s tool=%s tuid=%s reason=%s summary=%q: %v%s",
|
||||
t.ID, n, r.Worker, r.Kind, r.Tool, r.ToolUseID, dropReason(err), preview(r.Summary, 80), err, diag)
|
||||
e.touch(t.ID)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user