""" 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()