Add FastAPI backend with agent system

This commit is contained in:
2026-03-21 10:13:29 +08:00
parent ed6bab59fe
commit 6ffa07adde
82 changed files with 11138 additions and 0 deletions

View File

@@ -0,0 +1,240 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.database import get_db
from app.models.agent import Agent
from app.models.user import User
from app.routers.auth import get_current_user
from app.schemas.agent import AgentCreate, AgentOut, AgentStats, AgentConfigUpdate, AgentConfigOut
router = APIRouter(prefix="/api/agents", tags=["Agent"])
# 运行时调用统计(内存中,非持久化)
_agent_call_counts: dict[str, int] = {}
_agent_current_tasks: dict[str, str | None] = {}
_agent_statuses: dict[str, str] = {}
# 默认 Agent 角色列表
DEFAULT_AGENT_ROLES = ["master", "planner", "executor", "librarian", "analyst"]
def record_agent_call(agent_id: str):
_agent_call_counts[agent_id] = _agent_call_counts.get(agent_id, 0) + 1
def set_agent_task(agent_id: str, task: str | None):
_agent_current_tasks[agent_id] = task
_agent_statuses[agent_id] = "active" if task else "idle"
def set_agent_status(agent_id: str, status: str):
_agent_statuses[agent_id] = status
@router.get("", response_model=list[AgentOut])
async def list_agents(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(Agent).where(Agent.is_active == True).order_by(Agent.role)
)
return result.scalars().all()
# ———— 运行时统计(必须在 /{agent_id} 之前)————
@router.get("/stats", response_model=list[AgentStats])
async def get_agent_stats(
current_user: User = Depends(get_current_user),
):
"""
获取各 Agent 的运行时统计(调用次数、当前任务、状态)
"""
stats = []
for role in DEFAULT_AGENT_ROLES:
stats.append(AgentStats(
agent_id=role,
call_count=_agent_call_counts.get(role, 0),
current_task=_agent_current_tasks.get(role),
status=_agent_statuses.get(role, "idle"),
))
return stats
# ———— 配置管理(必须在 /{agent_id} 之前)————
@router.get("/config/{agent_id}", response_model=AgentConfigOut)
async def get_agent_config(
agent_id: str,
db: AsyncSession = Depends(get_db),
):
"""获取单个 Agent 完整配置"""
result = await db.execute(select(Agent).where(Agent.role == agent_id))
agent = result.scalar_one_or_none()
if not agent:
from app.agents.prompts import MASTER_SYSTEM_PROMPT, PLANNER_SYSTEM_PROMPT, EXECUTOR_SYSTEM_PROMPT, LIBRARIAN_SYSTEM_PROMPT, ANALYST_SYSTEM_PROMPT
defaults = {
"master": ("JARVIS", "主控制核心", MASTER_SYSTEM_PROMPT),
"planner": ("PLANNER", "规划专家", PLANNER_SYSTEM_PROMPT),
"executor": ("EXECUTOR", "执行专家", EXECUTOR_SYSTEM_PROMPT),
"librarian": ("LIBRARIAN", "知识管理员", LIBRARIAN_SYSTEM_PROMPT),
"analyst": ("ANALYST", "数据分析师", ANALYST_SYSTEM_PROMPT),
}
if agent_id not in defaults:
raise HTTPException(status_code=404, detail="Agent 不存在")
name, desc, prompt = defaults[agent_id]
return AgentConfigOut(
id=agent_id, name=name, role=agent_id,
description=desc, system_prompt=prompt, enabled=True, is_active=True,
)
return AgentConfigOut(
id=agent.role,
name=agent.name,
role=agent.role,
description=agent.description,
system_prompt=agent.system_prompt,
enabled=agent.is_active,
is_active=agent.is_active,
)
@router.put("/config/{agent_id}", response_model=AgentConfigOut)
async def update_agent_config(
agent_id: str,
data: AgentConfigUpdate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""更新 Agent 配置(名称、描述、提示词、启用状态)"""
result = await db.execute(select(Agent).where(Agent.role == agent_id))
agent = result.scalar_one_or_none()
if not agent:
raise HTTPException(status_code=404, detail="Agent 不存在")
if data.name is not None:
agent.name = data.name
if data.description is not None:
agent.description = data.description
if data.system_prompt is not None:
agent.system_prompt = data.system_prompt
if data.enabled is not None:
agent.is_active = data.enabled
_agent_statuses[agent_id] = "disabled" if not data.enabled else "idle"
await db.commit()
await db.refresh(agent)
return AgentConfigOut(
id=agent.role,
name=agent.name,
role=agent.role,
description=agent.description,
system_prompt=agent.system_prompt,
enabled=agent.is_active,
is_active=agent.is_active,
)
@router.post("", response_model=AgentOut, status_code=201)
async def create_agent(
data: AgentCreate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
agent = Agent(
name=data.name,
role=data.role,
description=data.description,
system_prompt=data.system_prompt,
)
db.add(agent)
await db.commit()
await db.refresh(agent)
return agent
@router.get("/{agent_id}", response_model=AgentOut)
async def get_agent(
agent_id: str,
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Agent).where(Agent.id == agent_id))
agent = result.scalar_one_or_none()
if not agent:
raise HTTPException(status_code=404, detail="Agent 不存在")
return agent
# ———— 配置管理 ————
@router.get("/config/{agent_id}", response_model=AgentConfigOut)
async def get_agent_config(
agent_id: str,
db: AsyncSession = Depends(get_db),
):
"""获取单个 Agent 完整配置"""
result = await db.execute(select(Agent).where(Agent.role == agent_id))
agent = result.scalar_one_or_none()
if not agent:
# 如果数据库中没有,返回默认配置
from app.agents.prompts import MASTER_SYSTEM_PROMPT, PLANNER_SYSTEM_PROMPT, EXECUTOR_SYSTEM_PROMPT, LIBRARIAN_SYSTEM_PROMPT, ANALYST_SYSTEM_PROMPT
defaults = {
"master": ("JARVIS", "主控制核心", MASTER_SYSTEM_PROMPT),
"planner": ("PLANNER", "规划专家", PLANNER_SYSTEM_PROMPT),
"executor": ("EXECUTOR", "执行专家", EXECUTOR_SYSTEM_PROMPT),
"librarian": ("LIBRARIAN", "知识管理员", LIBRARIAN_SYSTEM_PROMPT),
"analyst": ("ANALYST", "数据分析师", ANALYST_SYSTEM_PROMPT),
}
if agent_id not in defaults:
raise HTTPException(status_code=404, detail="Agent 不存在")
name, desc, prompt = defaults[agent_id]
return AgentConfigOut(
id=agent_id, name=name, role=agent_id,
description=desc, system_prompt=prompt, enabled=True, is_active=True,
)
return AgentConfigOut(
id=agent.role,
name=agent.name,
role=agent.role,
description=agent.description,
system_prompt=agent.system_prompt,
enabled=agent.is_active,
is_active=agent.is_active,
)
@router.put("/config/{agent_id}", response_model=AgentConfigOut)
async def update_agent_config(
agent_id: str,
data: AgentConfigUpdate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""更新 Agent 配置(名称、描述、提示词、启用状态)"""
result = await db.execute(select(Agent).where(Agent.role == agent_id))
agent = result.scalar_one_or_none()
if not agent:
raise HTTPException(status_code=404, detail="Agent 不存在")
if data.name is not None:
agent.name = data.name
if data.description is not None:
agent.description = data.description
if data.system_prompt is not None:
agent.system_prompt = data.system_prompt
if data.enabled is not None:
agent.is_active = data.enabled
_agent_statuses[agent_id] = "disabled" if not data.enabled else "idle"
await db.commit()
await db.refresh(agent)
return AgentConfigOut(
id=agent.role,
name=agent.name,
role=agent.role,
description=agent.description,
system_prompt=agent.system_prompt,
enabled=agent.is_active,
is_active=agent.is_active,
)