From 9c1591ad8e036c7b8b34f171fbc70bc0cf7285cd Mon Sep 17 00:00:00 2001 From: Autumn-27 <53844290+Autumn-27@users.noreply.github.com> Date: Sat, 8 Aug 2026 13:24:35 -0400 Subject: [PATCH] =?UTF-8?q?fix(worker):=20search=5Fall=5Fworker=5Ftraces?= =?UTF-8?q?=20=E6=8E=92=E9=99=A4=E8=B0=83=E7=94=A8=E8=80=85=E8=87=AA?= =?UTF-8?q?=E8=BA=AB=E7=9A=84=E6=84=8F=E5=9B=BE=20trace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前走 ActivityTraceSearch(nil) 会把 worker 自己这条意图的步骤也搜出来 (纯噪声,本就在其上下文里)。新增 ActivityTraceSearchExcluding(node_id<>x), 工具传 t.ownerNode:worker=自身意图→排除自己;planner/mainagent 不受影响。 Co-Authored-By: Claude Opus 4.8 --- agent/tools.go | 10 ++++++---- db/exploration.go | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/agent/tools.go b/agent/tools.go index a7d4721..f1bf998 100644 --- a/agent/tools.go +++ b/agent/tools.go @@ -995,8 +995,9 @@ func (t *ToolSet) getWorkerTrace() actool.CoreTool { // summaries (≤100 chars), each tagged with its intent_id for follow-up drill-down. func (t *ToolSet) searchAllWorkerTraces() actool.CoreTool { return readTool("search_all_worker_traces", - "在【本任务所有 work 的执行过程】里按关键字(q)检索——用于找回某个 worker 见过、却没写进 fact 的东西(某路径/token/报错等)。"+ - "只返回命中步骤的摘要(summary≤100字),每条带 intent_id;据此再用 get_worker_trace(intent_id, step_ids=[...]) 取完整内容。不含思考(thinking)步骤。", + "【通常不推荐使用,因为系统中已经给了大部分信息了】在【本任务其他 work 的执行过程】里按关键字(q)检索——用于找回某个 worker 见过、却没写进 fact 的东西(某路径/token/报错等)。"+ + "已自动排除你自己这条意图的步骤(那些本就在你上下文里)。"+ + "只返回命中步骤的摘要(summary≤100字),每条带 intent_id;据此再用 get_worker_trace(intent_id, step_ids=[...]) 取完整内容。", obj(map[string]any{ "q": str("关键字(在所有 work 步骤的摘要+完整输出里搜)"), "limit": intp("返回上限,默认 100(可选)"), @@ -1010,7 +1011,8 @@ func (t *ToolSet) searchAllWorkerTraces() actool.CoreTool { if strings.TrimSpace(a.Q) == "" { return actool.Errorf("q 必填"), nil } - acts, err := t.ts.ActivityTraceSearch(nil, a.Q, a.Limit) + // 排除调用者自身这条意图的步骤(worker 的自有 trace 已在其上下文里)。 + acts, err := t.ts.ActivityTraceSearchExcluding(t.ownerNode, a.Q, a.Limit) if err != nil { return actool.Errorf(err.Error()), nil } @@ -1038,7 +1040,7 @@ func (t *ToolSet) searchAllWorkerTraces() actool.CoreTool { // no process to inspect). func (t *ToolSet) listWorkerTraces() actool.CoreTool { return readTool("list_worker_traces", - "列出本任务里【已跑过的 work(意图)】索引:intent_id + 一句话方向(summary) + 状态。"+ + "【通常不推荐使用,因为系统中已经给了大部分信息了】列出本任务里【已跑过的 work(意图)】索引:intent_id + 一句话方向(summary) + 状态。"+ "你(worker)看不到探索图,用它来发现有哪些 work 值得翻看——再用 get_worker_trace(intent_id) 看其步骤、get_worker_trace(intent_id, step_ids=[...]) 取详情。"+ "只列已执行的(running/done/exhausted/blocked/stopped),不含还没跑的 open。注意:你的任务边界仍是你领到的那条意图,看别的 work 只为复用观察/避免重复劳动。", obj(map[string]any{ diff --git a/db/exploration.go b/db/exploration.go index 3bdfb25..4ec4f30 100644 --- a/db/exploration.go +++ b/db/exploration.go @@ -615,6 +615,29 @@ AND (summary ILIKE $2 OR detail ILIKE $2) ORDER BY id LIMIT $3`, s.expID, like, return scanTrace(rows) } +// ActivityTraceSearchExcluding keyword-searches every node's steps in this +// exploration EXCEPT one node's own (excludeNodeID) — so a worker's +// search_all_worker_traces doesn't return its own in-progress trace (which is +// already in its context). excludeNodeID<=0 → no exclusion (searches all nodes). +func (s *ExplorationStore) ActivityTraceSearchExcluding(excludeNodeID int64, q string, limit int) ([]Activity, error) { + if excludeNodeID <= 0 { + return s.ActivityTraceSearch(nil, q, limit) + } + if limit <= 0 { + limit = 100 + } + like := "%" + q + "%" + rows, err := s.db.Query(`SELECT `+traceCols+` +FROM activity WHERE exploration_id=$1 AND node_id IS NOT NULL AND node_id <> $2 +AND kind NOT IN ('thinking','usage') +AND (summary ILIKE $3 OR detail ILIKE $3) ORDER BY id LIMIT $4`, s.expID, excludeNodeID, like, limit) + if err != nil { + return nil, err + } + defer rows.Close() + return scanTrace(rows) +} + // ActivityByIDs loads full detail for specific step ids (the trace drill-down), // scoped to this exploration. thinking rows are skipped (never returned, even if // their id is asked for). Order is ascending id, not the input order.