/** * 星环OPC中心 — 智能记账意图解析器 * 使用 LLM 解析自然语言记账输入 */ import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; export interface AccountingIntent { type: "income" | "expense"; amount: number; description: string; category: string; date: string; counterparty?: string; is_deductible?: boolean; tax_note?: string; confidence: number; } /** * 解析自然语言记账意图 * @param input 用户输入的自然语言 * @param api OpenClaw API * @returns 解析后的记账意图 */ export async function parseAccountingIntent( input: string, api: OpenClawPluginApi, ): Promise { const systemPrompt = `你是一个专业的会计助手,负责解析用户的自然语言记账输入。 你的任务是从用户输入中提取以下信息: 1. 交易类型(income 或 expense) 2. 金额(数字,单位:元) 3. 描述(简短描述) 4. 分类(从以下类别中选择): - 收入类:service_income(服务收入), product_income(产品销售), investment_income(投资收益), other_income(其他收入) - 支出类:business_entertainment(业务招待费), office_supplies(办公费用), marketing(营销费用), salary(工资), rent(租金), utilities(水电费), fixed_asset(固定资产), tax(税费), other_expense(其他支出) 5. 日期(YYYY-MM-DD 格式,如果未指定则使用今天) 6. 交易对方(如果提到) 7. 是否可抵税(布尔值) 8. 税务提示(如果可抵税或有特殊税务处理,给出提示) 分类规则: - 业务招待费:请客吃饭、送礼、招待客户等 → 可抵扣,需保存发票 - 办公费用:购买办公用品、设备维护等 → 可抵扣 - 固定资产:购买电脑、家具等大额设备(>2000元) → 可折旧 - 营销费用:广告投放、推广活动等 → 可抵扣 - 工资:员工工资 → 需代扣个税 - 租金:办公场地租金 → 可抵扣 - 税费:各类税费缴纳 → 不可抵扣 - 生活支出:个人消费 → 不可抵扣 请以 JSON 格式返回解析结果,格式如下: { "type": "income" 或 "expense", "amount": 金额(数字), "description": "描述", "category": "分类", "date": "YYYY-MM-DD", "counterparty": "交易对方(可选)", "is_deductible": true/false, "tax_note": "税务提示(可选)", "confidence": 0-100(解析置信度) } 如果无法准确解析,confidence 设为较低值,并在 description 中说明原因。`; const userPrompt = `请解析以下记账输入:\n\n${input}`; try { const response = await api.callLLM({ messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userPrompt }, ], maxTokens: 500, temperature: 0.1, // 低温度确保稳定输出 }); const content = response.content[0]; if (content.type !== "text") { throw new Error("LLM 返回非文本内容"); } // 尝试提取 JSON const text = content.text; const jsonMatch = text.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error("无法从 LLM 响应中提取 JSON"); } const result = JSON.parse(jsonMatch[0]) as AccountingIntent; // 验证必填字段 if (!result.type || !result.amount || !result.description || !result.category) { throw new Error("解析结果缺少必填字段"); } // 如果没有日期,使用今天 if (!result.date) { result.date = new Date().toISOString().slice(0, 10); } return result; } catch (error) { api.logger.error("opc: 智能记账解析失败", error); // 返回一个低置信度的默认结果 return { type: "expense", amount: 0, description: `解析失败:${error instanceof Error ? error.message : String(error)}`, category: "other_expense", date: new Date().toISOString().slice(0, 10), is_deductible: false, confidence: 0, }; } } /** * 基于历史记录智能建议分类 * @param description 交易描述 * @param api OpenClaw API * @returns 建议的分类 */ export async function suggestCategory( description: string, api: OpenClawPluginApi, ): Promise<{ category: string; reason: string; confidence: number }> { const systemPrompt = `你是一个专业的会计分类助手。根据交易描述,建议最合适的分类。 分类选项: - 收入类:service_income(服务收入), product_income(产品销售), investment_income(投资收益), other_income(其他收入) - 支出类:business_entertainment(业务招待费), office_supplies(办公费用), marketing(营销费用), salary(工资), rent(租金), utilities(水电费), fixed_asset(固定资产), tax(税费), other_expense(其他支出) 请以 JSON 格式返回: { "category": "分类代码", "reason": "选择理由", "confidence": 0-100 }`; const userPrompt = `请为以下交易描述建议分类:\n\n${description}`; try { const response = await api.callLLM({ messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userPrompt }, ], maxTokens: 200, temperature: 0.1, }); const content = response.content[0]; if (content.type !== "text") { throw new Error("LLM 返回非文本内容"); } const text = content.text; const jsonMatch = text.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error("无法从 LLM 响应中提取 JSON"); } const result = JSON.parse(jsonMatch[0]) as { category: string; reason: string; confidence: number }; return result; } catch (error) { api.logger.error("opc: 智能分类建议失败", error); return { category: "other_expense", reason: `分类失败:${error instanceof Error ? error.message : String(error)}`, confidence: 0, }; } }