How to Extract String Output from Agent Responses Using `Output.string()` in Sandcastle
Use Output.string() to declare that an agent should emit plain text wrapped in custom XML-style tags, and Sandcastle will automatically extract, trim, and return that content as a JavaScript string.
The Sandcastle library (mattpocock/sandcastle) provides a structured output API for reliably parsing agent responses without manual string manipulation. When you need to capture free-form text, code snippets, or any content that doesn't require JSON schema validation, Output.string() offers a lightweight mechanism to extract raw string data from the agent's stdout using declarative XML-style tags.
Declaring String Output with Output.string()
According to the Sandcastle source code in src/Output.ts, the Output.string() method creates an output definition that instructs the framework to look for a specific tag name in the agent's response. The method accepts a configuration object with a tag property, which defines the literal XML-style tag that must enclose the desired output.
import { Output } from "@ai-hero/sandcastle";
// Declare that the agent should wrap its response in <summary> tags
const stringOutput = Output.string({ tag: "summary" });
As implemented in src/Output.ts (lines 14–18), this creates an OutputStringDefinition with an internal _tag property set to "string". Unlike structured object outputs, this definition performs no JSON parsing or schema validation, making it ideal for unstructured text extraction.
How the Extraction Pipeline Works
The string extraction process involves three distinct phases: prompt validation, stdout collection, and content extraction. Single-iteration runs are required for structured output in Sandcastle.
Validating the Prompt Tag
Before executing the sandbox, Sandcastle validates that the prompt contains the opening tag defined in the output configuration. In src/run.ts (lines 96–100), the framework checks for the presence of <${tag}> in the resolved prompt string. If the tag is missing, run() throws an error immediately, preventing execution of a prompt that cannot satisfy the output contract.
// Example prompt that satisfies the validation
const prompt = `
You are a helpful assistant. Summarize the outcome and wrap it in <summary> tags.
<summary>
</summary>
`;
Collecting Agent Output
During execution, Sandcastle collects the agent's stdout across all iterations. As noted in src/run.ts (lines 60–66), the framework aggregates output from the sandbox process. For structured output extraction, the system operates on the complete stdout buffer after the agent finishes execution.
Extracting and Trimming the String
The extraction logic resides in src/extractStructuredOutput.ts. When processing a string-type output definition (lines 24–36), the framework delegates to the extractString() helper function (lines 86–92).
The extraction process follows these steps:
-
Locate the last occurrence: The
findLastTagContent()function (lines 12–34) scans the entire stdout buffer and identifies the last occurrence of the specified<tag>…</tag>pair. This allows agents to include multiple outputs or logging while ensuring the final tagged content is captured. -
Extract inner content: The function extracts the text content between the opening and closing tags.
-
Trim whitespace: As implemented in lines 94–101, the raw extracted content undergoes a
trim()operation to remove leading and trailing whitespace, including newlines and indentation introduced by the agent. -
Error handling: If the closing tag is absent or malformed, the extraction throws a
StructuredOutputError, which bubbles up from therun()function to alert callers that the output contract was violated.
Practical Implementation Example
The following example demonstrates a complete implementation using Output.string() to extract a summary from an agent:
import { run, Output } from "@ai-hero/sandcastle";
const prompt = `
You are a helpful assistant. Summarize the outcome of the computation
and wrap the summary in <summary> tags.
<summary>
</summary>
`;
async function main() {
const result = await run({
sandbox: { tag: "docker", env: {} },
prompt,
output: Output.string({ tag: "summary" }),
});
// result.output is a plain string: "The calculation finished successfully in 12ms."
console.log("Agent summary:", result.output);
}
The agent must emit output matching the declared tag structure:
... other stdout ...
<summary>
The calculation finished successfully in 12ms.
</summary>
Sandcastle locates the last <summary> block, trims the newline and indentation, and exposes the clean string via result.output.
Summary
Output.string()declares a string-type output insrc/Output.tsthat captures plain text without JSON parsing.- The prompt must contain the opening tag, validated in
src/run.ts(lines 96–100), or execution fails immediately. - Stdout collection happens across the run lifecycle (
src/run.ts, lines 60–66), with extraction occurring after the agent completes. - Last-tag matching in
src/extractStructuredOutput.ts(lines 12–34) ensures the final occurrence of the tag is extracted, supporting verbose agent output. - Automatic trimming removes whitespace from the extracted content before returning the JavaScript string.
- StructuredOutputError is thrown if the tag is missing or malformed, providing clear error boundaries.
Frequently Asked Questions
What is Output.string() used for?
Output.string() is designed for extracting free-form text, code snippets, or natural language responses that don't fit a strict JSON schema. According to the Sandcastle implementation, it bypasses JSON parsing and validation, returning the raw string content directly. This makes it ideal for capturing summaries, explanations, or generated code where wrapping the content in XML-style tags is more reliable than parsing unstructured stdout.
How does Sandcastle handle multiple occurrences of the same tag?
When multiple instances of the declared tag appear in the stdout, Sandcastle extracts the last occurrence. The findLastTagContent() function in src/extractStructuredOutput.ts (lines 12–34) performs a reverse search through the output buffer. This behavior allows agents to output debugging information or previous attempts while ensuring the final tagged block is captured as the authoritative response.
What happens if the agent doesn't include the required tag?
If the opening tag is missing from the prompt, run() throws an error during validation (src/run.ts, lines 96–100). If the agent fails to output the closing tag during execution, the extraction phase throws a StructuredOutputError (src/extractStructuredOutput.ts, lines 94–98). Both errors bubble up to the caller, allowing you to implement retry logic or error handling for malformed agent responses.
Can I use Output.string() with multi-iteration runs?
No. According to the source code analysis of src/run.ts (lines 60–66), structured output extraction requires single-iteration runs. The framework validates this constraint because the extraction logic assumes a complete stdout buffer collected from a single execution context. Attempting to use structured output with multi-iteration configurations will result in validation errors or undefined behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →