Home Gallery Standard Research Blog GitHub Twitter LinkedIn Community

12-factor-agents system prompt

Category: General-purpose assistants. Audited against the AISPA standard.

What is in 12-factor-agents's system prompt?

12-factor-agents's full system prompt: 3 versions, 14,457 characters. Audited against AISPA.

The full text of 3 prompts is reproduced below, 14,457 characters in all, each read instruction by instruction against the eight AISPA dimensions. Nothing was flagged as working against the person on the other end.

3 Prompts on record
0 Flagged instructions
AI audit Audit source

12-factor-agents - workshops 2025 05 sections 01 cli and agent README

4471 characters

# Chapter 1 - CLI and Agent Loop Now let's add BAML and create our first agent with a CLI interface. First, we'll need to install [BAML](https://github.com/boundaryml/baml) which is a tool for prompting and structured outputs. npm install @boundaryml/baml Initialize BAML npx baml-cli init Remove default resume.baml rm baml_src/resume.baml Add our starter agent, a single baml prompt that we'll build on cp ./walkthrough/01-agent.baml baml_src/agent.baml <details> <summary>show file</summary> ```rust // ./walkthrough/01-agent.baml class DoneForNow { intent "done_for_now" message string } function DetermineNextStep( thread: string ) -> DoneForNow { client "openai/gpt-4o" prompt #" {{ _.role("system") }} You are a helpful assistant that can help with tasks. {{ _.role("user") }} You are working on the following thread: {{ thread }} What should the next step be? {{ ctx.output_format }} "# } test HelloWorld { functions [DetermineNextStep] args { thread #" { "type": "user_input", "data": "hello!" } "# } } ``` </details> Generate BAML client code npx baml-cli generate Enable BAML logging for this section export BAML_LOG=debug Add the CLI interface cp ./walkthrough/01-cli.ts src/cli.ts <details> <summary>show file</summary> ```ts // ./walkthrough/01-cli.ts // cli.ts lets you invoke the agent loop from the command line import { agentLoop, Thread, Event } from "./agent"; export async function cli() { // Get command line arguments, skipping the first two (node and script name) const args = process.argv.slice(2); if (args.length === 0) { console.error("Error: Please provide a message as a command line argument"); process.exit(1); } // Join all arguments into a single message const message = args.join(" "); // Create a new thread with the user's message as the initial event const thread = new Thread([{ type: "user_input", data: message }]); // Run the agent loop with the thread const result = await agentLoop(thread); console.log(result); } ``` </details> Update index.ts to use the CLI ```diff src/index.ts +import { cli } from "./cli" + async function hello(): Promise<void> { console.log('hello, world!') async function main() { - await hello() + await cli() } ``` <details> <summary>skip this step</summary> cp ./walkthrough/01-index.ts src/index.ts </details> Add the agent implementation cp ./walkthrough/01-agent.ts src/agent.ts <details> <summary>show file</summary> ```ts // ./walkthrough/01-agent.ts import { b } from "../baml_client"; // tool call or a respond to human tool type AgentResponse = Awaited<ReturnType<typeof b.DetermineNextStep>>; export interface Event { type: string data: any; } export class Thread { events: Event[] = []; constructor(events: Event[]) { this.events = events; } serializeForLLM() { // can change this to whatever custom serialization you want to do, XML, etc // e.g. https://github.com/got-agents/agents/blob/59ebbfa236fc376618f16ee08eb0f3bf7b698892/linear-assistant-ts/src/agent.ts#L66-L105 return JSON.stringify(this.events); } } // right now this just runs one turn with the LLM, but // we'll update this function to handle all the agent logic export async function agentLoop(thread: Thread): Promise<AgentResponse> { const nextStep = await b.DetermineNextStep(thread.serializeForLLM()); return nextStep; } ``` </details> The the BAML code is configured to use OPENAI_API_KEY by default As you're testing, you can change the model / provider to something else as you please client "openai/gpt-4o" [Docs on baml clients can be found here](https://docs.boundaryml.com/guide/baml-basics/switching-llms) For example, you can configure [gemini](https://docs.boundaryml.com/ref/llm-client-providers/google-ai-gemini) or [anthropic](https://docs.boundaryml.com/ref/llm-client-providers/anthropic) as your model provider. If you want to run the example with no changes, you can set the OPENAI_API_KEY env var to any valid openai key. export OPENAI_API_KEY=... Try it out npx tsx src/index.ts hello you should see a familiar response from the model { intent: 'done_for_now', message: 'Hello! How can I assist you today?' }

12-factor-agents - workshops 2025 05 17 sections 01 cli and agent ...

5147 characters

