- 新增 context_rewrite 工具:允许 AI 在单轮内压缩工具调用上下文 - 清理 persona_query_max 和 task_query_max 死配置(commit 9be60ba 引入) - 同步全栈:后端/前端/文档/测试 18 个文件 - 测试:70/70 通过
330 lines
11 KiB
Python
330 lines
11 KiB
Python
"""
|
|
工具执行器
|
|
"""
|
|
import json
|
|
from typing import Any, Dict
|
|
|
|
|
|
def execute_tool(graph: Any, tool_name: str, arguments: dict) -> str:
|
|
"""执行工具调用"""
|
|
print(f"\n[工具调用] {tool_name}")
|
|
print(f"[参数] {json.dumps(arguments, ensure_ascii=False, indent=2)}")
|
|
|
|
try:
|
|
# 基础记忆工具
|
|
if tool_name == "memory_recall":
|
|
result = graph.recall(
|
|
query_intent=arguments.get("query_intent", ""),
|
|
seed_entities=arguments.get("seed_entities"),
|
|
depth=arguments.get("depth", 2),
|
|
time_range=arguments.get("time_range"),
|
|
session_filter=arguments.get("session_filter")
|
|
)
|
|
return format_recall_result(result)
|
|
|
|
elif tool_name == "memory_commit":
|
|
result = graph.commit(
|
|
triplets=arguments.get("triplets", []),
|
|
entity_types=arguments.get("entity_types"),
|
|
temporal_tag=arguments.get("temporal_tag")
|
|
)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "memory_purge":
|
|
result = graph.purge(
|
|
criteria=arguments.get("criteria", {}),
|
|
mode=arguments.get("mode", "soft"),
|
|
new_relation=arguments.get("new_relation")
|
|
)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "memory_introspect":
|
|
result = graph.introspect(session_id=arguments.get("session_id"))
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "memory_archive":
|
|
result = graph.archive(days=arguments.get("days", 30))
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "memory_cleanup":
|
|
result = graph.cleanup(dry_run=arguments.get("dry_run", True))
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "context_rewrite":
|
|
result = execute_context_rewrite(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
# 人设图管理工具
|
|
elif tool_name == "persona_update":
|
|
result = execute_persona_update(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "persona_clear":
|
|
result = execute_persona_clear(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
# 工作记忆链管理工具
|
|
elif tool_name == "task_create":
|
|
result = execute_task_create(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "task_set_state":
|
|
result = execute_task_set_state(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "task_delete":
|
|
result = execute_task_delete(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
elif tool_name == "task_link_info":
|
|
result = execute_task_link_info(graph, arguments)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|
|
|
|
return f"未知工具: {tool_name}"
|
|
|
|
except Exception as e:
|
|
return f"工具执行错误: {str(e)}"
|
|
|
|
|
|
def format_recall_result(result: dict) -> str:
|
|
"""格式化检索结果"""
|
|
lines = ["===== 记忆检索结果 ====="]
|
|
|
|
if result.get("entities"):
|
|
lines.append(f"\n实体 ({len(result['entities'])} 个):")
|
|
for e in result["entities"]:
|
|
if e and isinstance(e, dict):
|
|
lines.append(f" - {e.get('name', 'N/A')} (类型: {e.get('type', 'unknown')}, 提及: {e.get('mention_count', 1)}次)")
|
|
|
|
if result.get("relations"):
|
|
lines.append(f"\n关系 ({len(result['relations'])} 条):")
|
|
for r in result["relations"]:
|
|
if r and isinstance(r, dict):
|
|
lines.append(f" - {r.get('source', 'N/A')} --[{r.get('type', 'N/A')}]--> {r.get('target', 'N/A')}")
|
|
created = r.get("created_at", "N/A")
|
|
if created and created != "N/A":
|
|
created = created[:19] if "T" in str(created) else str(created)
|
|
session_id = r.get('session_id', 'N/A')
|
|
session_display = session_id[:20] if session_id and session_id != 'N/A' else 'N/A'
|
|
lines.append(f" 时间: {created}, 会话: {session_display}, 轮次: {r.get('turn_id', 0)}, 置信度: {r.get('confidence', 1.0)}")
|
|
|
|
if not result.get("entities") and not result.get("relations"):
|
|
lines.append("\n(未找到相关记忆)")
|
|
|
|
lines.append("=" * 30)
|
|
return "\n".join(lines)
|
|
|
|
|
|
def execute_context_rewrite(graph: Any, arguments: dict) -> dict:
|
|
"""压缩工具调用上下文"""
|
|
summary = arguments.get("summary", "")
|
|
|
|
# 验证格式:必须包含工具调用标记
|
|
if "[工具调用总结" not in summary:
|
|
return {
|
|
"status": "error",
|
|
"message": "总结格式错误:必须包含 [工具调用总结: 本次总结了 N 次工具调用 | 调用工具: ...] 标记"
|
|
}
|
|
|
|
return {
|
|
"status": "success",
|
|
"message": "上下文已压缩",
|
|
"summary": summary
|
|
}
|
|
|
|
|
|
# 人设图管理工具实现
|
|
def execute_persona_update(graph: Any, arguments: dict) -> dict:
|
|
"""更新人设"""
|
|
attributes = arguments.get("attributes", [])
|
|
mode = arguments.get("mode", "merge")
|
|
|
|
if mode == "replace":
|
|
# 先清除旧人设
|
|
graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "扮演角色"},
|
|
mode="soft"
|
|
)
|
|
graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "说话风格"},
|
|
mode="soft"
|
|
)
|
|
graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "性格特点"},
|
|
mode="soft"
|
|
)
|
|
|
|
# 写入新人设
|
|
triplets = []
|
|
for attr in attributes:
|
|
triplets.append({
|
|
"subject": "AI",
|
|
"relation": attr["attribute"],
|
|
"object": attr["value"],
|
|
"confidence": 1.0
|
|
})
|
|
|
|
result = graph.commit(triplets=triplets)
|
|
return {
|
|
"status": "success",
|
|
"mode": mode,
|
|
"updated_attributes": len(attributes),
|
|
"details": result
|
|
}
|
|
|
|
|
|
def execute_persona_clear(graph: Any, arguments: dict) -> dict:
|
|
"""清除人设"""
|
|
if not arguments.get("confirm", True):
|
|
return {"status": "cancelled", "message": "需要确认才能清除人设"}
|
|
|
|
# 删除所有人设相关关系
|
|
result1 = graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "扮演角色"},
|
|
mode="soft"
|
|
)
|
|
result2 = graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "说话风格"},
|
|
mode="soft"
|
|
)
|
|
result3 = graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "性格特点"},
|
|
mode="soft"
|
|
)
|
|
result4 = graph.purge(
|
|
criteria={"subject_contains": "AI", "relation_type": "语气特征"},
|
|
mode="soft"
|
|
)
|
|
|
|
total_deleted = (
|
|
result1.get("deleted_count", 0) +
|
|
result2.get("deleted_count", 0) +
|
|
result3.get("deleted_count", 0) +
|
|
result4.get("deleted_count", 0)
|
|
)
|
|
|
|
return {
|
|
"status": "success",
|
|
"deleted_count": total_deleted,
|
|
"message": "人设已清除,恢复默认身份"
|
|
}
|
|
|
|
|
|
# 工作记忆链管理工具实现
|
|
def execute_task_create(graph: Any, arguments: dict) -> dict:
|
|
"""创建任务节点"""
|
|
task_id = arguments.get("task_id")
|
|
description = arguments.get("description")
|
|
info_nodes = arguments.get("info_nodes", [])
|
|
|
|
# 创建任务节点
|
|
triplets = [
|
|
{"subject": task_id, "relation": "is_type", "object": "TaskNode"},
|
|
{"subject": task_id, "relation": "has_description", "object": description},
|
|
{"subject": task_id, "relation": "HAS_STATE", "object": "State_进行中"}
|
|
]
|
|
|
|
result = graph.commit(triplets=triplets)
|
|
|
|
# 关联信息节点
|
|
if info_nodes:
|
|
link_triplets = []
|
|
for node_name in info_nodes:
|
|
link_triplets.append({
|
|
"subject": task_id,
|
|
"relation": "CONTAINS_INFO",
|
|
"object": node_name
|
|
})
|
|
graph.commit(triplets=link_triplets)
|
|
|
|
return {
|
|
"status": "success",
|
|
"task_id": task_id,
|
|
"description": description,
|
|
"info_nodes": info_nodes,
|
|
"details": result
|
|
}
|
|
|
|
|
|
def execute_task_set_state(graph: Any, arguments: dict) -> dict:
|
|
"""设置任务状态"""
|
|
task_id = arguments.get("task_id")
|
|
state = arguments.get("state")
|
|
|
|
# 删除旧状态
|
|
graph.purge(
|
|
criteria={"subject_contains": task_id, "relation_type": "HAS_STATE"},
|
|
mode="soft"
|
|
)
|
|
|
|
# 设置新状态
|
|
state_node = f"State_{state}"
|
|
result = graph.commit(
|
|
triplets=[{"subject": task_id, "relation": "HAS_STATE", "object": state_node}]
|
|
)
|
|
|
|
return {
|
|
"status": "success",
|
|
"task_id": task_id,
|
|
"new_state": state,
|
|
"details": result
|
|
}
|
|
|
|
|
|
def execute_task_delete(graph: Any, arguments: dict) -> dict:
|
|
"""删除任务节点"""
|
|
task_id = arguments.get("task_id")
|
|
delete_info_nodes = arguments.get("delete_info_nodes", True)
|
|
|
|
# 查询关联的信息节点
|
|
if delete_info_nodes:
|
|
recall_result = graph.recall(
|
|
query_intent=f"{task_id},CONTAINS_INFO",
|
|
depth=1
|
|
)
|
|
|
|
# 删除信息节点
|
|
for relation in recall_result.get("relations", []):
|
|
if relation.get("type") == "CONTAINS_INFO" and relation.get("source") == task_id:
|
|
info_node = relation.get("target")
|
|
graph.purge(
|
|
criteria={"subject_contains": info_node},
|
|
mode="soft"
|
|
)
|
|
|
|
# 删除任务节点
|
|
result = graph.purge(
|
|
criteria={"subject_contains": task_id},
|
|
mode="soft"
|
|
)
|
|
|
|
return {
|
|
"status": "success",
|
|
"task_id": task_id,
|
|
"deleted_info_nodes": delete_info_nodes,
|
|
"details": result
|
|
}
|
|
|
|
|
|
def execute_task_link_info(graph: Any, arguments: dict) -> dict:
|
|
"""关联信息节点"""
|
|
task_id = arguments.get("task_id")
|
|
info_node_names = arguments.get("info_node_names", [])
|
|
|
|
triplets = []
|
|
for node_name in info_node_names:
|
|
triplets.append({
|
|
"subject": task_id,
|
|
"relation": "CONTAINS_INFO",
|
|
"object": node_name
|
|
})
|
|
|
|
result = graph.commit(triplets=triplets)
|
|
|
|
return {
|
|
"status": "success",
|
|
"task_id": task_id,
|
|
"linked_nodes": info_node_names,
|
|
"details": result
|
|
}
|