- agent/tools.py:Tool 元数据(JSON Schema)+ 白名单调用(异常转可读反馈,不中断对话) - agent/tools_impl.py:6 个受控工具 search_stocks / get_market_data / test_factor / run_backtest / get_experiment / compare_experiments —— 全部只读经 Job/Experiment 链路,研究自动归档;无 shell/任意执行/写删数据能力 - agent/llm.py:LLMClient 抽象 + OpenAI 兼容客户端(LLM_API_KEY/LLM_BASE_URL/LLM_MODEL 走 .env,未配置给出引导提示)+ 研究纪律 system prompt(反过拟合/样本外/成本) - agent/service.py:编排循环(tool/final JSON 决策 → 执行 → 回喂 → 结论),轮次上限兜底,未知工具拒绝 - /api/agent/chat;httpx 移至主依赖;Job 默认工厂抽取(api/agent/executor 复用) - 测试 6 项(白名单无 shell、完整研究循环产出、未知工具拒绝、轮次兜底),全量 79 passed / ruff clean
41 lines
1.1 KiB
Python
41 lines
1.1 KiB
Python
"""AI Research Agent API(Phase 5)。
|
||
|
||
POST /api/agent/chat {message} → {reply, actions:[{tool,args,output}]}
|
||
未配置 LLM Key 时返回 400 引导(不会崩溃)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Annotated
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException
|
||
from pydantic import BaseModel, Field
|
||
|
||
from app.agent.llm import LLMClient, build_llm_from_settings
|
||
from app.agent.service import AgentService
|
||
|
||
router = APIRouter(prefix="/agent", tags=["agent"])
|
||
|
||
|
||
class AgentChatRequest(BaseModel):
|
||
message: str = Field(min_length=1, max_length=2000)
|
||
|
||
|
||
def _llm_or_raise() -> LLMClient:
|
||
llm = build_llm_from_settings()
|
||
if llm is None:
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="未配置 LLM:请在根目录 .env 中设置 LLM_API_KEY(可选 LLM_BASE_URL / LLM_MODEL),"
|
||
"参考 .env.example 与 AGENT.md §33",
|
||
)
|
||
return llm
|
||
|
||
|
||
@router.post("/chat", summary="与 AI 研究助手对话(受控工具)")
|
||
def agent_chat(
|
||
body: AgentChatRequest,
|
||
llm: Annotated[LLMClient, Depends(_llm_or_raise)],
|
||
) -> dict:
|
||
return AgentService(llm).chat(body.message)
|