diff --git a/src/agent_prompting.py b/src/agent_prompting.py index faa2d1d..1f2264f 100644 --- a/src/agent_prompting.py +++ b/src/agent_prompting.py @@ -217,20 +217,20 @@ def get_using_your_tools_section(enabled_tool_names: set[str]) -> str: items.append('编辑文件时,优先使用 edit_file,而不是 shell 文本替换。') if 'write_file' in enabled_tool_names: items.append( - '创建或追加通用文本文件时,优先使用 write_file,而不是 heredoc 或 echo 重定向;默认写入当前 session 目录。' + 'write_file 只适合短文本、少量 Markdown、轻量配置或小型手写文件;默认写入当前 session 目录。' ) items.append( - 'write_file 是通用保存文件工具。普通文本、Markdown、Python 脚本一律优先用 content_lines(一行一个字符串);content 只用于很短的简单文本,避免把多行内容或大量引号塞进单个 JSON 字符串。' + '不要用 write_file 写长 Python 脚本、JSONL、CSV、大 JSON、生成数据集或包含大量引号/换行的内容;这些内容会让工具参数 JSON 很容易失效。' ) items.append( - '保存结构化数据时,不要自己拼 JSON/JSONL/CSV 字符串:JSON 用 json_content,JSONL 用 jsonl_records,CSV 用 csv_headers + csv_rows,让工具负责序列化和转义。' + '如果必须用 write_file:普通文本/Markdown 用 content_lines;很小的 JSON 用 json_content;很少量 JSONL 用 jsonl_records;很小的 CSV 用 csv_headers + csv_rows;content 只用于一行短文本。' ) items.append( - '需要分批写入长文件时,使用 write_file 的 append=true 追加稳定文件;不要反复覆盖同一个中间产物。' + '不要把 JSON/JSONL/CSV/脚本先拼成一个大字符串再塞进 write_file.content。' ) if 'python_exec' in enabled_tool_names: items.append( - '如果 write_file 因内容过长、引号/换行转义复杂或工具参数生成失败而无法稳定完成写入,下一轮应优先改用 python_exec 写文件:把文本作为 stdin 或 Python 字符串/列表处理,并写入同一个 session 路径。' + '写 Python 脚本、批量数据、JSONL、CSV、大 JSON、长 Markdown 或复杂多行文本时,优先用 python_exec 通过 pathlib/json/csv 写入文件;这是复杂写文件的默认可靠路径。' ) if 'glob_search' in enabled_tool_names: items.append('搜索文件时,优先使用 glob_search,而不是 find 或 ls。') @@ -246,6 +246,9 @@ def get_using_your_tools_section(enabled_tool_names: set[str]) -> str: items.append( 'python_exec 会注入 PYTHON_EXEC_SCRATCHPAD 环境变量,指向当前用户当前会话隔离的 scratchpad。一次性脚本、缓存和中间输出都应写入这里,交付产物写入 session/output。' ) + items.append( + '需要可靠保存文件时,可以在 python_exec.code 中使用 pathlib.Path(...).write_text(...)、json.dump/json.dumps、csv.writer 或流式逐行写入;这比把大段内容塞进 write_file 参数更稳定。' + ) if 'python_package' in enabled_tool_names: items.append( '当 python_exec 因缺少 pandas、pyarrow、openpyxl 等 Python 包失败时,使用 python_package 在当前用户独立 venv 中安装缺失包,然后重试;不要安装到系统 Python 或项目 .venv。' diff --git a/src/agent_runtime.py b/src/agent_runtime.py index 770a107..f97c2b8 100644 --- a/src/agent_runtime.py +++ b/src/agent_runtime.py @@ -1614,11 +1614,11 @@ class LocalCodingAgent: f'工具:{name};位置:line {exc.lineno} column {exc.colno};' f'原因:{exc.msg};参数片段:{preview!r}。' '请回复“继续”,我会要求模型重新生成合法 JSON 参数;' - '如果是 write_file 写普通文本/脚本/Markdown,应改用 content_lines;' - '如果是保存 JSON/JSONL/CSV,应改用 json_content、jsonl_records 或 csv_rows;' - '如果 write_file 仍不稳定,应改用 python_exec 作为通用写文件兜底;' + '如果是 write_file 写短文本/Markdown,应改用 content_lines;' + '如果是很小的 JSON/JSONL/CSV,可用 json_content、jsonl_records 或 csv_rows;' + '如果是脚本、长文本、大 JSON、JSONL、CSV 或生成数据,应直接改用 python_exec 写文件;' '如果是生成/转换结构化数据,应优先调用对应 skill 脚本或 python_exec,' - '不要把大段 JSON/CSV/draft 直接塞进工具参数;bash 仅作为最后兜底。' + '不要把大段内容直接塞进工具参数;bash 仅作为最后兜底。' ) from exc if not isinstance(arguments, dict): raise OpenAICompatError( diff --git a/src/agent_tool_specs/files.py b/src/agent_tool_specs/files.py index 5c82215..a4468fd 100644 --- a/src/agent_tool_specs/files.py +++ b/src/agent_tool_specs/files.py @@ -39,9 +39,10 @@ def build_file_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentTool]: AgentTool( name='write_file', description=( - 'Write or append a UTF-8 text file inside the workspace. Creates parent directories when needed. ' - 'Use content_lines for text/scripts/markdown. Use json_content, jsonl_records, or csv_rows for structured data ' - 'so the tool serializes the file instead of forcing the model to escape large strings.' + 'Write or append a SMALL UTF-8 file inside the workspace. Creates parent directories when needed. ' + 'Use this for short notes, lightweight markdown, and small config files. ' + 'For Python scripts, JSONL, large JSON/CSV, generated datasets, or quote-heavy/multi-line content, prefer python_exec ' + 'to write the file with pathlib/json/csv; do not pack large content into tool arguments.' ), parameters={ 'type': 'object', @@ -50,11 +51,11 @@ def build_file_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentTool]: 'content_lines': { 'type': 'array', 'items': {'type': 'string'}, - 'description': 'Lines to join with "\\n"; use this for normal text, scripts, markdown, and any multi-line content.', + 'description': 'Lines to join with "\\n"; use only for short text/markdown or small scripts. For long scripts use python_exec to create the file.', }, 'content': { 'type': 'string', - 'description': 'Short one-line/simple text only. For multi-line or quote-heavy content use content_lines or structured fields.', + 'description': 'Short one-line/simple text only. Never use for multi-line scripts, JSON, JSONL, CSV, or quote-heavy content.', }, 'content_base64': { 'type': 'string', @@ -62,12 +63,12 @@ def build_file_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentTool]: }, 'json_content': { 'type': 'object', - 'description': 'JSON value to serialize with ensure_ascii=false and indent=2. Use for .json files instead of stringifying JSON manually.', + 'description': 'Small JSON value to serialize with ensure_ascii=false and indent=2. For large JSON use python_exec streaming write.', }, 'jsonl_records': { 'type': 'array', 'items': {'type': 'object'}, - 'description': 'Objects to serialize as compact JSONL, one object per line. Use for .jsonl files.', + 'description': 'Small list of objects to serialize as compact JSONL. For more than a few dozen records or generated datasets use python_exec.', }, 'csv_headers': { 'type': 'array',