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:
parent
b9f9f75c31
commit
79b994ce4b
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in New Issue