recommend_graph.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. from langchain_openai import ChatOpenAI
  2. from langchain_core.messages import SystemMessage, HumanMessage
  3. from app.tools.product_tools import search_product_by_keyword, search_activity_by_keyword, search_article_by_keyword
  4. from app.config import settings
  5. from app.monitoring import monitor_agent
  6. import json
  7. import logging
  8. logger = logging.getLogger(__name__)
  9. SYSTEM_PROMPT = """你是一个儿童成长营养推荐助手。根据用户的需求和营养标签, 推荐合适的商品、活动或文章。
  10. 推荐原则:
  11. 1. 首先尝试使用搜索工具查找匹配的内容
  12. 2. 如果搜索结果为空, 基于你的知识给出建议
  13. 3. 每项推荐必须附带推荐理由
  14. 4. 以 JSON 格式输出推荐结果
  15. 输出格式:
  16. {
  17. "items": [
  18. {
  19. "source": "tool" 或 "knowledge",
  20. "type": "product" / "activity" / "article",
  21. "id": 数字,
  22. "name": "名称",
  23. "description": "描述",
  24. "reason": "为什么推荐这个"
  25. }
  26. ]
  27. }
  28. """
  29. class RecommendAgent:
  30. def __init__(self):
  31. self.llm = ChatOpenAI(
  32. model=settings.llm_model,
  33. api_key=settings.llm_api_key,
  34. base_url=settings.llm_base_url,
  35. temperature=settings.llm_temperature,
  36. )
  37. self.tools = [
  38. search_product_by_keyword,
  39. search_activity_by_keyword,
  40. search_article_by_keyword,
  41. ]
  42. self.llm_with_tools = self.llm.bind_tools(self.tools)
  43. @monitor_agent("recommend")
  44. async def run(self, query: str, tags: list[str], limit: int = 5) -> dict:
  45. """执行推荐 Agent, 返回推荐结果"""
  46. # 如果传入了 tags, 构造搜索关键词
  47. search_query = query or " ".join(tags)
  48. messages = [
  49. SystemMessage(content=SYSTEM_PROMPT),
  50. HumanMessage(content=f"用户需求: {search_query}\n最大返回数量: {limit}\n请搜索并推荐合适的内容。"),
  51. ]
  52. # LangChain Tool calling 自动完成: LLM 决定调哪个 Tool → 工具返回结果 → LLM 组织回答
  53. response = await self.llm_with_tools.ainvoke(messages)
  54. # 尝试解析 JSON 输出
  55. content = response.content
  56. try:
  57. # 提取 JSON 块
  58. if "```json" in content:
  59. json_str = content.split("```json")[1].split("```")[0].strip()
  60. elif "```" in content:
  61. json_str = content.split("```")[1].split("```")[0].strip()
  62. else:
  63. json_str = content.strip()
  64. result = json.loads(json_str)
  65. return result
  66. except (json.JSONDecodeError, IndexError):
  67. # 非 JSON 输出, 包装为文本回答
  68. logger.warning("Agent 输出非 JSON, raw: %s", content[:200])
  69. return {"items": [], "text": content}