Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
Ftt_Common
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Ftt_Common
Commits
e6773c00
Commit
e6773c00
authored
Sep 01, 2026
by
段廷瑞
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: markdown渲染优化;socket关闭异常处理
parent
c6d90d06
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
158 additions
and
39 deletions
+158
-39
AgentEngineLogicTests.cs
AgentTest/AgentEngineLogicTests.cs
+16
-0
MarkdownRendererTests.cs
AgentTest/MarkdownRendererTests.cs
+20
-0
AgentEngine.cs
Tools.Agent/Agent/AgentEngine.cs
+17
-7
KbIndex.cs
Tools.Agent/Kb/KbIndex.cs
+24
-7
KbService.cs
Tools.Agent/Kb/KbService.cs
+13
-0
MarkdownRenderer.cs
Tools.Agent/Support/MarkdownRenderer.cs
+1
-2
wMyAgent.Designer.cs
Tools.Agent/wMyAgent.Designer.cs
+2
-1
wMyAgent.cs
Tools.Agent/wMyAgent.cs
+65
-22
No files found.
AgentTest/AgentEngineLogicTests.cs
View file @
e6773c00
using
Newtonsoft.Json.Linq
;
using
Newtonsoft.Json.Linq
;
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Threading
;
using
Tools.MyAgent
;
using
Tools.MyAgent
;
using
Tools.MyAgent.Agent
;
using
Tools.MyAgent.Agent
;
using
Tools.MyAgent.Kb
;
namespace
AgentTest
namespace
AgentTest
{
{
...
@@ -11,6 +13,20 @@ namespace AgentTest
...
@@ -11,6 +13,20 @@ namespace AgentTest
/// </summary>
/// </summary>
public
sealed
class
AgentEngineLogicTests
:
TestRunner
public
sealed
class
AgentEngineLogicTests
:
TestRunner
{
{
[
Test
]
public
void
Dispose_PreventsLateRunAndIsIdempotent
()
{
var
engine
=
new
AgentEngine
(
new
KbService
());
int
errorCount
=
0
;
engine
.
OnError
+=
_
=>
errorCount
++;
engine
.
Dispose
();
engine
.
Dispose
();
engine
.
RunAsync
(
new
List
<
ChatMessage
>(),
CancellationToken
.
None
).
GetAwaiter
().
GetResult
();
Assert
.
Equal
(
0
,
errorCount
,
"释放后的迟到请求不应再向 UI 上报网络错误"
);
}
// ── token 估算 ──
// ── token 估算 ──
[
Test
]
[
Test
]
...
...
AgentTest/MarkdownRendererTests.cs
View file @
e6773c00
...
@@ -32,6 +32,26 @@ namespace AgentTest
...
@@ -32,6 +32,26 @@ namespace AgentTest
Assert
.
Contains
(
rtf
,
@"\u26041?\u27861?\u-29766?"
,
"正文缺失"
);
Assert
.
Contains
(
rtf
,
@"\u26041?\u27861?\u-29766?"
,
"正文缺失"
);
}
}
[
Test
]
public
void
ThinkingMarker_UsesFontSafeText
()
{
string
rtf
=
MarkdownRenderer
.
BuildChatRtf
(
new
List
<
ChatMessage
>
{
new
ChatMessage
{
Role
=
"assistant"
,
ThinkingContent
=
"正在分析"
,
Content
=
"回答"
,
Timestamp
=
DateTime
.
Now
,
}
},
null
);
Assert
.
Contains
(
rtf
,
"> Thinking..."
,
"thinking 标记应使用可稳定渲染的文本"
);
Assert
.
NotContains
(
rtf
,
@"{\f2 \u9656?}"
,
"thinking 标记不应切换到缺少字形的 Emoji 字体"
);
}
[
Test
]
[
Test
]
public
void
OrderedList_StillRendered
()
public
void
OrderedList_StillRendered
()
{
{
...
...
Tools.Agent/Agent/AgentEngine.cs
View file @
e6773c00
...
@@ -26,6 +26,7 @@ namespace Tools.MyAgent.Agent
...
@@ -26,6 +26,7 @@ namespace Tools.MyAgent.Agent
private
readonly
DialectService
_dialect
;
// 内置方言/行业知识(程序目录 Agent/dialect + Agent/industry)
private
readonly
DialectService
_dialect
;
// 内置方言/行业知识(程序目录 Agent/dialect + Agent/industry)
private
AgentProductContext
_productContext
;
private
AgentProductContext
_productContext
;
private
CancellationTokenSource
_cts
;
private
CancellationTokenSource
_cts
;
private
int
_disposeStarted
;
/// <summary>上下文 token 预算(由配置注入;默认 32000)</summary>
/// <summary>上下文 token 预算(由配置注入;默认 32000)</summary>
private
int
_contextTokens
=
32000
;
private
int
_contextTokens
=
32000
;
...
@@ -72,6 +73,8 @@ namespace Tools.MyAgent.Agent
...
@@ -72,6 +73,8 @@ namespace Tools.MyAgent.Agent
public
bool
IsRunning
{
get
;
private
set
;
}
public
bool
IsRunning
{
get
;
private
set
;
}
private
bool
IsDisposed
=>
Volatile
.
Read
(
ref
_disposeStarted
)
!=
0
;
/// <summary>本会话累计 prompt tokens(D16 用量统计;由 SendAndLogAsync 累加,NewChat 时重置)</summary>
/// <summary>本会话累计 prompt tokens(D16 用量统计;由 SendAndLogAsync 累加,NewChat 时重置)</summary>
public
int
TotalPromptTokens
{
get
;
private
set
;
}
public
int
TotalPromptTokens
{
get
;
private
set
;
}
...
@@ -235,7 +238,7 @@ namespace Tools.MyAgent.Agent
...
@@ -235,7 +238,7 @@ namespace Tools.MyAgent.Agent
/// </summary>
/// </summary>
public
async
Task
RunAsync
(
IReadOnlyList
<
ChatMessage
>
history
,
CancellationToken
ct
)
public
async
Task
RunAsync
(
IReadOnlyList
<
ChatMessage
>
history
,
CancellationToken
ct
)
{
{
if
(
IsRunning
)
return
;
if
(
Is
Disposed
||
Is
Running
)
return
;
IsRunning
=
true
;
IsRunning
=
true
;
_cts
=
CancellationTokenSource
.
CreateLinkedTokenSource
(
ct
);
_cts
=
CancellationTokenSource
.
CreateLinkedTokenSource
(
ct
);
OnStatusChanged
?.
Invoke
();
OnStatusChanged
?.
Invoke
();
...
@@ -325,16 +328,21 @@ namespace Tools.MyAgent.Agent
...
@@ -325,16 +328,21 @@ namespace Tools.MyAgent.Agent
}
}
}
}
if
(!
IsDisposed
)
{
OnComplete
?.
Invoke
();
OnComplete
?.
Invoke
();
if
(
_toolTrace
.
Count
>
0
)
if
(
_toolTrace
.
Count
>
0
)
OnToolTrace
?.
Invoke
(
_toolTrace
.
ToArray
());
OnToolTrace
?.
Invoke
(
_toolTrace
.
ToArray
());
}
}
}
catch
(
OperationCanceledException
)
catch
(
OperationCanceledException
)
{
{
if
(!
IsDisposed
)
OnError
?.
Invoke
(
"已取消。"
);
OnError
?.
Invoke
(
"已取消。"
);
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
if
(!
IsDisposed
)
OnError
?.
Invoke
(
DescribeError
(
ex
));
OnError
?.
Invoke
(
DescribeError
(
ex
));
}
}
finally
finally
...
@@ -342,6 +350,7 @@ namespace Tools.MyAgent.Agent
...
@@ -342,6 +350,7 @@ namespace Tools.MyAgent.Agent
IsRunning
=
false
;
IsRunning
=
false
;
try
{
_cts
?.
Dispose
();
}
catch
{
}
try
{
_cts
?.
Dispose
();
}
catch
{
}
_cts
=
null
;
_cts
=
null
;
if
(!
IsDisposed
)
OnStatusChanged
?.
Invoke
();
OnStatusChanged
?.
Invoke
();
}
}
}
}
...
@@ -1072,6 +1081,7 @@ namespace Tools.MyAgent.Agent
...
@@ -1072,6 +1081,7 @@ namespace Tools.MyAgent.Agent
public
void
Dispose
()
public
void
Dispose
()
{
{
if
(
Interlocked
.
Exchange
(
ref
_disposeStarted
,
1
)
!=
0
)
return
;
Cancel
();
Cancel
();
try
{
_client
.
Dispose
();
}
catch
{
}
try
{
_client
.
Dispose
();
}
catch
{
}
try
{
_backupClient
?.
Dispose
();
}
catch
{
}
try
{
_backupClient
?.
Dispose
();
}
catch
{
}
...
...
Tools.Agent/Kb/KbIndex.cs
View file @
e6773c00
...
@@ -77,26 +77,43 @@ namespace Tools.MyAgent.Kb
...
@@ -77,26 +77,43 @@ namespace Tools.MyAgent.Kb
_emb
=
emb
;
_emb
=
emb
;
}
}
/// <summary>启动
:打开/建库 + 后台构建(幂等)
</summary>
/// <summary>启动
索引:只在后台打开 SQLite 并开始构建,调用方不会被建库 IO 阻塞。
</summary>
public
void
Start
()
public
void
Start
()
{
{
lock
(
_lock
)
lock
(
_lock
)
{
{
if
(
_started
)
return
;
if
(
_started
)
return
;
_started
=
true
;
_started
=
true
;
}
Task
.
Run
(
StartCore
);
}
private
void
StartCore
()
{
try
try
{
{
_cts
.
Token
.
ThrowIfCancellationRequested
();
lock
(
_lock
)
{
if
(
_cts
.
IsCancellationRequested
)
return
;
OpenDb
();
OpenDb
();
}
}
if
(
_cts
.
IsCancellationRequested
||
_conn
==
null
)
return
;
_sweepTimer
=
new
Timer
(
_
=>
Invalidate
(),
null
,
SweepInterval
,
SweepInterval
);
Invalidate
();
}
catch
(
OperationCanceledException
)
{
// 关闭窗口时取消尚未完成的后台初始化。
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
lock
(
_lock
)
{
{
_conn
=
null
;
_conn
=
null
;
OnStatus
?.
Invoke
(
"索引不可用:"
+
ex
.
Message
);
return
;
}
}
OnStatus
?.
Invoke
(
"索引不可用:"
+
ex
.
Message
);
}
}
_sweepTimer
=
new
Timer
(
_
=>
Invalidate
(),
null
,
SweepInterval
,
SweepInterval
);
Invalidate
();
}
}
/// <summary>标记变更,调度后台增量重建(带去抖;构建中则完成后自动续跑)</summary>
/// <summary>标记变更,调度后台增量重建(带去抖;构建中则完成后自动续跑)</summary>
...
...
Tools.Agent/Kb/KbService.cs
View file @
e6773c00
...
@@ -410,6 +410,19 @@ namespace Tools.MyAgent.Kb
...
@@ -410,6 +410,19 @@ namespace Tools.MyAgent.Kb
/// <summary>搜索范围是否有效(目录存在)</summary>
/// <summary>搜索范围是否有效(目录存在)</summary>
public
bool
IsSearchValid
=>
!
string
.
IsNullOrEmpty
(
SearchRoot
)
&&
Directory
.
Exists
(
SearchRoot
);
public
bool
IsSearchValid
=>
!
string
.
IsNullOrEmpty
(
SearchRoot
)
&&
Directory
.
Exists
(
SearchRoot
);
private
const
int
AutoIndexFileThreshold
=
2000
;
/// <summary>
/// 仅为小型知识库自动启动索引。文件数未知或超过阈值时保持未建索引,避免打开窗口触发大规模 SQLite 建库。
/// </summary>
public
void
EnsureIndexStartedIfSmall
(
int
maxFiles
=
AutoIndexFileThreshold
)
{
if
(
maxFiles
<
1
)
return
;
int
count
=
FileCount
;
if
(
count
<
0
||
count
>
maxFiles
)
return
;
EnsureIndexStarted
();
}
/// <summary>
/// <summary>
/// 启动后台检索索引(幂等):索引目录固定为输出目录下 index 文件夹,与所选知识库无关。
/// 启动后台检索索引(幂等):索引目录固定为输出目录下 index 文件夹,与所选知识库无关。
/// <para>索引覆盖整个知识库根目录;搜索范围限定时在查询侧过滤,索引本身始终按根目录构建。</para>
/// <para>索引覆盖整个知识库根目录;搜索范围限定时在查询侧过滤,索引本身始终按根目录构建。</para>
...
...
Tools.Agent/Support/MarkdownRenderer.cs
View file @
e6773c00
...
@@ -145,8 +145,7 @@ namespace Tools.MyAgent
...
@@ -145,8 +145,7 @@ namespace Tools.MyAgent
{
{
sb
.
Append
(
@"\cf"
);
sb
.
Append
(
ClThinking
);
sb
.
Append
(
@"\cf"
);
sb
.
Append
(
ClThinking
);
sb
.
Append
(
@"\i\fs18 "
);
sb
.
Append
(
@"\i\fs18 "
);
sb
.
Append
(
EscapeChar
(
'▸'
));
sb
.
Append
(
"> Thinking..."
);
sb
.
Append
(
" Thinking..."
);
sb
.
Append
(
@"\par "
);
sb
.
Append
(
@"\par "
);
sb
.
Append
(
Escape
(
msg
.
ThinkingContent
));
sb
.
Append
(
Escape
(
msg
.
ThinkingContent
));
sb
.
Append
(
@"\i0\fs20\cf"
);
sb
.
Append
(
ClText
);
sb
.
Append
(
@"\i0\fs20\cf"
);
sb
.
Append
(
ClText
);
...
...
Tools.Agent/wMyAgent.Designer.cs
View file @
e6773c00
...
@@ -208,7 +208,7 @@ namespace Tools.MyAgent
...
@@ -208,7 +208,7 @@ namespace Tools.MyAgent
//
//
this
.
AutoScaleDimensions
=
new
System
.
Drawing
.
SizeF
(
7F
,
17F
);
this
.
AutoScaleDimensions
=
new
System
.
Drawing
.
SizeF
(
7F
,
17F
);
this
.
AutoScaleMode
=
System
.
Windows
.
Forms
.
AutoScaleMode
.
Font
;
this
.
AutoScaleMode
=
System
.
Windows
.
Forms
.
AutoScaleMode
.
Font
;
this
.
ClientSize
=
new
System
.
Drawing
.
Size
(
817
,
638
);
this
.
ClientSize
=
new
System
.
Drawing
.
Size
(
1000
,
600
);
this
.
Controls
.
Add
(
this
.
panelChat
);
this
.
Controls
.
Add
(
this
.
panelChat
);
this
.
Controls
.
Add
(
this
.
panelToolbar
);
this
.
Controls
.
Add
(
this
.
panelToolbar
);
this
.
Controls
.
Add
(
this
.
panelBottom
);
this
.
Controls
.
Add
(
this
.
panelBottom
);
...
@@ -216,6 +216,7 @@ namespace Tools.MyAgent
...
@@ -216,6 +216,7 @@ namespace Tools.MyAgent
this
.
KeyPreview
=
true
;
this
.
KeyPreview
=
true
;
this
.
Margin
=
new
System
.
Windows
.
Forms
.
Padding
(
5
,
6
,
5
,
6
);
this
.
Margin
=
new
System
.
Windows
.
Forms
.
Padding
(
5
,
6
,
5
,
6
);
this
.
Name
=
"wMyAgent"
;
this
.
Name
=
"wMyAgent"
;
this
.
StartPosition
=
System
.
Windows
.
Forms
.
FormStartPosition
.
CenterScreen
;
this
.
Text
=
"智能助手"
;
this
.
Text
=
"智能助手"
;
this
.
panelBottom
.
ResumeLayout
(
false
);
this
.
panelBottom
.
ResumeLayout
(
false
);
this
.
panelBottom
.
PerformLayout
();
this
.
panelBottom
.
PerformLayout
();
...
...
Tools.Agent/wMyAgent.cs
View file @
e6773c00
...
@@ -29,6 +29,8 @@ namespace Tools.MyAgent
...
@@ -29,6 +29,8 @@ namespace Tools.MyAgent
private
RichTextBox
_rtfFallback
;
private
RichTextBox
_rtfFallback
;
private
AgentConfig
_config
=
new
AgentConfig
();
private
AgentConfig
_config
=
new
AgentConfig
();
private
CancellationTokenSource
_sendCts
;
private
CancellationTokenSource
_sendCts
;
private
volatile
bool
_isClosing
;
private
int
_disposeStarted
;
private
string
_historyDir
;
private
string
_historyDir
;
private
bool
_sessionSaved
;
private
bool
_sessionSaved
;
/// <summary>v2.3 待发送附图列表:粘贴截图后暂存,随下一轮提问发送(启用图片提问时;最多 5 张)</summary>
/// <summary>v2.3 待发送附图列表:粘贴截图后暂存,随下一轮提问发送(启用图片提问时;最多 5 张)</summary>
...
@@ -143,6 +145,8 @@ namespace Tools.MyAgent
...
@@ -143,6 +145,8 @@ namespace Tools.MyAgent
/// </summary>
/// </summary>
partial
void
OnFormDisposing
()
partial
void
OnFormDisposing
()
{
{
if
(
Interlocked
.
Exchange
(
ref
_disposeStarted
,
1
)
!=
0
)
return
;
_isClosing
=
true
;
SaveUnsavedSession
();
SaveUnsavedSession
();
try
{
_cmdNameFont
?.
Dispose
();
}
catch
{
}
try
{
_cmdNameFont
?.
Dispose
();
}
catch
{
}
try
{
_cmdDescFont
?.
Dispose
();
}
catch
{
}
try
{
_cmdDescFont
?.
Dispose
();
}
catch
{
}
...
@@ -339,7 +343,8 @@ namespace Tools.MyAgent
...
@@ -339,7 +343,8 @@ namespace Tools.MyAgent
_kb
.
SetOutputRoot
(
DefaultKbRoot
);
_kb
.
SetOutputRoot
(
DefaultKbRoot
);
// 评估#1:存储自动清理(日志 30 天 / 中间产物 7~30 天 / 正式产出 90 天;后台执行不阻塞启动)
// 评估#1:存储自动清理(日志 30 天 / 中间产物 7~30 天 / 正式产出 90 天;后台执行不阻塞启动)
try
{
System
.
Threading
.
Tasks
.
Task
.
Run
(()
=>
StorageCleanup
.
Run
(
DefaultKbRoot
));
}
catch
{
}
try
{
System
.
Threading
.
Tasks
.
Task
.
Run
(()
=>
StorageCleanup
.
Run
(
DefaultKbRoot
));
}
catch
{
}
_kb
.
EnsureIndexStarted
();
// 后台构建知识库检索索引(含 docx/xlsx 内容),检索提速
// 大知识库不在窗口初始化阶段创建 SQLite 索引,避免打开业务引用时被 IO/SQLite 锁住。
// 小知识库由统计完成事件按需后台启动,实际搜索也会触发惰性启动。
RefreshScopeCombo
();
// 先归一化搜索范围(命中下拉项/归一化为全部),再应用
RefreshScopeCombo
();
// 先归一化搜索范围(命中下拉项/归一化为全部),再应用
_kb
.
SetScope
(
_config
.
SearchScope
);
_kb
.
SetScope
(
_config
.
SearchScope
);
_historyDir
=
HistoryManager
.
EnsureHistoryDir
(
WorkingDirForHistory
());
_historyDir
=
HistoryManager
.
EnsureHistoryDir
(
WorkingDirForHistory
());
...
@@ -353,6 +358,28 @@ namespace Tools.MyAgent
...
@@ -353,6 +358,28 @@ namespace Tools.MyAgent
// ── 事件绑定 ─────────────────────────────────────
// ── 事件绑定 ─────────────────────────────────────
private
void
PostToUi
(
Action
action
)
{
if
(
action
==
null
||
_isClosing
||
IsDisposed
||
Disposing
||
!
IsHandleCreated
)
return
;
if
(!
InvokeRequired
)
{
if
(
_isClosing
||
IsDisposed
||
Disposing
)
return
;
action
();
return
;
}
try
{
BeginInvoke
((
MethodInvoker
)
delegate
{
if
(
_isClosing
||
IsDisposed
||
Disposing
)
return
;
action
();
});
}
catch
(
ObjectDisposedException
)
{
}
catch
(
InvalidOperationException
)
{
}
}
private
void
WireEvents
()
private
void
WireEvents
()
{
{
lblNewChat
.
LinkClicked
+=
(
s
,
e
)
=>
NewChat
();
lblNewChat
.
LinkClicked
+=
(
s
,
e
)
=>
NewChat
();
...
@@ -371,25 +398,30 @@ namespace Tools.MyAgent
...
@@ -371,25 +398,30 @@ namespace Tools.MyAgent
// RichBox 模式的点击/悬停交互(路径点击、details 折叠切换)
// RichBox 模式的点击/悬停交互(路径点击、details 折叠切换)
WireRtfEvents
();
WireRtfEvents
();
_engine
.
OnTextDelta
+=
t
=>
this
.
AsyncUI
(()
=>
OnAssistantTextDelta
(
t
));
_engine
.
OnTextDelta
+=
t
=>
PostToUi
(()
=>
OnAssistantTextDelta
(
t
));
_engine
.
OnThinkingDelta
+=
t
=>
this
.
AsyncUI
(()
=>
OnAssistantThinkingDelta
(
t
));
_engine
.
OnThinkingDelta
+=
t
=>
PostToUi
(()
=>
OnAssistantThinkingDelta
(
t
));
_engine
.
OnToolStatus
+=
s
=>
this
.
AsyncUI
(()
=>
OnToolStatusChanged
(
s
));
_engine
.
OnToolStatus
+=
s
=>
PostToUi
(()
=>
OnToolStatusChanged
(
s
));
_engine
.
OnFileProduced
+=
f
=>
this
.
AsyncUI
(()
=>
OnFileProduced
(
f
));
_engine
.
OnFileProduced
+=
f
=>
PostToUi
(()
=>
OnFileProduced
(
f
));
_engine
.
OnComplete
+=
()
=>
this
.
AsyncUI
(
OnAssistantComplete
);
_engine
.
OnComplete
+=
()
=>
PostToUi
(
OnAssistantComplete
);
_engine
.
OnToolTrace
+=
trace
=>
this
.
AsyncUI
(()
=>
OnToolTrace
(
trace
));
_engine
.
OnToolTrace
+=
trace
=>
PostToUi
(()
=>
OnToolTrace
(
trace
));
_engine
.
OnError
+=
m
=>
this
.
AsyncUI
(()
=>
OnEngineError
(
m
));
_engine
.
OnError
+=
m
=>
PostToUi
(()
=>
OnEngineError
(
m
));
_engine
.
OnStatusChanged
+=
()
=>
this
.
AsyncUI
(
UpdateStatusIndicator
);
_engine
.
OnStatusChanged
+=
()
=>
PostToUi
(
UpdateStatusIndicator
);
// 知识库文件数后台统计完成 → 回刷状态栏与欢迎语(窗体句柄未就绪时跳过,Load 时会重新触发)
// 知识库文件数后台统计完成 → 回刷状态栏与欢迎语(窗体句柄未就绪时跳过,Load 时会重新触发)
_kb
.
StatsRefreshed
+=
()
=>
this
.
AsyncUI
(()
=>
_kb
.
StatsRefreshed
+=
()
=>
{
_kb
.
EnsureIndexStartedIfSmall
();
PostToUi
(()
=>
{
{
if
(!
IsHandleCreated
)
return
;
if
(!
IsHandleCreated
)
return
;
UpdateStatusIndicator
();
UpdateStatusIndicator
();
RefreshChat
();
RefreshChat
();
});
});
};
FormClosing
+=
(
s
,
e
)
=>
FormClosing
+=
(
s
,
e
)
=>
{
{
SaveCurrentSession
();
_isClosing
=
true
;
SaveUnsavedSession
();
};
};
Load
+=
(
s
,
e
)
=>
Load
+=
(
s
,
e
)
=>
...
@@ -686,6 +718,7 @@ namespace Tools.MyAgent
...
@@ -686,6 +718,7 @@ namespace Tools.MyAgent
private
void
OnEngineError
(
string
message
)
private
void
OnEngineError
(
string
message
)
{
{
if
(
_isClosing
||
IsDisposed
||
Disposing
)
return
;
var
last
=
LastAssistantMessage
();
var
last
=
LastAssistantMessage
();
if
(
last
!=
null
&&
last
.
IsStreaming
)
if
(
last
!=
null
&&
last
.
IsStreaming
)
{
{
...
@@ -2277,16 +2310,26 @@ namespace Tools.MyAgent
...
@@ -2277,16 +2310,26 @@ namespace Tools.MyAgent
}
}
/// <summary>/kb <关键词>:直接检索知识库并展示结果</summary>
/// <summary>/kb <关键词>:直接检索知识库并展示结果</summary>
private
void
RunKbQuickSearch
(
string
keyword
)
private
async
void
RunKbQuickSearch
(
string
keyword
)
{
var
sw
=
System
.
Diagnostics
.
Stopwatch
.
StartNew
();
string
result
;
try
{
result
=
await
Task
.
Run
(()
=>
{
{
if
(!
_kb
.
ConnectIfNeeded
()
||
!
_kb
.
IsSearchValid
)
if
(!
_kb
.
ConnectIfNeeded
()
||
!
_kb
.
IsSearchValid
)
return
"知识库不可用(目录不存在/共享未连接),请先选择知识库。"
;
// 首次实际检索才惰性启动索引;大知识库的无索引回退扫描也不占用 UI 线程。
_kb
.
EnsureIndexStarted
();
return
_kb
.
Grep
(
keyword
,
null
,
10
);
});
}
catch
(
Exception
ex
)
{
{
AddSystemMessage
(
"知识库不可用(目录不存在/共享未连接),请先选择知识库。"
);
result
=
"知识库检索失败:"
+
ex
.
Message
;
return
;
}
}
_kb
.
EnsureIndexStarted
();
var
sw
=
System
.
Diagnostics
.
Stopwatch
.
StartNew
();
string
result
=
_kb
.
Grep
(
keyword
,
null
,
10
);
sw
.
Stop
();
sw
.
Stop
();
AddSystemMessage
(
$"**知识库检索:
{
keyword
}
**(
{
sw
.
ElapsedMilliseconds
}
ms)\n\n
{
result
}
"
);
AddSystemMessage
(
$"**知识库检索:
{
keyword
}
**(
{
sw
.
ElapsedMilliseconds
}
ms)\n\n
{
result
}
"
);
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment