feat: 拆分 CLI 与网页宿主,并在无有效城市时暂停等人确认
将原单体 MAF1 拆成 MAF1(控制台工作流)和 MAF1.Web(可视化编排)。 抽城市失败时暂停:网页弹确认、CLI 控制台询问,超时采用默认结束查询; 也可改填城市后继续。共享决策模型放在 MAF1.Core。
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
using MAF1.Agents;
|
||||
|
||||
namespace MAF1.Decisions;
|
||||
|
||||
public sealed class DecisionRequest
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string NodeId { get; set; } = "";
|
||||
public string Prompt { get; set; } = "";
|
||||
public string? Reason { get; set; }
|
||||
public List<DecisionOption> Options { get; set; } = [];
|
||||
public string DefaultOptionId { get; set; } = "";
|
||||
public int TimeoutSeconds { get; set; }
|
||||
public DateTimeOffset Deadline { get; set; }
|
||||
}
|
||||
|
||||
public sealed class DecisionOption
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string Label { get; set; } = "";
|
||||
public string? Description { get; set; }
|
||||
public bool RequiresText { get; set; }
|
||||
public string? TextPlaceholder { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
public sealed class DecisionAnswer
|
||||
{
|
||||
public string OptionId { get; set; } = "";
|
||||
public string? Text { get; set; }
|
||||
public bool TimedOut { get; set; }
|
||||
}
|
||||
|
||||
public static class DecisionOptionIds
|
||||
{
|
||||
public const string Stop = "stop";
|
||||
public const string QueryCities = "query-cities";
|
||||
}
|
||||
|
||||
public static class EmptyCityDecision
|
||||
{
|
||||
public static DecisionRequest Create(string nodeId, string? reason, int timeoutSeconds)
|
||||
{
|
||||
int seconds = Math.Clamp(timeoutSeconds, 1, 600);
|
||||
return new DecisionRequest
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("n")[..12],
|
||||
NodeId = nodeId,
|
||||
Reason = reason,
|
||||
Prompt = "没有读到有效城市。默认结束后续查询;也可以改填其它城市后继续。超时将自动采用默认方案。",
|
||||
DefaultOptionId = DecisionOptionIds.Stop,
|
||||
TimeoutSeconds = seconds,
|
||||
Deadline = DateTimeOffset.UtcNow.AddSeconds(seconds),
|
||||
Options =
|
||||
[
|
||||
new DecisionOption
|
||||
{
|
||||
Id = DecisionOptionIds.Stop,
|
||||
Label = "结束查询(默认)",
|
||||
Description = "保持当前结果,跳过需要城市的后续节点。",
|
||||
IsDefault = true,
|
||||
},
|
||||
new DecisionOption
|
||||
{
|
||||
Id = DecisionOptionIds.QueryCities,
|
||||
Label = "改查其它城市",
|
||||
Description = "手动输入城市名,下游天气节点会按这份名单继续。",
|
||||
RequiresText = true,
|
||||
TextPlaceholder = "例如:成都、北京",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
public static bool TryContinueWithCities(DecisionAnswer answer, out List<string> cities)
|
||||
{
|
||||
cities = [];
|
||||
if (answer.TimedOut)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool queryCities = answer.OptionId.Equals(DecisionOptionIds.QueryCities, StringComparison.OrdinalIgnoreCase);
|
||||
if (!queryCities)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cities = ParseCities(answer.Text);
|
||||
return cities.Count > 0;
|
||||
}
|
||||
|
||||
public static List<string> ParseCities(string? text)
|
||||
=> AgentInputs.ReadStringList(
|
||||
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase) { ["cities"] = text },
|
||||
"cities");
|
||||
}
|
||||
Reference in New Issue
Block a user