feat(memory): P1 长期记忆升级 —— 异步攒批 Consolidate + 软删 + importance/last_seen #1

Merged
Blizzard merged 181 commits from feat/wails3 into main 2026-07-17 01:12:32 +00:00
2 changed files with 37 additions and 14 deletions
Showing only changes of commit 79437e1c1c - Show all commits
@@ -101,16 +101,26 @@ type toolCatalogEntry struct {
Inject []string `json:"inject"`
}
// agentTools 动态构建 ReAct 可用的工具集:调 MCP list_tools 自描述目录 → 取 agent_exposed 的工具
// → 用上报参数 schema 建 InvokableToolinject 参数(user_id/session_id/kb/task_id)服务端运行时
// 绑定、不暴露给模型。新增工具只需在 mcp-go 注册表标 agent,无需改这里(杜绝硬编码)。
// agentTools 动态构建 ReAct 可用的工具集:分别向 mcp-go / mcp-py 探 list_tools 自描述目录,
// 取 agent_exposed 的工具按上报参数 schema 建 InvokableToolinject 参数(user_id/session_id/
// kb/task_id)服务端运行时绑定、不暴露给模型。某台 MCP 离线即跳过(降级)。
// 新增工具只需在对应 MCP 注册表标 agent,无需改这里(杜绝硬编码)。
func (o *Orchestrator) agentTools(b *board, taskID string, tr *execTracer) []tool.BaseTool {
if o.tools == nil {
return nil
}
var out []tool.BaseTool
out = append(out, o.discoverTools(contract.ToolSubjectGo, b, taskID, tr)...)
out = append(out, o.discoverTools(contract.ToolSubjectPy, b, taskID, tr)...)
return out
}
// discoverTools 向某台 MCPsubject 前缀决定 go/py)探 list_tools,把 agent_exposed 的工具
// 转成 Eino InvokableTool。该 MCP 不可用 / 无应答时返回空(不阻断)。
func (o *Orchestrator) discoverTools(subject func(string) string, b *board, taskID string, tr *execTracer) []tool.BaseTool {
cctx, cancel := context.WithTimeout(context.Background(), toolCallTimeout)
defer cancel()
res, err := o.tools.CallTool(cctx, contract.ToolSubjectGo("list_tools"), &contract.ToolCall{Tool: "list_tools"})
res, err := o.tools.CallTool(cctx, subject("list_tools"), &contract.ToolCall{Tool: "list_tools"})
if err != nil || res == nil || !res.OK {
return nil
}
@@ -120,7 +130,6 @@ func (o *Orchestrator) agentTools(b *board, taskID string, tr *execTracer) []too
if json.Unmarshal([]byte(res.Content), &cat) != nil {
return nil
}
// inject 参数名 → 本任务的运行时值(不进模型菜单)。
injectVal := map[string]any{"user_id": b.uid, "session_id": b.sid, "task_id": taskID, "kb": b.kb}
var out []tool.BaseTool
@@ -144,7 +153,7 @@ func (o *Orchestrator) agentTools(b *board, taskID string, tr *execTracer) []too
}
out = append(out, &mcpTool{
mcpName: e.Name,
subject: contract.ToolSubjectGo,
subject: subject,
caller: o.tools, taskID: taskID, tr: tr,
bind: bind,
info: &schema.ToolInfo{
@@ -25,12 +25,18 @@ log = logging.getLogger("mcp_py")
SUBJECT_PY_ALL = "sundynix.tools.py.>"
QUEUE_PY = "mcp-py-workers"
# 工具元信息:名称 → (中文名, 作用简述)。list_tools 据此上报给管理端展示。
# 工具元信息(与 mcp-go 注册表同形):list_tools 上报给管理端展示 + dispatcher 动态构建自主 agent 工具集
# agent=是否暴露给自主 agentparams=模型可填参数;inject=服务端注入参数(不暴露给模型)。
TOOL_META = {
"echo": ("回显", "原样返回入参(调试用)"),
"run_code": ("代码执行", "静态守卫 + Docker 隔离沙箱运行代码(标准档 256m/10s)"),
"parse_document": ("文档解析", "文件 → 纯文本(MinerU / PaddleOCR"),
"secure_sandbox": ("安全沙箱", "更严资源档(128m/5s)的隔离执行,用于高风险代码"),
"echo": {"cn": "回显", "desc": "原样返回入参(调试用)"},
"run_code": {
"cn": "代码执行",
"desc": "在隔离沙箱里执行 Python 代码(静态守卫 + Docker,256m/10s)。用于计算、数据处理、逻辑验证。",
"agent": True,
"params": [{"name": "code", "type": "string", "desc": "要执行的 Python 代码", "required": True}],
},
"parse_document": {"cn": "文档解析", "desc": "文件 → 纯文本(MinerU / PaddleOCR"},
"secure_sandbox": {"cn": "安全沙箱", "desc": "更严资源档(128m/5s)的隔离执行,用于高风险代码"},
}
@@ -102,10 +108,18 @@ class McpGateway:
return str(args.get("text", ""))
async def _list_tools(self, args: dict) -> str:
"""自省:上报业务工具清单(名称 + 中文名 + 作用),供管理端探活 + 展示"""
"""自省:上报业务工具清单(名称/中文名/作用 + agent 暴露元信息),供管理端展示 + dispatcher 动态发现"""
tools = [
{"name": n, "cn": cn, "desc": d}
for n, (cn, d) in TOOL_META.items()
{
"name": n,
"cn": m["cn"],
"desc": m["desc"],
"agent_exposed": m.get("agent", False),
"agent_name": m.get("agent_name", ""),
"params": m.get("params", []),
"inject": m.get("inject", []),
}
for n, m in TOOL_META.items()
if n in self._tools # 仅上报真正注册的业务工具(list_tools 自身不计入)
]
return json.dumps({"service": "mcp-py", "tools": tools})