Pi - Tools
Pi - Tools
Tools
- 支持的内置工具 (coding-agent)
- read、bash、edit、write、grep、find、ls
- pi分为 core 和 coding-agent/tui 部分
Agent Tool Define
- runtime protocal ```ts /**
- agent-core 运行时使用的工具协议。 *
- 它继承 pi-ai 的基础 Tool,因此 name/description/parameters 会被传给 provider,
- 让模型知道有哪些工具以及参数 schema。execute 只在模型真的返回 toolCall 后由 agent-loop 调用。 *
- coding-agent 的 ToolDefinition 会在 session 层被包装成这个类型;agent-core 不关心
- TUI 渲染、promptSnippet 或扩展加载来源。 */ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool
{ /** UI 展示用的人类可读名称。 */ label: string; /** - 参数兼容处理钩子。 *
- 模型或历史 transcript 可能使用旧字段名/旧结构;这里允许工具在 schema 校验前
- 做轻量归一化。返回值必须符合
TParameters。 */ prepareArguments?: (args: unknown) => Static; /** - 执行工具调用。 *
- 约定:工具失败时直接 throw,由 agent-loop 统一编码成错误 toolResult;
- 工具自身不需要也不应该手动构造错误 transcript。 */ execute: ( toolCallId: string, params: Static
, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback , ) => Promise<AgentToolResult >; /** - 单工具执行模式覆盖。 *
- 如果工具声明 “sequential”,包含它的整批 toolCall 都会串行执行。
- 文件写入、shell 命令等有副作用工具通常应使用串行模式。 *
- 未设置时使用 loop 的默认执行模式。 */ executionMode?: ToolExecutionMode; } ```
- tool result ```ts /**
- 工具产生的结果。 *
- 同一个结构既用于最终结果,也用于
onUpdate上报的流式部分结果。 content会进入 transcript 并返回给模型,details主要给 UI、日志和导出使用。 */ export interface AgentToolResult{ /** 返回给模型的文本或图片内容。 */ content: (TextContent | ImageContent)[]; /** 任意结构化详情,不直接作为模型上下文的一部分。 */ details: T; /** - 提示当前工具批次结束后停止继续请求模型。
- 只有同一批所有 finalized 结果都为 true,批次才会真正 terminate。 */ terminate?: boolean; } ```
Execute Tool Calls
- 在loop中串行或并发执行
- 执行方法为AgentTool中定义的execute
真正执行前会有prepare 阶段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
async function prepareToolCall( currentContext: AgentContext, assistantMessage: AssistantMessage, toolCall: AgentToolCall, config: AgentLoopConfig, signal: AbortSignal | undefined, ): Promise<PreparedToolCall | ImmediateToolCallOutcome> { // prepare 阶段只负责“让工具变成可执行状态”,不真正跑工具。 // 它统一处理:查找工具、参数预处理、schema 校验、beforeToolCall 审批/拦截。 // // 工具查找只发生在当前 context.tools 中,也就是本次 run 开始时 active tools 的快照。 // registry 中存在但未启用的工具,或者运行中才被启用的工具,都不会被本次 toolCall 找到。 const tool = currentContext.tools?.find((t) => t.name === toolCall.name); if (!tool) { return { kind: "immediate", result: createErrorToolResult(`Tool ${toolCall.name} not found`), isError: true, }; } try { const preparedToolCall = prepareToolCallArguments(tool, toolCall); // validateToolArguments 会按工具的 TypeBox/JSON schema 校验,并尽量做安全的类型转换。 // 通过校验后的 args 才会传给 before hook 和真正的 tool.execute。 const validatedArgs = validateToolArguments(tool, preparedToolCall); if (config.beforeToolCall) { // before hook 常用于权限检查、审计、策略拦截、额度控制等。 const beforeResult = await config.beforeToolCall( { assistantMessage, toolCall, args: validatedArgs, context: currentContext, }, signal, ); // ... } // ... } }
Tool Definition Extention 扩展工具定义
- define ```ts /**
- 扩展和 coding-agent 内部使用的完整工具定义。 *
- 这个类型比 agent-core 的 AgentTool 更“上层”:
- name/description/parameters 会传给模型,用于 provider tool/function calling;
- execute 是真正的工具实现;
- promptSnippet/promptGuidelines 会参与默认系统提示词构建;
- renderCall/renderResult/renderShell 只服务于 TUI/HTML 展示;
- ctx 参数让扩展工具能访问 session、UI、资源等 coding-agent 运行时能力。 *
- AgentSession 会先收集 ToolDefinition,再通过 tool-definition-wrapper 包装成 AgentTool 给 agent-core 执行。 */ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = unknown, TState = any> { /** 工具名。模型返回 toolCall.name 时必须匹配这个名字。 */ name: string; /** UI 展示用的人类可读标签。 */ label: string; /** 模型可见描述。provider 会把它作为工具说明发给 LLM。 */ description: string; /**
- 可选的系统提示词摘要。
- 当工具处于 active 状态时,这段文本会出现在默认 system prompt 的 Available tools 区域。
- 自定义工具如果不提供该字段,就不会被加入这段提示。 */ promptSnippet?: string; /** 工具启用时追加到默认 system prompt Guidelines 区域的规则。 */ promptGuidelines?: string[]; /** 工具参数 schema。用于 provider tool schema,也用于执行前参数校验和类型转换。 */ parameters: TParams; /** 控制 ToolExecutionComponent 使用标准外壳,还是由工具渲染器自己负责完整外框。 */ renderShell?: “default” | “self”;
/** 参数兼容层:在 schema 校验前把模型返回的原始参数归一化成 TParams 形状。 */ prepareArguments?: (args: unknown) => Static
; /**
- 单工具执行模式覆盖。 *
- “sequential”:这个工具不能和同一批其他 toolCall 并发执行;
- “parallel”:这个工具允许并发执行。 *
- 未设置时使用 agent loop 的默认执行模式。 */ executionMode?: ToolExecutionMode;
/**
- 执行工具。 *
- agent-core 会先完成工具查找、参数准备、schema 校验和 before hook,然后调用这里。
- 工具执行失败时应抛错,agent-loop 会统一转成错误 toolResult。 */ execute( toolCallId: string, params: Static
, signal: AbortSignal | undefined, onUpdate: AgentToolUpdateCallback | undefined, ctx: ExtensionContext, ): Promise<AgentToolResult >;
/** 自定义 toolCall 展示。用于参数流式生成、工具开始执行前后的标题/摘要展示。 */ renderCall?: (args: Static
, theme: Theme, context: ToolRenderContext<TState, Static >) => Component; /** 自定义工具结果展示。用于最终结果和流式 partial result 的 TUI 渲染。 */ renderResult?: ( result: AgentToolResult
, options: ToolRenderResultOptions, theme: Theme, context: ToolRenderContext<TState, Static >, ) => Component; } ``` - 通过wrapper包装成agentTool ```ts /**
- 将 coding-agent 层的 ToolDefinition 包装成 agent-core 层的 AgentTool。 *
- ToolDefinition 比 AgentTool 信息更全:它知道 UI 渲染、promptSnippet/promptGuidelines、
- 以及扩展运行时上下文 ctx。agent-core 不应该依赖这些 coding-agent 概念,所以这里做一次边界适配:
- schema、名称、描述和执行模式原样交给 agent-core;
- execute 调用时临时创建 ExtensionContext,再传回 ToolDefinition.execute;
- 渲染器和 prompt 元数据不进入 AgentTool,它们仍由 AgentSession 的定义注册表维护。 */ export function wrapToolDefinition<TDetails = unknown>( definition: ToolDefinition<any, TDetails>, ctxFactory?: () => ExtensionContext, ): AgentTool<any, TDetails> { return { name: definition.name, label: definition.label, description: definition.description, parameters: definition.parameters, prepareArguments: definition.prepareArguments, executionMode: definition.executionMode, execute: (toolCallId, params, signal, onUpdate) => definition.execute(toolCallId, params, signal, onUpdate, ctxFactory?.() as ExtensionContext), }; } ```
Register Tool
- 渲染器和 prompt 元数据不进入 AgentTool,它们仍由 AgentSession 的定义注册表维护。 */ export function wrapToolDefinition<TDetails = unknown>( definition: ToolDefinition<any, TDetails>, ctxFactory?: () => ExtensionContext, ): AgentTool<any, TDetails> { return { name: definition.name, label: definition.label, description: definition.description, parameters: definition.parameters, prepareArguments: definition.prepareArguments, executionMode: definition.executionMode, execute: (toolCallId, params, signal, onUpdate) => definition.execute(toolCallId, params, signal, onUpdate, ctxFactory?.() as ExtensionContext), }; } ```
- 写ts func通过pi.registerTool()注册
1 2 3 4 5 6 7 8
registerTool(tool: ToolDefinition): void { runtime.assertActive(); extension.tools.set(tool.name, { definition: tool, sourceInfo: extension.sourceInfo, }); runtime.refreshTools(); },
- extensions会自动发现固定目录下的文件 ```ts /**
- Discover extensions in a directory. *
- Discovery rules:
- Direct files:
extensions/*.tsor*.js→ load
- Direct files:
- Subdirectory with index:
extensions/* /index.tsorindex.js→ load
- Subdirectory with index:
- Subdirectory with package.json:
extensions/* /package.jsonwith “pi” field → load what it declares *
- Subdirectory with package.json:
- No recursion beyond one level. Complex packages must use package.json manifest. */ function discoverExtensionsInDir(dir: string): string[] {} ```
- extension定义
1 2 3 4 5 6 7 8 9 10 11 12
/** Loaded extension with all registered items. */ export interface Extension { path: string; resolvedPath: string; sourceInfo: SourceInfo; handlers: Map<string, HandlerFn[]>; tools: Map<string, RegisteredTool>; messageRenderers: Map<string, MessageRenderer>; commands: Map<string, RegisteredCommand>; flags: Map<string, ExtensionFlag>; shortcuts: Map<KeyId, ExtensionShortcut>; }
This post is licensed under CC BY 4.0 by the author.