package memory import ( "yunyan/comm" "yunyan/modules" "yunyan/lego/utils/mapstructure" ) type Options struct { modules.Options // 周期报告 MaxReportProcess int `json:"max_report_process" mapstructure:"max_report_process"` //报告生成并发 worker 数,默认 3 ReportCronWeek string `json:"report_cron_week" mapstructure:"report_cron_week"` //周报 cron(秒级 6 位),默认每周一 00:30 ReportCronMonth string `json:"report_cron_month" mapstructure:"report_cron_month"` //月报 cron,默认每月 1 号 00:30 ReportMinItems int `json:"report_min_items" mapstructure:"report_min_items"` //上周期至少多少条记录才生成报告,默认 3 ReportPrompt string `json:"report_prompt" mapstructure:"report_prompt"` //报告播报文案的 system prompt ReportStaleSec int64 `json:"report_stale_sec" mapstructure:"report_stale_sec"` //报告卡在 pending/processing 多久算死,默认 900s // 会议纪要 → 待办抽取 MeetingExtract bool `json:"meeting_extract" mapstructure:"meeting_extract"` //是否开启会议待办抽取,默认 true MeetingExtractPrompt string `json:"meeting_extract_prompt" mapstructure:"meeting_extract_prompt"` //抽取 prompt(代码内置默认值,不放 echomeet_template) // 内容门槛:低于任一阈值的录音不抽待办(纪要照出,只是不进拾忆)。 // 6 秒的测试通话、20 秒的单人自述被抽成「继续进行进一步的功能测试」这类废话待办, // 再靠条数凑够周报阈值又触发一次 LLM 调用——2026-09-18 测试库里 id=23/24/26 都是这么来的。 MeetingExtractMinSeconds int32 `json:"meeting_extract_min_seconds" mapstructure:"meeting_extract_min_seconds"` //短于多少秒不抽取,默认 60;<=0 表示不限 MeetingExtractMinChars int `json:"meeting_extract_min_chars" mapstructure:"meeting_extract_min_chars"` //转写正文少于多少字不抽取,默认 120;<=0 表示不限 } func (this *Options) LoadConfig(settings map[string]interface{}) (err error) { // 布尔项的默认值必须在 Decode 之前给,否则配置里没写这一项时会被零值覆盖成 false this.MeetingExtract = true if settings != nil { if err = this.Options.LoadConfig(settings); err != nil { return } if err = mapstructure.Decode(settings, this); err != nil { return } } if this.MaxReportProcess <= 0 { this.MaxReportProcess = 3 } if this.ReportCronWeek == "" { // 秒 分 时 日 月 周:每周一 00:30:00 // ⚠️ 这是容器本地时区(Asia/Shanghai,Dockerfile 与 compose 都设了 TZ), // 对所有区域所有用户都是北京时间。海外用户的「上周」边界会偏,见设计文档 §5.3。 this.ReportCronWeek = "0 30 0 ? * MON" } if this.ReportCronMonth == "" { this.ReportCronMonth = "0 30 0 1 * ?" } if this.ReportMinItems <= 0 { this.ReportMinItems = 3 } if this.ReportStaleSec <= 0 { this.ReportStaleSec = 900 } // ⚠️ 这两个门槛允许配 0(= 不限),所以只在「配置里没写这一项」时才回默认值。 // mapstructure 解不出来时字段保持零值,与「运营显式填了 0」无法区分, // 于是判据取「负数才算没配」——想关掉门槛就填 0,想用默认就别写这两行。 if this.MeetingExtractMinSeconds < 0 { this.MeetingExtractMinSeconds = 0 } else if _, ok := settings["meeting_extract_min_seconds"]; !ok { this.MeetingExtractMinSeconds = comm.MeetingMinSeconds } if this.MeetingExtractMinChars < 0 { this.MeetingExtractMinChars = 0 } else if _, ok := settings["meeting_extract_min_chars"]; !ok { this.MeetingExtractMinChars = 120 } if this.ReportPrompt == "" { this.ReportPrompt = "你是一位贴心的生活助理。用户会给你一段 JSON 格式的周期统计数据(已由程序精确算出)。" + "请把它组织成一段适合语音朗读的自然中文短文,控制在 150 字以内。要求:" + "1) 直接说结论,不要复述字段名,不要出现 JSON、字段、数据这类词;" + "2) 数字必须原样引用,一个都不能改、不能四舍五入、不能自己做加减;" + "3) 依次覆盖:完成情况、未完成的事、花销、灵感;某一项数据为空就跳过不提;" + "4) 语气平实友好,不要用感叹号堆砌,不要说教;" + "5) 只输出这段话本身,不要标题、不要分点、不要任何解释。" } return }