diff options
Diffstat (limited to 'Omni/Agent')
| -rw-r--r-- | Omni/Agent/Subagent/Coder.hs | 43 | ||||
| -rw-r--r-- | Omni/Agent/Tools.hs | 24 |
2 files changed, 46 insertions, 21 deletions
diff --git a/Omni/Agent/Subagent/Coder.hs b/Omni/Agent/Subagent/Coder.hs index ad97ee7..63273f6 100644 --- a/Omni/Agent/Subagent/Coder.hs +++ b/Omni/Agent/Subagent/Coder.hs @@ -71,7 +71,7 @@ test = [ Test.unit "CoderConfig has sensible defaults" <| do let cfg = defaultCoderConfig "Omni/Task" "Implement feature X" coderNamespace cfg Test.@=? "Omni/Task" - coderTimeout cfg Test.@=? 1200, + coderTimeout cfg Test.@=? 600, Test.unit "coderSystemPrompt includes namespace" <| do let cfg = defaultCoderConfig "Biz/Cloud" "Fix the bug" let prompt = coderSystemPrompt cfg Nothing @@ -131,11 +131,11 @@ defaultCoderConfig namespace task = coderTask = task, coderContext = Nothing, coderModel = "anthropic/claude-sonnet-4", - coderTimeout = 1200, - coderMaxCost = 200.0, - coderMaxTokens = 300000, - coderMaxIterations = 30, - coderMaxVerifyRetries = 3 + coderTimeout = 600, + coderMaxCost = 50.0, + coderMaxTokens = 100000, + coderMaxIterations = 20, + coderMaxVerifyRetries = 2 } -- | Run a bash command and capture output @@ -265,7 +265,7 @@ loadCoderSystemPrompt cfg maybeInitState = do coderSystemPrompt :: CoderConfig -> Maybe Text -> Text coderSystemPrompt cfg maybeInitState = Text.unlines - [ "You are a specialized Coder subagent.", + [ "You are a specialized Coder subagent with a STRICT TOKEN BUDGET.", "", "## Your Task", coderTask cfg, @@ -276,24 +276,35 @@ coderSystemPrompt cfg maybeInitState = "", maybe "" (\ctx -> "## Context\n" <> ctx <> "\n") (coderContext cfg), maybe "" (\st -> "## Current State\n" <> st <> "\n") maybeInitState, + "## TOKEN EFFICIENCY (CRITICAL)", + "", + "You have LIMITED tokens. Every tool output consumes budget. Violating these rules will cause task failure:", + "", + "1. NEVER read entire files. Use search_and_read or read_file with start_line/end_line", + "2. ALWAYS search first (search_codebase or search_and_read) to find exact locations", + "3. Use edit_file for changes - NEVER write_file unless creating new files", + "4. Limit bash output: pipe through `head -50` or `tail -50` when possible", + "5. One focused action per step - don't chain unnecessary operations", + "", + "## Tool Strategy", + "", + "To find code: search_and_read (returns matches + context in one call)", + "To understand a function: read_file with start_line/end_line (get just that function)", + "To edit: edit_file with old_str/new_str (surgical patch, not full file rewrite)", + "To explore structure: search_codebase with small max_results", + "", "## Guidelines", "1. Make incremental changes - edit one file at a time", "2. After each edit, consider running `lint " <> coderNamespace cfg <> ".hs` to catch issues early", "3. Use `bild " <> coderNamespace cfg <> "` to check your work compiles", - "4. If tests exist, run `bild --test " <> coderNamespace cfg <> "` to verify", - "5. Keep changes focused on the task - don't refactor unrelated code", - "6. If you find bugs unrelated to your task, note them but don't fix them", + "4. Keep changes focused on the task - don't refactor unrelated code", "", "## Completion", - "When you believe you're done:", - "1. Ensure all edits are complete", - "2. The harness will automatically run lint --fix, build, and tests", - "3. If verification fails, you'll be asked to fix the issues", - "4. Provide a brief summary of what you changed", + "When done: provide a brief summary. The harness runs lint --fix, build, and tests automatically.", "", "## Important", "- Do NOT commit - the harness handles git operations", - "- Focus on making the code changes correctly" + "- Do NOT read files you don't need to edit" ] -- | Format verify error for agent retry prompt diff --git a/Omni/Agent/Tools.hs b/Omni/Agent/Tools.hs index 22cc8a1..0bd394f 100644 --- a/Omni/Agent/Tools.hs +++ b/Omni/Agent/Tools.hs @@ -210,8 +210,22 @@ instance Aeson.FromJSON ToolResult where <*> (v .:? "output" .!= "") <*> (v .:? "error") +-- | Maximum characters for tool output to prevent token explosion +maxOutputChars :: Int +maxOutputChars = 8000 + +-- | Truncate output if too long, adding a notice +truncateOutput :: Text -> Text +truncateOutput output + | Text.length output <= maxOutputChars = output + | otherwise = + Text.take maxOutputChars output + <> "\n\n[OUTPUT TRUNCATED - " + <> tshow (Text.length output - maxOutputChars) + <> " chars omitted. Use line ranges or more specific searches.]" + mkSuccess :: Text -> Aeson.Value -mkSuccess output = Aeson.toJSON <| ToolResult True output Nothing +mkSuccess output = Aeson.toJSON <| ToolResult True (truncateOutput output) Nothing mkError :: Text -> Aeson.Value mkError err = Aeson.toJSON <| ToolResult False "" (Just err) @@ -244,7 +258,7 @@ readFileTool :: Engine.Tool readFileTool = Engine.Tool { Engine.toolName = "read_file", - Engine.toolDescription = "Read the contents of a file. Can optionally read a specific line range.", + Engine.toolDescription = "Read file contents. PREFER using start_line/end_line to read only what you need. Large files will be truncated.", Engine.toolJsonSchema = Aeson.object [ "type" .= ("object" :: Text), @@ -588,7 +602,7 @@ executeSearchCodebase v = Aeson.Error e -> pure <| mkError (Text.pack e) Aeson.Success args -> do let pat = Text.unpack (searchPattern args) - maxRes = fromMaybe 100 (searchMaxResults args) + maxRes = min 50 (fromMaybe 30 (searchMaxResults args)) caseSensitive = fromMaybe False (searchCaseSensitive args) baseArgs = ["--line-number", "--no-heading", "--max-count=" <> show maxRes, pat] @@ -660,13 +674,13 @@ executeSearchAndRead v = Aeson.Error e -> pure <| mkError (Text.pack e) Aeson.Success args -> do let pat = Text.unpack (sarPattern args) - ctx = fromMaybe 10 (sarContextLines args) + ctx = min 15 (fromMaybe 5 (sarContextLines args)) pathArg = maybe ["."] (\p -> [Text.unpack p]) (sarPath args) rgArgs = [ "--line-number", "--no-heading", "--context=" <> show ctx, - "--max-count=20", + "--max-count=10", "--ignore-case", pat ] |
