Skip to content

Reframe prompt guardrails, document the SWAIG wire protocol, and replace AI diagrams with themed SVGs - #512

Merged
Devon-White merged 16 commits into
Devon/ai-tool-guidefrom
fern/prompt-guardrails
Jul 30, 2026
Merged

Reframe prompt guardrails, document the SWAIG wire protocol, and replace AI diagrams with themed SVGs#512
Devon-White merged 16 commits into
Devon/ai-tool-guidefrom
fern/prompt-guardrails

Conversation

@fern-api

@fern-api fern-api Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Requested by: Fern Support

Targets Devon/ai-tool-guide (#490), not main.

Summary

Three related pieces of feedback on the AI platform section, all fixed on this branch.

1. Prompt guardrails (original scope)

The Boundaries section taught prompt-level don'ts as the default way to constrain an agent ("Don't ask for passwords", "Don't offer discounts"). That runs against both the rest of #490 and current model guidance: Anthropic (tell the model what to do, not what not to do), OpenAI's GPT-4.1 guidance (literal instruction following, so a pile of rules mostly produces conflicts), and Google's prompt-design guidance (prefer goals and success criteria; constrain only where exactness or safety demands it).

### Boundaries### Guardrails, rewritten as an ordered ladder: state success criteria → enforce hard rules in code behind a tool call → remove the possibility with context steps and step-scoped functions → put global non-negotiables in params.conscience (which is reinforced through the call, including after function calls) → then, sparingly, a short list of don'ts phrased as the behavior you want. The pizza example's five-don't block becomes a ## Guardrails block pointing at get_menu/place_order; prompt-areas.mdx no longer models the anti-pattern and names the conscience as the home for cross-cutting rules; best-practices.mdx renames "Defining boundaries" to "Define success, not a list of don'ts".

2. SWAIG is now described as a protocol

Feedback was that SWAIG is described many ways but never plainly as JSON over HTTP. The field-level spec did exist, but only inside two SWML reference pages and never framed as a wire format.

  • New ## The protocol: JSON over HTTP section on the tool calling guide: SignalWire POSTs one JSON document to web_hook_url (HTTP basic auth when the URL carries username:password@host), you return one JSON document with response and optional action. Complete request and reply field tables live there in accordions.
  • _web-hook-url.mdx was split so nothing is duplicated: the field lists moved into _swaig-request-fields.mdx and _swaig-response-fields.mdx, and both the SWML reference pages and the tool calling guide render the same snippets.
  • No new schema artifacts: the request SignalWire sends is already a webhook in the OpenAPI spec (aiSwaigToolWebhook / AiSwaigToolWebhookPayload on the Calls namespace), so the guide links that reference page instead of publishing a second copy of the same contract.
  • The SWML SWAIG overview's opening ("instead of messy query strings requiring tedious parsing…") is replaced with a descriptive lede that states the protocol and links up to the guide.

Deliberately left out, pending confirmation from engineering: status-code handling, timeout and retry behavior, payload size limits, and what version: "2.0" tracks.

3. Diagrams

The clipped diagrams were mermaid rendering at natural width in a narrow column. Two of them are now themed SVGs following ai-agent-flow-themed.svg — the pair in "AI in the media path" on /ai/capabilities, plus the prompt-impact chart on the prompt engineering page: one file per diagram tracking the site theme through color-scheme plus a prefers-color-scheme query, role="img" with a descriptive aria-label, wrapped in a <Frame caption> inside <llms-ignore>, with the original mermaid kept in <llms-only> so LLM consumers lose nothing. The sequence diagrams stay in mermaid, and styles.css lets .mermaid-container scroll horizontally so they no longer clip.

4. meta_data under content redaction

Added a limitations bullet: redaction masks what gets recorded, it does not decide where a value lives. Data that must outlive the call belongs on your server; data that only matters during the call can go in the call's meta_data, set through the APIs at call creation or by a SWAIG function while the call runs, which is not stored afterward.

Validation

fern docs md check passes (all 2811 MDX files valid), and specs/ is unchanged by this branch. fern check can't run its link rule in this environment (needs auth plus a rolldown native binding), so link validation is left to CI.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@fern-api

fern-api Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Requested by: Devon White
Slack thread: View conversation

@fern-api fern-api Bot closed this Jul 29, 2026
@fern-api fern-api Bot reopened this Jul 29, 2026
@fern-api fern-api Bot changed the title Reframe prompt boundaries as a guardrail hierarchy Reframe prompt guardrails, document the SWAIG wire protocol, and replace AI diagrams with themed SVGs Jul 29, 2026
fern-api Bot and others added 11 commits July 29, 2026 20:51
…za example

The request field list came from the SWML reference snippet unchanged, and
promoting it onto the tool calling guide as "complete" put it next to the
OpenAPI webhook describing the same payload with different contents. Verified
the list against the post_data construction in mod_openai actions.c:

- purpose is not a request field. The engine reads functions.purpose (or its
  replacement, functions.description) from the config and sends it as
  description. Documented description; dropped purpose.
- Added the eight fields that were missing: conversation_id, meta_data_token,
  SWMLCall, call_log, raw_call_log, fatal_error, error_reason, description.
- argument.parsed is an array, not an object; argument_desc is always an object
  when the function declares parameters, and absent for the legacy string form.
- Split the list into always-present and conditional, and said what each
  condition is, since more than half the payload is conditional.

Retitled the accordions off the completeness claim so the OpenAPI webhook stays
the contract of record.

Also fixed three things the guardrails rewrite left inconsistent:

- The pizza prompt told the agent to quote sizes and toppings only from
  get_menu while its own knowledge base listed them. Only prices were ever
  absent from that knowledge base, so the guardrails now cover prices, totals,
  discounts, and substitutions, and the response guideline that promised "clear
  pricing information" defers to place_order.
- The diagram's discount turn claimed a priced order before one existed.
- meta_data is declared in the SWML document or written by set_meta_data and
  scoped by meta_data_token; it is not call-global and not set through the APIs
  at call creation.

Framed the prompt impact diagram to match the other two.
The SWAIG snippets on the amazon_bedrock reference pages described the `ai`
agent's payload. Bedrock builds its request on a separate code path
(bedrock.c:4333 -> bedrock_get_post_data(bs, FALSE, "agent.function")), so most
of what those pages claimed was wrong.

Omit<> can't express the difference: it is not a subset. Against the ai payload
Bedrock drops 10 fields, adds 10, and changes 6 more, including two whose
documented constant values differ and a nested object that loses a member. The
sidecar webhook already sets the precedent of a separate model for a divergent
SWAIG payload, so Bedrock gets one too.

Spec:
- Added BedrockSwaigToolWebhookPayload and registered it on the Calls namespace.
  content_type is text/json, content_disposition agent.function, plus
  conversation_type, action, caller_id, the call/agent timestamps (microseconds)
  and times; argument has no substituted; no version, description, or
  argument_desc.

Docs:
- New _bedrock-swaig-request-fields, _bedrock-swaig-actions, and
  _bedrock-web-hook-url snippets, and pointed the three Bedrock pages at them.
- Bedrock accepts only 6 actions (SWML, transfer, set/unset_global_data,
  set/unset_meta_data) against the ai path's 17, and its transfer is a boolean
  modifier on SWML rather than a standalone action with a destination. Verified
  DataMap output actions run through the same restricted handler, so that page
  was relisting ai-only actions too.
- Unrecognized action keys are silently ignored, which is worth stating: an
  ai-shaped reply fails quietly on Bedrock.

Two corrections to the previous commit, both found while checking Bedrock:
- meta_data_token and meta_data are not conditional on either path. The engine
  falls back to a derived token when you don't set one, so both are always sent
  to a webhook-backed function. Moved them and described the fallback.
- The ai request example still showed purpose; the wire field is description.
Webhooks only render if they are listed in apis.yml; without the entry the page
404s even though the operation is in the OpenAPI document.
- Updated AI SWAIG function documentation to clarify request and response formats, including examples for webhook payloads.
- Revised Bedrock SWAIG function documentation to streamline function definitions and clarify webhook interactions.
- Enhanced TypeSpec models for SWAIG webhook payloads, adding detailed descriptions and examples for better clarity.
- Removed deprecated fields from SWAIG models and documentation, ensuring alignment with current API specifications.
- Introduced new conventions for TypeSpec documentation and editing practices to maintain consistency and accuracy across specs.
@Devon-White
Devon-White merged commit 5515774 into Devon/ai-tool-guide Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant