Skip to content

Commit 7903e46

Browse files
committed
fix funasr runtime
1 parent 68591eb commit 7903e46

13 files changed

Lines changed: 647 additions & 9 deletions

App.config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@
6565
<add key="VoiceConversationExtractEnabled" value="true" />
6666
<add key="VoiceConversationWindowSeconds" value="45" />
6767
<add key="VoiceConversationMinTurns" value="3" />
68+
<!-- 语音VAD调优,降低首句漏检 -->
69+
<add key="VoiceVadMinStartMs" value="260" />
70+
<add key="VoiceVadHangoverMs" value="900" />
71+
<add key="VoiceEnergyThresholdDb" value="-30" />
6872
<!-- 声纹识别配置 -->
6973
<add key="VoiceSpeakerVerifyEnabled" value="true" />
7074
<add key="VoiceSpeakerEnrollMode" value="true" />
@@ -77,6 +81,7 @@
7781
<add key="BehaviorLearningEnabled" value="true" />
7882
<add key="StuckNudgesEnabled" value="true" />
7983
<add key="LlmSkillAssistEnabled" value="true" />
84+
<add key="EnabledSkillIds" value="decompose,focus_sprint,priority_rebalance,risk_check,delegate_prepare,clarify_goal" />
8085
<!-- 安静时段(24小时制,跨天区间) -->
8186
<add key="QuietHoursStart" value="22" />
8287
<add key="QuietHoursEnd" value="8" />

DraftViewerWindow.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
<!-- 说明文字 -->
2727
<TextBlock Grid.Row="1" Margin="15,10" TextWrapping="Wrap" Foreground="Gray">
28-
这些是系统通过监听您的语音自动识别的潜在任务。点击"添加到任务"将任务添加到对应的四象限,或点击"忽略"删除草稿
28+
这些是系统通过监听您的语音自动识别的潜在任务。若识别到时间,系统会显示“提醒时间”并在加入任务后自动带入提醒
2929
</TextBlock>
3030

3131
<!-- 草稿列表 -->
@@ -41,6 +41,7 @@
4141
<DataGridTextColumn Header="任务描述" Width="*" Binding="{Binding CleanedText}"/>
4242
<DataGridTextColumn Header="象限" Width="100" Binding="{Binding EstimatedQuadrant}"/>
4343
<DataGridTextColumn Header="优先级" Width="80" Binding="{Binding Importance}"/>
44+
<DataGridTextColumn Header="提醒时间" Width="140" Binding="{Binding ReminderTime, StringFormat='yyyy-MM-dd HH:mm'}"/>
4445
<DataGridTextColumn Header="检测次数" Width="80" Binding="{Binding DetectionCount}"/>
4546
</DataGrid.Columns>
4647
</DataGrid>

DraftViewerWindow.xaml.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ private void AddDraftsToQuadrants(List<TaskDraft> drafts)
8989
IsActive = true,
9090
CreatedDate = DateTime.Now,
9191
LastModifiedDate = DateTime.Now,
92+
ReminderTime = draft.ReminderTime,
9293
IsActiveInQuadrant = true,
9394
InactiveWarningCount = 0,
9495
Result = string.Empty

EnhancedAudioCaptureService.cs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,12 @@ public sealed class EnhancedAudioCaptureService : IDisposable
104104
// 意图识别和草稿管理
105105
private IntentRecognizer _intentRecognizer;
106106
private TaskDraftManager _draftManager;
107+
private VoiceReminderTimeParser _reminderTimeParser;
107108

108109
// VAD 参数 - 更保守的配置
109-
private readonly double _energyThresholdDb = -30.0; // 能量阈值,-30dBFS 表示要有一定音量
110-
private readonly int _minStartMs = 500; // 连续高能量达到 500ms 才开始录音
111-
private readonly int _hangoverMs = 1500; // 录音进行中,连续低能量达到 1.5s 才停止
110+
private double _energyThresholdDb = -30.0; // 能量阈值,-30dBFS 表示要有一定音量
111+
private int _minStartMs = 260; // 连续高能量达到 260ms 即可开始,减少首句漏检
112+
private int _hangoverMs = 900; // 连续低能量达到 900ms 即停止,提升响应速度
112113
private int _consecAboveMs = 0;
113114
private int _consecBelowMs = 0;
114115
private DateTime _lastAudioStateSpeech = DateTime.MinValue;
@@ -128,6 +129,7 @@ public EnhancedAudioCaptureService(TimeSpan? silenceTimeout = null, float confid
128129

129130
_intentRecognizer = new IntentRecognizer();
130131
_draftManager = new TaskDraftManager();
132+
_reminderTimeParser = new VoiceReminderTimeParser();
131133
_speechModelManager = new SpeechModelManager();
132134
_modelBootstrapTask = _speechModelManager.EnsureReadyAsync();
133135
_autoAddVoiceTasks = ReadBoolSetting("VoiceAutoAddToQuadrant", false);
@@ -155,6 +157,9 @@ public EnhancedAudioCaptureService(TimeSpan? silenceTimeout = null, float confid
155157
_funAsrWorkerStartupTimeoutSeconds = ReadIntSetting("FunAsrWorkerStartupTimeoutSeconds", 600);
156158
_funAsrUsePersistentWorker = ReadBoolSetting("FunAsrUsePersistentWorker", true);
157159
_funAsrMinSegmentSeconds = ReadDoubleSetting("FunAsrMinSegmentSeconds", 0.5);
160+
_energyThresholdDb = ReadDoubleSetting("VoiceEnergyThresholdDb", -30.0);
161+
_minStartMs = ReadIntSetting("VoiceVadMinStartMs", 260);
162+
_hangoverMs = ReadIntSetting("VoiceVadHangoverMs", 900);
158163
if (_funAsrEnabled)
159164
{
160165
VoiceRuntimeLog.Info($"EnhancedAudioCaptureService ctor: requesting FunASR bootstrap task. provider={_asrProvider}");
@@ -437,6 +442,12 @@ private TaskDraft ProcessPotentialTask(string text, float confidence)
437442
if (string.IsNullOrWhiteSpace(cleanedText))
438443
return null;
439444

445+
DateTime? reminderTime = null;
446+
if (_reminderTimeParser != null && _reminderTimeParser.TryParse(text, DateTime.Now, out DateTime parsedTime))
447+
{
448+
reminderTime = parsedTime;
449+
}
450+
440451
// 估计优先级
441452
var (importance, urgency) = _intentRecognizer.EstimatePriority(cleanedText);
442453

@@ -448,6 +459,8 @@ private TaskDraft ProcessPotentialTask(string text, float confidence)
448459
{
449460
RawText = text,
450461
CleanedText = cleanedText,
462+
ReminderTime = reminderTime,
463+
ReminderHintText = reminderTime.HasValue ? reminderTime.Value.ToString("yyyy-MM-dd HH:mm") : null,
451464
Importance = importance,
452465
Urgency = urgency,
453466
EstimatedQuadrant = quadrant,
@@ -456,8 +469,8 @@ private TaskDraft ProcessPotentialTask(string text, float confidence)
456469

457470
_draftManager.AddDraft(draft);
458471

459-
Console.WriteLine($"[EnhancedAudioCaptureService] Task draft created: \"{cleanedText}\" (Quadrant: {quadrant}, Conf: {confidence:P0})");
460-
VoiceRuntimeLog.Info($"Task draft created from voice. text={cleanedText}, quadrant={quadrant}, conf={confidence:F2}");
472+
Console.WriteLine($"[EnhancedAudioCaptureService] Task draft created: \"{cleanedText}\" (Quadrant: {quadrant}, Conf: {confidence:P0}, Reminder: {reminderTime?.ToString("yyyy-MM-dd HH:mm") ?? "none"})");
473+
VoiceRuntimeLog.Info($"Task draft created from voice. text={cleanedText}, quadrant={quadrant}, conf={confidence:F2}, reminder={reminderTime?.ToString("o") ?? "none"}");
461474
return draft;
462475
}
463476
catch (Exception ex)
@@ -1008,14 +1021,20 @@ private void HandleRecognizedText(string rawText, float confidence, string sourc
10081021

10091022
bool isSystemFallback = _classicFallbackEnabled && string.Equals(source, "system-speech", StringComparison.OrdinalIgnoreCase);
10101023
double taskLikelihood = _intentRecognizer.ScoreTaskLikelihood(text);
1024+
bool reminderLike = _intentRecognizer.IsReminderLike(text);
10111025
float minConfidence = isSystemFallback
10121026
? Math.Max(0.05f, _confidenceThreshold * 0.12f)
1013-
: _confidenceThreshold * 0.55f;
1027+
: _confidenceThreshold * 0.45f;
1028+
1029+
if (reminderLike)
1030+
{
1031+
minConfidence = Math.Max(0.15f, minConfidence * 0.6f);
1032+
}
10141033

1015-
if (confidence < minConfidence && taskLikelihood < 0.55 && !_intentRecognizer.IsReminderLike(text))
1034+
if (confidence < minConfidence && taskLikelihood < 0.45 && !reminderLike)
10161035
return;
10171036

1018-
if (_intentRecognizer.IsPotentialTask(text) || _intentRecognizer.IsReminderLike(text))
1037+
if (_intentRecognizer.IsPotentialTask(text) || reminderLike)
10191038
{
10201039
TotalPotentialTasks++;
10211040
var draft = ProcessPotentialTask(text, confidence);
@@ -1114,6 +1133,7 @@ private void TryAutoAddTaskToQuadrant(TaskDraft draft, float confidence)
11141133
IsActive = true,
11151134
CreatedDate = DateTime.Now,
11161135
LastModifiedDate = DateTime.Now,
1136+
ReminderTime = draft.ReminderTime,
11171137
IsActiveInQuadrant = true,
11181138
InactiveWarningCount = 0,
11191139
Result = string.Empty

MainWindow.xaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,34 @@
231231
</Grid>
232232
</Button>
233233

234+
<Button x:Name="SkillCenterButton"
235+
Width="30"
236+
Height="30"
237+
FontSize="16"
238+
FontWeight="Bold"
239+
Margin="5,0,0,0"
240+
ToolTip="Skill 管理"
241+
Click="SkillCenterButton_Click">
242+
<Button.Resources>
243+
<Style TargetType="Border">
244+
<Setter Property="CornerRadius" Value="15"/>
245+
</Style>
246+
</Button.Resources>
247+
<Button.Style>
248+
<Style TargetType="Button">
249+
<Setter Property="Background" Value="#DDDDDD"/>
250+
<Setter Property="BorderBrush" Value="#AAAAAA"/>
251+
<Setter Property="Foreground" Value="#333333"/>
252+
<Style.Triggers>
253+
<Trigger Property="IsMouseOver" Value="True">
254+
<Setter Property="Background" Value="#EEEEEE"/>
255+
</Trigger>
256+
</Style.Triggers>
257+
</Style>
258+
</Button.Style>
259+
<TextBlock Text="🧠" HorizontalAlignment="Center" VerticalAlignment="Center"/>
260+
</Button>
261+
234262
</StackPanel>
235263

236264
<!-- Quadrant 1: Important & Urgent -->

MainWindow.xaml.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,7 @@ private async void ShowFriendlyReminder(ItemGrid task, string message)
11151115
inactiveDuration,
11161116
reminderContext,
11171117
2);
1118+
skillRecs = SkillManagementService.FilterEnabled(skillRecs);
11181119
var shownSkillIds = new List<string>();
11191120
mergedSuggestions = MergeSkillSuggestions(mergedSuggestions, skillRecs, shownSkillIds);
11201121
RememberPendingReminderSkills(task, shownSkillIds);
@@ -2809,6 +2810,11 @@ private void DraftsButton_Click(object sender, RoutedEventArgs e)
28092810
UpdateDraftBadge();
28102811
}
28112812

2813+
private void SkillCenterButton_Click(object sender, RoutedEventArgs e)
2814+
{
2815+
OpenSkillManagement();
2816+
}
2817+
28122818
private void ShowSettingsMenu()
28132819
{
28142820
var contextMenu = new ContextMenu();
@@ -2830,6 +2836,14 @@ private void ShowSettingsMenu()
28302836
};
28312837
reminderSettingsItem.Click += (s, e) => OpenReminderSettings();
28322838
contextMenu.Items.Add(reminderSettingsItem);
2839+
2840+
var skillManagementItem = new MenuItem
2841+
{
2842+
Header = "🧠 Skill 管理",
2843+
ToolTip = "启用/停用技能并查看反馈效果"
2844+
};
2845+
skillManagementItem.Click += (s, e) => OpenSkillManagement();
2846+
contextMenu.Items.Add(skillManagementItem);
28332847

28342848
contextMenu.Items.Add(new Separator());
28352849

@@ -3240,6 +3254,15 @@ private void OpenReminderSettings()
32403254
settingsWindow.ShowDialog();
32413255
}
32423256

3257+
private void OpenSkillManagement()
3258+
{
3259+
var window = new SkillManagementWindow
3260+
{
3261+
Owner = this
3262+
};
3263+
window.ShowDialog();
3264+
}
3265+
32433266
private void ShowAbout()
32443267
{
32453268
string aboutText = "TimeTask - 智能任务管理系统\n\n" +

SkillManagementService.cs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Configuration;
4+
using System.Linq;
5+
6+
namespace TimeTask
7+
{
8+
public class SkillDefinition
9+
{
10+
public string SkillId { get; set; }
11+
public string Title { get; set; }
12+
public string Description { get; set; }
13+
public bool Enabled { get; set; }
14+
}
15+
16+
public static class SkillManagementService
17+
{
18+
private const string EnabledSkillIdsKey = "EnabledSkillIds";
19+
20+
public static readonly string[] AllowedSkillIds = new[]
21+
{
22+
"decompose", "focus_sprint", "priority_rebalance", "risk_check", "delegate_prepare", "clarify_goal"
23+
};
24+
25+
public static List<SkillDefinition> GetSkillDefinitions()
26+
{
27+
var enabled = LoadEnabledSkillIds();
28+
var map = new Dictionary<string, (string title, string desc)>(StringComparer.OrdinalIgnoreCase)
29+
{
30+
["decompose"] = ("任务拆解", "把模糊任务拆成可执行的下一步"),
31+
["focus_sprint"] = ("专注冲刺", "给当前任务安排短时高专注执行"),
32+
["priority_rebalance"] = ("优先级重排", "根据紧急/重要性调整顺序"),
33+
["risk_check"] = ("风险检查", "提前检查阻塞点与失败风险"),
34+
["delegate_prepare"] = ("委托准备", "为委托/协作准备最小信息包"),
35+
["clarify_goal"] = ("目标澄清", "澄清任务目标、边界和完成标准")
36+
};
37+
38+
return AllowedSkillIds
39+
.Select(id => new SkillDefinition
40+
{
41+
SkillId = id,
42+
Title = map.TryGetValue(id, out var v) ? v.title : id,
43+
Description = map.TryGetValue(id, out var vv) ? vv.desc : id,
44+
Enabled = enabled.Contains(id)
45+
})
46+
.ToList();
47+
}
48+
49+
public static HashSet<string> LoadEnabledSkillIds()
50+
{
51+
try
52+
{
53+
string raw = ConfigurationManager.AppSettings[EnabledSkillIdsKey];
54+
if (string.IsNullOrWhiteSpace(raw))
55+
return new HashSet<string>(AllowedSkillIds, StringComparer.OrdinalIgnoreCase);
56+
57+
var ids = raw.Split(new[] { ',', ';', '|', ' ' }, StringSplitOptions.RemoveEmptyEntries)
58+
.Select(x => x.Trim().ToLowerInvariant())
59+
.Where(x => AllowedSkillIds.Contains(x, StringComparer.OrdinalIgnoreCase))
60+
.ToList();
61+
var set = new HashSet<string>(ids, StringComparer.OrdinalIgnoreCase);
62+
if (set.Count == 0)
63+
{
64+
return new HashSet<string>(AllowedSkillIds, StringComparer.OrdinalIgnoreCase);
65+
}
66+
return set;
67+
}
68+
catch
69+
{
70+
return new HashSet<string>(AllowedSkillIds, StringComparer.OrdinalIgnoreCase);
71+
}
72+
}
73+
74+
public static List<LlmSkillRecommendation> FilterEnabled(List<LlmSkillRecommendation> skills)
75+
{
76+
if (skills == null || skills.Count == 0)
77+
return new List<LlmSkillRecommendation>();
78+
79+
var enabled = LoadEnabledSkillIds();
80+
return skills
81+
.Where(s => s != null && !string.IsNullOrWhiteSpace(s.SkillId))
82+
.Where(s => enabled.Contains(s.SkillId.Trim()))
83+
.ToList();
84+
}
85+
86+
public static void SaveEnabledSkillIds(IEnumerable<string> enabledIds)
87+
{
88+
var target = (enabledIds ?? Enumerable.Empty<string>())
89+
.Where(x => !string.IsNullOrWhiteSpace(x))
90+
.Select(x => x.Trim().ToLowerInvariant())
91+
.Where(x => AllowedSkillIds.Contains(x, StringComparer.OrdinalIgnoreCase))
92+
.Distinct(StringComparer.OrdinalIgnoreCase)
93+
.ToList();
94+
95+
if (target.Count == 0)
96+
{
97+
target = new List<string>(AllowedSkillIds);
98+
}
99+
100+
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
101+
var settings = config.AppSettings.Settings;
102+
string value = string.Join(",", target);
103+
if (settings[EnabledSkillIdsKey] == null)
104+
{
105+
settings.Add(EnabledSkillIdsKey, value);
106+
}
107+
else
108+
{
109+
settings[EnabledSkillIdsKey].Value = value;
110+
}
111+
112+
config.Save(ConfigurationSaveMode.Modified);
113+
ConfigurationManager.RefreshSection("appSettings");
114+
}
115+
}
116+
}

SkillManagementWindow.xaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<Window x:Class="TimeTask.SkillManagementWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
Title="Skill 管理" Height="520" Width="760"
5+
WindowStartupLocation="CenterOwner">
6+
<Grid Margin="12">
7+
<Grid.RowDefinitions>
8+
<RowDefinition Height="Auto"/>
9+
<RowDefinition Height="*"/>
10+
<RowDefinition Height="Auto"/>
11+
</Grid.RowDefinitions>
12+
13+
<TextBlock Grid.Row="0"
14+
Text="管理大模型 Skills:可启用/停用技能,并查看最近30天触达与反馈。"
15+
Margin="0,0,0,10"
16+
Foreground="#555"
17+
TextWrapping="Wrap"/>
18+
19+
<DataGrid x:Name="SkillsDataGrid"
20+
Grid.Row="1"
21+
AutoGenerateColumns="False"
22+
CanUserAddRows="False"
23+
CanUserDeleteRows="False"
24+
HeadersVisibility="Column"
25+
SelectionMode="Extended"
26+
SelectionUnit="FullRow">
27+
<DataGrid.Columns>
28+
<DataGridCheckBoxColumn Header="启用" Width="60" Binding="{Binding Enabled, Mode=TwoWay}"/>
29+
<DataGridTextColumn Header="Skill" Width="140" Binding="{Binding Title}"/>
30+
<DataGridTextColumn Header="说明" Width="*" Binding="{Binding Description}"/>
31+
<DataGridTextColumn Header="Shown(30d)" Width="90" Binding="{Binding Shown}"/>
32+
<DataGridTextColumn Header="Accepted" Width="80" Binding="{Binding Accepted}"/>
33+
<DataGridTextColumn Header="Deferred" Width="80" Binding="{Binding Deferred}"/>
34+
<DataGridTextColumn Header="Rejected" Width="80" Binding="{Binding Rejected}"/>
35+
</DataGrid.Columns>
36+
</DataGrid>
37+
38+
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
39+
<Button Content="全选" Width="72" Height="32" Margin="0,0,8,0" Click="EnableAll_Click"/>
40+
<Button Content="全不选" Width="72" Height="32" Margin="0,0,8,0" Click="DisableAll_Click"/>
41+
<Button Content="保存" Width="72" Height="32" Margin="0,0,8,0" Click="Save_Click"/>
42+
<Button Content="关闭" Width="72" Height="32" Click="Close_Click"/>
43+
</StackPanel>
44+
</Grid>
45+
</Window>

0 commit comments

Comments
 (0)