Commit 3ebae990 authored by 段廷瑞's avatar 段廷瑞

feat: 增加分享功能;共享知识库连接优化

parent e9ea0397
......@@ -53,6 +53,7 @@ bin
/AppHome/AutoUpdate/AutoUpdate.exe
/AppHome/config/sys_cfg.xml
/AppHome/Package
/AppHome/Shares/
/AppHome/caches/default
/AppHome/WebKit/caches/default
/AppHome/config/links.xml
......@@ -71,3 +72,4 @@ bin
/AppHome/Agent/config/AgentConfig.json
/cc
/AppHome/Logs
/AppHome/Package
......@@ -71,6 +71,7 @@
<Compile Include="Program.cs" />
<Compile Include="TestUtil.cs" />
<Compile Include="SecretProtectorTests.cs" />
<Compile Include="ShareHelperTests.cs" />
<Compile Include="DbQueryToolTests.cs" />
<Compile Include="AgentEngineLogicTests.cs" />
<Compile Include="FailoverPolicyTests.cs" />
......
using Tools.MyAgent;
namespace AgentTest
{
/// <summary>
/// Windows 共享连接提示测试:覆盖凭据冲突识别及设置页操作引导。
/// </summary>
public sealed class ShareHelperTests : TestRunner
{
[Test]
public void CredentialConflictMessage_ExplainsClearCredentialsAndRetry()
{
string message = ShareHelper.BuildCredentialConflictMessage(@"\\server\share");
Assert.Contains(message, "凭据冲突", "应明确说明 Windows 凭据冲突");
Assert.Contains(message, "不需要填写账号密码", "应说明无需填写账号密码");
Assert.Contains(message, "清空账号和密码后重试", "应给出清空凭据并重试的操作");
}
[Test]
public void CredentialConflict_IsRecognizedByExitCodeOrOutput()
{
Assert.True(ShareHelper.IsCredentialConflict(1219, ""), "错误码 1219 应识别为凭据冲突");
Assert.True(ShareHelper.IsCredentialConflict(2, "System error 1219"), "输出中的 1219 应识别为凭据冲突");
Assert.False(ShareHelper.IsCredentialConflict(5, "Access is denied"), "其他错误不应误判为凭据冲突");
}
}
}
No preview for this file type
......@@ -13,12 +13,18 @@
<member name="P:Tools.MyAgent.AgentRuntimeOptions.CurrentUser">
<summary>当前用户或角色,用于聊天展示、工具权限和数据库审计。</summary>
</member>
<member name="P:Tools.MyAgent.AgentRuntimeOptions.DiagnosticRoots">
<summary>宿主注入的诊断根目录;为空时由产品上下文 ProjectRoot 自动生成 source 根。</summary>
</member>
<member name="P:Tools.MyAgent.AgentRuntimeOptions.ProductContext">
<summary>宿主固定的产品上下文;空值表示未绑定产品线。</summary>
</member>
<member name="T:Tools.MyAgent.AgentRuntime">
<summary>其他 .NET Framework 项目引用 Tools.Agent 后使用的稳定入口。</summary>
</member>
<member name="P:Tools.MyAgent.AgentRuntime.DiagnosticRoots">
<summary>当前运行时诊断根目录快照;返回独立副本,避免外部修改。</summary>
</member>
<member name="P:Tools.MyAgent.AgentRuntime.ProductContext">
<summary>当前宿主绑定的产品上下文;返回副本,避免运行期间被外部对象修改。</summary>
</member>
......@@ -162,9 +168,12 @@
<summary>本轮上下文处理提示(历史省略/压缩);经 OnTextDelta 即时显示,
并在最终回答前拼接保留(修复:此前被回答正文全量替换而丢失)</summary>
</member>
<member name="M:Tools.MyAgent.Agent.AgentEngine.#ctor(Tools.MyAgent.Kb.KbService,System.String,Tools.MyAgent.AgentProductContext)">
<member name="M:Tools.MyAgent.Agent.AgentEngine.#ctor(Tools.MyAgent.Kb.KbService,System.String,Tools.MyAgent.AgentProductContext,System.Collections.Generic.IEnumerable{Tools.MyAgent.Diagnostics.DiagnosticRoot})">
<summary>创建引擎,并允许宿主指定内置提示词、方言、行业知识和产品上下文所在的 Agent 根目录。</summary>
</member>
<member name="P:Tools.MyAgent.Agent.AgentEngine.DiagnosticRoots">
<summary>当前运行时诊断根目录快照;运行期间不可切换。</summary>
</member>
<member name="M:Tools.MyAgent.Agent.AgentEngine.SetProductContext(Tools.MyAgent.AgentProductContext)">
<summary>切换产品上下文。运行中的回答禁止切换,调用方应同时创建新会话。</summary>
</member>
......@@ -456,6 +465,9 @@
<member name="P:Tools.MyAgent.Agent.PromptBuilder.ProductContext">
<summary>宿主绑定的产品上下文;由运行时注入,不接受聊天内容修改</summary>
</member>
<member name="P:Tools.MyAgent.Agent.PromptBuilder.DiagnosticRoots">
<summary>宿主注入的诊断根目录;只用于显示根目录 ID,不显示完整机器路径。</summary>
</member>
<member name="M:Tools.MyAgent.Agent.PromptBuilder.LoadLayer(System.String,System.String,System.String)">
<summary>加载提示词模板:存在且非空 → 用文件内容;缺失 → 生成默认模板;异常 → 内置回退</summary>
</member>
......@@ -536,6 +548,12 @@
<member name="P:Tools.MyAgent.Agent.Tools.ToolContext.ProductContext">
<summary>宿主绑定的产品上下文,用于限制 d_* 工具检索范围</summary>
</member>
<member name="P:Tools.MyAgent.Agent.Tools.ToolContext.DiagnosticRoots">
<summary>宿主注入的诊断根目录;工具只能以 rootId + 相对路径访问。</summary>
</member>
<member name="P:Tools.MyAgent.Agent.Tools.ToolContext.DiagnosticSessionId">
<summary>诊断会话标识,用于将日志、工具调用和证据关联到同一次排查。</summary>
</member>
<member name="M:Tools.MyAgent.Agent.Tools.ToolContext.SafeResolve(System.Func{System.String},System.String)">
<summary>沙箱校验解析失败时回退为原路径</summary>
</member>
......@@ -640,6 +658,36 @@
<summary>图像检索工具(#38):以参考图为模板,在知识库图片目录中模板匹配定位相似图</summary>
</member>
<!-- Badly formed XML comment ignored for member "T:Tools.MyAgent.Agent.Tools.ModulePatchTool" -->
<member name="T:Tools.MyAgent.Agent.Tools.DiagnosticToolHelper">
<summary>诊断工具适配器:所有路径均由 ToolContext 的诊断根目录策略解析。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.DiagnosticRoot">
<summary>宿主注入的只读诊断根目录。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.DiagnosticRootPolicy">
<summary>诊断根目录解析策略:只允许 rootId + 相对路径,并拒绝穿越与 reparse point。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.DiagnosticEvidence">
<summary>进入模型前使用的诊断证据。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.DiagnosticResult">
<summary>诊断工具统一返回结构。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.DiagnosticSanitizer">
<summary>诊断文本脱敏,避免密码、令牌和连接串进入模型上下文。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.LogAnalyzer">
<summary>日志发现、时间范围检索和异常上下文提取。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.ConfigAnalyzer">
<summary>JSON/XML/INI 配置读取、脱敏和基础绑定信息提取。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.AssemblyInspector">
<summary>只读程序集元数据检查。仅使用 ReflectionOnlyLoad,不执行目标程序集代码。</summary>
</member>
<member name="T:Tools.MyAgent.Diagnostics.DiagnosticOrchestrator">
<summary>固定顺序编排日志、配置和程序集证据,避免完全依赖模型自由规划。</summary>
</member>
<member name="T:Tools.MyAgent.Config.AgentConfig">
<summary>
自研智能体配置(保存于 Agent/config/AgentConfig.json)。
......@@ -1632,9 +1680,21 @@
<member name="M:Tools.MyAgent.ShareHelper.Connect(System.String,System.String,System.String)">
<summary>
建立到 UNC 共享的会话连接。返回 (是否成功, 描述)。
<para>通过 net use 实现;若之前已用其他凭据连接同一服务器导致凭据冲突(1219),先强制断开再重试一次。</para>
<para>优先使用当前 Windows 会话连接;当前身份无法访问时,且用户填写了账号,再使用显式凭据重试。</para>
<para>凭据冲突(1219)时返回明确引导,避免测试时静默断开现有会话。</para>
</summary>
</member>
<member name="M:Tools.MyAgent.ShareHelper.ConnectWithFallback(System.String,System.String,System.String)">
<summary>
连接共享的兼容回退流程:先复用当前 Windows 会话,失败后才使用显式凭据。
</summary>
</member>
<member name="M:Tools.MyAgent.ShareHelper.IsCredentialConflict(System.Int32,System.String)">
<summary>判断 net use 返回结果是否为 Windows 凭据冲突(错误码 1219)。</summary>
</member>
<member name="M:Tools.MyAgent.ShareHelper.BuildCredentialConflictMessage(System.String)">
<summary>生成面向设置页用户的凭据冲突处理提示。</summary>
</member>
<member name="M:Tools.MyAgent.ShareHelper.Disconnect(System.String)">
<summary>断开到共享的会话连接(忽略错误)</summary>
</member>
......@@ -1665,10 +1725,10 @@
<member name="M:Tools.MyAgent.AgentLogger.Log(System.String)">
<summary>追加一行日志(自动脱敏 + 时间戳)</summary>
</member>
<member name="M:Tools.MyAgent.AgentLogger.LogCall(System.String,System.Int32,System.Int32,System.Int64,System.String,System.String)">
<member name="M:Tools.MyAgent.AgentLogger.LogCall(System.String,System.Int32,System.Int32,System.Int64,System.String,System.String,System.String)">
<summary>模型调用记录(#29):model / token / 耗时 / 工具序列 / 结果</summary>
</member>
<member name="M:Tools.MyAgent.AgentLogger.LogTool(System.String,System.String,System.Int64,System.String)">
<member name="M:Tools.MyAgent.AgentLogger.LogTool(System.String,System.String,System.Int64,System.String,System.String)">
<summary>工具执行记录</summary>
</member>
<member name="M:Tools.MyAgent.AgentLogger.Mask(System.String)">
......@@ -2228,6 +2288,9 @@
<member name="M:Tools.MyAgent.wMyAgent.#ctor(System.String,System.String,Tools.MyAgent.AgentProductContext)">
<summary>创建可嵌入的智能体窗体,并允许宿主指定数据目录、当前用户和产品上下文。</summary>
</member>
<member name="M:Tools.MyAgent.wMyAgent.#ctor(System.String,System.String,Tools.MyAgent.AgentProductContext,System.Collections.Generic.IReadOnlyList{Tools.MyAgent.Diagnostics.DiagnosticRoot})">
<summary>创建可嵌入的智能体窗体,并允许宿主指定独立诊断根目录。</summary>
</member>
<member name="M:Tools.MyAgent.wMyAgent.CreateChatControl">
<summary>
创建聊天区控件:统一使用 RichBox(RichTextBox + RTF 渲染,轻量、启动快、无浏览器依赖)。
......
......@@ -34,7 +34,8 @@ namespace Tools.MyAgent
/// <summary>
/// 建立到 UNC 共享的会话连接。返回 (是否成功, 描述)。
/// <para>通过 net use 实现;若之前已用其他凭据连接同一服务器导致凭据冲突(1219),先强制断开再重试一次。</para>
/// <para>优先使用当前 Windows 会话连接;当前身份无法访问时,且用户填写了账号,再使用显式凭据重试。</para>
/// <para>凭据冲突(1219)时返回明确引导,避免测试时静默断开现有会话。</para>
/// </summary>
public static (bool Ok, string Message) Connect(string uncPath, string user, string password)
{
......@@ -44,21 +45,41 @@ namespace Tools.MyAgent
if (string.IsNullOrEmpty(share))
return (false, "共享路径格式不正确,应为 \\\\服务器\\共享名\\目录");
var result = RunNetUse(BuildNetUseArgs(share, user, password), 25000);
var result = ConnectWithFallback(share, user, password);
if (result.Exit == 0)
return (true, $"已连接共享:{share}");
// 凭据冲突(1219):先断开该共享的旧连接再重试一次
if (result.Exit == ERROR_SESSION_CREDENTIAL_CONFLICT ||
result.Output.IndexOf("1219", StringComparison.Ordinal) >= 0)
if (IsCredentialConflict(result.Exit, result.Output))
return (false, BuildCredentialConflictMessage(share));
return (false, BuildFailureMessage(result, share));
}
/// <summary>
/// 连接共享的兼容回退流程:先复用当前 Windows 会话,失败后才使用显式凭据。
/// </summary>
private static (int Exit, string Output) ConnectWithFallback(string share, string user, string password)
{
RunNetUse($"\"{share}\" /delete /y", 10000);
result = RunNetUse(BuildNetUseArgs(share, user, password), 25000);
if (result.Exit == 0)
return (true, $"已连接共享:{share}");
var currentSession = RunNetUse(BuildNetUseArgs(share, "", ""), 25000);
if (currentSession.Exit == 0 || string.IsNullOrWhiteSpace(user))
return currentSession;
return RunNetUse(BuildNetUseArgs(share, user, password), 25000);
}
return (false, BuildFailureMessage(result, share));
/// <summary>判断 net use 返回结果是否为 Windows 凭据冲突(错误码 1219)。</summary>
internal static bool IsCredentialConflict(int exitCode, string output)
{
return exitCode == ERROR_SESSION_CREDENTIAL_CONFLICT
|| (output ?? "").IndexOf("1219", StringComparison.Ordinal) >= 0;
}
/// <summary>生成面向设置页用户的凭据冲突处理提示。</summary>
internal static string BuildCredentialConflictMessage(string share)
{
return $"连接共享失败({share}):检测到 Windows 凭据冲突(错误 1219)。"
+ "当前 Windows 会话已使用其他账号连接此服务器;测试连接不需要填写账号密码,请清空账号和密码后重试。"
+ "若仍失败,请先断开该服务器已有网络连接,再重试。";
}
/// <summary>断开到共享的会话连接(忽略错误)</summary>
......
This diff is collapsed.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tools
{
/// <summary>
/// 分享打包进度窗口:显示文件处理进度,完成后可直接打开 Shares 目录。
/// </summary>
internal sealed class SharePackageProgressForm : Form
{
private readonly string _sourceRoot;
private readonly string _outputDirectory;
private ProgressBar _progressBar;
private Label _statusLabel;
private Button _openFolderButton;
private Button _closeButton;
private bool _started;
private bool _finished;
public SharePackageProgressForm(string sourceRoot, string outputDirectory)
{
_sourceRoot = sourceRoot;
_outputDirectory = outputDirectory;
InitializeControls();
Shown += StartBuild;
}
private void InitializeControls()
{
Text = "分享打包";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MinimizeBox = false;
MaximizeBox = false;
ShowInTaskbar = false;
ClientSize = new Size(520, 174);
var titleLabel = new Label
{
AutoSize = true,
Location = new Point(20, 16),
Text = "正在生成分享包,请稍候...",
};
_statusLabel = new Label
{
AutoEllipsis = true,
Location = new Point(20, 43),
Size = new Size(480, 22),
Text = "准备中...",
};
_progressBar = new ProgressBar
{
Location = new Point(20, 72),
Size = new Size(480, 24),
Minimum = 0,
Maximum = 1,
Value = 0,
Style = ProgressBarStyle.Blocks,
};
_openFolderButton = new Button
{
Location = new Point(290, 122),
Size = new Size(104, 30),
Text = "打开文件夹",
Enabled = false,
};
_openFolderButton.Click += OpenFolder;
_closeButton = new Button
{
Location = new Point(406, 122),
Size = new Size(94, 30),
Text = "关闭",
Enabled = false,
};
_closeButton.Click += (sender, e) => Close();
Controls.Add(titleLabel);
Controls.Add(_statusLabel);
Controls.Add(_progressBar);
Controls.Add(_openFolderButton);
Controls.Add(_closeButton);
}
private async void StartBuild(object sender, EventArgs e)
{
if (_started) return;
_started = true;
try
{
var progress = new Progress<SharePackageProgress>(UpdateProgress);
var result = await Task.Run(() =>
SharePackageBuilder.Create(_sourceRoot, _outputDirectory, progress));
_finished = true;
_statusLabel.Text = $"打包完成:{result.FileCount} 个文件,{result.Length / 1024d / 1024d:0.00} MB";
_openFolderButton.Enabled = true;
_closeButton.Enabled = true;
}
catch (Exception ex)
{
_finished = true;
_statusLabel.Text = "打包失败:" + ex.Message;
_closeButton.Enabled = true;
}
}
private void UpdateProgress(SharePackageProgress progress)
{
if (IsDisposed || progress == null) return;
int total = Math.Max(progress.Total, 1);
_progressBar.Maximum = total;
_progressBar.Value = Math.Min(Math.Max(progress.Current, 0), total);
_statusLabel.Text = progress.Total > 0
? $"{progress.Current}/{progress.Total} {progress.Status}"
: progress.Status;
}
private void OpenFolder(object sender, EventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = _outputDirectory,
UseShellExecute = true,
});
}
catch (Exception ex)
{
MessageBox.Show(this, "打开分享目录失败:\r\n" + ex.Message,
"分享打包", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (!_finished)
{
e.Cancel = true;
return;
}
base.OnFormClosing(e);
}
}
}
......@@ -424,6 +424,10 @@
<DependentUpon>wXmlGenerator.cs</DependentUpon>
</Compile>
<Compile Include="wExtend.cs" />
<Compile Include="SharePackageBuilder.cs" />
<Compile Include="SharePackageProgressForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="wMain.cs">
<SubType>Form</SubType>
</Compile>
......
......@@ -42,6 +42,7 @@
this.tsmiSysUpload = new System.Windows.Forms.ToolStripMenuItem();
this.tsmiSysUpdate = new System.Windows.Forms.ToolStripMenuItem();
this.tsmiSysUpdateLog = new System.Windows.Forms.ToolStripMenuItem();
this.tsmiSysShare = new System.Windows.Forms.ToolStripMenuItem();
this.tsmiSysExit = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
this.tsmiAccPasswordMgr = new System.Windows.Forms.ToolStripMenuItem();
......@@ -199,6 +200,7 @@
this.tsmiSysUpload,
this.tsmiSysUpdate,
this.tsmiSysUpdateLog,
this.tsmiSysShare,
this.tsmiSysExit});
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(44, 21);
......@@ -222,6 +224,12 @@
this.tsmiSysUpdateLog.Size = new System.Drawing.Size(124, 22);
this.tsmiSysUpdateLog.Text = "更新记录";
//
// tsmiSysShare
//
this.tsmiSysShare.Name = "tsmiSysShare";
this.tsmiSysShare.Size = new System.Drawing.Size(124, 22);
this.tsmiSysShare.Text = "分享打包";
//
// tsmiSysExit
//
this.tsmiSysExit.Name = "tsmiSysExit";
......@@ -551,6 +559,7 @@
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.ToolStripMenuItem tsmiSysUpdateLog;
private System.Windows.Forms.ToolStripMenuItem tsmiSysShare;
private Tools.MyCommon.controls.TabControlEx tabControl1;
private System.Windows.Forms.TabPage HomePage;
private System.Windows.Forms.ToolStripMenuItem tsmiShowFetetoWork;
......
......@@ -376,6 +376,7 @@ namespace Tools
case "SysUpload": this.UploadUpdate(); break;
case "SysUpdate": this.CheckUpdate(); break;
case "SysUpdateLog": this.ViewUpdateLog(); break;
case "SysShare": this.SharePackage(); break;
case "SysExit": this.Exit(); break;
case "AccPasswordMgr": this.PasswordMgr(); break;
case "AccPersonalSetting": this.PersonalSetting(); break;
......@@ -465,6 +466,17 @@ namespace Tools
//
Utils.Tip(sb.ToString(), 600, 400);
}
/// <summary>
/// 系统:生成分享包
/// </summary>
private void SharePackage()
{
string root = AppDomain.CurrentDomain.BaseDirectory;
string output = Path.Combine(root, "Shares");
using (var progress = new SharePackageProgressForm(root, output))
progress.ShowDialog(this);
}
/// <summary>
/// 系统:退出系统
/// </summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment