index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /**
  2. * AI 命盘解读云函数
  3. *
  4. * 接收命盘数据,调用 LLM API 生成数字能量学解读
  5. * 支持 DeepSeek / OpenAI 兼容接口
  6. * 通过云调用鉴权,不在前端暴露 API Key
  7. */
  8. const cloud = require('wx-server-sdk');
  9. cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
  10. // LLM API 配置(通过云托管环境变量配置,不硬编码密钥)
  11. const LLM_API_KEY = process.env.LLM_API_KEY || 'YOUR_API_KEY';
  12. const LLM_API_URL = process.env.LLM_API_URL || 'https://api.deepseek.com/v1/chat/completions';
  13. const LLM_MODEL = process.env.LLM_MODEL || 'deepseek-chat';
  14. // 系统提示词 — 设定 AI 的角色和行为
  15. const SYSTEM_PROMPT = `你是一位专业的数字能量学解读师,精通生命密码(数字能量学)。你的任务是基于用户的三角形命盘数据,提供专业、准确、有洞见的解读。
  16. 解读风格要求:
  17. 1. 专业但不晦涩,让普通人也能理解
  18. 2. 每段解读 100-200 字,言简意赅
  19. 3. 结合数字能量学理论,给出性格特质、优势、成长建议
  20. 4. 语气温和正向,避免恐吓或绝对化表述
  21. 5. 以 "你" 称呼用户,亲切自然
  22. 重要原则:
  23. - 解读必须基于命盘中的实际数字,不能编造
  24. - 避免宿命论,强调个人选择和发展的可能性
  25. - 所有解读最后加一句 "仅供参考" 的提示`;
  26. function buildPrompt(chartData, name) {
  27. const { positions, zones, mainCharacter, isMasterNumber } = chartData;
  28. let prompt = `请为以下命盘生成解读。\n\n`;
  29. prompt += `姓名:${name || '用户'}\n`;
  30. prompt += `主性格数字:${mainCharacter}${isMasterNumber ? '(卓越数)' : ''}\n\n`;
  31. prompt += `完整命盘数据:\n`;
  32. prompt += `底层:A=${positions.A} B=${positions.B} C=${positions.C} D=${positions.D} E=${positions.E}\n`;
  33. prompt += `第二层:F=${positions.F} G=${positions.G} H=${positions.H} I=${positions.I}\n`;
  34. prompt += `第三层:J=${positions.J} K=${positions.K} L=${positions.L}\n`;
  35. prompt += `第四层:M=${positions.M} N=${positions.N}\n`;
  36. prompt += `顶端:O=${positions.O}\n\n`;
  37. prompt += `请以 JSON 格式返回,包含以下字段:\n`;
  38. prompt += `{
  39. "mainCharacter": "主性格解读(重点突出${mainCharacter}号人的核心特质、优势、适合的发展方向)",
  40. "fatherSource": "父源区解读(F=${positions.F}, J=${positions.J} 的含义和影响)",
  41. "motherSource": "母源区解读(I=${positions.I}, L=${positions.L} 的含义和影响)",
  42. "leftZone": "左区(0-20岁)解读",
  43. "middleZone": "中区(20-40岁)解读",
  44. "rightZone": "右区(40-60岁)解读"
  45. }\n\n`;
  46. prompt += `请只返回 JSON,不要包含其他文字。`;
  47. return prompt;
  48. }
  49. exports.main = async (event, context) => {
  50. const { chartData, name } = event;
  51. if (!chartData || !chartData.positions) {
  52. return { code: 400, error: '缺少命盘数据' };
  53. }
  54. try {
  55. const userPrompt = buildPrompt(chartData, name);
  56. const response = await fetch(LLM_API_URL, {
  57. method: 'POST',
  58. headers: {
  59. 'Content-Type': 'application/json',
  60. 'Authorization': `Bearer ${LLM_API_KEY}`,
  61. },
  62. body: JSON.stringify({
  63. model: LLM_MODEL,
  64. messages: [
  65. { role: 'system', content: SYSTEM_PROMPT },
  66. { role: 'user', content: userPrompt },
  67. ],
  68. temperature: 0.7,
  69. max_tokens: 2000,
  70. }),
  71. });
  72. const data = await response.json();
  73. if (!data.choices || !data.choices[0]) {
  74. console.error('LLM API error:', data);
  75. return { code: 500, error: 'AI 服务异常', reading: null };
  76. }
  77. const content = data.choices[0].message.content;
  78. // Try to parse JSON from the response
  79. try {
  80. // Find JSON in the response (handle cases where model wraps in markdown)
  81. const jsonMatch = content.match(/\{[\s\S]*\}/);
  82. const jsonStr = jsonMatch ? jsonMatch[0] : content;
  83. const reading = JSON.parse(jsonStr);
  84. return {
  85. code: 0,
  86. reading: {
  87. mainCharacter: reading.mainCharacter || '',
  88. fatherSource: reading.fatherSource || '',
  89. motherSource: reading.motherSource || '',
  90. leftZone: reading.leftZone || '',
  91. middleZone: reading.middleZone || '',
  92. rightZone: reading.rightZone || '',
  93. },
  94. };
  95. } catch (parseErr) {
  96. // If JSON parsing fails, return raw text
  97. return {
  98. code: 0,
  99. reading: {
  100. mainCharacter: content,
  101. fatherSource: '解读生成中,请稍后再试',
  102. motherSource: '解读生成中,请稍后再试',
  103. leftZone: '解读生成中,请稍后再试',
  104. middleZone: '解读生成中,请稍后再试',
  105. rightZone: '解读生成中,请稍后再试',
  106. },
  107. };
  108. }
  109. } catch (err) {
  110. console.error('Cloud function error:', err);
  111. return { code: 500, error: err.message, reading: null };
  112. }
  113. };