Commit 5b9144c4 authored by 段廷瑞's avatar 段廷瑞

feat: Agent可观测性功能

parent 4230d763
No preview for this file type
This diff is collapsed.
This diff is collapsed.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
...@@ -30,6 +31,9 @@ namespace Tools.MyAgent.Agent ...@@ -30,6 +31,9 @@ namespace Tools.MyAgent.Agent
public string FinishReason { get; set; } = ""; public string FinishReason { get; set; } = "";
/// <summary>用量(SSE 末帧 usage;无则 -1)</summary> /// <summary>用量(SSE 末帧 usage;无则 -1)</summary>
public int PromptTokens { get; set; } = -1; public int PromptTokens { get; set; } = -1;
/// <summary>首包延迟(ms):发出请求到收到首个有效 delta;无数据为 0(M6,向后兼容)</summary>
public long TtfbMs { get; set; }
public int CompletionTokens { get; set; } = -1; public int CompletionTokens { get; set; } = -1;
} }
...@@ -542,6 +546,8 @@ namespace Tools.MyAgent.Agent ...@@ -542,6 +546,8 @@ namespace Tools.MyAgent.Agent
private async Task<ChatResult> ReadSseAsync(StreamReader reader, CancellationToken ct) private async Task<ChatResult> ReadSseAsync(StreamReader reader, CancellationToken ct)
{ {
var result = new ChatResult(); var result = new ChatResult();
var ttfbSw = Stopwatch.StartNew();
bool ttfbSet = false;
var contentBuffer = new StringBuilder(); var contentBuffer = new StringBuilder();
var reasoningBuffer = new StringBuilder(); var reasoningBuffer = new StringBuilder();
var toolCalls = new Dictionary<int, ApiToolCall>(); var toolCalls = new Dictionary<int, ApiToolCall>();
...@@ -582,6 +588,13 @@ namespace Tools.MyAgent.Agent ...@@ -582,6 +588,13 @@ namespace Tools.MyAgent.Agent
var delta = first["delta"] as JObject; var delta = first["delta"] as JObject;
if (delta == null) continue; if (delta == null) continue;
// M6:首包延迟打点(首个有效 delta,正文/思维链任一均可)
if (!ttfbSet && (delta["content"] != null || delta["reasoning_content"] != null))
{
ttfbSet = true;
result.TtfbMs = ttfbSw.ElapsedMilliseconds;
}
// deepseek-reasoner 思维链 // deepseek-reasoner 思维链
var reasoning = delta["reasoning_content"]?.Value<string>(); var reasoning = delta["reasoning_content"]?.Value<string>();
if (!string.IsNullOrEmpty(reasoning)) if (!string.IsNullOrEmpty(reasoning))
......
using System; using System;
using System.Windows.Forms;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
...@@ -99,6 +100,12 @@ namespace Tools.MyAgent ...@@ -99,6 +100,12 @@ namespace Tools.MyAgent
return new AgentEngine(knowledgeBase, DataRoot, ProductContext, _diagnosticRoots) { CurrentUser = _options.CurrentUser ?? "访客" }; return new AgentEngine(knowledgeBase, DataRoot, ProductContext, _diagnosticRoots) { CurrentUser = _options.CurrentUser ?? "访客" };
} }
/// <summary>创建 Admin 看板窗体(供宿主挂到菜单/工具栏)。</summary>
public Form CreateAdminForm(int days = 7, bool allowDetailed = false)
{
return new Tools.MyAgent.Forms.AgentAdminForm(null, days, allowDetailed);
}
private void EnsureDefaultResources() private void EnsureDefaultResources()
{ {
Directory.CreateDirectory(DataRoot); Directory.CreateDirectory(DataRoot);
......
...@@ -174,5 +174,36 @@ ...@@ -174,5 +174,36 @@
/// <para>空字典/缺省 = 所有登录用户可用(保持现状);值为空串或 "*" = 不限制。</para> /// <para>空字典/缺省 = 所有登录用户可用(保持现状);值为空串或 "*" = 不限制。</para>
/// </summary> /// </summary>
public Dictionary<string, string> ToolRoleAllow { get; set; } = new Dictionary<string, string>(); public Dictionary<string, string> ToolRoleAllow { get; set; } = new Dictionary<string, string>();
// ══════════════ 高级项:可观测性(不进设置页,内部默认 + 手改配置文件微调) ══════════════
/// <summary>可观测性总开关;关闭则不写 jsonl、不建队列(纯文本日志照旧)。</summary>
public bool TelemetryEnabled { get; set; } = true;
/// <summary>隐私级别:<c>off</c> 不采集 / <c>basic</c> 结构化字段不写原文(默认) / <c>detailed</c> 追加提问与工具返回摘要。</summary>
public string ObservabilityLevel { get; set; } = "basic";
/// <summary>启动时执行一次依赖自检(默认关闭,仅 /diag 与看板触发)。</summary>
public bool HealthCheckOnStart { get; set; } = false;
/// <summary>runs_*.jsonl 保留天数(默认 14)。</summary>
public int RunsKeepDays { get; set; } = 14;
/// <summary>回复尾注是否带本轮摘要(耗时/工具数/通道);默认关闭,避免打扰。</summary>
public bool ShowRunSummary { get; set; } = false;
// ── Admin 看板 ──
/// <summary>看板总开关;关闭时 /admin 提示不可用。</summary>
public bool AdminEnabled { get; set; } = true;
/// <summary>允许打开看板的角色(逗号分隔);空 = 不限制(保持现状,不破坏既有宿主)。</summary>
public string AdminRoles { get; set; } = "";
/// <summary>看板读取最近 N 天(1~30)。</summary>
public int DashboardDays { get; set; } = 7;
/// <summary>看板 30s 自动刷新(默认关闭,避免常驻定时器拖住宿主)。</summary>
public bool AdminAutoRefresh { get; set; } = false;
} }
} }
This diff is collapsed.
...@@ -120,6 +120,7 @@ namespace Tools.MyAgent.Forms ...@@ -120,6 +120,7 @@ namespace Tools.MyAgent.Forms
InitializeComponent(); InitializeComponent();
BuildDbConnectionsTab(); // P0:预存数据库连接(db_query 按 ID 引用,聊天不回显密码) BuildDbConnectionsTab(); // P0:预存数据库连接(db_query 按 ID 引用,聊天不回显密码)
BuildToolPermissionUi(); // P3:工具级权限(按角色)输入区 BuildToolPermissionUi(); // P3:工具级权限(按角色)输入区
BuildDashboardTab(); // 可观测性:统计看板入口(替代隐晦的 /admin)
// 回答形态不再提供设置页(v1.2):全部内部默认(见 AgentConfig 注释),降低设置页认知负担 // 回答形态不再提供设置页(v1.2):全部内部默认(见 AgentConfig 注释),降低设置页认知负担
BindConfig(); // 配置值回填控件(控件创建见 Designer.cs) BindConfig(); // 配置值回填控件(控件创建见 Designer.cs)
ApplyProviderMode(); // 布局联动:BaseUrl 行显隐 ApplyProviderMode(); // 布局联动:BaseUrl 行显隐
...@@ -208,6 +209,125 @@ namespace Tools.MyAgent.Forms ...@@ -208,6 +209,125 @@ namespace Tools.MyAgent.Forms
grpOpt.Height = 216; grpOpt.Height = 216;
} }
// ══ 可观测性:统计看板入口(程序化创建,避免侵入设计器;替代隐晦的 /admin) ══
private TabPage _dashTab;
private Label _lblDashKpi;
private Button _btnOpenDashboard;
/// <summary>
/// 新增「统计看板」选项卡:实时进程用量 + 一键打开完整看板(5 Tab)。
/// <para>看板本体仍是独立窗体 AgentAdminForm(1000×680 设计尺寸),此处仅做入口与即时 KPI,避免在窄设置页里硬塞大面板。</para>
/// </summary>
private void BuildDashboardTab()
{
_dashTab = new TabPage { Text = "统计看板", Padding = new Padding(3) };
var grpKpi = new GroupBox
{
Text = "运行统计(本进程实时累计)",
Location = new Point(8, 8),
Size = new Size(596, 172),
};
_lblDashKpi = new Label
{
Location = new Point(16, 26),
Size = new Size(564, 138),
AutoSize = false,
Font = new Font("Consolas", 9.5F),
UseMnemonic = false,
};
grpKpi.Controls.Add(_lblDashKpi);
var grpOpen = new GroupBox
{
Text = "完整看板",
Location = new Point(8, 188),
Size = new Size(596, 116),
};
_btnOpenDashboard = new Button
{
Text = "打开完整看板(多日趋势 · 工具 · 知识库 · 错误与健康)",
Location = new Point(16, 26),
Size = new Size(564, 32),
};
var lblOpen = new Label
{
Text = "完整看板含 5 个 Tab,可导出 CSV / Markdown / HTML / 诊断包。\n等价命令:在聊天窗输入 /admin。",
Location = new Point(16, 64),
Size = new Size(564, 44),
AutoSize = false,
ForeColor = Color.Gray,
};
grpOpen.Controls.Add(_btnOpenDashboard);
grpOpen.Controls.Add(lblOpen);
_dashTab.Controls.Add(grpKpi);
_dashTab.Controls.Add(grpOpen);
tabMain.TabPages.Add(_dashTab);
_btnOpenDashboard.Click += (s, e) => OpenDashboard();
// 切到该 Tab 时刷新 KPI(设置窗是模态,进来看的就是最新值)
tabMain.SelectedIndexChanged += (s, e) =>
{
if (tabMain.SelectedTab == _dashTab) RefreshDashKpi();
};
RefreshDashKpi();
}
/// <summary>打开完整看板(与 /admin 同源,受 AdminEnabled 开关约束)。</summary>
private void OpenDashboard()
{
try
{
if (!_config.AdminEnabled)
{
MessageBox.Show(this, "统计看板已关闭(配置项 AdminEnabled=false)。", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
int days = _config.DashboardDays > 0 && _config.DashboardDays <= 30 ? _config.DashboardDays : 7;
// 设置窗本身是模态对话框:用 ShowDialog(this) 保证看板置顶
using (var f = new AgentAdminForm(null, days, Tools.MyAgent.AgentTelemetry.IsDetailed))
f.ShowDialog(this);
}
catch (Exception ex)
{
MessageBox.Show(this, "打开看板失败:" + ex.Message, "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>即时 KPI(与 /stats 同源:AgentTelemetry.Snapshot)。</summary>
private void RefreshDashKpi()
{
if (_lblDashKpi == null) return;
try
{
var st = Tools.MyAgent.AgentTelemetry.Snapshot();
var sb = new System.Text.StringBuilder();
sb.Append("本轮进程累计:\n");
sb.Append(string.Format(" 轮数 {0}(成功 {1} · 失败 {2} · 取消 {3})\n",
st.Runs, st.Success, st.Errors, st.Cancels));
if (st.Duration.Count > 0)
sb.Append(string.Format(" 耗时 P50 {0}ms · P95 {1}ms · 最大 {2}ms\n",
st.Duration.P50, st.Duration.P95, st.Duration.Max));
if (st.Ttfb.Count > 0)
sb.Append(string.Format(" 首包 P50 {0}ms · P95 {1}ms\n", st.Ttfb.P50, st.Ttfb.P95));
sb.Append(string.Format(" 工具 {0} 次 · LLM {1} 次\n", st.ToolCalls, st.LlmCalls));
long tokens = st.PromptTokens + st.CompletionTokens;
if (tokens > 0) sb.Append(string.Format(" Token {0} + {1}\n", st.PromptTokens, st.CompletionTokens));
if (st.Cost > 0) sb.Append(" 费用 " + Tools.MyAgent.CostEstimator.Format(st.Cost) + "\n");
if (st.Dropped > 0) sb.Append(" ⚠ 丢弃 " + st.Dropped + " 条(采集跟不上)\n");
if (st.Runs == 0) sb.Append(" (本进程尚未完成任何一轮回答;多日历史见「打开完整看板」)\n");
_lblDashKpi.Text = sb.ToString();
}
catch
{
_lblDashKpi.Text = "(统计暂不可用)";
}
}
/// <summary>角色规则字典 → 编辑文本(排序保证稳定回显)</summary> /// <summary>角色规则字典 → 编辑文本(排序保证稳定回显)</summary>
private static string[] ToolRoleRulesToLines(Dictionary<string, string> rules) private static string[] ToolRoleRulesToLines(Dictionary<string, string> rules)
{ {
......
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Tools.MyAgent.Forms
{
/// <summary>
/// 看板自绘图表(柱状 / 横向条形)。零第三方依赖:GDI+ 直接画,约 120 行。
/// <para>不开 DoubleBuffered 会闪;不画网格线会看不出量级 —— 两者都保留。</para>
/// </summary>
public sealed class DashChart : Panel
{
/// <summary>柱标签(如小时 "00".."23" 或工具名)。</summary>
public string[] Labels = new string[0];
/// <summary>主序列数值。</summary>
public double[] Values = new double[0];
/// <summary>次序列数值(叠加,如失败数);可为空。</summary>
public double[] Values2 = new double[0];
/// <summary>是否横向条形图(工具排名用)。</summary>
public bool Horizontal { get; set; }
/// <summary>空数据占位文案。</summary>
public string EmptyText { get; set; } = "暂无数据";
/// <summary>图表标题 / 坐标轴说明;非空时在顶部绘制白底说明条(避免与最高柱重叠)。</summary>
public string Caption { get; set; } = "";
public Color BarColor { get; set; } = Color.FromArgb(29, 158, 117);
public Color BarColor2 { get; set; } = Color.FromArgb(226, 75, 74);
public DashChart()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
BackColor = Color.White;
}
protected override void OnPaint(PaintEventArgs e)
{
try
{
base.OnPaint(e);
var g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.Clear(BackColor);
if (Values == null || Values.Length == 0 || Labels == null || Labels.Length == 0)
{
using (var b = new SolidBrush(Color.Gray))
using (var f = new Font(FontFamily.GenericSansSerif, 9f))
g.DrawString(EmptyText, f, b, 8, Height / 2 - 8);
return;
}
int n = Math.Min(Values.Length, Labels.Length);
double max = 0;
for (int i = 0; i < n; i++)
{
if (Values[i] > max) max = Values[i];
if (Values2 != null && i < Values2.Length && Values2[i] > max) max = Values2[i];
}
if (max <= 0) max = 1;
const float baseline = 18f; // 柱底留白
const float axisTopPad = 4f; // 顶部网格留白
int topPad = string.IsNullOrEmpty(Caption) ? 0 : 16; // 说明条占用
float baseY = Height - baseline;
float avail = Height - baseline - axisTopPad - topPad;
if (avail < 10) avail = 10;
using (var gridPen = new Pen(Color.FromArgb(230, 230, 230)))
using (var textBrush = new SolidBrush(Color.FromArgb(90, 90, 90)))
using (var font = new Font(FontFamily.GenericSansSerif, 8f))
using (var capBrush = new SolidBrush(Color.FromArgb(110, 110, 110)))
using (var capFont = new Font(FontFamily.GenericSansSerif, 8.5f))
{
// 网格(4 等分)
for (int k = 1; k <= 4; k++)
{
float y = baseY - avail * k / 4f;
g.DrawLine(gridPen, 4, y, Width - 4, y);
}
// 标题 / 坐标轴说明(白底,避免与最高柱重叠)
if (topPad > 0 && !string.IsNullOrEmpty(Caption))
{
var capSize = g.MeasureString(Caption, capFont);
g.FillRectangle(Brushes.White, 2, 1, capSize.Width + 6, 14);
g.DrawString(Caption, capFont, capBrush, 4, 2);
}
if (Horizontal)
{
float rowH = (Height - 12f) / n;
for (int i = 0; i < n; i++)
{
float y = 6 + i * rowH;
float w = (float)(Values[i] / max * (Width - 130));
using (var br = new SolidBrush(BarColor))
g.FillRectangle(br, 100, y + rowH * 0.2f, Math.Max(5, w), rowH * 0.6f);
g.DrawString(Trim(Labels[i], 14), font, textBrush, 4, y + rowH * 0.25f);
g.DrawString(Fmt(Values[i]), font, textBrush, 100 + w + 4, y + rowH * 0.25f);
}
}
else
{
float slot = (Width - 12f) / n;
float bw = Math.Max(2, slot * 0.62f);
for (int i = 0; i < n; i++)
{
double frac = BarFrac(Values[i], max);
float h1 = (float)(frac * avail);
h1 = Math.Max(5, h1); // 每柱默认高度 5(0 值也可见)
float x = 6 + i * slot + (slot - bw) / 2;
using (var br = new SolidBrush(BarColor))
g.FillRectangle(br, x, baseY - h1, bw, h1);
float h2 = 0;
if (Values2 != null && i < Values2.Length && Values2[i] > 0)
{
double frac2 = BarFrac(Values2[i], max);
h2 = (float)(frac2 * avail);
h2 = Math.Max(1, h2);
using (var br = new SolidBrush(BarColor2))
g.FillRectangle(br, x, baseY - h1 - h2, bw, h2);
}
if (n <= 24)
g.DrawString(Labels[i], font, textBrush, x, baseY + 1);
// 柱顶计数值(仅非零,零柱不标以区分)
if (Values[i] > 0)
{
string tag = Fmt(Values[i]);
var tagSize = g.MeasureString(tag, font);
float ty = baseY - h1 - h2 - 12;
if (ty < topPad + 1) ty = topPad + 1;
g.DrawString(tag, font, textBrush, x + (bw - tagSize.Width) / 2, ty);
}
}
}
}
}
catch { }
}
/// <summary>柱高比例:0 → 0;其余按 log2 倍增刻度(每翻倍增量恒定),避免小计次不可见、大计次一家独大。</summary>
private static double BarFrac(double v, double max)
{
if (v <= 0) return 0;
if (max <= 1) return 1;
return Math.Log(v + 1, 2) / Math.Log(max + 1, 2);
}
private static string Trim(string s, int max)
{
if (string.IsNullOrEmpty(s)) return "";
return s.Length <= max ? s : s.Substring(0, max - 1) + "…";
}
private static string Fmt(double v)
{
if (v >= 1000) return (v / 1000.0).ToString("0.0") + "k";
return ((long)v).ToString();
}
}
}
...@@ -410,6 +410,18 @@ namespace Tools.MyAgent.Kb ...@@ -410,6 +410,18 @@ 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);
/// <summary>
/// 索引是否已就绪(未启动 / 构建中为 false)。供可观测性健康检查读取,不改变任何检索行为。
/// </summary>
public bool IndexReady
{
get
{
try { return _index != null && _index.IsReady; }
catch { return false; }
}
}
private const int AutoIndexFileThreshold = 2000; private const int AutoIndexFileThreshold = 2000;
/// <summary> /// <summary>
......
This diff is collapsed.
This diff is collapsed.
...@@ -14,6 +14,9 @@ namespace Tools.MyAgent ...@@ -14,6 +14,9 @@ namespace Tools.MyAgent
/// <summary>日志类文件保留天数(agent 日志 / search_misses / db 审计 / tool_results)</summary> /// <summary>日志类文件保留天数(agent 日志 / search_misses / db 审计 / tool_results)</summary>
public const int LogKeepDays = 30; public const int LogKeepDays = 30;
/// <summary>可观测性 runs_*.jsonl 保留天数(比纯日志短:jsonl 更占空间)</summary>
public const int RunsKeepDays = 14;
/// <summary>中间产物保留天数(kb_view_image 扫描件图片,可再生)</summary> /// <summary>中间产物保留天数(kb_view_image 扫描件图片,可再生)</summary>
public const int ImageKeepDays = 7; public const int ImageKeepDays = 7;
...@@ -28,7 +31,8 @@ namespace Tools.MyAgent ...@@ -28,7 +31,8 @@ namespace Tools.MyAgent
/// </summary> /// </summary>
/// <param name="agentRoot">默认 Agent 目录(output 位于其下);日志目录按程序基目录 Logs 计算</param> /// <param name="agentRoot">默认 Agent 目录(output 位于其下);日志目录按程序基目录 Logs 计算</param>
/// <param name="logsRoot">日志根目录(默认 程序基目录/Logs;测试可注入临时目录)</param> /// <param name="logsRoot">日志根目录(默认 程序基目录/Logs;测试可注入临时目录)</param>
public static void Run(string agentRoot, string logsRoot = null) /// <param name="runsKeepDays">可观测性 runs_*.jsonl 的保留天数(默认 <see cref="RunsKeepDays"/>)</param>
public static void Run(string agentRoot, string logsRoot = null, int runsKeepDays = RunsKeepDays)
{ {
if (logsRoot == null) if (logsRoot == null)
logsRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); logsRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
...@@ -43,6 +47,8 @@ namespace Tools.MyAgent ...@@ -43,6 +47,8 @@ namespace Tools.MyAgent
{ {
removed += CleanDir(logsRoot, "agent_*.log", LogKeepDays, now); removed += CleanDir(logsRoot, "agent_*.log", LogKeepDays, now);
removed += CleanDir(logsRoot, "search_misses.csv", LogKeepDays, now); removed += CleanDir(logsRoot, "search_misses.csv", LogKeepDays, now);
removed += CleanDir(logsRoot, "runs_*.jsonl", runsKeepDays, now);
removed += CleanDir(logsRoot, "metrics_*.jsonl", LogKeepDays, now);
removed += CleanDir(logsRoot, "db_query_audit_*.csv", LogKeepDays, now); removed += CleanDir(logsRoot, "db_query_audit_*.csv", LogKeepDays, now);
// 2. 工具结果落盘(超长工具结果溯源文件) // 2. 工具结果落盘(超长工具结果溯源文件)
string toolResults = Path.Combine(logsRoot, "tool_results"); string toolResults = Path.Combine(logsRoot, "tool_results");
......
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
...@@ -122,6 +122,12 @@ ...@@ -122,6 +122,12 @@
<Compile Include="Config\AgentConfig.cs" /> <Compile Include="Config\AgentConfig.cs" />
<Compile Include="Config\RoleTemplateStore.cs" /> <Compile Include="Config\RoleTemplateStore.cs" />
<Compile Include="Config\SecretProtector.cs" /> <Compile Include="Config\SecretProtector.cs" />
<Compile Include="Forms\AgentAdminForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\DashChart.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Forms\AgentHistoryForm.cs"> <Compile Include="Forms\AgentHistoryForm.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
...@@ -153,6 +159,9 @@ ...@@ -153,6 +159,9 @@
<Compile Include="Kb\SynonymIndex.cs" /> <Compile Include="Kb\SynonymIndex.cs" />
<Compile Include="ShareHelper.cs" /> <Compile Include="ShareHelper.cs" />
<Compile Include="Support\AgentLogger.cs" /> <Compile Include="Support\AgentLogger.cs" />
<Compile Include="Support\AgentTelemetry.cs" />
<Compile Include="Support\AgentHealth.cs" />
<Compile Include="Support\TelemetryStore.cs" />
<Compile Include="Support\AgentUtils.cs" /> <Compile Include="Support\AgentUtils.cs" />
<Compile Include="Support\BackupHelper.cs" /> <Compile Include="Support\BackupHelper.cs" />
<Compile Include="Support\ChatMessage.cs" /> <Compile Include="Support\ChatMessage.cs" />
......
This diff is collapsed.
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