From 66701c56acb06c9cb56601f5deb65235f5178071 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Tue, 11 Aug 2026 22:04:42 +0900 Subject: Preserve planned tool parameters and bound failures --- doc/Design.md | 3 ++- src/Agent.Core/Prompts/PromptComposer.cs | 4 ++-- src/Agent.Core/Runtime/AgentRuntime.cs | 4 ++++ src/Agent.Core/Runtime/PlannerService.cs | 2 ++ tests/Agent.Core.Tests/PromptAndRuntimeTests.cs | 6 ++++-- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/doc/Design.md b/doc/Design.md index a637e3b..dad6692 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -375,6 +375,7 @@ topological sort後にforward passで各Taskの累積expected token costを計 - completed TaskのID、状態、usageを保持する。failed Taskをdependency解決済みとして扱わない。 - 同一deny fingerprintの再発、計画未変更、実行可能Taskなし、structured replan失敗は即Failedとする。全Taskがcompletedの場合だけAgent Taskをcompletedにする。 - structured planが空または不正な場合は`think=false`で1回だけ再試行し、それでも失敗した場合はplanning failureとする。外部効果要否が不明なfallback graphは作成しない。 +- 利用者が指定したabsolute path、executable、cwd、引数配列、operation、literal値は、それを使用するTask descriptionへそのまま保持する。後続Taskの操作を先行Taskへ混在させない。 ### 8.5 Ollama usage集計 1 Ollama callの `prompt_eval_count` をTask inputへ、`eval_count` をTask outputへ加算する。tool実行そのものにはtoken costを加算しない。context summary生成に使ったtokenはSession maintenance Taskとして別集計する。 --- @@ -403,7 +404,7 @@ AgentSession 9. current Taskの`requires_external_effect`に基づく実行要件 10. recent Conversation -実行要件は、外部効果が必要なTaskでは文章上の宣言、計画、JSON例を実行済みと扱わず、対応するInternal Toolを直ちに呼び出すよう指示する。成功済みのread結果はExecution Resultから再利用し、不要な再読込を避けてwrite/processへ進む。外部効果が不要なTaskにはToolを強制しない。 +実行要件は、現在runningのTaskだけを実行してqueued Taskを先取りしないことを指示する。外部効果が必要なTaskでは文章上の宣言、計画、JSON例を実行済みと扱わず、対応するInternal Toolを直ちに呼び出すよう指示する。成功済みのread結果はExecution Resultから再利用し、不要な再読込を避けてwrite/processへ進む。外部効果が不要なTaskにはToolを強制しない。未解決のTool失敗後にToolを呼ばない応答も同一失敗として数え、既定の同一失敗上限でfailedにする。 ### 9.3 Summary policy `num_ctx`に対する利用率が設定閾値を超えたとき、古いConversation/Execution Resultを要約する。利用率 = latest `prompt_eval_count` / `num_ctx`(累積 InputTokens ではない)。既定閾値は80%とする。次は削除・曖昧化してはならない。 - 利用者が明示した制約 diff --git a/src/Agent.Core/Prompts/PromptComposer.cs b/src/Agent.Core/Prompts/PromptComposer.cs index 2899088..de209be 100644 --- a/src/Agent.Core/Prompts/PromptComposer.cs +++ b/src/Agent.Core/Prompts/PromptComposer.cs @@ -44,10 +44,10 @@ public static class PromptComposer } private static string ComposeExecutionRequirement(bool requiresExternalEffect) => requiresExternalEffect - ? "The current Planner Task requires a verified external effect. Call the appropriate available Internal Tool now. " + + ? "Perform only the current Running Planner Task; do not perform Queued Tasks. The current Planner Task requires a verified external effect. Call the appropriate available Internal Tool now. " + "A prose description, promise, plan, or fenced JSON example does not perform the operation and cannot complete this Task. " + "After a successful read, use its returned content and proceed to the required write or process operation without repeating the read unnecessarily." - : "The current Planner Task does not require an external effect. Use an Internal Tool only when its verified result is needed; do not claim an unverified result."; + : "Perform only the current Running Planner Task; do not perform Queued Tasks. The current Planner Task does not require an external effect. Use an Internal Tool only when its verified result is needed; do not claim an unverified result."; private static string ComposeExecutionResults(IReadOnlyList results) { diff --git a/src/Agent.Core/Runtime/AgentRuntime.cs b/src/Agent.Core/Runtime/AgentRuntime.cs index f1c7186..3722ff3 100644 --- a/src/Agent.Core/Runtime/AgentRuntime.cs +++ b/src/Agent.Core/Runtime/AgentRuntime.cs @@ -302,6 +302,10 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag if (unresolvedToolFailures.Count > 0) { + string unresolvedFingerprint = string.Join(";", unresolvedToolFailures.OrderBy(static item => item.Key, StringComparer.Ordinal) + .Select(static item => $"{item.Key}={item.Value}")); + ToolUseGuardResult unresolvedGuard = guard.Evaluate(true, false, false, unresolvedFingerprint); + if (unresolvedGuard.Decision == ToolUseGuardDecision.Fail) throw new InvalidOperationException(unresolvedGuard.Observation); messages.Add(new OllamaMessage("system", "Completion is not allowed while tool failures remain unresolved. Retry or correct: " + string.Join("; ", unresolvedToolFailures.Select(static item => $"{item.Key}={item.Value}")))); continue; diff --git a/src/Agent.Core/Runtime/PlannerService.cs b/src/Agent.Core/Runtime/PlannerService.cs index 7d8c3e1..aa7d3a5 100644 --- a/src/Agent.Core/Runtime/PlannerService.cs +++ b/src/Agent.Core/Runtime/PlannerService.cs @@ -289,6 +289,8 @@ public sealed class PlannerService(IOllamaClient ollama, AgentRuntimeSettings se For pure analysis or informational tasks, requires_external_effect should be false. For tasks that modify files, run processes, or produce verifiable side effects, requires_external_effect should be true. Read-only operations (file_read, file_search, and dir list) do not create an external effect and must use requires_external_effect=false. + Preserve every user-supplied absolute path, executable, cwd, argument array, operation name, and literal value exactly in the task that uses it; never abbreviate these values into a shell-like command. + Keep each task limited to its own described operation. Do not put later dependent operations into an earlier task. A single task is valid for simple requests. """; diff --git a/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs b/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs index a40720f..910ea2a 100644 --- a/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs +++ b/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs @@ -29,6 +29,7 @@ public sealed class PromptAndRuntimeTests Assert.DoesNotContain(messages, m => m.Content == "skill instructions"); Assert.Contains(messages, m => m.Role == "system" && m.Content.Contains("requires a verified external effect", StringComparison.Ordinal)); Assert.Contains(messages, m => m.Role == "system" && m.Content.Contains("fenced JSON example does not perform", StringComparison.Ordinal)); + Assert.Contains(messages, m => m.Role == "system" && m.Content.Contains("do not perform Queued Tasks", StringComparison.Ordinal)); Assert.Contains(messages, m => m.Role == "system" && m.Content.Contains("file_read (success): verified content", StringComparison.Ordinal)); Assert.Equal("system", messages[0].Role); Assert.Equal("user", messages[^1].Role); @@ -147,7 +148,8 @@ public sealed class PromptAndRuntimeTests TaskRegistry registry = new(); var dispatcher = new InternalToolDispatcher( new DenyByDefaultPolicy(new HashSet(["process_run"], StringComparer.Ordinal)), new FailingProcessBackend()); - var runtime = new AgentRuntime(registry, new FailingThenFinalOllamaClient(), new AgentRuntimeSettings("", "en", [], "sandbox", MaxIterations: 2), dispatcher); + var runtime = new AgentRuntime(registry, new FailingThenFinalOllamaClient(), + new AgentRuntimeSettings("", "en", [], "sandbox", MaxIterations: 10, SameFailureLimit: 2), dispatcher); TaskSnapshot task = registry.Enqueue("run build", null, null); registry.Transition(task.TaskId, AgentTaskState.Planning); registry.Transition(task.TaskId, AgentTaskState.Running); @@ -156,7 +158,7 @@ public sealed class PromptAndRuntimeTests Assert.True(registry.TryGet(task.TaskId, null, out TaskSnapshot? failed)); Assert.Equal(AgentTaskState.Failed, failed!.State); - Assert.Contains("iteration limit", failed.Result, StringComparison.OrdinalIgnoreCase); + Assert.Contains("external effect", failed.Result, StringComparison.OrdinalIgnoreCase); } [Fact] -- cgit v1.2.3