# Chapter 1 - CLI and Agent Loop Now let's add BAML and create our first agent with a CLI interface. First, we'll need to install [BAML](https://github.com/boundaryml/baml) which is a tool for prompting and structured outputs. npm install @boundaryml/baml Initialize BAML npx baml-cli init Remove default resume.baml rm baml_src/resume.baml Add our starter agent, a single baml prompt that we'll build on cp ./walkthrough/01-agent.baml baml_src/agent.baml <details> <summary>show file</summary> ```rust // ./walkthrough/01-agent.baml class DoneForNow { intent "done_for_now" message string } client<llm> Qwen3 { provider "openai-generic" options { base_url env.BASETEN_BASE_URL api_key env.BASETEN_API_KEY } } function DetermineNextStep( thread: string ) -> DoneForNow { client Qwen3 // use /nothink for now because the thinking tokens (or streaming thereof) screw with baml (i think (no pun intended)) prompt #" {{ _.role("system") }} /nothink You are a helpful assistant that can help with tasks. {{ _.role("user") }} You are working on the following thread: {{ thread }} What should the next step be? {{ ctx.output_format }} "# } test HelloWorld { functions [DetermineNextStep] args { thread #" { "type": "user_input", "data": "hello!" } "# } } ``` </details> Generate BAML client code npx baml-cli generate Enable BAML logging for this section export BAML_LOG=debug Add the CLI interface cp ./walkthrough/01-cli.ts src/cli.ts <details> <summary>show file</summary> ```ts // ./walkthrough/01-cli.ts // cli.ts lets you invoke the agent loop from the command line import { agentLoop, Thread, Event } from "./agent"; export async function cli() { // Get command line arguments, skipping the first two (node and script name) const args = process.argv.slice(2); if (args.length === 0) { console.error("Error: Please provide a message as a command line argument"); process.exit(1); } // Join all arguments into a single message const message = args.join(" "); // Create a new thread with the user's message as the initial event const thread = new Thread([{ type: "user_input", data: message }]); // Run the agent loop with the thread const result = await agentLoop(thread); console.log(result); } ``` </details> Update index.ts to use the CLI ```diff src/index.ts +import { cli } from "./cli" + async function hello(): Promise<void> { console.log('hello, world!') async function main() { - await hello() + await cli() } ``` <details> <summary>skip this step</summary> cp ./walkthrough/01-index.ts src/index.ts </details> Add the agent implementation cp ./walkthrough/01-agent.ts src/agent.ts <details> <summary>show file</summary> ```ts // ./walkthrough/01-agent.ts import { b } from "../baml_client"; // tool call or a respond to human tool type AgentResponse = Awaited<ReturnType<typeof b.DetermineNextStep>>; export interface Event { type: string data: any; } export class Thread { events: Event[] = []; constructor(events: Event[]) { this.events = events; } serializeForLLM() { // can change this to whatever custom serialization you want to do, XML, etc // e.g. https://github.com/got-agents/agents/blob/59ebbfa236fc376618f16ee08eb0f3bf7b698892/linear-assistant-ts/src/agent.ts#L66-L105 return JSON.stringify(this.events); } } // right now this just runs one turn with the LLM, but // we'll update this function to handle all the agent logic export async function agentLoop(thread: Thread): Promise<AgentResponse> { const nextStep = await b.DetermineNextStep(thread.serializeForLLM()); return nextStep; } ``` </details> The the BAML code is configured to use BASETEN_API_KEY by default To get a Baseten API key and URL, create an account at [baseten.co](https://baseten.co), and then deploy [Qwen3 32B from the model library](https://www.baseten.co/library/qwen-3-32b/). ```rust function DetermineNextStep(thread: string) -> DoneForNow { client Qwen3 // ... ``` If you want to run the example with no changes, you can set the BASETEN_API_KEY env var to any valid baseten key. If you want to try swapping out the model, you can change the `client` line. [Docs on baml clients can be found here](https://docs.boundaryml.com/guide/baml-basics/switching-llms) For example, you can configure [gemini](https://docs.boundaryml.com/ref/llm-client-providers/google-ai-gemini) or [anthropic](https://docs.boundaryml.com/ref/llm-client-providers/anthropic) as your model provider. For example, to use openai with an OPENAI_API_KEY, you can do: client "openai/gpt-4o" Set your env vars export BASETEN_API_KEY=... export BASETEN_BASE_URL=... Try it out npx tsx src/index.ts hello you should see a familiar response from the model { intent: 'done_for_now', message: 'Hello! How can I assist you today?' }

12-factor-agents - packages walkthroughgen prompt

4839 characters

