innate_portrait_graph.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. from langchain_openai import ChatOpenAI
  2. from langchain_core.messages import SystemMessage, HumanMessage
  3. from app.config import settings
  4. from app.monitoring import monitor_agent
  5. from app.prompt_service import get_prompt
  6. import logging
  7. logger = logging.getLogger(__name__)
  8. SYSTEM_PROMPT = """你是一位融合中国传统文化与现代心理学的家庭成长解读师。
  9. 请根据用户的先天画像数据,生成一段温情、专业、可执行的成长解读文案。
  10. 要求:
  11. 1. 用第二人称"你"称呼
  12. 2. 先认可先天特质优势,再给出1-2条心维度成长建议
  13. 3. 语气温暖克制,避免玄学恐吓或过度承诺
  14. 4. 800字以内,用中文,分2-3段
  15. 5. 以"心能量小贴士"收尾,给一个当天就能做的行动
  16. """
  17. class InnatePortraitAgent:
  18. def __init__(self):
  19. self.llm = ChatOpenAI(
  20. model=settings.llm_model,
  21. api_key=settings.llm_api_key,
  22. base_url=settings.llm_base_url,
  23. temperature=settings.llm_temperature,
  24. )
  25. @monitor_agent("innate_portrait")
  26. async def run(self, portrait: dict) -> dict:
  27. """生成先天画像解读,失败返回空 reading(调用方降级模板)"""
  28. try:
  29. source_summary = self._summarize(portrait)
  30. messages = [
  31. SystemMessage(content=await get_prompt("innate_portrait") or SYSTEM_PROMPT),
  32. HumanMessage(content=f"先天画像数据:\n{source_summary}"),
  33. ]
  34. response = await self.llm.ainvoke(messages)
  35. return {"reading": response.content.strip()}
  36. except Exception as e:
  37. logger.warning("先天画像解读生成失败: %s", e)
  38. return {"reading": ""}
  39. @staticmethod
  40. def _summarize(portrait: dict) -> str:
  41. parts = []
  42. if portrait.get("zodiac"):
  43. parts.append(f"生肖: {portrait['zodiac']}")
  44. if portrait.get("bloodType"):
  45. parts.append(f"血型: {portrait['bloodType']}")
  46. if portrait.get("wuxingElements"):
  47. parts.append(f"五行: {portrait['wuxingElements']}")
  48. if portrait.get("mindBaseScore") is not None:
  49. parts.append(f"心先天基础分: {portrait['mindBaseScore']}")
  50. if portrait.get("wisdomBaseScore") is not None:
  51. parts.append(f"智先天基础分: {portrait['wisdomBaseScore']}")
  52. if portrait.get("eightCharacters"):
  53. parts.append(f"八字四柱: {portrait['eightCharacters']}")
  54. ns = portrait.get("numSoul") or {}
  55. if ns.get("lifePath") is not None:
  56. parts.append(
  57. f"生命灵数: {ns['lifePath']}"
  58. + (f"({ns['lifePathTitle']})" if ns.get("lifePathTitle") else "")
  59. )
  60. return "\n".join(parts) if parts else "暂无画像数据"