feat: Agent 运行时、对话 API、作业助手与引擎修复及前端执行超时
- agent_runtime 模块与 agent_chat API,前端 AgentChat 视图与路由对接 - workflow_engine: code 节点命名空间与 json 引用修复 - llm_service: 工具调用 extra_body(如 DeepSeek) - create_homework_manager_agent / _3 脚本与测试脚本扩展 - frontend: WORKFLOW_EXECUTION_HTTP_TIMEOUT_MS、AgentChatPreview/MainLayout 等 - 文档:架构说明与自主 Agent 改造完成情况 Made-with: Cursor
This commit is contained in:
137
backend/app/api/agent_chat.py
Normal file
137
backend/app/api/agent_chat.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
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 {}
|
||||
Reference in New Issue
Block a user