|
|
@@ -223,8 +223,8 @@ class KnowledgeIn(BaseModel):
|
|
|
resource_ids: list[str] = []
|
|
|
source: dict = {} # {name, category, urls, agent_id, submitted_by, timestamp}
|
|
|
eval: dict = {} # {score, helpful, harmful, confidence}
|
|
|
- support_capability: list[str] = []
|
|
|
- tools: list[str] = []
|
|
|
+ capability_ids: list[str] = []
|
|
|
+ tool_ids: list[str] = []
|
|
|
|
|
|
|
|
|
class KnowledgeOut(BaseModel):
|
|
|
@@ -236,7 +236,6 @@ class KnowledgeOut(BaseModel):
|
|
|
scopes: list[str]
|
|
|
owner: str
|
|
|
content: str
|
|
|
- resource_ids: list[str]
|
|
|
source: dict
|
|
|
eval: dict
|
|
|
created_at: str
|
|
|
@@ -258,8 +257,8 @@ class KnowledgePatchIn(BaseModel):
|
|
|
tags: Optional[dict] = None
|
|
|
scopes: Optional[list[str]] = None
|
|
|
owner: Optional[str] = None
|
|
|
- support_capability: Optional[list[str]] = None
|
|
|
- tools: Optional[list[str]] = None
|
|
|
+ capability_ids: Optional[list[str]] = None
|
|
|
+ tool_ids: Optional[list[str]] = None
|
|
|
|
|
|
|
|
|
class MessageExtractIn(BaseModel):
|
|
|
@@ -302,11 +301,9 @@ class ToolIn(BaseModel):
|
|
|
input: dict | str = ""
|
|
|
output: dict | str = ""
|
|
|
status: str = "未接入"
|
|
|
- capabilities: list[str] = []
|
|
|
- tool_knowledge: list[str] = []
|
|
|
- case_knowledge: list[str] = []
|
|
|
- process_knowledge: list[str] = []
|
|
|
- implemented_tool_ids: list[str] = []
|
|
|
+ capability_ids: list[str] = []
|
|
|
+ knowledge_ids: list[str] = []
|
|
|
+ provider_ids: list[str] = []
|
|
|
|
|
|
|
|
|
class ToolPatchIn(BaseModel):
|
|
|
@@ -317,11 +314,9 @@ class ToolPatchIn(BaseModel):
|
|
|
input: Optional[dict | str] = None
|
|
|
output: Optional[dict | str] = None
|
|
|
status: Optional[str] = None
|
|
|
- capabilities: Optional[list[str]] = None
|
|
|
- tool_knowledge: Optional[list[str]] = None
|
|
|
- case_knowledge: Optional[list[str]] = None
|
|
|
- process_knowledge: Optional[list[str]] = None
|
|
|
- implemented_tool_ids: Optional[list[str]] = None
|
|
|
+ capability_ids: Optional[list[str]] = None
|
|
|
+ knowledge_ids: Optional[list[str]] = None
|
|
|
+ provider_ids: Optional[list[str]] = None
|
|
|
|
|
|
|
|
|
# --- Capability Models ---
|
|
|
@@ -331,20 +326,20 @@ class CapabilityIn(BaseModel):
|
|
|
name: str = ""
|
|
|
criterion: str = ""
|
|
|
description: str = ""
|
|
|
- requirements: list[str] = []
|
|
|
+ requirement_ids: list[str] = []
|
|
|
implements: dict = {}
|
|
|
- tools: list[str] = []
|
|
|
- source_knowledge: list[str] = []
|
|
|
+ tool_ids: list[str] = []
|
|
|
+ knowledge_ids: list[str] = []
|
|
|
|
|
|
|
|
|
class CapabilityPatchIn(BaseModel):
|
|
|
name: Optional[str] = None
|
|
|
criterion: Optional[str] = None
|
|
|
description: Optional[str] = None
|
|
|
- requirements: Optional[list[str]] = None
|
|
|
+ requirement_ids: Optional[list[str]] = None
|
|
|
implements: Optional[dict] = None
|
|
|
- tools: Optional[list[str]] = None
|
|
|
- source_knowledge: Optional[list[str]] = None
|
|
|
+ tool_ids: Optional[list[str]] = None
|
|
|
+ knowledge_ids: Optional[list[str]] = None
|
|
|
|
|
|
|
|
|
# --- Requirement Models ---
|
|
|
@@ -352,7 +347,8 @@ class CapabilityPatchIn(BaseModel):
|
|
|
class RequirementIn(BaseModel):
|
|
|
id: str
|
|
|
description: str = ""
|
|
|
- atomics: list[str] = []
|
|
|
+ capability_ids: list[str] = []
|
|
|
+ knowledge_ids: list[str] = []
|
|
|
source_nodes: list[dict] = []
|
|
|
status: str = "未满足"
|
|
|
match_result: str = ""
|
|
|
@@ -360,7 +356,8 @@ class RequirementIn(BaseModel):
|
|
|
|
|
|
class RequirementPatchIn(BaseModel):
|
|
|
description: Optional[str] = None
|
|
|
- atomics: Optional[list[str]] = None
|
|
|
+ capability_ids: Optional[list[str]] = None
|
|
|
+ knowledge_ids: Optional[list[str]] = None
|
|
|
source_nodes: Optional[list[dict]] = None
|
|
|
status: Optional[str] = None
|
|
|
match_result: Optional[str] = None
|
|
|
@@ -532,13 +529,12 @@ class KnowledgeProcessor:
|
|
|
final_decision = "rejected"
|
|
|
|
|
|
if final_decision == "rejected":
|
|
|
- # 记录 rejected 知识的关系(便于溯源为什么被拒绝)
|
|
|
- rejected_relationships = []
|
|
|
+ # 记录 rejected 知识的关系到 knowledge_relation 表
|
|
|
for rel in relations:
|
|
|
old_id = rel.get("old_id")
|
|
|
rel_type = rel.get("type", "none")
|
|
|
if old_id and rel_type != "none":
|
|
|
- rejected_relationships.append({"type": rel_type, "target": old_id})
|
|
|
+ pg_store.add_relation(kid, old_id, rel_type)
|
|
|
if rel_type in ("duplicate", "subset") and old_id:
|
|
|
try:
|
|
|
old = pg_store.get_by_id(old_id)
|
|
|
@@ -557,32 +553,26 @@ class KnowledgeProcessor:
|
|
|
pg_store.update(old_id, {"eval": eval_data, "updated_at": now})
|
|
|
except Exception as e:
|
|
|
print(f"[Apply Decision] 更新旧知识 {old_id} helpful 失败: {e}")
|
|
|
- pg_store.update(kid, {"status": "rejected", "relationships": json.dumps(rejected_relationships), "updated_at": now})
|
|
|
+ pg_store.update(kid, {"status": "rejected", "updated_at": now})
|
|
|
else:
|
|
|
- new_relationships = []
|
|
|
for rel in relations:
|
|
|
rel_type = rel.get("type", "none")
|
|
|
reverse_type = rel.get("reverse_type", "none")
|
|
|
old_id = rel.get("old_id")
|
|
|
if not old_id or rel_type == "none":
|
|
|
continue
|
|
|
- new_relationships.append({"type": rel_type, "target": old_id})
|
|
|
+ pg_store.add_relation(kid, old_id, rel_type)
|
|
|
self._relation_cache.add_relation(rel_type, kid)
|
|
|
self._relation_cache.add_relation(rel_type, old_id)
|
|
|
if reverse_type and reverse_type != "none":
|
|
|
try:
|
|
|
- old = pg_store.get_by_id(old_id)
|
|
|
- if old:
|
|
|
- old_rels = old.get("relationships") or []
|
|
|
- old_rels.append({"type": reverse_type, "target": kid})
|
|
|
- pg_store.update(old_id, {"relationships": json.dumps(old_rels), "updated_at": now})
|
|
|
- self._relation_cache.add_relation(reverse_type, old_id)
|
|
|
- self._relation_cache.add_relation(reverse_type, kid)
|
|
|
+ pg_store.add_relation(old_id, kid, reverse_type)
|
|
|
+ self._relation_cache.add_relation(reverse_type, old_id)
|
|
|
+ self._relation_cache.add_relation(reverse_type, kid)
|
|
|
except Exception as e:
|
|
|
- print(f"[Apply Decision] 更新旧知识关系 {old_id} 失败: {e}")
|
|
|
+ print(f"[Apply Decision] 写入反向关系 {old_id} 失败: {e}")
|
|
|
pg_store.update(kid, {
|
|
|
"status": "dedup_passed",
|
|
|
- "relationships": json.dumps(new_relationships),
|
|
|
"updated_at": now
|
|
|
})
|
|
|
|
|
|
@@ -615,7 +605,7 @@ class KnowledgeProcessor:
|
|
|
raise
|
|
|
|
|
|
async def _create_or_get_tool_resource(self, tool_info: dict) -> Optional[str]:
|
|
|
- """创建或获取工具资源(存入 PostgreSQL tool_table)"""
|
|
|
+ """创建或获取工具资源(存入 PostgreSQL tool 表)"""
|
|
|
category = tool_info.get("category", "other")
|
|
|
slug = tool_info.get("slug", "")
|
|
|
if not slug:
|
|
|
@@ -624,13 +614,13 @@ class KnowledgeProcessor:
|
|
|
now_ts = int(time.time())
|
|
|
cursor = pg_store._get_cursor()
|
|
|
try:
|
|
|
- cursor.execute("SELECT id FROM tool_table WHERE id = %s", (tool_id,))
|
|
|
+ cursor.execute("SELECT id FROM tool WHERE id = %s", (tool_id,))
|
|
|
if cursor.fetchone():
|
|
|
return tool_id
|
|
|
cursor.execute("""
|
|
|
- INSERT INTO tool_table (id, name, version, introduction, tutorial, input, output,
|
|
|
- updated_time, status, tool_knowledge, case_knowledge, process_knowledge)
|
|
|
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
|
+ INSERT INTO tool (id, name, version, introduction, tutorial, input, output,
|
|
|
+ updated_time, status)
|
|
|
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
|
""", (
|
|
|
tool_id,
|
|
|
tool_info.get("name", slug),
|
|
|
@@ -641,9 +631,6 @@ class KnowledgeProcessor:
|
|
|
json.dumps(tool_info.get("output", "")),
|
|
|
now_ts,
|
|
|
tool_info.get("status", "未接入"),
|
|
|
- json.dumps([]),
|
|
|
- json.dumps([]),
|
|
|
- json.dumps([]),
|
|
|
))
|
|
|
pg_store.conn.commit()
|
|
|
print(f"[Tool Resource] 创建新工具: {tool_id}")
|
|
|
@@ -651,33 +638,9 @@ class KnowledgeProcessor:
|
|
|
finally:
|
|
|
cursor.close()
|
|
|
|
|
|
- async def _update_tool_knowledge_index(self, tool_id: str, knowledge_id: str, knowledge_types: list = None):
|
|
|
- """根据知识类型更新工具的关联索引(tool_knowledge / case_knowledge / process_knowledge)"""
|
|
|
- # 确定目标字段
|
|
|
- if knowledge_types and 'plan' in knowledge_types:
|
|
|
- target_field = 'process_knowledge'
|
|
|
- elif knowledge_types and 'usecase' in knowledge_types:
|
|
|
- target_field = 'case_knowledge'
|
|
|
- else:
|
|
|
- target_field = 'tool_knowledge'
|
|
|
-
|
|
|
- now_ts = int(time.time())
|
|
|
- cursor = pg_store._get_cursor()
|
|
|
- try:
|
|
|
- cursor.execute(f"SELECT {target_field} FROM tool_table WHERE id = %s", (tool_id,))
|
|
|
- row = cursor.fetchone()
|
|
|
- if not row:
|
|
|
- return
|
|
|
- knowledge_ids = row[target_field] if isinstance(row[target_field], list) else json.loads(row[target_field] or "[]")
|
|
|
- if knowledge_id not in knowledge_ids:
|
|
|
- knowledge_ids.append(knowledge_id)
|
|
|
- cursor.execute(
|
|
|
- f"UPDATE tool_table SET {target_field} = %s, updated_time = %s WHERE id = %s",
|
|
|
- (json.dumps(knowledge_ids), now_ts, tool_id)
|
|
|
- )
|
|
|
- pg_store.conn.commit()
|
|
|
- finally:
|
|
|
- cursor.close()
|
|
|
+ async def _update_tool_knowledge_index(self, tool_id: str, knowledge_id: str):
|
|
|
+ """向工具添加知识关联(写入 tool_knowledge 关联表)"""
|
|
|
+ pg_tool_store.add_knowledge(tool_id, knowledge_id)
|
|
|
|
|
|
async def _analyze_tool_relation(self, knowledge: dict):
|
|
|
"""分析知识与工具的关联关系"""
|
|
|
@@ -712,19 +675,15 @@ class KnowledgeProcessor:
|
|
|
pg_store.update(kid, {"status": "approved", "updated_at": now})
|
|
|
return
|
|
|
|
|
|
- # 情况3/4:有工具 → 创建资源并关联
|
|
|
+ # 情况3/4:有工具 → 创建工具并关联
|
|
|
tool_ids = []
|
|
|
for tool_info in (tool_analysis.get("tools") or []):
|
|
|
tool_id = await self._create_or_get_tool_resource(tool_info)
|
|
|
if tool_id:
|
|
|
tool_ids.append(tool_id)
|
|
|
|
|
|
- existing_resource_ids = knowledge.get("resource_ids") or []
|
|
|
- updated_resource_ids = list(set(existing_resource_ids + tool_ids))
|
|
|
-
|
|
|
updates: dict = {
|
|
|
"status": "approved",
|
|
|
- "resource_ids": updated_resource_ids,
|
|
|
"updated_at": now
|
|
|
}
|
|
|
# 有工具但无 tool tag → 添加 tag
|
|
|
@@ -736,9 +695,9 @@ class KnowledgeProcessor:
|
|
|
|
|
|
pg_store.update(kid, updates)
|
|
|
|
|
|
- k_types = knowledge.get("types") or []
|
|
|
+ # 写入 tool_knowledge 关联
|
|
|
for tool_id in tool_ids:
|
|
|
- await self._update_tool_knowledge_index(tool_id, kid, k_types)
|
|
|
+ await self._update_tool_knowledge_index(tool_id, kid)
|
|
|
|
|
|
print(f"[Tool Analysis] {kid} 关联了 {len(tool_ids)} 个工具")
|
|
|
|
|
|
@@ -1007,6 +966,128 @@ async def _llm_rerank(query: str, candidates: list[dict], top_k: int) -> list[st
|
|
|
return []
|
|
|
|
|
|
|
|
|
+# --- Knowledge Ask / Upload API (Librarian Agent HTTP 接口) ---
|
|
|
+
|
|
|
+
|
|
|
+class KnowledgeAskRequest(BaseModel):
|
|
|
+ query: str
|
|
|
+ trace_id: str # 必填:调用方的 trace_id,用于 Librarian 续跑
|
|
|
+
|
|
|
+
|
|
|
+class KnowledgeAskResponse(BaseModel):
|
|
|
+ response: str # 整合后的回答
|
|
|
+ source_ids: list[str] = []
|
|
|
+ sources: list[dict] = [] # [{id, task, content}]
|
|
|
+
|
|
|
+
|
|
|
+class KnowledgeUploadRequest(BaseModel):
|
|
|
+ data: dict # {tools, resources, knowledge}
|
|
|
+ trace_id: str # 必填:调用方的 trace_id
|
|
|
+ finalize: bool = False
|
|
|
+
|
|
|
+
|
|
|
+@app.post("/api/knowledge/ask")
|
|
|
+async def ask_knowledge_api(req: KnowledgeAskRequest):
|
|
|
+ """
|
|
|
+ 智能知识查询。运行 Librarian Agent 检索 + LLM 整合,返回带引用的结构化结果。
|
|
|
+
|
|
|
+ 同步阻塞:Agent 运行完成后返回。
|
|
|
+ trace_id 用于续跑:同一 caller trace_id 复用同一个 Librarian trace,积累上下文。
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ from agents.librarian import ask
|
|
|
+ result = await ask(query=req.query, caller_trace_id=req.trace_id)
|
|
|
+ return KnowledgeAskResponse(**result)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print(f"[Knowledge Ask] 错误: {e}")
|
|
|
+ raise HTTPException(status_code=500, detail=str(e))
|
|
|
+
|
|
|
+
|
|
|
+@app.post("/api/knowledge/upload", status_code=202)
|
|
|
+async def upload_knowledge_api(req: KnowledgeUploadRequest, background_tasks: BackgroundTasks):
|
|
|
+ """
|
|
|
+ 异步知识上传。校验后立即返回 202,后台运行 Librarian Agent 处理。
|
|
|
+
|
|
|
+ Librarian Agent 负责图谱编排:去重、关联已有 capability/tool、构建关系。
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ data = req.data
|
|
|
+ knowledge_list = data.get("knowledge", [])
|
|
|
+ tools_list = data.get("tools", [])
|
|
|
+ resources_list = data.get("resources", [])
|
|
|
+ total_items = len(knowledge_list) + len(tools_list) + len(resources_list)
|
|
|
+
|
|
|
+ if total_items == 0:
|
|
|
+ raise HTTPException(status_code=400, detail="data 中无有效条目")
|
|
|
+
|
|
|
+ # 存 buffer(便于回溯)
|
|
|
+ from datetime import datetime as dt
|
|
|
+ buffer_dir = Path(".cache/.knowledge/buffer")
|
|
|
+ buffer_dir.mkdir(parents=True, exist_ok=True)
|
|
|
+ timestamp = dt.now().strftime("%Y%m%d_%H%M%S")
|
|
|
+ trace_suffix = f"_{req.trace_id[:8]}" if req.trace_id else ""
|
|
|
+ buffer_file = buffer_dir / f"upload_{timestamp}{trace_suffix}.json"
|
|
|
+ buffer_file.write_text(json.dumps({
|
|
|
+ "data": data, "trace_id": req.trace_id, "finalize": req.finalize,
|
|
|
+ "received_at": dt.now().isoformat(),
|
|
|
+ }, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
|
+
|
|
|
+ # 后台运行 Librarian Agent 处理
|
|
|
+ from agents.librarian import process_upload
|
|
|
+ background_tasks.add_task(
|
|
|
+ process_upload,
|
|
|
+ data=data,
|
|
|
+ caller_trace_id=req.trace_id,
|
|
|
+ buffer_file=str(buffer_file),
|
|
|
+ )
|
|
|
+
|
|
|
+ summary = []
|
|
|
+ if tools_list: summary.append(f"工具: {len(tools_list)}")
|
|
|
+ if resources_list: summary.append(f"资源: {len(resources_list)}")
|
|
|
+ if knowledge_list: summary.append(f"知识: {len(knowledge_list)}")
|
|
|
+
|
|
|
+ return {
|
|
|
+ "status": "accepted",
|
|
|
+ "message": f"已接收 {', '.join(summary)},Librarian Agent 后台处理中",
|
|
|
+ }
|
|
|
+
|
|
|
+ except HTTPException:
|
|
|
+ raise
|
|
|
+ except Exception as e:
|
|
|
+ print(f"[Knowledge Upload] 错误: {e}")
|
|
|
+ raise HTTPException(status_code=500, detail=str(e))
|
|
|
+
|
|
|
+
|
|
|
+@app.get("/api/knowledge/upload/pending")
|
|
|
+async def list_pending_uploads_api():
|
|
|
+ """列出所有未处理或失败的 upload 任务(用于排查和重跑)"""
|
|
|
+ from agents.librarian import list_pending_uploads
|
|
|
+ pending = list_pending_uploads()
|
|
|
+ return {"pending": pending, "count": len(pending)}
|
|
|
+
|
|
|
+
|
|
|
+@app.post("/api/knowledge/upload/retry")
|
|
|
+async def retry_pending_uploads_api(background_tasks: BackgroundTasks):
|
|
|
+ """重跑所有失败的 upload 任务"""
|
|
|
+ from agents.librarian import list_pending_uploads, process_upload
|
|
|
+
|
|
|
+ pending = list_pending_uploads()
|
|
|
+ failed = [p for p in pending if p["status"] == "failed"]
|
|
|
+
|
|
|
+ for item in failed:
|
|
|
+ buffer_file = item["file"]
|
|
|
+ data = json.loads(Path(buffer_file).read_text(encoding="utf-8"))
|
|
|
+ background_tasks.add_task(
|
|
|
+ process_upload,
|
|
|
+ data=data.get("data", {}),
|
|
|
+ caller_trace_id=data.get("trace_id", ""),
|
|
|
+ buffer_file=buffer_file,
|
|
|
+ )
|
|
|
+
|
|
|
+ return {"retried": len(failed), "message": f"已触发 {len(failed)} 个失败任务的重跑"}
|
|
|
+
|
|
|
+
|
|
|
@app.get("/api/knowledge/search")
|
|
|
async def search_knowledge_api(
|
|
|
q: str = Query(..., description="查询文本"),
|
|
|
@@ -1120,15 +1201,14 @@ async def save_knowledge(knowledge: KnowledgeIn, background_tasks: BackgroundTas
|
|
|
"tag_keys": tag_keys,
|
|
|
"scopes": knowledge.scopes,
|
|
|
"owner": owner,
|
|
|
- "resource_ids": knowledge.resource_ids,
|
|
|
"source": source,
|
|
|
"eval": eval_data,
|
|
|
"created_at": now,
|
|
|
"updated_at": now,
|
|
|
"status": "pending",
|
|
|
- "relationships": json.dumps([]),
|
|
|
- "support_capability": knowledge.support_capability,
|
|
|
- "tools": knowledge.tools,
|
|
|
+ "capability_ids": knowledge.capability_ids,
|
|
|
+ "tool_ids": knowledge.tool_ids,
|
|
|
+ "resource_ids": knowledge.resource_ids,
|
|
|
}
|
|
|
|
|
|
print(f"[Save Knowledge] 插入数据: {json.dumps({k: v for k, v in insert_data.items() if k != 'embedding'}, ensure_ascii=False)}")
|
|
|
@@ -1301,7 +1381,7 @@ def get_knowledge_status(knowledge_id: str):
|
|
|
return {
|
|
|
"id": knowledge_id,
|
|
|
"status": serialized.get("status", "approved"),
|
|
|
- "relationships": serialized.get("relationships", []),
|
|
|
+ "relations": serialized.get("relations", []),
|
|
|
"created_at": serialized.get("created_at"),
|
|
|
"updated_at": serialized.get("updated_at"),
|
|
|
}
|
|
|
@@ -1444,11 +1524,11 @@ async def patch_knowledge(knowledge_id: str, patch: KnowledgePatchIn):
|
|
|
if patch.owner is not None:
|
|
|
updates["owner"] = patch.owner
|
|
|
|
|
|
- if patch.support_capability is not None:
|
|
|
- updates["support_capability"] = patch.support_capability
|
|
|
+ if patch.capability_ids is not None:
|
|
|
+ updates["capability_ids"] = patch.capability_ids
|
|
|
|
|
|
- if patch.tools is not None:
|
|
|
- updates["tools"] = patch.tools
|
|
|
+ if patch.tool_ids is not None:
|
|
|
+ updates["tool_ids"] = patch.tool_ids
|
|
|
|
|
|
if not updates:
|
|
|
return {"status": "ok", "knowledge_id": knowledge_id}
|
|
|
@@ -1506,19 +1586,12 @@ def batch_delete_knowledge(knowledge_ids: List[str] = Body(...)):
|
|
|
if not knowledge_ids:
|
|
|
raise HTTPException(status_code=400, detail="knowledge_ids cannot be empty")
|
|
|
|
|
|
- # 批量删除
|
|
|
- cursor = pg_store._get_cursor()
|
|
|
- try:
|
|
|
- cursor.execute(
|
|
|
- "DELETE FROM knowledge WHERE id = ANY(%s)",
|
|
|
- (knowledge_ids,)
|
|
|
- )
|
|
|
- pg_store.conn.commit()
|
|
|
- deleted_count = cursor.rowcount
|
|
|
- print(f"[Batch Delete] 已删除 {deleted_count} 条知识")
|
|
|
- return {"status": "ok", "deleted_count": deleted_count}
|
|
|
- finally:
|
|
|
- cursor.close()
|
|
|
+ deleted_count = 0
|
|
|
+ for kid in knowledge_ids:
|
|
|
+ pg_store.delete(kid)
|
|
|
+ deleted_count += 1
|
|
|
+ print(f"[Batch Delete] 已删除 {deleted_count} 条知识")
|
|
|
+ return {"status": "ok", "deleted_count": deleted_count}
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
@@ -1771,6 +1844,10 @@ async def slim_knowledge(model: str = "google/gemini-2.5-flash-lite"):
|
|
|
# 清空并重建(PostgreSQL使用TRUNCATE)
|
|
|
cursor = pg_store._get_cursor()
|
|
|
try:
|
|
|
+ # 先清关联表再清主表
|
|
|
+ for jt in ('requirement_knowledge', 'capability_knowledge', 'tool_knowledge',
|
|
|
+ 'knowledge_resource', 'knowledge_relation'):
|
|
|
+ cursor.execute(f"TRUNCATE TABLE {jt}")
|
|
|
cursor.execute("TRUNCATE TABLE knowledge")
|
|
|
pg_store.conn.commit()
|
|
|
finally:
|
|
|
@@ -1805,13 +1882,11 @@ async def slim_knowledge(model: str = "google/gemini-2.5-flash-lite"):
|
|
|
"tag_keys": [],
|
|
|
"scopes": ["org:cybertogether"],
|
|
|
"owner": "agent:slim",
|
|
|
- "resource_ids": [],
|
|
|
"source": source,
|
|
|
"eval": eval_data,
|
|
|
"created_at": now,
|
|
|
"updated_at": now,
|
|
|
"status": "approved",
|
|
|
- "relationships": json.dumps([])
|
|
|
})
|
|
|
|
|
|
pg_store.insert_batch(knowledge_list)
|
|
|
@@ -1931,13 +2006,11 @@ async def extract_knowledge_from_messages(extract_req: MessageExtractIn, backgro
|
|
|
"tag_keys": [],
|
|
|
"scopes": ["org:cybertogether"],
|
|
|
"owner": extract_req.submitted_by,
|
|
|
- "resource_ids": [],
|
|
|
"source": source,
|
|
|
"eval": eval_data,
|
|
|
"created_at": now,
|
|
|
"updated_at": now,
|
|
|
"status": "pending",
|
|
|
- "relationships": json.dumps([]),
|
|
|
})
|
|
|
knowledge_ids.append(knowledge_id)
|
|
|
|
|
|
@@ -1981,10 +2054,9 @@ async def create_tool(tool: ToolIn):
|
|
|
'output': tool.output,
|
|
|
'updated_time': now,
|
|
|
'status': tool.status,
|
|
|
- 'capabilities': tool.capabilities,
|
|
|
- 'tool_knowledge': tool.tool_knowledge,
|
|
|
- 'case_knowledge': tool.case_knowledge,
|
|
|
- 'process_knowledge': tool.process_knowledge,
|
|
|
+ 'capability_ids': tool.capability_ids,
|
|
|
+ 'knowledge_ids': tool.knowledge_ids,
|
|
|
+ 'provider_ids': tool.provider_ids,
|
|
|
'embedding': embedding,
|
|
|
})
|
|
|
return {"status": "ok", "id": tool.id}
|
|
|
@@ -2047,7 +2119,7 @@ async def patch_tool(tool_id: str, patch: ToolPatchIn):
|
|
|
need_reembed = False
|
|
|
|
|
|
for field in ('name', 'version', 'introduction', 'tutorial', 'input', 'output',
|
|
|
- 'status', 'capabilities', 'tool_knowledge', 'case_knowledge', 'process_knowledge'):
|
|
|
+ 'status', 'capability_ids', 'knowledge_ids', 'provider_ids'):
|
|
|
value = getattr(patch, field)
|
|
|
if value is not None:
|
|
|
updates[field] = value
|
|
|
@@ -2099,10 +2171,10 @@ async def create_capability(cap: CapabilityIn):
|
|
|
'name': cap.name,
|
|
|
'criterion': cap.criterion,
|
|
|
'description': cap.description,
|
|
|
- 'requirements': cap.requirements,
|
|
|
+ 'requirement_ids': cap.requirement_ids,
|
|
|
'implements': cap.implements,
|
|
|
- 'tools': cap.tools,
|
|
|
- 'source_knowledge': cap.source_knowledge,
|
|
|
+ 'tool_ids': cap.tool_ids,
|
|
|
+ 'knowledge_ids': cap.knowledge_ids,
|
|
|
'embedding': embedding,
|
|
|
})
|
|
|
return {"status": "ok", "id": cap.id}
|
|
|
@@ -2163,8 +2235,8 @@ async def patch_capability(cap_id: str, patch: CapabilityPatchIn):
|
|
|
updates = {}
|
|
|
need_reembed = False
|
|
|
|
|
|
- for field in ('name', 'criterion', 'description', 'requirements',
|
|
|
- 'implements', 'tools', 'source_knowledge'):
|
|
|
+ for field in ('name', 'criterion', 'description', 'requirement_ids',
|
|
|
+ 'implements', 'tool_ids', 'knowledge_ids'):
|
|
|
value = getattr(patch, field)
|
|
|
if value is not None:
|
|
|
updates[field] = value
|
|
|
@@ -2211,7 +2283,8 @@ async def create_requirement(req: RequirementIn):
|
|
|
pg_requirement_store.insert_or_update({
|
|
|
'id': req.id,
|
|
|
'description': req.description,
|
|
|
- 'atomics': req.atomics,
|
|
|
+ 'capability_ids': req.capability_ids,
|
|
|
+ 'knowledge_ids': req.knowledge_ids,
|
|
|
'source_nodes': req.source_nodes,
|
|
|
'status': req.status,
|
|
|
'match_result': req.match_result,
|
|
|
@@ -2276,7 +2349,7 @@ async def patch_requirement(req_id: str, patch: RequirementPatchIn):
|
|
|
updates = {}
|
|
|
need_reembed = False
|
|
|
|
|
|
- for field in ('description', 'atomics', 'source_nodes', 'status', 'match_result'):
|
|
|
+ for field in ('description', 'capability_ids', 'knowledge_ids', 'source_nodes', 'status', 'match_result'):
|
|
|
value = getattr(patch, field)
|
|
|
if value is not None:
|
|
|
updates[field] = value
|