recommend_graph.py 2.7 KB

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