Walkthroughgen is a tool for creating walkthroughs, tutorials, readmes, and documentation. ## Usage You create a walkthrough by writing a simple yaml file that describes the walkthrough. In the file, you reference the incremental files that should exist at each step of the walkthrough ``` ├── walkthrough │   ├── 00-package-lock.json │   ├── 00-package.json │   ├── 01-index.ts │   ├── 02-cli.ts │   └── 02-index.ts └── walkthrough.yaml ``` Your walkthrough.yaml file might look like this (runnable example in [examples/typescript-cli](./examples/typescript)) ```yaml title: "setting up a typescript cli" text: "this is a walkthrough for setting up a typescript cli" targets: - markdown: "./build/walkthrough.md" # generates a walkthrough.md file onChange: # default behavior - on changes, show diffs and cp commands diff: true cp: true newFiles: # when new files are created, just show the copy command cat: false cp: true - final: "./build/final" # outputs the final project to the final folder - folders: "./build/by-section" # creates a separate working folder for each section sections: - name: setup title: "Copy initial files" steps: - file: {src: ./walkthrough/00-package.json, dest: package.json} - file: {src: ./walkthrough/00-package-lock.json, dest: package-lock.json} - file: {src: ./walkthrough/00-tsconfig.json, dest: tsconfig.json} - name: initialize title: "Initialize the project" steps: - text: "initialize the project" command: | npm install - text: "then add index.ts" file: {src: ./walkthrough/01-index.ts, dest: src/index.ts} - text: "run it with tsx" command: | npx tsx src/index.ts results: - text: "you should see a hello world message" code: | hello world - name: add-cli title: "Add a CLI" steps: - text: "add a cli" file: {src: ./walkthrough/02-cli.ts, dest: src/cli.ts} - text: "add a cli" file: {src: ./walkthrough/02-index.ts, dest: src/index.ts} ``` Build the project with: ``` npm i -g wtg wtg build ``` based on your targets, this would create the following files ``` ├── walkthrough │   ├── 00-package-lock.json │   ├── 00-package.json │   ├── 01-index.ts │   ├── 02-cli.ts │   └── 02-index.ts ├── build │ ├── by-section │ │ ├── 00-initialize # only contains the files in `init` │ │ │ ├── readme.md # contains steps for this section │ │ │ ├── package.json │ │ │ ├── package-lock.json │ │ │ └── tsconfig.json │ │ └── 01-add-cli # contains the files up to the START of section 1 │ │ ├── readme.md # contains steps for this section │ │ ├── package.json │ │ ├── package-lock.json │ │ ├── tsconfig.json │ │ └── src │ │ └── index.ts │ ├── final │ │ ├── package.json │ │ ├── package-lock.json │ │ ├── tsconfig.json │ │ └── src │ │ ├── cli.ts │ │ └── index.ts │ └── walkthrough.md and your walkthrough.md file will look like: ```markdown # Setting up a typescript cli this is a walkthrough for setting up a typescript cli ## Copy initial files cp walkthrough/00-package.json package.json cp walkthrough/00-package-lock.json package-lock.json cp walkthrough/00-tsconfig.json tsconfig.json ## Initialize the project initialize the project npm install then add index.ts cp walkthrough/01-index.ts src/index.ts and run it with tsx npx tsx src/index.ts you should see a hello world message hello world ## Add a CLI add a cli ``` ``` cp walkthrough/02-cli.ts src/cli.ts update index.ts to use the cli ```diff const main = async () => { + return cli(); }; main(); ``` or just: cp walkthrough/02-index.ts src/index.ts ``` ## Features ### Targets - `file`: generates a single markdown file - `folder`: creates a set of folders, one for each section - `final`: outputs the final project to the current directory ### Init ### Sections ### Steps #### Step ## Walkthrough.yaml for walkthroughgen ## Implementation Plan - [ ] implement core walkthroughgen CLI - `wtg build` # defaults to walkthrough.yaml in current directory - Scope 1: generating walkthrough.md - [ ] create end-to-end test for a simple walkthrough file, just a single yaml file with no sections - [ ] create end-to-end test for a walkthrough file with a single section - [ ] test generation of diffs and cp commands - Scope 2: generating final/ project build - [ ] create end-to-end test for a walkthrough file with a final target - Scope 3: generating by-section project builds with readmes - [ ] create end-to-end test for a walkthrough file with a by-section target

Questions about 12-factor-agents's system prompt

Does 12-factor-agents's system prompt contain instructions that work against the user?

No. Nothing in 12-factor-agents's system prompt was flagged as working against the person the product is talking to. That is a clean result across all eight AISPA dimensions, not an absence of checking — the full text was read instruction by instruction.

How long is 12-factor-agents's system prompt?

14,457 characters across 3 prompts on this page. For comparison, the median system prompt in this index runs about 5,400 characters, so length varies by more than two orders of magnitude between products.

How many versions of 12-factor-agents's system prompt are on record?

3. Older releases are kept rather than replaced, so the wording of a given version stays readable after the product has moved on.

Where did this 12-factor-agents system prompt come from?

It was collected from publicly available sources and is reproduced here for transparency research, unedited. This site does not extract prompts from products itself.

How was 12-factor-agents's system prompt audited?

Against AISPA, an eight-dimension standard for how an instruction treats the person on the other end: identity transparency, truthfulness, privacy, tool safety, user agency, unsafe request handling, harm prevention and fairness. This audit was ai audit. The method is described in the paper behind the standard.

How this page was made

The prompt text above is reproduced verbatim from a public source. Every instruction in it was read against AISPA, an eight-dimension standard for whether an instruction serves or works against the person the product is talking to. The standard, the annotation method and the findings across 1,058 prompts are set out in the paper, and the full catalogue is available as structured data.

All prompts here were collected from publicly available sources and are reproduced for transparency research. Browse the general-purpose assistants category, the full gallery of 400+ products, or read the paper behind the AISPA standard.