recommend.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. """智能推荐 API:多智能体流水线(画像 → 检索 → 推荐)。
  2. 同步 Agent/LLM 通过 asyncio.to_thread 执行,避免阻塞事件循环。
  3. """
  4. from __future__ import annotations
  5. import asyncio
  6. from fastapi import APIRouter
  7. from ...agents.movie_recommender_agent import get_movie_recommender
  8. from ...exceptions import AppError
  9. from ...models.schemas import RecommendRequest, RecommendResponse
  10. from ...utils.logger import get_logger
  11. router = APIRouter(prefix="/recommend", tags=["Recommend"])
  12. logger = get_logger("app.recommend")
  13. @router.post(
  14. "",
  15. response_model=RecommendResponse,
  16. summary="智能电影推荐",
  17. description=(
  18. "串行多智能体:画像(无工具)→ 检索(TMDB Tool)→ 推荐(候选内决策)。"
  19. "耗时可能较长(视 LLM),建议客户端超时 ≥ 120s。"
  20. ),
  21. )
  22. async def recommend_movies(request: RecommendRequest) -> RecommendResponse:
  23. logger.info(
  24. "recommend 请求 mood=%s party=%s genres=%s",
  25. request.mood,
  26. request.party_type,
  27. request.genres,
  28. )
  29. agent = get_movie_recommender()
  30. # recommend() 内含多次同步 LLM/HTTP,放到线程池
  31. result, message = await asyncio.to_thread(agent.recommend, request)
  32. return RecommendResponse(success=True, message=message, data=result)
  33. @router.get(
  34. "/health",
  35. summary="推荐服务健康检查",
  36. description="返回各 Agent 名称与工具数量;初始化失败时 503。",
  37. )
  38. async def recommend_health():
  39. try:
  40. agent = get_movie_recommender()
  41. snap = agent.health_snapshot()
  42. return {
  43. "status": "healthy",
  44. "service": "recommend",
  45. **snap,
  46. }
  47. except Exception as e:
  48. logger.exception("recommend health 失败")
  49. raise AppError(
  50. f"推荐服务不可用: {e}",
  51. code="RECOMMEND_UNAVAILABLE",
  52. status_code=503,
  53. ) from e