summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-08-11 22:23:27 +0900
committertanaka takashi <isazaka@gmail.com>2026-08-11 22:23:27 +0900
commitab69aaa72903b0ac5a35c4823ee97a629b3984fb (patch)
tree204e3ca65df933da44299ac6a35741105ec38358
parent842e55654db060c84448f3a0deddb24f77fad5a7 (diff)
Enforce planner task effect scope
-rw-r--r--doc/Design.md2
-rw-r--r--src/Agent.Core/Runtime/AgentRuntime.cs19
-rw-r--r--tests/Agent.Core.Tests/PromptAndRuntimeTests.cs2
3 files changed, 19 insertions, 4 deletions
diff --git a/doc/Design.md b/doc/Design.md
index ac496bb..6897064 100644
--- a/doc/Design.md
+++ b/doc/Design.md
@@ -358,7 +358,7 @@ Task
RequiresExternalEffect: bool
RequiredEffectTools: set<Internal Tool name>
```
-`RequiresExternalEffect=true`のTaskは、Tool catalogに存在し副作用を持つTool名を`RequiredEffectTools`へ1件以上持つ。Planner structured outputの`required_effect_tools`から構築し、Runtimeは成功した副作用Toolがこの集合に含まれる場合だけ期待副作用の証跡として扱う。例えば`process_run`が必要なTask中の`file_edit`成功は完了根拠にしない。`RequiresExternalEffect=false`では集合を空にする。
+`RequiresExternalEffect=true`のTaskは、Tool catalogに存在し副作用を持つTool名を`RequiredEffectTools`へ1件以上持つ。Planner structured outputの`required_effect_tools`から構築し、Runtimeは成功した副作用Toolがこの集合に含まれる場合だけ期待副作用の証跡として扱う。例えば`process_run`が必要なTask中の`file_edit`成功は完了根拠にしない。current Taskの集合に含まれない副作用ToolはBackendへdispatchせず、Task scope外のTool resultとしてLLMへ返してqueued Taskの先取りを決定的に防ぐ。`RequiresExternalEffect=false`では集合を空にする。
### 8.2 PERT計算
```text
ExpectedTokens = (O + 4*M + P) / 6.0
diff --git a/src/Agent.Core/Runtime/AgentRuntime.cs b/src/Agent.Core/Runtime/AgentRuntime.cs
index 333d557..9c051e9 100644
--- a/src/Agent.Core/Runtime/AgentRuntime.cs
+++ b/src/Agent.Core/Runtime/AgentRuntime.cs
@@ -266,7 +266,14 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag
foreach (OllamaToolCall call in calls)
{
ToolExecutionResult toolResult;
- try
+ bool outOfScopeEffect = IsOutOfScopeEffectCall(call, plannerTask.RequiredEffectTools);
+ if (outOfScopeEffect)
+ {
+ toolResult = new(false,
+ $"The '{call.Function.Name}' external effect belongs to another Planner Task. Perform only the current Task; required effect Tool(s): {string.Join(", ", plannerTask.RequiredEffectTools.Order(StringComparer.Ordinal))}.",
+ false, $"task_scope:{plannerTask.Id}:{call.Function.Name}");
+ }
+ else try
{
toolResult = await _tools.DispatchAsync(taskId, call.Function.Name, call.Function.Arguments, cancellationToken).ConfigureAwait(false);
}
@@ -283,7 +290,7 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag
// task_update is a metadata tool for graph bookkeeping; its failures should not block completion.
bool isMetadataTool = call.Function.Name == "task_update";
if (toolResult.Success) unresolvedToolFailures.Remove(call.Function.Name);
- else if (!isMetadataTool) unresolvedToolFailures[call.Function.Name] = toolResult.FailureFingerprint ?? toolResult.Output;
+ else if (!isMetadataTool && !outOfScopeEffect) unresolvedToolFailures[call.Function.Name] = toolResult.FailureFingerprint ?? toolResult.Output;
// Record ExecutionResult in session for context summary.
session.AppendExecutionResult(new ExecutionResult(taskId, call.Function.Name, toolResult.Success, toolResult.Output));
@@ -330,6 +337,14 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag
return finalContent;
}
+ private static bool IsOutOfScopeEffectCall(OllamaToolCall call, IReadOnlySet<string> requiredEffectTools)
+ {
+ bool createsExternalEffect = call.Function.Name is "file_edit" or "process_run" ||
+ call.Function.Name == "dir" && call.Function.Arguments.TryGetProperty("operation", out JsonElement operation) &&
+ operation.GetString() is "mkdir" or "rmdir";
+ return createsExternalEffect && !requiredEffectTools.Contains(call.Function.Name);
+ }
+
/// <summary>
/// Builds a TaskGraph summary for the prompt context (Design §9.2 item 6).
/// Shows the current task position within the overall graph.
diff --git a/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs b/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs
index d72e02a..c13f03c 100644
--- a/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs
+++ b/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs
@@ -142,7 +142,7 @@ public sealed class PromptAndRuntimeTests
Assert.True(registry.TryGet(task.TaskId, null, out TaskSnapshot? completed));
Assert.Equal(AgentTaskState.Completed, completed!.State);
- Assert.Equal(["file_edit", "process_run"], backend.Calls.Select(static call => call.Name));
+ Assert.Equal(["process_run"], backend.Calls.Select(static call => call.Name));
}
[Fact]