feat: add memory_graph_navigate and memory_graph_stats tools (v1.1.0)

- Add graph_navigate() and graph_stats() to ZhiYiClient
- Add memory_graph_navigate tool: N-hop knowledge graph navigation
- Add memory_graph_stats tool: graph statistics (nodes/edges/density)
- Update system prompt to list all available tools
- Bump version 1.0.0 -> 1.1.0
This commit is contained in:
小唯 2026-06-15 17:28:16 +08:00
parent b9f9f75c31
commit 79b994ce4b
2 changed files with 118 additions and 6 deletions

View File

@ -228,6 +228,33 @@ class ZhiYiClient:
logger.warning("ZhiYi search_notes error: %s", e)
return []
def graph_navigate(self, entity: str, max_hops: int = 2,
namespace: Optional[str] = None) -> Dict[str, Any]:
"""图谱导航:查询实体的 N 跳关系网络。返回 {entity, count, paths}。"""
try:
payload = {"entity": entity, "max_hops": max_hops}
if namespace:
payload["namespace"] = namespace
r = self._session.post(self._url("/api/v1/graph/navigate"), json=payload, timeout=self.timeout)
if r.status_code == 200:
return r.json()
logger.warning("ZhiYi graph_navigate failed: %s %s", r.status_code, r.text[:200])
return {}
except Exception as e:
logger.warning("ZhiYi graph_navigate error: %s", e)
return {}
def graph_stats(self) -> Dict[str, Any]:
"""获取图谱统计:节点数、边数、密度。"""
try:
r = self._session.get(self._url("/api/v1/graph/stats"), timeout=self.timeout)
if r.status_code == 200:
return r.json()
return {}
except Exception as e:
logger.warning("ZhiYi graph_stats error: %s", e)
return {}
# ── Memory Provider ──────────────────────────────────────────────────────────
@ -536,6 +563,33 @@ class HermesZhiYiMemoryProvider(MemoryProvider):
"properties": {},
},
},
{
"name": "memory_graph_navigate",
"description": "Navigate the ZhiYi knowledge graph to understand relationships around an entity. Shows N-hop relationship network — what concepts, people, or projects are connected, and how. Use when: analyzing project structure, understanding who/what is related to a topic, exploring context dependencies.",
"parameters": {
"type": "object",
"properties": {
"entity": {
"type": "string",
"description": "The central entity to navigate from (e.g. '牧尘', '织忆', '小唯', 'openclaw'). Chinese and English both work.",
},
"max_hops": {
"type": "integer",
"description": "Maximum path depth (default: 2). 1-hop = direct neighbors only; 2-hop = friends-of-friends. Larger values return more paths but are slower.",
"default": 2,
},
},
"required": ["entity"],
},
},
{
"name": "memory_graph_stats",
"description": "Get ZhiYi knowledge graph statistics: total nodes, edges, and graph density. Use to check graph size and health.",
"parameters": {
"type": "object",
"properties": {},
},
},
]
def handle_tool_call(self, tool_name: str, args: Dict[str, Any], **kwargs) -> str:
@ -550,6 +604,11 @@ class HermesZhiYiMemoryProvider(MemoryProvider):
return self._tool_memory_metrics()
elif tool_name == "memory_stats":
return self._tool_memory_stats()
elif tool_name == "memory_graph_navigate":
return self._tool_memory_graph_navigate(
args.get("entity", ""), args.get("max_hops", 2))
elif tool_name == "memory_graph_stats":
return self._tool_memory_graph_stats()
return tool_error(tool_name, "Unknown tool")
# ── Tool handlers ─────────────────────────────────────────────────────────
@ -617,6 +676,55 @@ class HermesZhiYiMemoryProvider(MemoryProvider):
return json.dumps({"success": False, "error": "Could not reach ZhiYi /metrics/self"})
return json.dumps({"success": True, "metrics": metrics})
def _tool_memory_graph_navigate(self, entity: str, max_hops: int) -> str:
"""图谱导航:查询实体的 N 跳关系网络。"""
if not self._client:
return json.dumps({"success": False, "error": "ZhiYi client not initialized"})
if not entity or len(entity.strip()) < 1:
return json.dumps({"success": False, "error": "entity is required"})
if max_hops < 1:
max_hops = 1
if max_hops > 5:
max_hops = 5
result = self._client.graph_navigate(entity.strip(), max_hops=max_hops)
if not result:
return json.dumps({"success": False, "error": "Graph navigate failed — check ZhiYi server logs"})
# 格式化路径输出,保留关键信息
paths = result.get("paths", [])
formatted_paths = []
for p in paths[:50]: # 最多返回50条路径避免太长
formatted_paths.append({
"from": p.get("from", ""),
"relation": p.get("relation", ""),
"to": p.get("to", ""),
"hop": p.get("hop", 1),
"weight": round(p.get("weight", 0), 3),
})
return json.dumps({
"success": True,
"entity": result.get("entity", entity),
"bidirectional": result.get("bidirectional", False),
"count": result.get("count", len(formatted_paths)),
"paths": formatted_paths,
"message": f"Found {result.get('count', 0)} paths within {max_hops}-hop network",
})
def _tool_memory_graph_stats(self) -> str:
"""获取图谱统计:节点数、边数、密度。"""
if not self._client:
return json.dumps({"success": False, "error": "ZhiYi client not initialized"})
stats = self._client.graph_stats()
if not stats:
return json.dumps({"success": False, "error": "Could not reach ZhiYi /graph/stats"})
return json.dumps({
"success": True,
"stats": stats,
"message": f"Graph has {stats.get('node_count', 0)} nodes and {stats.get('edge_count', 0)} edges (density: {stats.get('density', 0):.4f})",
})
# ── Session management ────────────────────────────────────────────────────
def on_session_switch(self, new_session_id: str, *, parent_session_id: str = "", reset: bool = False, **kwargs) -> None:
@ -635,10 +743,14 @@ class HermesZhiYiMemoryProvider(MemoryProvider):
return (
"\\n[ZhiYi Memory] You have access to ZhiYi MemoryWeave — a semantic memory system with knowledge graph.\\n"
"Your prefetch automatically retrieves: (1) relevant semantic memories, (2) Obsidian notes related via graph navigation.\\n"
"Use memory_search to recall past facts, decisions, and context before answering.\\n"
"Use memory_write to save important information the user tells you.\\n"
"Use memory_feedback to mark memories as useful/not-useful — this drives self-optimization.\\n"
"Use memory_metrics to check memory system health (recall hit rate, gap closures, etc.).\\n"
"Tools available:\\n"
" memory_search — semantic search for relevant past information\\n"
" memory_write — save an important fact or preference\\n"
" memory_feedback — mark memories as useful/not-useful (drives self-optimization)\\n"
" memory_metrics — check memory system health (recall hit rate, gap closures, etc.)\\n"
" memory_stats — get memory statistics (total docs, vector dimensions)\\n"
" memory_graph_navigate — navigate the knowledge graph to understand entity relationships (N-hop network)\\n"
" memory_graph_stats — get knowledge graph statistics (nodes, edges, density)\\n"
)

View File

@ -1,6 +1,6 @@
name: hermes-zhiyi
version: 1.0.0
description: Bridge to ZhiYi MemoryWeave — bge-m3 1024-dim semantic memory with FAISS + bge-reranker-v2-m3. Replaces local lanceDB with shared ZhiYi server.
version: 1.1.0
description: "Bridge to ZhiYi MemoryWeave - bge-m3 1024-dim semantic memory with FAISS + bge-reranker. Replaces local lanceDB with shared ZhiYi server. Now includes knowledge graph tools: memory_graph_navigate and memory_graph_stats."
provider: zhiyi
entry: __init__.register
memory_provider: true