mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-22 17:42:24 +00:00
docs(tools): clarify coding tool guidance
This commit is contained in:
parent
7e122d6e49
commit
44ef697aac
@ -298,11 +298,14 @@ class ApplyPatchTool(_FsTool):
|
|||||||
@property
|
@property
|
||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
return (
|
return (
|
||||||
"Apply a structured patch for code edits. The patch must include "
|
"Default tool for code edits. Apply a structured patch with "
|
||||||
"*** Begin Patch and *** End Patch. Supports Add File, Update File, "
|
"*** Begin Patch and *** End Patch. Supports Add File, Update File, "
|
||||||
"Delete File, and Move to. Paths must be relative. Prefer this for "
|
"Delete File, and Move to across one or more files. Use this for "
|
||||||
"multi-file coding changes; use edit_file for small exact replacements. "
|
"multi-file changes, structural edits, generated code, or any edit "
|
||||||
"Set dry_run=true to validate and preview the change without writing files."
|
"where a reviewable patch is clearer than an exact replacement. "
|
||||||
|
"Paths must be relative. Set dry_run=true to validate and preview "
|
||||||
|
"the change summary without writing files. Use edit_file only for "
|
||||||
|
"small exact replacements copied from read_file."
|
||||||
)
|
)
|
||||||
|
|
||||||
async def execute(self, patch: str, dry_run: bool = False, **kwargs: Any) -> str:
|
async def execute(self, patch: str, dry_run: bool = False, **kwargs: Any) -> str:
|
||||||
|
|||||||
@ -424,11 +424,12 @@ class WriteStdinTool(Tool):
|
|||||||
@property
|
@property
|
||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
return (
|
return (
|
||||||
"Write text to a running exec session and return recent output. "
|
"Interact with a running exec session created by exec with "
|
||||||
"Use chars='' to poll without writing. Set close_stdin=true to send EOF, "
|
"yield_time_ms. Use chars='' to poll without writing, chars to send "
|
||||||
"or terminate=true to stop the session. Use wait_for to keep polling "
|
"stdin, close_stdin=true to send EOF, or terminate=true to stop the "
|
||||||
"until expected output appears. Sessions finish automatically when "
|
"process. Use wait_for with wait_timeout_ms for dev servers, test "
|
||||||
"their process exits."
|
"watchers, and prompts where you need to wait for expected output. "
|
||||||
|
"Do not use this to start new commands; start them with exec."
|
||||||
)
|
)
|
||||||
|
|
||||||
async def execute(
|
async def execute(
|
||||||
@ -561,7 +562,8 @@ class ListExecSessionsTool(Tool):
|
|||||||
return (
|
return (
|
||||||
"List active long-running exec sessions, including session_id, cwd, "
|
"List active long-running exec sessions, including session_id, cwd, "
|
||||||
"elapsed time, idle time, remaining timeout, and command preview. "
|
"elapsed time, idle time, remaining timeout, and command preview. "
|
||||||
"Use this to recover a session_id before polling with write_stdin."
|
"Use this to recover a session_id after context shifts before "
|
||||||
|
"polling, writing stdin, or terminating with write_stdin."
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -158,6 +158,9 @@ class ReadFileTool(_FsTool):
|
|||||||
"Text output format: LINE_NUM|CONTENT. "
|
"Text output format: LINE_NUM|CONTENT. "
|
||||||
"Images return visual content for analysis. "
|
"Images return visual content for analysis. "
|
||||||
"Supports PDF, DOCX, XLSX, PPTX documents. "
|
"Supports PDF, DOCX, XLSX, PPTX documents. "
|
||||||
|
"Use find_files/list_dir first when the path is uncertain. "
|
||||||
|
"Read the relevant range before editing so replacements or patches "
|
||||||
|
"are based on current content. "
|
||||||
"Use offset and limit for large text files. "
|
"Use offset and limit for large text files. "
|
||||||
"Use force=true to re-read content even if unchanged. "
|
"Use force=true to re-read content even if unchanged. "
|
||||||
"Reads exceeding ~128K chars are truncated."
|
"Reads exceeding ~128K chars are truncated."
|
||||||
@ -384,9 +387,10 @@ class WriteFileTool(_FsTool):
|
|||||||
@property
|
@property
|
||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
return (
|
return (
|
||||||
"Write content to a file. Overwrites if the file already exists; "
|
"Create a new file or intentionally replace an entire file with "
|
||||||
"creates parent directories as needed. "
|
"the provided content. Overwrites existing files and creates parent "
|
||||||
"For partial edits, prefer edit_file instead."
|
"directories as needed. For code changes or partial edits, prefer "
|
||||||
|
"apply_patch; use edit_file only for small exact replacements."
|
||||||
)
|
)
|
||||||
|
|
||||||
async def execute(self, path: str | None = None, content: str | None = None, **kwargs: Any) -> str:
|
async def execute(self, path: str | None = None, content: str | None = None, **kwargs: Any) -> str:
|
||||||
@ -711,10 +715,13 @@ class EditFileTool(_FsTool):
|
|||||||
@property
|
@property
|
||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
return (
|
return (
|
||||||
"Edit a file by replacing old_text with new_text. "
|
"Perform a small, exact replacement in one file by replacing "
|
||||||
"Tolerates minor whitespace/indentation differences and curly/straight quote mismatches. "
|
"old_text with new_text. Use this for narrow text substitutions "
|
||||||
"If old_text matches multiple times, you must provide more context "
|
"with old_text copied from read_file. For multi-file, structural, "
|
||||||
"or set occurrence/line_hint/replace_all. Shows a diff of the closest match on failure."
|
"or generated code edits, prefer apply_patch. If old_text matches "
|
||||||
|
"multiple times, provide more context or set occurrence, line_hint, "
|
||||||
|
"replace_all, and expected_replacements. Shows closest-match "
|
||||||
|
"diagnostics on failure."
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -130,8 +130,10 @@ class FindFilesTool(_SearchTool):
|
|||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
return (
|
return (
|
||||||
"Find files by path fragment, glob, or file type. "
|
"Find files by path fragment, glob, or file type. "
|
||||||
"Use this before read_file when you need to locate files. "
|
"Use this before read_file when you need to locate files, and "
|
||||||
"Returns workspace-relative paths and skips common dependency/build directories."
|
"prefer it over shell find/ls for ordinary workspace discovery. "
|
||||||
|
"Returns workspace-relative paths and skips common dependency/build "
|
||||||
|
"directories."
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -289,7 +291,8 @@ class GrepTool(_SearchTool):
|
|||||||
return (
|
return (
|
||||||
"Search file contents with a regex pattern. "
|
"Search file contents with a regex pattern. "
|
||||||
"Default output_mode is files_with_matches (file paths only); "
|
"Default output_mode is files_with_matches (file paths only); "
|
||||||
"use content mode for matching lines with context. "
|
"use content mode for matching lines with context. Prefer this "
|
||||||
|
"over shell grep for ordinary workspace searches. "
|
||||||
"Skips binary and files >2 MB. Supports glob/type filtering."
|
"Skips binary and files >2 MB. Supports glob/type filtering."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -211,8 +211,10 @@ class ExecTool(Tool):
|
|||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
return (
|
return (
|
||||||
"Execute a shell command and return its output. "
|
"Execute a shell command and return its output. "
|
||||||
"Prefer read_file/write_file/edit_file over cat/echo/sed, "
|
"Use this for tests, builds, package commands, git commands, and "
|
||||||
"and grep/glob over shell find/grep. "
|
"other process execution. Prefer read_file/find_files/grep for "
|
||||||
|
"inspection and apply_patch/write_file/edit_file for file changes "
|
||||||
|
"instead of cat, shell find/grep, echo, or sed. "
|
||||||
"Use -y or --yes flags to avoid interactive prompts. "
|
"Use -y or --yes flags to avoid interactive prompts. "
|
||||||
"For long-running or interactive commands, pass yield_time_ms; "
|
"For long-running or interactive commands, pass yield_time_ms; "
|
||||||
"if the command keeps running, exec returns a session_id that can "
|
"if the command keeps running, exec returns a session_id that can "
|
||||||
|
|||||||
46
tests/tools/test_tool_descriptions.py
Normal file
46
tests/tools/test_tool_descriptions.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from nanobot.agent.tools.apply_patch import ApplyPatchTool
|
||||||
|
from nanobot.agent.tools.exec_session import ListExecSessionsTool, WriteStdinTool
|
||||||
|
from nanobot.agent.tools.filesystem import EditFileTool, ReadFileTool, WriteFileTool
|
||||||
|
from nanobot.agent.tools.search import FindFilesTool, GrepTool
|
||||||
|
from nanobot.agent.tools.shell import ExecTool
|
||||||
|
|
||||||
|
|
||||||
|
def test_coding_tool_descriptions_steer_editing_priority() -> None:
|
||||||
|
apply_patch = ApplyPatchTool().description.lower()
|
||||||
|
edit_file = EditFileTool().description.lower()
|
||||||
|
write_file = WriteFileTool().description.lower()
|
||||||
|
|
||||||
|
assert "default tool for code edits" in apply_patch
|
||||||
|
assert "multi-file" in apply_patch
|
||||||
|
assert "dry_run=true" in apply_patch
|
||||||
|
assert "edit_file only for small exact replacements" in apply_patch
|
||||||
|
|
||||||
|
assert "small, exact replacement" in edit_file
|
||||||
|
assert "copied from read_file" in edit_file
|
||||||
|
assert "prefer apply_patch" in edit_file
|
||||||
|
|
||||||
|
assert "replace an entire file" in write_file
|
||||||
|
assert "prefer apply_patch" in write_file
|
||||||
|
|
||||||
|
|
||||||
|
def test_coding_tool_descriptions_steer_discovery_and_shell_usage() -> None:
|
||||||
|
read_file = ReadFileTool().description.lower()
|
||||||
|
find_files = FindFilesTool().description.lower()
|
||||||
|
grep = GrepTool().description.lower()
|
||||||
|
exec_tool = ExecTool().description.lower()
|
||||||
|
write_stdin = WriteStdinTool().description.lower()
|
||||||
|
list_sessions = ListExecSessionsTool().description.lower()
|
||||||
|
|
||||||
|
assert "find_files/list_dir first" in read_file
|
||||||
|
assert "before editing" in read_file
|
||||||
|
assert "prefer it over shell find/ls" in find_files
|
||||||
|
assert "prefer this over shell grep" in grep
|
||||||
|
|
||||||
|
assert "tests, builds" in exec_tool
|
||||||
|
assert "prefer read_file/find_files/grep" in exec_tool
|
||||||
|
assert "apply_patch/write_file/edit_file" in exec_tool
|
||||||
|
assert "yield_time_ms" in exec_tool
|
||||||
|
|
||||||
|
assert "do not use this to start new commands" in write_stdin
|
||||||
|
assert "wait_for" in write_stdin
|
||||||
|
assert "recover a session_id" in list_sessions
|
||||||
Loading…
x
Reference in New Issue
Block a user