fix: delete agent 500 error + dynamic personality + deployment guide
- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions, schedules, executions, team_members) and unbind goals/tasks before delete - Remove hardcoded personality templates in Android, replace with dynamic system prompt generation from name + description - Set promptSectionsEnabled=false to bypass PromptComposer for personality - Add Tencent Cloud Linux deployment guide (Docker Compose) - Accumulated backend service updates, frontend UI fixes, Android app changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
237
backend/app/services/knowledge_sharing.py
Normal file
237
backend/app/services/knowledge_sharing.py
Normal file
@@ -0,0 +1,237 @@
|
||||
"""
|
||||
Agent 间知识共享服务 — 父 Agent 经验继承、跨 Agent 知识流通
|
||||
|
||||
子 Agent 创建时可继承父 Agent 的知识条目、学习模式和全局知识,
|
||||
避免自主学习创建的 Agent 从零开始。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import SessionLocal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def inherit_knowledge_from_parent(
|
||||
child_agent_id: str,
|
||||
parent_agent_id: str,
|
||||
db: Optional[Session] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
从父 Agent 继承知识到子 Agent。
|
||||
|
||||
拷贝内容:
|
||||
1. KnowledgeEntry 条目(知识沉淀)
|
||||
2. GlobalKnowledge 条目(全局知识,标记为子 Agent 可见)
|
||||
3. AgentLearningPattern 模式(工具使用经验)
|
||||
"""
|
||||
should_close = False
|
||||
if db is None:
|
||||
db = SessionLocal()
|
||||
should_close = True
|
||||
|
||||
result = {
|
||||
"child_agent_id": child_agent_id,
|
||||
"parent_agent_id": parent_agent_id,
|
||||
"knowledge_entries_copied": 0,
|
||||
"global_knowledge_shared": 0,
|
||||
"learning_patterns_copied": 0,
|
||||
}
|
||||
|
||||
try:
|
||||
# 1. 拷贝 KnowledgeEntry
|
||||
from app.models.knowledge_entry import KnowledgeEntry
|
||||
|
||||
parent_entries = (
|
||||
db.query(KnowledgeEntry)
|
||||
.filter(
|
||||
KnowledgeEntry.is_active == True,
|
||||
)
|
||||
.filter(
|
||||
(KnowledgeEntry.agent_id == parent_agent_id)
|
||||
| (KnowledgeEntry.source_agent_name == parent_agent_id)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
|
||||
import uuid
|
||||
for entry in parent_entries:
|
||||
new_entry = KnowledgeEntry(
|
||||
id=str(uuid.uuid4()),
|
||||
agent_id=child_agent_id,
|
||||
title=f"[继承] {entry.title}",
|
||||
category=entry.category,
|
||||
tags=(entry.tags or []) + ["inherited"],
|
||||
situation=entry.situation,
|
||||
solution=entry.solution,
|
||||
caveats=entry.caveats,
|
||||
source_execution_ids=entry.source_execution_ids,
|
||||
source_agent_name=entry.source_agent_name,
|
||||
source_model=entry.source_model,
|
||||
retrieval_count=0,
|
||||
success_rate=entry.success_rate,
|
||||
confidence=(entry.confidence or 0.5) * 0.8, # 继承降低置信度
|
||||
is_active=True,
|
||||
)
|
||||
db.add(new_entry)
|
||||
result["knowledge_entries_copied"] += 1
|
||||
|
||||
# 2. 拷贝 GlobalKnowledge 标记(来源标记关联,不复制内容)
|
||||
from app.models.agent import GlobalKnowledge
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# 查找父 Agent 贡献的全局知识
|
||||
parent_knowledge = (
|
||||
db.query(GlobalKnowledge)
|
||||
.filter(GlobalKnowledge.source_agent_id == parent_agent_id)
|
||||
.all()
|
||||
)
|
||||
# 将父 Agent 的全局知识也关联到子 Agent(scope)
|
||||
for gk in parent_knowledge:
|
||||
# 不直接修改,而是创建新的 scope 关联(scope_kind=agent, scope_id=child)
|
||||
# 如果已有相同内容的记录则跳过
|
||||
existing = (
|
||||
db.query(GlobalKnowledge)
|
||||
.filter(
|
||||
GlobalKnowledge.content == gk.content,
|
||||
GlobalKnowledge.scope_kind == "agent",
|
||||
GlobalKnowledge.scope_id == child_agent_id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not existing:
|
||||
shared = GlobalKnowledge(
|
||||
id=str(uuid.uuid4()),
|
||||
content=gk.content,
|
||||
embedding=gk.embedding,
|
||||
source_agent_id=parent_agent_id,
|
||||
source_user_id=gk.source_user_id,
|
||||
tags=(gk.tags or []) + ["inherited_from_parent"],
|
||||
confidence=gk.confidence or "medium",
|
||||
scope_kind="agent",
|
||||
scope_id=child_agent_id,
|
||||
created_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(shared)
|
||||
result["global_knowledge_shared"] += 1
|
||||
|
||||
# 3. 拷贝 AgentLearningPattern
|
||||
from app.models.agent_learning_pattern import AgentLearningPattern
|
||||
|
||||
parent_patterns = (
|
||||
db.query(AgentLearningPattern)
|
||||
.filter(
|
||||
AgentLearningPattern.scope_kind == "agent",
|
||||
AgentLearningPattern.scope_id == parent_agent_id,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
|
||||
for pattern in parent_patterns:
|
||||
new_pattern = AgentLearningPattern(
|
||||
id=str(uuid.uuid4()),
|
||||
scope_kind="agent",
|
||||
scope_id=child_agent_id,
|
||||
task_category=pattern.task_category,
|
||||
task_keywords=pattern.task_keywords,
|
||||
suggested_tools=pattern.suggested_tools,
|
||||
effectiveness_score=pattern.effectiveness_score,
|
||||
total_runs=1, # 重置计数
|
||||
successful_runs=0,
|
||||
avg_iterations=pattern.avg_iterations,
|
||||
avg_tool_calls=pattern.avg_tool_calls,
|
||||
created_at=datetime.utcnow(),
|
||||
updated_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(new_pattern)
|
||||
result["learning_patterns_copied"] += 1
|
||||
|
||||
db.commit()
|
||||
logger.info(
|
||||
"Agent 知识继承完成: parent=%s → child=%s, entries=%d, global=%d, patterns=%d",
|
||||
parent_agent_id,
|
||||
child_agent_id,
|
||||
result["knowledge_entries_copied"],
|
||||
result["global_knowledge_shared"],
|
||||
result["learning_patterns_copied"],
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error("Agent 知识继承失败: %s", e)
|
||||
raise
|
||||
finally:
|
||||
if should_close and db:
|
||||
db.close()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_parent_knowledge_context(
|
||||
agent_id: str,
|
||||
max_entries: int = 5,
|
||||
db: Optional[Session] = None,
|
||||
) -> str:
|
||||
"""
|
||||
获取 Agent 的父级知识上下文(用于注入 system prompt)。
|
||||
先查直接父 Agent,再查祖父 Agent(最多上溯 3 层)。
|
||||
"""
|
||||
should_close = False
|
||||
if db is None:
|
||||
db = SessionLocal()
|
||||
should_close = True
|
||||
|
||||
try:
|
||||
from app.models.agent import Agent
|
||||
from app.models.knowledge_entry import KnowledgeEntry
|
||||
|
||||
# 收集祖先 Agent ID 列表(向上追溯最多 3 层)
|
||||
ancestor_ids: List[str] = []
|
||||
current_id = agent_id
|
||||
for _ in range(3):
|
||||
agent = db.query(Agent).filter(Agent.id == current_id).first()
|
||||
if not agent or not getattr(agent, "parent_agent_id", None):
|
||||
break
|
||||
parent_id = agent.parent_agent_id
|
||||
if parent_id in ancestor_ids: # 防止循环引用
|
||||
break
|
||||
ancestor_ids.append(parent_id)
|
||||
current_id = parent_id
|
||||
|
||||
if not ancestor_ids:
|
||||
return ""
|
||||
|
||||
# 查询祖先 Agent 的高质量知识
|
||||
entries = (
|
||||
db.query(KnowledgeEntry)
|
||||
.filter(
|
||||
KnowledgeEntry.agent_id.in_(ancestor_ids),
|
||||
KnowledgeEntry.is_active == True,
|
||||
KnowledgeEntry.confidence >= 0.6,
|
||||
)
|
||||
.order_by(KnowledgeEntry.retrieval_count.desc())
|
||||
.limit(max_entries)
|
||||
.all()
|
||||
)
|
||||
|
||||
if not entries:
|
||||
return ""
|
||||
|
||||
lines = [f"## 父级 Agent 经验 ({len(entries)} 条)"]
|
||||
for i, entry in enumerate(entries, 1):
|
||||
tag_str = f"[{entry.category}]" if entry.category else ""
|
||||
sol = (entry.solution or "")[:300]
|
||||
lines.append(f"{i}. {tag_str} {entry.title}: {sol}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning("获取父级知识上下文失败: %s", e)
|
||||
return ""
|
||||
finally:
|
||||
if should_close and db:
|
||||
db.close()
|
||||
Reference in New Issue
Block a user