diff options
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | prompts/user-system.md | 3 | ||||
| -rw-r--r-- | skills/python-via-uv/SKILL.md | 27 | ||||
| -rw-r--r-- | skills/python-via-uv/agents/openai.yaml | 4 | ||||
| -rw-r--r-- | src/Agent.App/Agent.App.csproj | 2 | ||||
| -rw-r--r-- | src/Agent.App/Configuration/SkillLoader.cs | 24 | ||||
| -rw-r--r-- | src/Agent.App/Program.cs | 4 | ||||
| -rw-r--r-- | tests/Agent.Core.Tests/ConfigurationTests.cs | 13 |
8 files changed, 77 insertions, 4 deletions
@@ -91,6 +91,10 @@ sandbox: Ollama通信は管理Broker側から行うため、Sandbox processのnetwork許可とは独立です。 +## Agent Skills + +`skills/<skill-name>/SKILL.md`形式と、互換用の`skills/*.md`を読み込みます。SkillはLLMへの補助指示であり、Filesystem/Process/Network権限は付与しません。Skillが使用する実行ファイルは、別途`agent.yaml`でabsolute pathとSHA-256を承認してください。 + ## stdio WFP操作のため、stdioではMCP Client自体を管理者PowerShellから起動します。Client設定例: diff --git a/prompts/user-system.md b/prompts/user-system.md index 2986739..f1d255b 100644 --- a/prompts/user-system.md +++ b/prompts/user-system.md @@ -1 +1,2 @@ -<!-- Add project-specific instructions here. This prompt cannot override the embedded Fixed System Prompt. --> +あなたはユーザーの身近な相棒です。親しみやすく率直に会話し、考えを整理し、必要なときだけ実作業を提案・実行してください。 +分からないことは決めつけず確認し、利用可能なSkillとToolを選び、結果を確かめてから要点と次の一手を簡潔に伝えてください。 diff --git a/skills/python-via-uv/SKILL.md b/skills/python-via-uv/SKILL.md new file mode 100644 index 0000000..d840e4d --- /dev/null +++ b/skills/python-via-uv/SKILL.md @@ -0,0 +1,27 @@ +--- +name: python-via-uv +description: uv経由でPythonコードを作成・実行・検証する。ユーザーがPythonによる計算、データ処理、自動化、試作、デバッグ、またはスクリプト実行を求めたときに使用する。 +--- + +# Python via uv + +1. 作業directoryと必要な入出力を確認し、コードは許可されたdirectory内の`.py`へ`file_edit`で保存する。複雑なコードを`python -c`へ詰め込まない。 +2. 承認済み`uv.exe`を`process_run`し、projectではcwdから`uv python find --offline --no-python-downloads --resolve-links`、単独scriptでは同じ引数へ`--no-project`を追加してinterpreterの絶対pathを取得する。 +3. 取得した承認済み`python.exe`を別の`process_run`で起動し、`-I <script>`を渡す。shellを経由せず、引数は配列で分離する。 +4. v1 Sandboxは全descendant生成を禁止するため、`uv run`は使用しない。uvが返したPythonをBrokerから直接起動することで、両方のpath/SHA-256 approvalとchild-process禁止を維持する。 +5. dependencyが環境にない場合は勝手にnetwork接続やinstallを行わず、必要なpackageと許可をユーザーへ確認する。依頼なしに`uv add`や`uv pip install`を実行しない。 +6. `stdout`、`stderr`、exit codeを確認する。exit code 0と期待結果を確認できた場合だけ成功と報告し、生成・変更したfileも示す。 + +`process_run`の基本形: + +```text +executable: <承認済みuv.exe> +args: ["python", "find", "--offline", "--no-python-downloads", "--resolve-links", "--no-project"] +cwd: <許可された作業directory> + +executable: <uvが返した承認済みpython.exe> +args: ["-I", "script.py"] +cwd: <許可された作業directory> +``` + +`uv.exe`または`python.exe`が未承認、あるいは作業directoryのread/write/execute権限が不足している場合は迂回せず、必要な設定を具体的に報告する。 diff --git a/skills/python-via-uv/agents/openai.yaml b/skills/python-via-uv/agents/openai.yaml new file mode 100644 index 0000000..de95aab --- /dev/null +++ b/skills/python-via-uv/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Python via uv" + short_description: "Run reproducible Python tasks safely through uv" + default_prompt: "Use $python-via-uv to create and run Python code, then verify the result." diff --git a/src/Agent.App/Agent.App.csproj b/src/Agent.App/Agent.App.csproj index a42ebad..4059624 100644 --- a/src/Agent.App/Agent.App.csproj +++ b/src/Agent.App/Agent.App.csproj @@ -18,7 +18,7 @@ <Content Include="..\..\config\agent.yaml" Link="agent.yaml" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" /> <Content Include="..\..\config\locales\**\*.json" Link="config\locales\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" /> <Content Include="..\..\prompts\user-system.md" Link="prompts\user-system.md" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" /> - <Content Include="..\..\skills\.keep" Link="skills\.keep" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" /> + <Content Include="..\..\skills\**\*" Link="skills\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" /> <Content Include="..\..\README.md" Link="README.md" CopyToPublishDirectory="PreserveNewest" /> </ItemGroup> </Project> diff --git a/src/Agent.App/Configuration/SkillLoader.cs b/src/Agent.App/Configuration/SkillLoader.cs new file mode 100644 index 0000000..6923c12 --- /dev/null +++ b/src/Agent.App/Configuration/SkillLoader.cs @@ -0,0 +1,24 @@ +namespace Agent.App.Configuration; + +public static class SkillLoader +{ + public static string[] Load(string directory) + { + var paths = new List<string>(); + paths.AddRange(Directory.EnumerateFiles(directory, "*.md", SearchOption.TopDirectoryOnly)); + + foreach (string child in Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly)) + { + var info = new DirectoryInfo(child); + if ((info.Attributes & FileAttributes.ReparsePoint) != 0) continue; + string standardSkill = Path.Combine(info.FullName, "SKILL.md"); + if (File.Exists(standardSkill)) paths.Add(standardSkill); + } + + return paths + .Distinct(StringComparer.OrdinalIgnoreCase) + .Order(StringComparer.OrdinalIgnoreCase) + .Select(File.ReadAllText) + .ToArray(); + } +} diff --git a/src/Agent.App/Program.cs b/src/Agent.App/Program.cs index d9ee17a..82a3f36 100644 --- a/src/Agent.App/Program.cs +++ b/src/Agent.App/Program.cs @@ -85,7 +85,7 @@ internal static class Program { string userPrompt = File.ReadAllText(ConfigurationLoader.Resolve(baseDirectory, configuration.Agent.UserSystemPrompt)); string skillDirectory = ConfigurationLoader.Resolve(baseDirectory, configuration.Skills.Directory); - string[] skills = Directory.EnumerateFiles(skillDirectory, "*.md", SearchOption.TopDirectoryOnly).Select(File.ReadAllText).ToArray(); + string[] skills = SkillLoader.Load(skillDirectory); var runtimeSettings = new AgentRuntimeSettings(userPrompt, language, skills, "Windows AppContainer, ACL, Job Object, child-process restriction, and WFP sandbox capabilities.", configuration.Agent.MaxIterations, configuration.Agent.SameFailureLimit, locale.Get("ollama.vision_unsupported")); var visionLimits = new VisionLimits(configuration.Mcp.Images.MaxCount, configuration.Mcp.Images.MaxBytesEach, configuration.Mcp.Images.MaxTotalBytes); await McpHost.RunStdioAsync(locale, OllamaSettingsFactory.Create(configuration.Ollama), runtimeSettings, visionLimits, CancellationToken.None, @@ -96,7 +96,7 @@ internal static class Program { string userPrompt = File.ReadAllText(ConfigurationLoader.Resolve(baseDirectory, configuration.Agent.UserSystemPrompt)); string skillDirectory = ConfigurationLoader.Resolve(baseDirectory, configuration.Skills.Directory); - string[] skills = Directory.EnumerateFiles(skillDirectory, "*.md", SearchOption.TopDirectoryOnly).Select(File.ReadAllText).ToArray(); + string[] skills = SkillLoader.Load(skillDirectory); var runtimeSettings = new AgentRuntimeSettings(userPrompt, language, skills, "Windows AppContainer, ACL, Job Object, child-process restriction, and WFP sandbox capabilities.", configuration.Agent.MaxIterations, configuration.Agent.SameFailureLimit, locale.Get("ollama.vision_unsupported")); var visionLimits = new VisionLimits(configuration.Mcp.Images.MaxCount, configuration.Mcp.Images.MaxBytesEach, configuration.Mcp.Images.MaxTotalBytes); await McpHost.RunHttpAsync(locale, OllamaSettingsFactory.Create(configuration.Ollama), runtimeSettings, visionLimits, diff --git a/tests/Agent.Core.Tests/ConfigurationTests.cs b/tests/Agent.Core.Tests/ConfigurationTests.cs index 7ef4afb..2c52b0c 100644 --- a/tests/Agent.Core.Tests/ConfigurationTests.cs +++ b/tests/Agent.Core.Tests/ConfigurationTests.cs @@ -42,6 +42,19 @@ public sealed class ConfigurationTests Assert.Equal("http", result.Transport); } + [Fact] + public void SkillLoader_supports_legacy_markdown_and_standard_skill_directories() + { + using var directory = new TestDirectory(); + directory.File("skills/legacy.md", "legacy skill"); + directory.File("skills/python-via-uv/SKILL.md", "standard skill"); + directory.File("skills/python-via-uv/reference.md", "must not be loaded as a skill"); + + string[] skills = SkillLoader.Load(Path.Combine(directory.Path, "skills")); + + Assert.Equal(["legacy skill", "standard skill"], skills); + } + private static string CreateValidConfiguration(TestDirectory directory) { directory.File("prompts/user-system.md", string.Empty); |
