Commit c64815c5 authored by 段廷瑞's avatar 段廷瑞

feat: 智能体功能完善

parent 79a55a4c
......@@ -65,3 +65,4 @@ bin
*.zip
/AppHome/AgentChat
/AppHome/Agent/history
/AppHome/Agent
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="UpdateTime" value="2021-12-30 10:52:06.703" />
<add key="ExceptFiles" value="" />
<add key="ContainFiles" value="" />
<add key="PatchTimeFrame_Dll" value="48" />
<add key="PatchTimeFrame_Sql" value="720" />
<add key="PatchTimeFrame_Xml" value="720" />
<add key="Exclude_PDB" value="1" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.3.2.10" newVersion="1.3.2.10" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<appSettings>
<add key="UpdateTime" value="2021-12-30 10:52:06.703" />
<add key="ExceptFiles" value="" />
<add key="ContainFiles" value="" />
<add key="PatchTimeFrame_Dll" value="48" />
<add key="PatchTimeFrame_Sql" value="720" />
<add key="PatchTimeFrame_Xml" value="720" />
<add key="Exclude_PDB" value="1" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.3.2.10" newVersion="1.3.2.10" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="BouncyCastle.Crypto" publicKeyToken="0e99375e54769942" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.8.6.0" newVersion="1.8.6.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.5.0" newVersion="4.1.5.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.4.0" newVersion="4.5.4.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.HashCode" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
using System;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace Tools.MyAgent.Agent
{
/// <summary>
/// 故障转移策略:主模型连续失败时自动切换到备用模型。
/// <para>从 AgentEngine 解耦,独立测试、独立配置。</para>
/// </summary>
public sealed class FailoverPolicy
{
private IOpenAiChatClient _primary;
private IOpenAiChatClient _backup;
private IOpenAiChatClient _current;
private int _consecutiveFailures;
private bool _failedOver;
public bool Enabled { get; private set; }
public bool IsActive => _failedOver;
public FailoverPolicy(IOpenAiChatClient primary, IOpenAiChatClient backup)
{
_primary = primary;
_backup = backup;
_current = primary;
}
public void Configure(bool enabled, string backupBaseUrl, string backupModel, string backupApiKey)
{
Enabled = enabled;
if (_backup != null)
{
_backup.BaseUrl = string.IsNullOrEmpty(backupBaseUrl) ? "http://localhost:11434/v1" : backupBaseUrl;
_backup.ApiKey = backupApiKey ?? "";
_backup.Model = string.IsNullOrEmpty(backupModel) ? "qwen2.5" : backupModel;
_backup.Temperature = 0.3;
_backup.MaxTokens = 8192;
}
if (!enabled && _failedOver)
{
RestorePrimary();
}
_consecutiveFailures = 0;
}
public IOpenAiChatClient CurrentClient => _current;
/// <summary>主客户端被外部重建(如代理开关变化)时同步替换引用,避免策略持有已释放的旧客户端</summary>
public void SetPrimary(IOpenAiChatClient primary)
{
if (primary == null) return;
_primary = primary;
if (!_failedOver) _current = primary; // 未处于备用状态时立即跟随新主客户端
}
/// <summary>记录一次成功调用</summary>
public void RecordSuccess()
{
_consecutiveFailures = 0;
if (_failedOver)
{
RestorePrimary();
_failedOver = false;
}
}
/// <summary>记录一次失败;若启用且连续失败达阈值,返回 true 表示已切换</summary>
public bool RecordFailure(Exception ex)
{
if (!IsServiceError(ex))
{
_consecutiveFailures = 0;
return false;
}
_consecutiveFailures++;
if (Enabled && _consecutiveFailures >= 2 && !_failedOver)
{
SwitchToBackup();
_failedOver = true;
_consecutiveFailures = 0;
return true;
}
return false;
}
private void SwitchToBackup()
{
try { _backup.UseSystemProxy = true; } catch { }
_current = _backup;
}
private void RestorePrimary()
{
_current = _primary;
}
private static bool IsServiceError(Exception ex)
{
var msg = ex?.InnerException?.Message ?? ex?.Message ?? "";
return msg.Contains("429") || msg.Contains("500") || msg.Contains("502") || msg.Contains("503")
|| msg.Contains("504") || msg.Contains("超时") || msg.Contains("timeout") || msg.Contains("timed out")
|| ex is System.Net.Http.HttpRequestException
|| msg.Contains("connection") || msg.Contains("连接");
}
}
}
\ No newline at end of file
using Newtonsoft.Json.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Tools.MyAgent.Agent
{
/// <summary>
/// OpenAI 兼容聊天补全客户端接口,支持 SSE 流式 + function calling。
/// <para>换厂商只改 BaseUrl / Model / ApiKey。</para>
/// </summary>
public interface IOpenAiChatClient
{
string BaseUrl { get; set; }
string ApiKey { get; set; }
string Model { get; set; }
double Temperature { get; set; }
int MaxTokens { get; set; }
bool UseSystemProxy { get; set; }
/// <summary>发起一次流式补全,返回最终结果</summary>
Task<ChatResult> SendAsync(JArray messages, JArray tools, bool enableTools, CancellationToken ct);
/// <summary>非流式补全,一次性返回正文</summary>
Task<string> CompleteAsync(JArray messages, int maxTokens, CancellationToken ct);
/// <summary>连接测试</summary>
Task<(bool Ok, string Message)> TestConnectionAsync(CancellationToken ct);
/// <summary>取消当前请求</summary>
void Cancel();
/// <summary>释放底层资源</summary>
void Dispose();
}
}
\ No newline at end of file
This diff is collapsed.
using Newtonsoft.Json.Linq;
using Tools.MyAgent.Agent.Tools;
namespace Tools.MyAgent.Agent
{
/// <summary>
/// 知识库工具定义(OpenAI function-calling JSON Schema)
/// <para>随首个请求下发给模型,实现见 KbService(沙箱校验路径防穿越)。</para>
/// 工具定义下发(OpenAI function-calling JSON Schema)。
/// <para>由 ToolRegistry 注册表驱动:注册了哪些工具就下发哪些,实现见各 ITool(沙箱校验路径防穿越)。</para>
/// </summary>
public static class ToolSchema
{
public static JArray Build()
{
var array = new JArray
{
Function("kb_find",
"在知识库中按文件名关键字或 glob 模式查找文件,返回匹配的文件相对路径。用于用户提到文件名、文档名或文件类型时定位。",
@"{'type':'object',
'properties':{
'pattern':{'type':'string','description':" + Quoted("文件名关键字或 glob 模式,例如 '销售报表'、'*.pdf'、'2024*'、'*合同*'") + @"},
'maxResults':{'type':'integer','description':'最多返回条数','minimum':1,'maximum':100}
},
'required':['pattern']}"),
Function("kb_grep",
"在知识库文件内容中搜索关键字或正则表达式,返回命中文件的相对路径、行号与上下文片段。用于内容检索与关键词定位。",
@"{'type':'object',
'properties':{
'query':{'type':'string','description':" + Quoted("搜索关键字或正则表达式,例如 '验收标准'、'退款(流程|规则)'") + @"},
'glob':{'type':'string','description':" + Quoted("限定搜索的文件类型,例如 '*.md';不填则搜索全部可检索文本文件") + @"},
'maxResults':{'type':'integer','description':'最多返回命中条数','minimum':1,'maximum':50}
},
'required':['query']}"),
Function("kb_list",
"列出知识库根目录或指定子目录下的文件与文件夹,用于了解知识库组织结构。",
@"{'type':'object',
'properties':{
'path':{'type':'string','description':" + Quoted("知识库内相对路径,默认根目录 '.'") + @"},
'recursive':{'type':'boolean','description':" + Quoted("是否递归列出子目录,默认 false") + @"}
}}"),
Function("kb_read",
"读取知识库内指定文件的文本内容(文本文件直接读取;docx/xlsx 自动提取文本,提取失败会明确标注)。",
@"{'type':'object',
'properties':{
'path':{'type':'string','description':" + Quoted("知识库内的相对路径,如 'docs/操作手册.md'") + @"},
'offset':{'type':'integer','description':'起始行号,默认 1','minimum':1},
'limit':{'type':'integer','description':'最多读取行数,默认 200','minimum':1,'maximum':500}
},
'required':['path']}"),
Function("kb_write",
"在默认输出目录(程序目录下 Agent 文件夹)创建或更新一个 UTF-8 文本文件,用于保存用户要求生成的文档或整理总结。输出目录固定,与所选知识库目录无关;覆盖已有文件前请先征得用户同意。",
@"{'type':'object',
'properties':{
'path':{'type':'string','description':" + Quoted("输出目录内的相对路径,如 'docs/新知识.md';已存在则覆盖,不存在则自动创建父目录") + @"},
'content':{'type':'string','description':" + Quoted("写入的完整文本内容(UTF-8)") + @"},
'append':{'type':'boolean','description':" + Quoted("true 表示在文件末尾追加,默认 false(整体覆盖)") + @"}
},
'required':['path','content']}"),
Function("kb_write_kb",
"在所选知识库目录内创建或更新一个 UTF-8 文本文件(知识库自我进化)。仅当用户明确要求「存入知识库 / 更新知识库文档 / 保存到共享文件夹」时使用;一般保存或生成文档请用 kb_write(写入默认输出目录)。覆盖已有文件前请先征得用户同意。",
@"{'type':'object',
'properties':{
'path':{'type':'string','description':" + Quoted("知识库内的相对路径,如 'docs/新知识.md';已存在则覆盖,不存在则自动创建父目录") + @"},
'content':{'type':'string','description':" + Quoted("写入的完整文本内容(UTF-8)") + @"},
'append':{'type':'boolean','description':" + Quoted("true 表示在文件末尾追加,默认 false(整体覆盖)") + @"}
},
'required':['path','content']}"),
};
return array;
}
/// <summary>构建默认工具(核心能力)的 JSON Schema</summary>
public static JArray Build() => Build(false);
private static JToken Function(string name, string description, string parametersJson)
/// <summary>构建工具 JSON Schema;includeOptional=true 时包含高级/可选工具</summary>
public static JArray Build(bool includeOptional)
{
return JObject.Parse($@"
{{'type':'function',
'function':{{
'name':{Quoted(name)},
'description':{Quoted(description)},
'parameters':{parametersJson}
}}}}");
var tools = includeOptional ? ToolRegistry.All : ToolRegistry.DefaultTools;
var array = new JArray();
foreach (var tool in tools)
array.Add(Function(tool.Name, tool.Description, tool.ParametersJson));
return array;
}
private static string Quoted(string s)
/// <summary>
/// 单个 function 定义。name/description 以 JToken 安全赋值(自动转义);
/// parameters 沿用各 ITool 的 Schema 字符串(JObject.Parse 支持单引号,解析失败时回退为标准空 object Schema)。
/// </summary>
private static JObject Function(string name, string description, string parametersJson)
{
return Newtonsoft.Json.JsonConvert.ToString(s);
JObject parameters;
try
{
parameters = JObject.Parse(parametersJson);
}
catch
{
parameters = new JObject { ["type"] = "object", ["properties"] = new JObject() };
}
return new JObject
{
["type"] = "function",
["function"] = new JObject
{
["name"] = name,
["description"] = description,
["parameters"] = parameters,
},
};
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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