抽屉优化
This commit is contained in:
1 parent
7234874797
commit
d968727460
14 files changed
+669
-154
No files matched your search
@@ -697,6 +697,21 @@ CREATE TABLE IF NOT EXISTS skill_usage (
|
||||
CREATE INDEX IF NOT EXISTS idx_skill_usage_skill ON skill_usage(skill, ts DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_skill_usage_task ON skill_usage(task_id);
|
||||
|
||||
-- 工具调用账本(见 db/tool_usage.go)。一次实际 CoreTool.Call 一行,只记归属维度,
|
||||
-- 不保存工具参数或返回内容。刻意不设外键,任务、会话或自定义工具删除后仍保留统计。
|
||||
CREATE TABLE IF NOT EXISTS tool_usage (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
ts TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
tool_key TEXT NOT NULL,
|
||||
agent_key TEXT,
|
||||
task_id BIGINT,
|
||||
exploration_id BIGINT,
|
||||
intent_id BIGINT,
|
||||
session_id TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_usage_tool ON tool_usage(tool_key, ts DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_usage_task ON tool_usage(task_id);
|
||||
|
||||
-- =====================================================================
|
||||
-- H. 内置工具目录
|
||||
-- =====================================================================
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package db
|
||||
|
||||
// ToolUsage is one catalog tool invocation. It stores attribution dimensions only:
|
||||
// tool arguments and results are deliberately excluded from the ledger.
|
||||
type ToolUsage struct {
|
||||
ToolKey string `json:"tool_key"`
|
||||
AgentKey string `json:"agent_key"`
|
||||
TaskID int64 `json:"task_id"`
|
||||
ExplorationID int64 `json:"exploration_id"`
|
||||
IntentID int64 `json:"intent_id"`
|
||||
SessionID string `json:"session_id"`
|
||||
}
|
||||
|
||||
// InsertToolUsage appends one ledger row. Runtime callers treat metering as
|
||||
// best-effort so a statistics failure never interrupts the tool itself.
|
||||
func (d *DB) InsertToolUsage(u *ToolUsage) error {
|
||||
_, err := d.Exec(`
|
||||
INSERT INTO tool_usage(tool_key, agent_key, task_id, exploration_id, intent_id, session_id)
|
||||
VALUES ($1, NULLIF($2,''), $3, $4, $5, NULLIF($6,''))`,
|
||||
u.ToolKey, u.AgentKey, nullIfZero(u.TaskID), nullIfZero(u.ExplorationID),
|
||||
nullIfZero(u.IntentID), u.SessionID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ToolUsageCounts returns invocation totals keyed by catalog tool key. Entries
|
||||
// without calls are absent; API callers merge the result into the tools catalog.
|
||||
func (d *DB) ToolUsageCounts() (map[string]int, error) {
|
||||
rows, err := d.Query(`
|
||||
SELECT tool_key, COUNT(*)
|
||||
FROM tool_usage
|
||||
GROUP BY tool_key`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]int{}
|
||||
for rows.Next() {
|
||||
var key string
|
||||
var calls int
|
||||
if err := rows.Scan(&key, &calls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[key] = calls
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package db
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestToolUsageLedger(t *testing.T) {
|
||||
d, err := Open(testDSN(t))
|
||||
if err != nil {
|
||||
t.Skipf("postgres unavailable (%v) - skipping", err)
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
const (
|
||||
toolA = "zz_test_tool_usage_a"
|
||||
toolB = "zz_test_tool_usage_b"
|
||||
)
|
||||
cleanup := func() {
|
||||
_, _ = d.Exec(`DELETE FROM tool_usage WHERE tool_key IN ($1,$2)`, toolA, toolB)
|
||||
}
|
||||
cleanup()
|
||||
defer cleanup()
|
||||
|
||||
rows := []*ToolUsage{
|
||||
{ToolKey: toolA, AgentKey: "worker", TaskID: 991, ExplorationID: 5, IntentID: 7},
|
||||
{ToolKey: toolA, AgentKey: "planner", TaskID: 992, ExplorationID: 6},
|
||||
{ToolKey: toolA, AgentKey: "chatbot", SessionID: "conv-1"},
|
||||
{ToolKey: toolB, AgentKey: "worker", TaskID: 991},
|
||||
}
|
||||
for _, row := range rows {
|
||||
if err := d.InsertToolUsage(row); err != nil {
|
||||
t.Fatalf("insert %s: %v", row.ToolKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
counts, err := d.ToolUsageCounts()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := counts[toolA]; got != 3 {
|
||||
t.Errorf("%s calls: want 3, got %d", toolA, got)
|
||||
}
|
||||
if got := counts[toolB]; got != 1 {
|
||||
t.Errorf("%s calls: want 1, got %d", toolB, got)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ type Tool struct {
|
||||
Kind string `json:"kind"` // builtin | command | script | http
|
||||
Exec json.RawMessage `json:"exec"` // 自定义工具执行规格(kind!=builtin)
|
||||
Deferred bool `json:"deferred"` // schema 延迟(走 SearchExtraTools/ExecuteExtraTool)
|
||||
Calls int `json:"calls"` // runtime ledger aggregate; not stored in tools
|
||||
}
|
||||
|
||||
const toolCols = `key, system, description, schema, agents, enabled, kind, exec, deferred`
|
||||
|
||||
Reference in new issue
Block a user