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
23830cb4
Commit
23830cb4
authored
Aug 20, 2026
by
段廷瑞
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 智能体交互优化:图片,命令行等
parent
c64815c5
Changes
10
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
793 additions
and
86 deletions
+793
-86
AgentEngine.cs
Tools/MyAgent/Agent/AgentEngine.cs
+93
-6
PromptBuilder.cs
Tools/MyAgent/Agent/PromptBuilder.cs
+0
-10
AgentConfig.cs
Tools/MyAgent/Config/AgentConfig.cs
+2
-2
AgentSettingsForm.Designer.cs
Tools/MyAgent/Forms/AgentSettingsForm.Designer.cs
+13
-11
AgentSettingsForm.cs
Tools/MyAgent/Forms/AgentSettingsForm.cs
+2
-2
wBitmapView.cs
Tools/MyAgent/Forms/wBitmapView.cs
+111
-0
ChatMessage.cs
Tools/MyAgent/Support/ChatMessage.cs
+7
-0
NoCaretRichTextBox.cs
Tools/MyAgent/Support/NoCaretRichTextBox.cs
+31
-0
wMyAgent.cs
Tools/MyAgent/wMyAgent.cs
+528
-55
Tools.csproj
Tools/Tools.csproj
+6
-0
No files found.
Tools/MyAgent/Agent/AgentEngine.cs
View file @
23830cb4
...
@@ -85,6 +85,9 @@ namespace Tools.MyAgent.Agent
...
@@ -85,6 +85,9 @@ namespace Tools.MyAgent.Agent
/// <summary>是否向模型下发可选/高级工具(默认只下发核心工具)</summary>
/// <summary>是否向模型下发可选/高级工具(默认只下发核心工具)</summary>
private
bool
_includeOptionalTools
;
private
bool
_includeOptionalTools
;
/// <summary>v2.2 多模态:是否启用图片提问(附图以 image_url 内容块发送)</summary>
private
bool
_imageInputEnabled
;
/// <summary>单次回答总时长兜底</summary>
/// <summary>单次回答总时长兜底</summary>
private
static
readonly
TimeSpan
MaxRunDuration
=
TimeSpan
.
FromMinutes
(
10
);
private
static
readonly
TimeSpan
MaxRunDuration
=
TimeSpan
.
FromMinutes
(
10
);
...
@@ -120,7 +123,7 @@ namespace Tools.MyAgent.Agent
...
@@ -120,7 +123,7 @@ namespace Tools.MyAgent.Agent
_contextTokens
=
cfg
.
MaxContextTokens
>
0
?
cfg
.
MaxContextTokens
:
32000
;
_contextTokens
=
cfg
.
MaxContextTokens
>
0
?
cfg
.
MaxContextTokens
:
32000
;
_prompts
.
CustomLayer
=
cfg
.
CustomSystemPrompt
??
""
;
_prompts
.
CustomLayer
=
cfg
.
CustomSystemPrompt
??
""
;
_prompts
.
SystemPromptEnabled
=
cfg
.
SystemPromptEnabled
;
_prompts
.
SystemPromptEnabled
=
cfg
.
SystemPromptEnabled
;
_
prompts
.
AutoMemory
=
cfg
.
EnableAutoMemory
;
// E3 问答沉淀提示(只提示确认,绝不静默写入)
_
imageInputEnabled
=
cfg
.
EnableImageInput
;
// v2.2 多模态:图片提问开关
// #31 故障转移配置
// #31 故障转移配置
_failover
.
Configure
(
cfg
.
EnableFailover
,
cfg
.
BackupBaseUrl
,
cfg
.
BackupModel
,
cfg
.
BackupApiKey
);
_failover
.
Configure
(
cfg
.
EnableFailover
,
cfg
.
BackupBaseUrl
,
cfg
.
BackupModel
,
cfg
.
BackupApiKey
);
...
@@ -445,7 +448,7 @@ namespace Tools.MyAgent.Agent
...
@@ -445,7 +448,7 @@ namespace Tools.MyAgent.Agent
{
{
if
(
m
.
Role
==
"user"
)
if
(
m
.
Role
==
"user"
)
{
{
list
.
Add
(
new
JObject
{
[
"role"
]
=
"user"
,
[
"content"
]
=
m
.
Content
??
""
});
list
.
Add
(
new
JObject
{
[
"role"
]
=
"user"
,
[
"content"
]
=
BuildUserContent
(
m
)
});
}
}
else
if
(
m
.
Role
==
"assistant"
&&
!
string
.
IsNullOrEmpty
(
m
.
Content
))
else
if
(
m
.
Role
==
"assistant"
&&
!
string
.
IsNullOrEmpty
(
m
.
Content
))
{
{
...
@@ -470,6 +473,80 @@ namespace Tools.MyAgent.Agent
...
@@ -470,6 +473,80 @@ namespace Tools.MyAgent.Agent
return
list
;
return
list
;
}
}
/// <summary>
/// 组装用户消息 content:启用图片提问(v2.2)且消息带有效附图时,
/// 返回多模态内容块数组(text + 多个 image_url,base64 data URL);否则返回纯文本。
/// <para>v2.3:支持多图(Images 列表优先,兼容旧会话单图 ImagePath);全部图片无效时降级纯文本。</para>
/// </summary>
private
JToken
BuildUserContent
(
ChatMessage
m
)
{
string
text
=
m
.
Content
??
""
;
if
(!
_imageInputEnabled
)
return
text
;
var
images
=
new
List
<
string
>();
if
(
m
.
Images
!=
null
)
foreach
(
var
p
in
m
.
Images
)
if
(!
string
.
IsNullOrEmpty
(
p
)
&&
!
images
.
Contains
(
p
))
images
.
Add
(
p
);
if
(!
string
.
IsNullOrEmpty
(
m
.
ImagePath
)
&&
!
images
.
Contains
(
m
.
ImagePath
))
images
.
Add
(
m
.
ImagePath
);
if
(
images
.
Count
==
0
)
return
text
;
var
arr
=
new
JArray
();
arr
.
Add
(
new
JObject
{
[
"type"
]
=
"text"
,
[
"text"
]
=
text
});
int
added
=
0
;
foreach
(
var
p
in
images
)
{
try
{
string
dataUrl
=
ImageToDataUrl
(
p
);
if
(
dataUrl
!=
null
)
{
arr
.
Add
(
new
JObject
{
[
"type"
]
=
"image_url"
,
[
"image_url"
]
=
new
JObject
{
[
"url"
]
=
dataUrl
}
});
added
++;
}
}
catch
{
}
}
return
added
>
0
?
(
JToken
)
arr
:
(
JToken
)
text
;
// 全部图片不可读/超限 → 降级纯文本
}
/// <summary>本地图片 → base64 data URL(原图上限 MaxImageBytes=4MB,避免超出 API 限制);失败返回 null</summary>
private
static
string
ImageToDataUrl
(
string
path
)
{
const
long
MaxImageBytes
=
4
*
1024
*
1024
;
if
(
string
.
IsNullOrEmpty
(
path
)
||
!
System
.
IO
.
File
.
Exists
(
path
))
return
null
;
var
fi
=
new
System
.
IO
.
FileInfo
(
path
);
if
(
fi
.
Length
<=
0
||
fi
.
Length
>
MaxImageBytes
)
return
null
;
string
ext
=
System
.
IO
.
Path
.
GetExtension
(
path
).
ToLowerInvariant
();
string
mime
=
ext
==
".jpg"
||
ext
==
".jpeg"
?
"image/jpeg"
:
ext
==
".gif"
?
"image/gif"
:
ext
==
".bmp"
?
"image/bmp"
:
"image/png"
;
byte
[]
bytes
=
System
.
IO
.
File
.
ReadAllBytes
(
path
);
return
"data:"
+
mime
+
";base64,"
+
Convert
.
ToBase64String
(
bytes
);
}
/// <summary>
/// 安全读取消息 content 文本(v2.2 多模态兼容):
/// content 为数组(text + image_url 内容块)时取 text 块文本(忽略图片);
/// 为字符串直接返回;其他返回 null。避免 JArray.Value<string>() 抛 InvalidCastException。
/// </summary>
private
static
string
ContentText
(
JToken
content
)
{
if
(
content
==
null
)
return
null
;
if
(
content
.
Type
==
JTokenType
.
Array
)
{
foreach
(
var
item
in
content
)
{
if
(
string
.
Equals
(
item
[
"type"
]?.
Value
<
string
>(),
"text"
,
StringComparison
.
OrdinalIgnoreCase
))
return
item
[
"text"
]?.
Value
<
string
>()
??
""
;
}
return
""
;
}
return
content
.
Value
<
string
>();
}
/// <summary>从消息列表头部(跳过 system)裁剪最早 user/assistant 对,直到预算内或只剩 3 条</summary>
/// <summary>从消息列表头部(跳过 system)裁剪最早 user/assistant 对,直到预算内或只剩 3 条</summary>
private
static
int
DropOldestUntilUnder
(
JArray
list
,
int
budget
)
private
static
int
DropOldestUntilUnder
(
JArray
list
,
int
budget
)
{
{
...
@@ -510,9 +587,19 @@ namespace Tools.MyAgent.Agent
...
@@ -510,9 +587,19 @@ namespace Tools.MyAgent.Agent
int
take
=
Math
.
Min
(
6
,
list
.
Count
-
1
);
int
take
=
Math
.
Min
(
6
,
list
.
Count
-
1
);
if
(
take
<
2
)
return
0
;
if
(
take
<
2
)
return
0
;
var
earliest
=
new
JArray
();
// 拼接最早消息文本(多模态消息仅取文本块,忽略图片,避免 data URL 大图进入压缩 prompt)
var
sb
=
new
StringBuilder
();
for
(
int
i
=
1
;
i
<=
take
;
i
++)
for
(
int
i
=
1
;
i
<=
take
;
i
++)
earliest
.
Add
(
list
[
i
]);
{
var
m
=
list
[
i
];
string
role
=
m
[
"role"
]?.
Value
<
string
>()
??
"user"
;
string
text
=
ContentText
(
m
[
"content"
]);
if
(
string
.
IsNullOrEmpty
(
text
))
continue
;
if
(
text
.
Length
>
400
)
text
=
text
.
Substring
(
0
,
400
)
+
"…"
;
sb
.
Append
(
role
).
Append
(
": "
).
Append
(
text
).
Append
(
'\n'
);
}
string
earliestText
=
sb
.
ToString
();
if
(
earliestText
.
Length
==
0
)
return
0
;
string
summary
;
string
summary
;
try
try
...
@@ -520,7 +607,7 @@ namespace Tools.MyAgent.Agent
...
@@ -520,7 +607,7 @@ namespace Tools.MyAgent.Agent
var
prompt
=
new
JArray
var
prompt
=
new
JArray
{
{
new
JObject
{
[
"role"
]
=
"system"
,
[
"content"
]
=
HistorySummaryPrompt
},
new
JObject
{
[
"role"
]
=
"system"
,
[
"content"
]
=
HistorySummaryPrompt
},
new
JObject
{
[
"role"
]
=
"user"
,
[
"content"
]
=
earliest
.
ToString
(
Newtonsoft
.
Json
.
Formatting
.
None
)
}
new
JObject
{
[
"role"
]
=
"user"
,
[
"content"
]
=
earliest
Text
}
};
};
summary
=
await
_client
.
CompleteAsync
(
prompt
,
512
,
ct
).
ConfigureAwait
(
false
);
summary
=
await
_client
.
CompleteAsync
(
prompt
,
512
,
ct
).
ConfigureAwait
(
false
);
}
}
...
@@ -554,7 +641,7 @@ namespace Tools.MyAgent.Agent
...
@@ -554,7 +641,7 @@ namespace Tools.MyAgent.Agent
foreach
(
var
m
in
messages
)
foreach
(
var
m
in
messages
)
{
{
total
+=
4
;
// role / 分隔符开销
total
+=
4
;
// role / 分隔符开销
var
content
=
m
[
"content"
]?.
Value
<
string
>(
);
var
content
=
ContentText
(
m
[
"content"
]
);
if
(!
string
.
IsNullOrEmpty
(
content
))
total
+=
EstimateTokens
(
content
);
if
(!
string
.
IsNullOrEmpty
(
content
))
total
+=
EstimateTokens
(
content
);
var
tc
=
m
[
"tool_calls"
]
as
JArray
;
var
tc
=
m
[
"tool_calls"
]
as
JArray
;
if
(
tc
!=
null
)
total
+=
8
*
tc
.
Count
;
if
(
tc
!=
null
)
total
+=
8
*
tc
.
Count
;
...
...
Tools/MyAgent/Agent/PromptBuilder.cs
View file @
23830cb4
...
@@ -18,9 +18,6 @@ namespace Tools.MyAgent.Agent
...
@@ -18,9 +18,6 @@ namespace Tools.MyAgent.Agent
/// <summary>系统提示词启用开关(由设置注入;仅启用后才注入 CustomLayer)</summary>
/// <summary>系统提示词启用开关(由设置注入;仅启用后才注入 CustomLayer)</summary>
public
bool
SystemPromptEnabled
{
get
;
set
;
}
public
bool
SystemPromptEnabled
{
get
;
set
;
}
/// <summary>E3 问答沉淀提示:开启后在工具规则尾部追加「可询问用户是否沉淀结论」(绝不静默写入)</summary>
public
bool
AutoMemory
{
get
;
set
;
}
public
string
BuildSystemMessage
(
KbService
kb
)
public
string
BuildSystemMessage
(
KbService
kb
)
{
{
// B2 提示词外置:静态层优先读 输出目录/prompts/*.md 模板(用户可定制),
// B2 提示词外置:静态层优先读 输出目录/prompts/*.md 模板(用户可定制),
...
@@ -42,13 +39,6 @@ namespace Tools.MyAgent.Agent
...
@@ -42,13 +39,6 @@ namespace Tools.MyAgent.Agent
sb
.
AppendLine
(
LoadLayer
(
promptsDir
,
"security.md"
,
SecurityLayer
));
sb
.
AppendLine
(
LoadLayer
(
promptsDir
,
"security.md"
,
SecurityLayer
));
sb
.
AppendLine
();
sb
.
AppendLine
();
sb
.
AppendLine
(
LoadLayer
(
promptsDir
,
"tool_rules.md"
,
ToolRules
));
sb
.
AppendLine
(
LoadLayer
(
promptsDir
,
"tool_rules.md"
,
ToolRules
));
if
(
AutoMemory
)
{
sb
.
AppendLine
();
sb
.
AppendLine
(
"【知识沉淀提示(E3)】"
);
sb
.
AppendLine
(
"- 当你产出了有价值的结论(问题解答、流程梳理、新知识)时,可在回答末尾询问用户是否沉淀到知识库(kb_write_kb)。"
);
sb
.
AppendLine
(
"- 未获得用户明确同意前,绝不允许写入知识库;用户拒绝则不再追问。"
);
}
return
sb
.
ToString
();
return
sb
.
ToString
();
}
}
...
...
Tools/MyAgent/Config/AgentConfig.cs
View file @
23830cb4
...
@@ -90,7 +90,7 @@
...
@@ -90,7 +90,7 @@
/// <summary>是否启用可选/高级工具(db_query、module_patch、img_search、/workflow)</summary>
/// <summary>是否启用可选/高级工具(db_query、module_patch、img_search、/workflow)</summary>
public
bool
EnableOptionalTools
{
get
;
set
;
}
=
false
;
public
bool
EnableOptionalTools
{
get
;
set
;
}
=
false
;
/// <summary>
回答结束后是否提示把有价值结论沉淀到知识库(只提示确认,绝不静默写入;默认关闭)
</summary>
/// <summary>
是否启用图片提问(多模态,v2.2):启用后粘贴截图随提问以 image_url 发送,需视觉模型支持(如 DeepSeek-VL / qwen-vl);默认启用
</summary>
public
bool
Enable
AutoMemory
{
get
;
set
;
}
=
fals
e
;
public
bool
Enable
ImageInput
{
get
;
set
;
}
=
tru
e
;
}
}
}
}
Tools/MyAgent/Forms/AgentSettingsForm.Designer.cs
View file @
23830cb4
...
@@ -82,7 +82,7 @@
...
@@ -82,7 +82,7 @@
this
.
tabOther
=
new
System
.
Windows
.
Forms
.
TabPage
();
this
.
tabOther
=
new
System
.
Windows
.
Forms
.
TabPage
();
this
.
grpOpt
=
new
System
.
Windows
.
Forms
.
GroupBox
();
this
.
grpOpt
=
new
System
.
Windows
.
Forms
.
GroupBox
();
this
.
chkOpt
=
new
System
.
Windows
.
Forms
.
CheckBox
();
this
.
chkOpt
=
new
System
.
Windows
.
Forms
.
CheckBox
();
this
.
chk
AutoMemory
=
new
System
.
Windows
.
Forms
.
CheckBox
();
this
.
chk
ImageInput
=
new
System
.
Windows
.
Forms
.
CheckBox
();
this
.
grpFail
=
new
System
.
Windows
.
Forms
.
GroupBox
();
this
.
grpFail
=
new
System
.
Windows
.
Forms
.
GroupBox
();
this
.
chkFail
=
new
System
.
Windows
.
Forms
.
CheckBox
();
this
.
chkFail
=
new
System
.
Windows
.
Forms
.
CheckBox
();
this
.
lblBUrl
=
new
System
.
Windows
.
Forms
.
Label
();
this
.
lblBUrl
=
new
System
.
Windows
.
Forms
.
Label
();
...
@@ -717,7 +717,7 @@
...
@@ -717,7 +717,7 @@
// grpOpt
// grpOpt
//
//
this
.
grpOpt
.
Controls
.
Add
(
this
.
chkOpt
);
this
.
grpOpt
.
Controls
.
Add
(
this
.
chkOpt
);
this
.
grpOpt
.
Controls
.
Add
(
this
.
chk
AutoMemory
);
this
.
grpOpt
.
Controls
.
Add
(
this
.
chk
ImageInput
);
this
.
grpOpt
.
Location
=
new
System
.
Drawing
.
Point
(
8
,
12
);
this
.
grpOpt
.
Location
=
new
System
.
Drawing
.
Point
(
8
,
12
);
this
.
grpOpt
.
Name
=
"grpOpt"
;
this
.
grpOpt
.
Name
=
"grpOpt"
;
this
.
grpOpt
.
Size
=
new
System
.
Drawing
.
Size
(
596
,
92
);
this
.
grpOpt
.
Size
=
new
System
.
Drawing
.
Size
(
596
,
92
);
...
@@ -735,15 +735,17 @@
...
@@ -735,15 +735,17 @@
this
.
chkOpt
.
Text
=
"启用可选/高级工具(db_query、module_patch、img_search、/workflow)"
;
this
.
chkOpt
.
Text
=
"启用可选/高级工具(db_query、module_patch、img_search、/workflow)"
;
this
.
chkOpt
.
UseVisualStyleBackColor
=
true
;
this
.
chkOpt
.
UseVisualStyleBackColor
=
true
;
//
//
// chk
AutoMemory
// chk
ImageInput
//
//
this
.
chkAutoMemory
.
AutoSize
=
true
;
this
.
chkImageInput
.
AutoSize
=
true
;
this
.
chkAutoMemory
.
Location
=
new
System
.
Drawing
.
Point
(
16
,
54
);
this
.
chkImageInput
.
Checked
=
true
;
this
.
chkAutoMemory
.
Name
=
"chkAutoMemory"
;
this
.
chkImageInput
.
CheckState
=
System
.
Windows
.
Forms
.
CheckState
.
Checked
;
this
.
chkAutoMemory
.
Size
=
new
System
.
Drawing
.
Size
(
351
,
21
);
this
.
chkImageInput
.
Location
=
new
System
.
Drawing
.
Point
(
16
,
54
);
this
.
chkAutoMemory
.
TabIndex
=
1
;
this
.
chkImageInput
.
Name
=
"chkImageInput"
;
this
.
chkAutoMemory
.
Text
=
"回答后提示沉淀有价值结论到知识库(只提示,不静默写入)"
;
this
.
chkImageInput
.
Size
=
new
System
.
Drawing
.
Size
(
540
,
21
);
this
.
chkAutoMemory
.
UseVisualStyleBackColor
=
true
;
this
.
chkImageInput
.
TabIndex
=
1
;
this
.
chkImageInput
.
Text
=
"启用图片提问(多模态):粘贴/拖入截图随提问以 image_url 发送,需视觉模型支持(如 qwen-vl)"
;
this
.
chkImageInput
.
UseVisualStyleBackColor
=
true
;
//
//
// grpFail
// grpFail
//
//
...
@@ -957,7 +959,7 @@
...
@@ -957,7 +959,7 @@
private
System
.
Windows
.
Forms
.
TabPage
tabOther
;
private
System
.
Windows
.
Forms
.
TabPage
tabOther
;
private
System
.
Windows
.
Forms
.
GroupBox
grpOpt
;
private
System
.
Windows
.
Forms
.
GroupBox
grpOpt
;
private
System
.
Windows
.
Forms
.
CheckBox
chkOpt
;
private
System
.
Windows
.
Forms
.
CheckBox
chkOpt
;
private
System
.
Windows
.
Forms
.
CheckBox
chk
AutoMemory
;
private
System
.
Windows
.
Forms
.
CheckBox
chk
ImageInput
;
private
System
.
Windows
.
Forms
.
GroupBox
grpFail
;
private
System
.
Windows
.
Forms
.
GroupBox
grpFail
;
private
System
.
Windows
.
Forms
.
CheckBox
chkFail
;
private
System
.
Windows
.
Forms
.
CheckBox
chkFail
;
private
System
.
Windows
.
Forms
.
Label
lblBUrl
;
private
System
.
Windows
.
Forms
.
Label
lblBUrl
;
...
...
Tools/MyAgent/Forms/AgentSettingsForm.cs
View file @
23830cb4
...
@@ -137,7 +137,7 @@ namespace Tools.MyAgent.Forms
...
@@ -137,7 +137,7 @@ namespace Tools.MyAgent.Forms
// 其他:可选能力
// 其他:可选能力
chkOpt
.
Checked
=
_config
.
EnableOptionalTools
;
chkOpt
.
Checked
=
_config
.
EnableOptionalTools
;
chk
AutoMemory
.
Checked
=
_config
.
EnableAutoMemory
;
chk
ImageInput
.
Checked
=
_config
.
EnableImageInput
;
// v2.2 多模态:图片提问开关
// 其他:故障转移
// 其他:故障转移
chkFail
.
Checked
=
_config
.
EnableFailover
;
chkFail
.
Checked
=
_config
.
EnableFailover
;
...
@@ -429,7 +429,7 @@ namespace Tools.MyAgent.Forms
...
@@ -429,7 +429,7 @@ namespace Tools.MyAgent.Forms
_config
.
CustomSystemPrompt
=
txtCustomPrompt
.
Text
.
Trim
();
_config
.
CustomSystemPrompt
=
txtCustomPrompt
.
Text
.
Trim
();
// 其他:可选能力
// 其他:可选能力
_config
.
EnableOptionalTools
=
chkOpt
.
Checked
;
_config
.
EnableOptionalTools
=
chkOpt
.
Checked
;
_config
.
Enable
AutoMemory
=
chkAutoMemory
.
Checked
;
_config
.
Enable
ImageInput
=
chkImageInput
.
Checked
;
// v2.2 多模态:图片提问开关
// 其他:故障转移
// 其他:故障转移
_config
.
EnableFailover
=
chkFail
.
Checked
;
_config
.
EnableFailover
=
chkFail
.
Checked
;
_config
.
BackupBaseUrl
=
txtBUrl
.
Text
.
Trim
();
_config
.
BackupBaseUrl
=
txtBUrl
.
Text
.
Trim
();
...
...
Tools/MyAgent/Forms/wBitmapView.cs
0 → 100644
View file @
23830cb4
using
System
;
using
System.Drawing
;
using
System.Windows.Forms
;
namespace
Tools.MyAgent
{
/// <summary>
/// 图片查看器:优先按图片原始尺寸展示(1:1 像素),但不超过屏幕工作区大小;
/// 图片大于工作区时窗体限制为工作区大小,可滚动查看。带边框与标题栏(可点 ✕ 关闭),置顶悬浮。
/// <para>点击任意处或按 Esc 也可关闭。纯代码构造,无 Designer 依赖。</para>
/// </summary>
public
sealed
class
wBitmapView
:
Form
{
private
Image
_img
;
private
Label
_hint
;
private
PictureBox
_picture
;
public
wBitmapView
(
Image
img
)
{
_img
=
img
;
//
this
.
TopMost
=
true
;
this
.
ShowInTaskbar
=
false
;
this
.
StartPosition
=
FormStartPosition
.
Manual
;
// 带边框 + 标题栏(含 ✕ 关闭按钮),避免无边框窗体被误解为无法关闭
this
.
FormBorderStyle
=
FormBorderStyle
.
FixedToolWindow
;
this
.
Text
=
$"图片查看
{
img
.
Width
}
×
{
img
.
Height
}
"
;
this
.
AutoScroll
=
true
;
this
.
KeyPreview
=
true
;
// 原始尺寸:PictureBox 随图片自动扩展,窗体收缩时出现滚动条
_picture
=
new
PictureBox
{
Location
=
new
Point
(
0
,
0
),
SizeMode
=
PictureBoxSizeMode
.
AutoSize
,
Image
=
img
,
};
this
.
Controls
.
Add
(
_picture
);
// 底部提示条:图片尺寸 + 关闭方式
_hint
=
new
Label
{
Text
=
$"
{
img
.
Width
}
×
{
img
.
Height
}
· 点击任意处关闭 "
,
AutoSize
=
true
,
BackColor
=
Color
.
FromArgb
(
170
,
0
,
0
,
0
),
ForeColor
=
Color
.
White
,
Font
=
new
Font
(
"微软雅黑"
,
9F
),
Padding
=
new
Padding
(
8
,
3
,
8
,
3
),
Anchor
=
AnchorStyles
.
Bottom
,
};
this
.
Controls
.
Add
(
_hint
);
_hint
.
BringToFront
();
this
.
Resize
+=
(
s
,
e
)
=>
PositionHint
();
this
.
Scroll
+=
(
s
,
e
)
=>
PositionHint
();
// 点击任意处 / Esc 关闭
this
.
Click
+=
(
s
,
e
)
=>
this
.
Close
();
_picture
.
Click
+=
(
s
,
e
)
=>
this
.
Close
();
this
.
KeyDown
+=
(
s
,
e
)
=>
{
if
(
e
.
KeyCode
==
Keys
.
Escape
)
this
.
Close
();
};
// 关闭时释放位图
this
.
FormClosed
+=
(
s
,
e
)
=>
{
if
(
_img
!=
null
)
{
_img
.
Dispose
();
_img
=
null
;
}
};
}
/// <summary>提示条水平居中于可视区底部</summary>
private
void
PositionHint
()
{
if
(
_hint
==
null
||
!
_hint
.
IsHandleCreated
)
return
;
_hint
.
Location
=
new
Point
(
Math
.
Max
(
0
,
(
this
.
ClientSize
.
Width
-
_hint
.
Width
)
/
2
),
Math
.
Max
(
0
,
this
.
ClientSize
.
Height
-
_hint
.
Height
-
8
));
}
/// <summary>
/// 居中显示:优先按图片原始尺寸,但不超过屏幕工作区大小(超屏则限制在工作区内,滚动查看)。
/// </summary>
/// <param name="owner">调用方窗体:用于选取其所在屏幕的工作区(多屏场景),可传 null</param>
public
void
ShowView
(
Form
owner
=
null
)
{
// 选取屏幕:优先调用方窗体所在屏,其次鼠标所在屏
Rectangle
wa
;
if
(
owner
!=
null
&&
owner
.
IsHandleCreated
)
wa
=
Screen
.
FromControl
(
owner
).
WorkingArea
;
else
wa
=
Screen
.
FromPoint
(
Cursor
.
Position
).
WorkingArea
;
// 核心限制:优先原尺寸,超屏取工作区大小(至少保留最小可视尺寸)
// 先强制创建句柄,使 Width/Height 反映窗体边框+标题栏装饰,保证窗体整体不超屏
IntPtr
dummy
=
this
.
Handle
;
int
decoW
=
this
.
Width
-
this
.
ClientSize
.
Width
;
int
decoH
=
this
.
Height
-
this
.
ClientSize
.
Height
;
int
w
=
Math
.
Max
(
Math
.
Min
(
_img
.
Width
,
wa
.
Width
-
decoW
),
120
);
int
h
=
Math
.
Max
(
Math
.
Min
(
_img
.
Height
,
wa
.
Height
-
decoH
),
80
);
this
.
ClientSize
=
new
Size
(
w
,
h
);
this
.
Location
=
new
Point
(
wa
.
Left
+
(
wa
.
Width
-
this
.
Width
)
/
2
,
wa
.
Top
+
(
wa
.
Height
-
this
.
Height
)
/
2
);
this
.
Show
();
this
.
Activate
();
PositionHint
();
}
/// <summary>从文件加载图片并按原始尺寸弹出查看(超屏自动限制在工作区内;内部负责释放位图)</summary>
public
static
wBitmapView
ShowImage
(
string
file
,
Form
owner
=
null
)
{
var
img
=
Image
.
FromFile
(
file
);
var
v
=
new
wBitmapView
(
img
);
if
(
owner
!=
null
&&
owner
.
IsHandleCreated
&&
owner
.
Visible
)
v
.
Owner
=
owner
;
// 跟随主窗体:最小化/置顶关系一致
v
.
ShowView
(
owner
);
return
v
;
}
}
}
Tools/MyAgent/Support/ChatMessage.cs
View file @
23830cb4
using
System
;
using
System
;
using
System.Collections.Generic
;
namespace
Tools.MyAgent
namespace
Tools.MyAgent
{
{
...
@@ -12,5 +13,11 @@ namespace Tools.MyAgent
...
@@ -12,5 +13,11 @@ namespace Tools.MyAgent
public
string
ThinkingContent
{
get
;
set
;
}
public
string
ThinkingContent
{
get
;
set
;
}
public
bool
IsStreaming
{
get
;
set
;
}
public
bool
IsStreaming
{
get
;
set
;
}
public
DateTime
Timestamp
{
get
;
set
;
}
=
DateTime
.
Now
;
public
DateTime
Timestamp
{
get
;
set
;
}
=
DateTime
.
Now
;
/// <summary>附图(多模态,v2.2):本地图片绝对路径;启用图片提问时随消息以 image_url 内容块发送</summary>
public
string
ImagePath
{
get
;
set
;
}
/// <summary>附图列表(多图,v2.3):一次提问可携带多张图片;优先于 ImagePath(旧会话兼容保留单图字段)</summary>
public
List
<
string
>
Images
{
get
;
set
;
}
}
}
}
}
Tools/MyAgent/Support/NoCaretRichTextBox.cs
0 → 100644
View file @
23830cb4
using
System
;
using
System.Runtime.InteropServices
;
using
System.Windows.Forms
;
namespace
Tools.MyAgent
{
/// <summary>
/// 只读展示用 RichTextBox:隐藏文本插入光标(I 型插入符,不闪烁)。
/// <para>问答区不支持输入,无需显示插入光标;仍保留鼠标拖动选中与复制能力。
/// RichEdit 在获得焦点 / 鼠标按下 / 按键移动光标时会重新显示插入符,故在这些消息处理后再次隐藏。</para>
/// </summary>
public
sealed
class
NoCaretRichTextBox
:
RichTextBox
{
private
const
int
WM_SETFOCUS
=
0x0007
;
private
const
int
WM_KEYDOWN
=
0x0100
;
private
const
int
WM_LBUTTONDOWN
=
0x0201
;
private
const
int
WM_LBUTTONUP
=
0x0202
;
[
DllImport
(
"user32.dll"
)]
private
static
extern
bool
HideCaret
(
IntPtr
hWnd
);
protected
override
void
WndProc
(
ref
Message
m
)
{
base
.
WndProc
(
ref
m
);
if
(
m
.
Msg
==
WM_SETFOCUS
||
m
.
Msg
==
WM_LBUTTONDOWN
||
m
.
Msg
==
WM_LBUTTONUP
||
m
.
Msg
==
WM_KEYDOWN
)
{
try
{
HideCaret
(
m
.
HWnd
);
}
catch
{
}
}
}
}
}
Tools/MyAgent/wMyAgent.cs
View file @
23830cb4
This diff is collapsed.
Click to expand it.
Tools/Tools.csproj
View file @
23830cb4
...
@@ -190,6 +190,9 @@
...
@@ -190,6 +190,9 @@
<Compile
Include=
"MyAgent\Forms\AgentSettingsForm.Designer.cs"
>
<Compile
Include=
"MyAgent\Forms\AgentSettingsForm.Designer.cs"
>
<DependentUpon>
AgentSettingsForm.cs
</DependentUpon>
<DependentUpon>
AgentSettingsForm.cs
</DependentUpon>
</Compile>
</Compile>
<Compile
Include=
"MyAgent\Forms\wBitmapView.cs"
>
<SubType>
Form
</SubType>
</Compile>
<Compile
Include=
"MyAgent\Kb\KbConfig.cs"
/>
<Compile
Include=
"MyAgent\Kb\KbConfig.cs"
/>
<Compile
Include=
"MyAgent\Kb\KbService.cs"
/>
<Compile
Include=
"MyAgent\Kb\KbService.cs"
/>
<Compile
Include=
"MyAgent\Kb\KbIndex.cs"
/>
<Compile
Include=
"MyAgent\Kb\KbIndex.cs"
/>
...
@@ -204,6 +207,9 @@
...
@@ -204,6 +207,9 @@
<Compile
Include=
"MyAgent\Support\ChatSession.cs"
/>
<Compile
Include=
"MyAgent\Support\ChatSession.cs"
/>
<Compile
Include=
"MyAgent\Support\ChatRtfCache.cs"
/>
<Compile
Include=
"MyAgent\Support\ChatRtfCache.cs"
/>
<Compile
Include=
"MyAgent\Support\MarkdownRenderer.cs"
/>
<Compile
Include=
"MyAgent\Support\MarkdownRenderer.cs"
/>
<Compile
Include=
"MyAgent\Support\NoCaretRichTextBox.cs"
>
<SubType>
Component
</SubType>
</Compile>
<Compile
Include=
"MyAgent\wMyAgent.cs"
>
<Compile
Include=
"MyAgent\wMyAgent.cs"
>
<SubType>
Form
</SubType>
<SubType>
Form
</SubType>
</Compile>
</Compile>
...
...
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