feat: 初始化数据库模式并引入AI记账服务。

This commit is contained in:
2026-01-28 15:46:21 +08:00
parent 6604f50448
commit 57def08201
5 changed files with 86 additions and 28 deletions

View File

@@ -223,17 +223,22 @@ type ChatCompletionResponse struct {
} `json:"choices"`
}
// extractCustomPrompt 从用户消息中提取自定义 System/Persona prompt
// 如果消息包含 "System:" 或 "Persona:",则提取其后的内容作为自定义 prompt
func extractCustomPrompt(text string) string {
prefixes := []string{"System:", "Persona:"}
for _, prefix := range prefixes {
if idx := strings.Index(text, prefix); idx != -1 {
// 提取 prefix 后的内容作为自定义 prompt
return strings.TrimSpace(text[idx+len(prefix):])
}
}
return ""
}
// ParseIntent extracts transaction parameters from text
// Requirements: 7.1, 7.5, 7.6
func (s *LLMService) ParseIntent(ctx context.Context, text string, history []ChatMessage) (*AITransactionParams, string, error) {
// Fast path: try simple parsing first for common patterns
// This avoids LLM call for simple inputs like "6块钱奶茶"
// TODO: 暂时禁用本地解析快速路径,始终使用 LLM
// simpleParams, simpleMsg, _ := s.parseIntentSimple(text)
// if simpleParams != nil && simpleParams.Amount != nil && simpleParams.Category != "" && simpleParams.Category != "其他" {
// // Simple parsing succeeded with amount and category, use it directly
// return simpleParams, simpleMsg, nil
// }
if s.config.OpenAIAPIKey == "" || s.config.OpenAIBaseURL == "" {
// No API key, return simple parsing result
@@ -242,24 +247,33 @@ func (s *LLMService) ParseIntent(ctx context.Context, text string, history []Cha
}
// Build messages with history
todayDate := time.Now().Format("2006-01-02")
systemPrompt := fmt.Sprintf(`你是一个智能记账助手。从用户描述中提取记账信息<EFBFBD>?
var systemPrompt string
今天的日期是<EFBFBD>?s
// 检查是否有自定义 System/Persona prompt用于财务建议等场景
// 如果有,直接使用自定义 prompt 覆盖默认记账 prompt
if customPrompt := extractCustomPrompt(text); customPrompt != "" {
systemPrompt = customPrompt
} else {
// 使用默认的记账 prompt
todayDate := time.Now().Format("2006-01-02")
systemPrompt = fmt.Sprintf(`你是一个智能记账助手。从用户描述中提取记账信息。
规则<EFBFBD>?
1. 金额提取数字<E5AD97>?6<>?=6<>?十五<E58D81>?=15
2. 分类根据内容推断<E696AD>?奶茶/咖啡/吃饭"=餐饮<E9A490>?打车/地铁"=交通,"买衣<E4B9B0>?=购物
今天的日期是%s
规则:
1. 金额:提取数字,如"6元"=6"十五"=15
2. 分类:根据内容推断,如"奶茶/咖啡/吃饭"=餐饮,"打车/地铁"=交通,"买衣服"=购物
3. 类型默认expense(支出),除非明确说"收入/工资/奖金/红包"
4. 日期:默认使用今天的日期<EFBFBD>?s除非用户明确指定其他日期
5. 备注:提取关键描<EFBFBD>?
4. 日期:默认使用今天的日期%s除非用户明确指定其他日期
5. 备注:提取关键描
直接返回JSON不要解释
{"amount":数字,"category":"分类","type":"expense或income","note":"备注","date":"YYYY-MM-DD","message":"简短确<EFBFBD>?}
{"amount":数字,"category":"分类","type":"expense或income","note":"备注","date":"YYYY-MM-DD","message":"简短确认"}
示例(假设今天是%s
用户<EFBFBD>?买了<E4B9B0>?块的奶茶"
返回:{"amount":6,"category":"餐饮","type":"expense","note":"奶茶","date":"%s","message":"记录:餐饮支<EFBFBD>?元,奶茶"}`, todayDate, todayDate, todayDate, todayDate)
用户"买了6块的奶茶"
返回:{"amount":6,"category":"餐饮","type":"expense","note":"奶茶","date":"%s","message":"记录:餐饮支出6元,奶茶"}`, todayDate, todayDate, todayDate, todayDate)
}
messages := []ChatMessage{
{
@@ -720,7 +734,7 @@ func (s *AIBookkeepingService) ProcessChat(ctx context.Context, userID uint, ses
// All params complete, generate confirmation card
card := s.GenerateConfirmationCard(session)
response.ConfirmationCard = card
response.Message = fmt.Sprintf("请确认:%s %.2f元,分类<EFBFBD>?s账户%s",
response.Message = fmt.Sprintf("请确认:%s %.2f元,分类%s账户%s",
s.getTypeLabel(session.Params.Type),
*session.Params.Amount,
session.Params.Category,