You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
7.1 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using AI.Interface;
using AI.Models;
using System;
using System.Diagnostics;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace AI.Workflow
{
/// <summary>
/// Planner - 负责规划任务,将任务分解成步骤
/// </summary>
public class Planner
{
private readonly IChatBackend _chatBackend;
private readonly ChatSession _planningSession;
public Planner(IChatBackend chatBackend)
{
_chatBackend = chatBackend;
_planningSession = new ChatSession();
// 初始化规划提示词
_planningSession.AddSystemMessage(GetPlanningPrompt());
}
/// <summary>
/// 规划任务
/// </summary>
/// <param name="goal">任务目标</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>规划结果</returns>
public async Task<Plan> PlanAsync(string goal, CancellationToken cancellationToken = default)
{
var plan = new Plan
{
Goal = goal
};
try
{
// 请求 Planner 生成规划
string planningPrompt = $"请为以下任务制定详细的执行计划:\n\n任务{goal}\n\n请将任务分解为具体的步骤每个步骤应该是可执行的、明确的。";
string response = await _chatBackend.AskAsync(planningPrompt, _planningSession, null, cancellationToken);
// 解析规划结果
ParsePlanFromResponse(response, plan);
Debug.WriteLine($"Planner 生成了 {plan.Steps.Count} 个步骤");
}
catch (Exception ex)
{
Debug.WriteLine($"Planner 规划失败: {ex.Message}");
throw;
}
return plan;
}
/// <summary>
/// 从响应中解析规划步骤
/// </summary>
private void ParsePlanFromResponse(string response, Plan plan)
{
// 尝试解析 JSON 格式
if (TryParseJsonPlan(response, plan))
{
return;
}
// 尝试解析列表格式1. 步骤1 2. 步骤2 ...
if (TryParseListPlan(response, plan))
{
return;
}
// 如果都解析失败,将整个响应作为单个步骤
plan.Steps.Add(new PlanStep
{
Order = 1,
Description = response.Trim()
});
}
/// <summary>
/// 尝试解析 JSON 格式的规划
/// </summary>
private bool TryParseJsonPlan(string response, Plan plan)
{
try
{
// 尝试提取 JSON 部分
var jsonMatch = Regex.Match(response, @"\{[\s\S]*\}");
if (!jsonMatch.Success)
{
return false;
}
var json = jsonMatch.Value;
var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.TryGetProperty("steps", out var stepsElement) && stepsElement.ValueKind == JsonValueKind.Array)
{
int order = 1;
foreach (var stepElement in stepsElement.EnumerateArray())
{
var step = new PlanStep
{
Order = order++,
Description = stepElement.GetProperty("description").GetString() ?? string.Empty
};
plan.Steps.Add(step);
}
return true;
}
}
catch
{
// JSON 解析失败,继续尝试其他格式
}
return false;
}
/// <summary>
/// 尝试解析列表格式的规划
/// </summary>
private bool TryParseListPlan(string response, Plan plan)
{
// 匹配各种列表格式1. 2. 3. 或 - 或 * 等
var patterns = new[]
{
@"^\s*(\d+)\.\s+(.+)$", // 1. 步骤
@"^\s*[-*]\s+(.+)$", // - 步骤 或 * 步骤
@"^\s*(\d+)\)\s+(.+)$" // 1) 步骤
};
var lines = response.Split('\n', StringSplitOptions.RemoveEmptyEntries);
int order = 1;
foreach (var line in lines)
{
var trimmedLine = line.Trim();
if (string.IsNullOrWhiteSpace(trimmedLine))
continue;
foreach (var pattern in patterns)
{
var match = Regex.Match(trimmedLine, pattern);
if (match.Success)
{
string description = match.Groups.Count > 2 ? match.Groups[2].Value : match.Groups[1].Value;
plan.Steps.Add(new PlanStep
{
Order = order++,
Description = description.Trim()
});
break;
}
}
}
return plan.Steps.Count > 0;
}
/// <summary>
/// 获取规划提示词
/// </summary>
private string GetPlanningPrompt()
{
return @"你是一个专业的任务规划专家,擅长将复杂任务分解为清晰、可执行的步骤序列。
【核心职责】
将用户提出的任务目标分解为一系列有序、可执行的步骤,确保每个步骤都有明确的目标和可衡量的结果。
【规划原则】
1. **原子性**:每个步骤应该是独立的、可单独执行的最小任务单元
2. **顺序性**:步骤之间必须有清晰的逻辑顺序和依赖关系
3. **可执行性**:每个步骤的描述应该具体、明确,能够直接指导执行
4. **完整性**:确保所有必要的步骤都被包含,不遗漏关键环节
5. **可验证性**:每个步骤完成后应该有明确的验证标准
【步骤描述要求】
- 使用动词开头,明确表达要执行的动作(如:""加载文件""、""验证数据""、""生成报告""
- 包含必要的上下文信息(如:""从指定路径加载 XYZ 文件""
- 避免模糊表述,使用具体、可操作的语言
- 每个步骤描述控制在 20-50 字之间
【输出格式】
优先使用 JSON 格式,结构如下:
{
""steps"": [
{""description"": ""步骤1的详细描述""},
{""description"": ""步骤2的详细描述""},
...
]
}
如果无法返回 JSON请使用编号列表格式
1. 步骤1的详细描述
2. 步骤2的详细描述
...
【注意事项】
- 不要创建过于细碎的步骤,也不要创建过于宏大的步骤
- 考虑步骤之间的依赖关系,确保顺序合理
- 如果任务涉及多个阶段,可以按阶段组织步骤
- 对于需要用户输入或确认的步骤,明确标注";
}
}
}