read
- 路径解析和 access。
- 文本分页与截断。
- 图片作为 image content。
04 · Default tools
这一章看 read、write、edit、bash:
它们如何注册成 AgentTool,如何处理文件和命令边界,以及工具结果如何回到模型上下文。
目标是能解释工具如何注册、如何执行、如何保护资源、如何把输出交还给模型。
模型不会自己读文件、改文件或跑命令。它只能生成 toolCall,由 Pi 在本地执行对应函数。
工具的稳定名字。模型在 toolCall 里用这个名字选择要调用的本地函数。
告诉模型这个工具能做什么、什么时候该用、有什么边界。
参数结构和类型。agent loop 必须拿到完整参数后才能校验和执行。
真正运行在本地的函数。它返回结果或错误,再被包装成 ToolResultMessage。
toolCall;Pi 执行本地 execute;
执行结果回填成 toolResult,下一轮模型才能看到。
默认工具先是产品层定义,最后才变成 agent loop 可执行的工具。
createAllToolDefinitions()_baseToolDefinitionsToolDefinition 适配成 AgentTool_toolRegistrysetActiveToolsByName()agent.state.toolslabel、promptSnippet、promptGuidelines、renderCall、renderResult、extension ctx。name、description、parameters、prepareArguments、executionMode、execute。ToolDefinition 给 pi 用,AgentTool 给 agent loop 用,wrapToolDefinition() 是中间适配器。
read 的目标不是一次读最多,而是读到足够信息,并保留继续探索的路径。
{
path: string,
offset?: number,
limit?: number
}
truncateHead() 做 2000 行 / 50KB 保护。offset/limit 是主动分页。truncateHead 是被动保护。write 风险高,因为它不读旧文件、不做 diff,只把文件变成模型给出的完整内容。
{
path: string,
content: string
}
writeFile() 覆盖内容。write 适合新文件或整文件重写。已有文件的小改更适合 edit,否则模型漏掉旧内容就等于删除。
它不是替代 agent loop 的 sequential,而是在文件路径级别保护同一份文件。
edit 的策略是宁愿失败,也不要猜模型想改哪一处。
{
path: string,
edits: [
{
oldText: string,
newText: string
}
]
}
oldText 不存在时失败,避免模型以为已经修改成功。
匹配到多处时失败,逼模型提供更多上下文。
同一批 edits 不能重叠,相近修改要合并成一个 edit。
oldText 太短或不唯一。bash 的 partial update 给 UI 和事件流看,最终 tool result 才给模型看。
{
command: string,
timeout?: number
}
OutputAccumulator。tool_execution_update。工具返回值不是直接塞给模型,而是先变成 ToolResultMessage。
AgentToolResult。ToolResultMessage。Context.messages。bash 的 partial update 只进事件流,最终结果才进上下文。
默认工具已经读完,下一节进入 session、历史、恢复和压缩。
ToolDefinition -> AgentTool。read 的分页和截断。write/edit 的文件安全边界。bash 的流式 update 和失败回传。AgentSession 的长期状态。ToolDefinition 和 AgentTool 要分层?为什么 edit
要求 oldText 唯一?为什么小改旧文件不该优先用 write?为什么
bash 失败也要把输出回传给模型?