Files
ARTEX/agent/tools_digest.go
T
Autumn-27 06a43f37ff refactor(graph_overview): 精简态势概览字段
- 删除 cold_index 字段及其 expand_index 工具(含两处注册),移除
  仅服务它的 coldDigestOverview 分桶逻辑(改用 activeDigestBodies)
- 删除 covered_members 字段(悬空血缘解析表)
- 删除 coverage.scope 字段(范围根资产 + company 富化)
- finding_list 每条仅保留 id/summary/from_intent,去掉 evidence/assets,
  并清理随之无用的 findingMeta 预计算与 assetValue 辅助函数
2026-09-24 12:24:01 -04:00

140 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package agent
// cold-digest §6: graph_overview folding + the restore tools.
//
// activeDigestBodies — builds the folded cold region for graph_overview:
// cold_digests (flat {id, body, member_count}).
// expand_digest(id) — level-1 restore: a digest's member compact list.
import (
"context"
"encoding/json"
"fmt"
"github.com/Autumn-27/artex/db"
actool "github.com/Autumn-27/norma/tool"
)
// digestMemberEntry builds the compact per-member view expand_digest returns —
// same shape as recent_facts / recent_done_intents (§6.1 middle level). store is
// the digest's OWNING store (the current task, or a read-only source task §2).
func (t *ToolSet) digestMemberEntry(store *db.ExplorationStore, id int64) map[string]any {
n, _ := store.GetNode(id)
if n == nil {
return map[string]any{"id": id, "missing": true}
}
m := compactNode(n)
m["state"] = n.State
var p map[string]any
if json.Unmarshal(n.Payload, &p) == nil {
if c, ok := p["confidence"].(string); ok && c != "" {
m["confidence"] = c
}
}
return m
}
// activeDigestBodies returns [{id, body, member_count}] for a store's active
// digests — the folded cold region as flat bodies (§6.1). Shared by the current
// task overview and the read-only related-task overview (§2 cross-task reuse).
func activeDigestBodies(store *db.ExplorationStore) []map[string]any {
ads, err := store.ActiveDigests()
if err != nil || len(ads) == 0 {
return nil
}
out := make([]map[string]any, 0, len(ads))
for _, d := range ads {
var p struct {
Body string `json:"body"`
}
_ = json.Unmarshal(d.Payload, &p)
ms, _ := store.DigestMembers(d.ID)
out = append(out, map[string]any{"id": d.ID, "body": p.Body, "member_count": len(ms)})
}
return out
}
// hiddenMembersFor returns a predicate telling whether a member is hidden (folded
// into an active digest AND still cold) in the given store — so a source task's
// overview folds exactly the way that task folds itself (§2 cross-task: "当前任务
// 什么展示逻辑,关联任务就什么逻辑"). A revived (now hot) covered member is NOT
// hidden (§6 render-time revival check). Returns a never-hidden predicate when the
// store has no digests.
func hiddenMembersFor(store *db.ExplorationStore) func(int64) bool {
covered, err := store.CoveredMembers()
if err != nil || len(covered) == 0 {
return func(int64) bool { return false }
}
var hot map[int64]bool
if cg, _, err := loadColdGraph(store); err == nil {
hot = cg.hotSet()
}
return func(id int64) bool { _, c := covered[id]; return c && !hot[id] }
}
// resolveDigest finds a digest node by id in the current task, else in a direct
// source task (read-only, §2). Returns the node, its owning store, and the source
// task id (0 = current task).
func (t *ToolSet) resolveDigest(id int64) (*db.Node, *db.ExplorationStore, int64) {
if n, _ := t.ts.GetNode(id); n != nil && n.Kind == db.KindDigest {
return n, t.ts, 0
}
srcs, _ := t.ts.DirectSourceStores()
for _, s := range srcs {
if n, _ := s.Store.GetNode(id); n != nil && n.Kind == db.KindDigest {
return n, s.Store, s.Task.TaskID
}
}
return nil, nil, 0
}
// expandDigest returns a digest's covered members as a compact list (§6.1). It is
// a distinct tool from node_detail because it returns a LIST of members, not one
// node's full detail.
func (t *ToolSet) expandDigest() actool.CoreTool {
return t.writeExpTool("expand_digest",
"展开一个 cold digest:返回它折叠的成员紧凑列表(id/summary/state/confidence),与概览 recent_facts/recent_done_intents 同形状。要某条完整细节/证据用 node_detail(member_id)。",
map[string]any{
"type": "object",
"properties": map[string]any{
"id": map[string]any{"type": "integer", "description": "digest 节点 id(来自概览 cold_digests)"},
},
"required": []any{"id"},
},
func(ctx context.Context, raw json.RawMessage) (actool.Result, error) {
var in struct {
ID int64 `json:"id"`
}
_ = json.Unmarshal(raw, &in)
n, store, srcTaskID := t.resolveDigest(in.ID)
if n == nil {
return jsonResult(map[string]any{"error": fmt.Sprintf("#%d 不是 digest 节点(本任务或直接关联任务里都没找到)", in.ID)})
}
var p struct {
Body string `json:"body"`
}
_ = json.Unmarshal(n.Payload, &p)
members, _ := store.DigestMembers(in.ID)
list := make([]map[string]any, 0, len(members))
for _, m := range members {
entry := t.digestMemberEntry(store, m)
if srcTaskID > 0 { // 关联任务的成员:只读,带继承标记(§2)
entry["inherited"] = true
entry["source_task_id"] = srcTaskID
}
list = append(list, entry)
}
out := map[string]any{
"id": in.ID,
"state": n.State, // active / superseded
"body": p.Body,
"members": list,
}
if srcTaskID > 0 {
out["inherited"] = true
out["source_task_id"] = srcTaskID
}
return jsonResult(out)
})
}