Files
aiagent/backend/app/services/knowledge_sharing.py
renjianbo beff3fac8d 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>
2026-06-29 01:17:21 +08:00

238 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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 的全局知识也关联到子 Agentscope
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()