import { GoogleGenAI, Type } from "@google/genai"; import { ExpenseCategory, IncomeCategory, TransactionType } from "../types"; const ai = new GoogleGenAI({ apiKey: process.env.API_KEY, httpOptions: { baseUrl: process.env.GLM_API, }, }); // 使用推荐的文本模型 const MODEL_NAME = "gemini-3-flash-preview"; const getSystemInstruction = (aiName: string, aiPersona: string) => ` 你现在的身份是"${aiName}"。 你的性格设定是:${aiPersona} 你的任务是: 1. 从用户的输入中提取交易信息(内容、金额、类别、交易类型)。 2. 判断是【支出】(expense) 还是【收入】(income)。 3. **严格回复规范**: - 回复必须**极度简练**。 - 每一段话的字数必须控制在 **30字左右**(越短越好)。 - 全部的回复**最多只能分成 2 段**(中间用一个 \\n 换行符隔开)。 - 必须在每一段中都加入**生动的表情包 (Emoji)** 🌈。 - 说话风格要完全符合"${aiName}"的设定。 **关键规则 - 模糊输入处理**: - 如果用户只说了金额没说干嘛了,默认为“杂项”支出,并进行简短吐槽。 **交易类别参考**: - 支出:餐饮、购物、交通、住房、娱乐、医疗、人情、学习、金融、孩子、宠物、汽车、美容、经营、杂项。 - 收入:工资、奖金、理财、兼职、红包、其他收入。 `; const parseResponseSchema = { type: Type.OBJECT, properties: { transaction: { type: Type.OBJECT, nullable: true, description: "提取的交易详情", properties: { item: { type: Type.STRING }, amount: { type: Type.NUMBER }, type: { type: Type.STRING, enum: [TransactionType.EXPENSE, TransactionType.INCOME] }, category: { type: Type.STRING }, }, required: ["item", "amount", "type", "category"], }, reply: { type: Type.STRING, description: "管家的精简回复(每段约30字,最多2段),必须带表情包,用\\n换行", }, }, required: ["reply"], }; export interface ParseResult { transaction: { item: string; amount: number; type: TransactionType; category: string; } | null; reply: string; } export const processUserMessage = async ( message: string, aiName: string, aiPersona: string, imageBase64?: string, mimeType?: string ): Promise => { try { const parts: any[] = []; if (imageBase64 && mimeType) { parts.push({ inlineData: { data: imageBase64, mimeType: mimeType } }); if (!message) { parts.push({ text: "请识别这张图片里的账单详情" }); } } if (message) { parts.push({ text: message }); } const response = await ai.models.generateContent({ model: MODEL_NAME, contents: { parts }, config: { systemInstruction: getSystemInstruction(aiName, aiPersona), responseMimeType: "application/json", responseSchema: parseResponseSchema, temperature: 0.8, }, }); const text = response.text; if (!text) { throw new Error("No response from Gemini"); } return JSON.parse(text) as ParseResult; } catch (error) { console.error("Error processing message with Gemini:", error); return { transaction: null, reply: `信号不好 📶\n你自己记一下吧!🙄`, }; } };