Files
MAF1/MAF1.Core/Tools/FileTools.cs
T
admin777 950d09ddb0 docs: 为 CLI、网页编排器和插件协议补充学习向注释
在核心类型、图执行器、stdio 插件和前端入口写清职责与对照路径,不改运行行为。
2026-08-28 11:51:48 +08:00

81 lines
2.4 KiB
C#

using System.ComponentModel;
using System.Text;
namespace MAF1.Tools;
/// <summary>
/// 给 LLM 调用的本地读文件工具。Description 属性会进工具 schema,模型据此决定何时调用。
/// 路径解析会试当前目录、exe 目录、以及 MAF1_CONTENT_ROOT(插件进程由宿主注入)。
/// </summary>
public static class FileTools
{
[Description("Read the full UTF-8 text of a local file. Always call this before judging city names.")]
public static string ReadTextFile([Description("Absolute or relative path of the file to read.")] string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return "未提供文件路径。";
}
string? resolved = Resolve(path.Trim().Trim('"'));
if (resolved is null)
{
return $"找不到文件:{path}";
}
FileInfo info = new(resolved);
if (info.Length > 64 * 1024)
{
return $"文件过大({info.Length} 字节),请换一个不超过 64KB 的文本文件。";
}
return File.ReadAllText(resolved, Encoding.UTF8);
}
/// <summary>相对路径时依次试 cwd、exe 目录、内容根、再往上两级父目录(适配 bin/Debug/netX)。</summary>
private static string? Resolve(string path)
{
if (Path.IsPathRooted(path) && File.Exists(path))
{
return Path.GetFullPath(path);
}
string fromCwd = Path.GetFullPath(path);
if (File.Exists(fromCwd))
{
return fromCwd;
}
foreach (string root in SearchRoots())
{
string candidate = Path.GetFullPath(Path.Combine(root, path));
if (File.Exists(candidate))
{
return candidate;
}
}
return null;
}
private static IEnumerable<string> SearchRoots()
{
yield return AppContext.BaseDirectory;
string? contentRoot = Environment.GetEnvironmentVariable("MAF1_CONTENT_ROOT");
if (!string.IsNullOrWhiteSpace(contentRoot))
{
yield return contentRoot;
}
DirectoryInfo? parent = Directory.GetParent(AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar));
if (parent is not null)
{
yield return parent.FullName;
if (parent.Parent is not null)
{
yield return parent.Parent.FullName;
}
}
}
}