238 lines
7.9 KiB
Python
238 lines
7.9 KiB
Python
|
|
"""
|
|||
|
|
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()
|