Skip to content

Conjunto de herramientas read/bash/edit/write/grep/find/ls

源码版本v0.73.1

El directorio tools/ es el conjunto de herramientas integradas de pi. Cada herramienta es una estructura de dos capas, ToolDefinition (con schema, descripción, prompt snippet, guidelines) y AgentTool (función de ejecución), unidas vía wrapToolDefinition. index.ts re-exporta todas las fábricas y ofrece tres combinaciones predefinidas: createCodingTools / createReadOnlyTools / createAllTools. Las herramientas de escritura (edit, write) pasan por withFileMutationQueue para serializar y evitar carreras al modificar el mismo archivo concurrentemente.

Responsabilidades

  1. read: lee archivos de texto/imagen; las imágenes soportan resize automático (autoResizeImages); el texto se trunca por DEFAULT_MAX_LINES / DEFAULT_MAX_BYTES. Ver packages/coding-agent/src/core/tools/read.ts:205-236.
  2. bash: ejecuta comandos de shell, rastrea procesos detached hijos, ofrece abort y timeout. Ver packages/coding-agent/src/core/tools/bash.ts:264-280.
  3. edit / write: edición y creación de archivos; ambos pasan por withFileMutationQueue. Ver packages/coding-agent/src/core/tools/edit.ts:288-316 y packages/coding-agent/src/core/tools/write.ts:181-210.
  4. grep / find / ls: búsqueda y listado de archivos basados en fd y rg, respetando .gitignore. Ver packages/coding-agent/src/core/tools/grep.ts:122-135, packages/coding-agent/src/core/tools/find.ts:112-125, packages/coding-agent/src/core/tools/ls.ts:99-112.
  5. Fábricas de combinación: createToolDefinition / createTool dispatchan por ToolName; createCodingTools por defecto da 4 piezas (read/bash/edit/write), createReadOnlyTools da 4 piezas (read/grep/find/ls). Ver packages/coding-agent/src/core/tools/index.ts:96-136 y packages/coding-agent/src/core/tools/index.ts:138-184.
  6. Serialización de escrituras: withFileMutationQueue mantiene una cadena de Promises por realpath del archivo; las escrituras del mismo archivo se encolan en orden de invocación; archivos distintos se paralelizan. Ver packages/coding-agent/src/core/tools/file-mutation-queue.ts:19-39.

Motivación de diseño

¿Por qué herramientas en dos capas? ToolDefinition es para el LLM: schema, description, prompt snippet, guidelines, todo eso se inyecta en el system prompt. AgentTool es el cuerpo de ejecución y sólo le importa al runtime. Separarlas permite que la misma definición impulse varios cuerpos: por ejemplo, la definición de read puede tener en modo RPC una implementación distinta de operations (los unit tests de read pueden inyectar un fs mock).

¿Por qué serializar escrituras? El LLM puede invocar edit/write varias veces al mismo archivo en un turno concurrente; sin serializar, el último en escribir sobrescribe los anteriores y el contenido se corrompe. withFileMutationQueue usa realpathSync.native como key para que symlinks y rutas reales compartan la misma cola. Archivos distintos siguen en paralelo, sin perder throughput.

¿Por qué read incluye por defecto promptGuidelines: ["Use read to examine files instead of cat or sed."]? Porque al construir el system prompt se pegan los guidelines de cada tool, y el LLM al ver "prefiere read sobre bash con cat" evita caminos ineficientes.

Archivos clave

La implementación de withFileMutationQueue es una cadena de promises; cada invocación nueva se cuelga del .then del promise anterior:

typescript
// packages/coding-agent/src/core/tools/file-mutation-queue.ts:19-39
export async function withFileMutationQueue<T>(filePath: string, fn: () => Promise<T>): Promise<T> {
	const key = getMutationQueueKey(filePath);
	const currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();

	let releaseNext!: () => void;
	const nextQueue = new Promise<void>((resolveQueue) => {
		releaseNext = resolveQueue;
	});
	const chainedQueue = currentQueue.then(() => nextQueue);
	fileMutationQueues.set(key, chainedQueue);

	await currentQueue;
	try {
		return await fn();
	} finally {
		releaseNext();
		if (fileMutationQueues.get(key) === chainedQueue) {
			fileMutationQueues.delete(key);
		}
	}
}

El execute de read acepta signal: AbortSignal y rechaza inmediatamente al Ctrl+C del usuario:

typescript
// packages/coding-agent/src/core/tools/read.ts:225-237
const absolutePath = resolveReadPath(path, cwd);
return new Promise<{ content: (TextContent | ImageContent)[]; details: ReadToolDetails | undefined }>(
	(resolve, reject) => {
		if (signal?.aborted) {
			reject(new Error("Operation aborted"));
			return;
		}
		let aborted = false;
		const onAbort = () => {
			aborted = true;
			reject(new Error("Operation aborted"));
		};
		signal?.addEventListener("abort", onAbort, { once: true });

Flujo de datos

Selección y ejecución de herramientas:

Límites y fallos

Resumen

tools/ es la caja de herramientas integrada de pi, estructura de dos capas (ToolDefinition para el LLM, AgentTool para el runtime); las herramientas de escritura se serializan con withFileMutationQueue para evitar carreras concurrentes. Las tres combinaciones predefinidas cubren codificación, sólo lectura y conjunto completo. Cómo se ensamblan estas herramientas en la sesión en ensamblaje createAgentSession; los hooks en capa de orquestación AgentSession.