""" Agent 独立聊天 API — 不依赖工作流 DAG,直接与 Agent Runtime 对话。 POST /api/v1/agent-chat/bare {"message": "你好,帮我..."} → {"content": "...", "iterations": 3, "tool_calls": 5} """ from __future__ import annotations import logging from typing import Any, Dict, Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from app.core.database import get_db from sqlalchemy.orm import Session from app.api.auth import get_current_user from app.models.user import User from app.models.agent import Agent from app.agent_runtime import ( AgentRuntime, AgentConfig, AgentLLMConfig, AgentToolConfig, ) from app.core.config import settings logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1/agent-chat", tags=["agent-chat"]) class ChatRequest(BaseModel): message: str session_id: Optional[str] = None model: Optional[str] = None temperature: Optional[float] = None max_iterations: Optional[int] = None class ChatResponse(BaseModel): content: str iterations_used: int tool_calls_made: int truncated: bool session_id: str agent_id: Optional[str] = None @router.post("/bare", response_model=ChatResponse) async def chat_bare( req: ChatRequest, current_user: User = Depends(get_current_user), ): """无需 Agent 配置,使用默认设置直接对话。""" config = AgentConfig( name="bare_agent", system_prompt="你是一个有用的AI助手。请使用可用工具来帮助用户完成任务。", llm=AgentLLMConfig( model=req.model or ( "gpt-4o-mini" if settings.OPENAI_API_KEY and settings.OPENAI_API_KEY != "your-openai-api-key" else "deepseek-v4-flash" ), temperature=req.temperature or 0.7, max_iterations=req.max_iterations or 10, ), user_id=current_user.id, ) runtime = AgentRuntime(config=config) result = await runtime.run(req.message) return ChatResponse( content=result.content, iterations_used=result.iterations_used, tool_calls_made=result.tool_calls_made, truncated=result.truncated, session_id=runtime.context.session_id, ) @router.post("/{agent_id}", response_model=ChatResponse) async def chat_with_agent( agent_id: str, req: ChatRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ): """与指定的 Agent 对话。Agent 的工作流配置会用于构建 Runtime。""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise HTTPException(status_code=404, detail="Agent 不存在") if agent.user_id and agent.user_id != current_user.id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权访问该 Agent") # 从 Agent 配置构建 Runtime wc = agent.workflow_config or {} nodes = wc.get("nodes", []) # 查找 agent 节点的配置(或第一个 llm 节点的配置) agent_node_cfg = _find_agent_node_config(nodes) config = AgentConfig( name=agent.name, system_prompt=agent_node_cfg.get("system_prompt") or agent.description or "你是一个有用的AI助手。", llm=AgentLLMConfig( provider=agent_node_cfg.get("provider", "openai"), model=req.model or agent_node_cfg.get("model", "gpt-4o-mini"), temperature=req.temperature or float(agent_node_cfg.get("temperature", 0.7)), max_iterations=req.max_iterations or int(agent_node_cfg.get("max_iterations", 10)), ), tools=AgentToolConfig( include_tools=agent_node_cfg.get("tools", []), exclude_tools=agent_node_cfg.get("exclude_tools", []), ), user_id=current_user.id, ) runtime = AgentRuntime(config=config) result = await runtime.run(req.message) return ChatResponse( content=result.content, iterations_used=result.iterations_used, tool_calls_made=result.tool_calls_made, truncated=result.truncated, session_id=runtime.context.session_id, agent_id=agent_id, ) def _find_agent_node_config(nodes: list) -> Dict[str, Any]: """从工作流节点列表中查找第一个 agent 类型或 llm 类型的节点配置。""" if not nodes: return {} for node in nodes: typ = node.get("type", "") if typ in ("agent", "llm", "template"): return node.get("data") or {} return {}