> Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header `Accept: text/markdown`. The root index at https://signalwire.com/docs/llms.txt lists the available documentation indexes. # Prompt engineering > Learn where to place AI agent instructions, how to structure an effective prompt, and when to enforce behavior in code instead. [tool-calling]: /docs/platform/ai/tool-calling A prompt tells an AI agent who it is, what it needs to accomplish, and how to handle the conversation. The most effective prompts give the model the context only your application can provide, then leave exact data and hard rules to your code. Start with the smallest prompt that handles the common case. Add instructions when testing reveals a specific gap, and remove instructions that don't change the agent's behavior. ## Prompt surfaces SignalWire AI Agents accept instructions in five places. Put each instruction as close as possible to the behavior it controls. | Prompt surface | Use it for | | ----------------------------------------------------------- | -------------------------------------------------------------------------------------- | | [Main prompt](#main-prompt) | Identity, objective, stable knowledge, conversation flow, and response style | | [Context steps](#context-steps) | Goals, available functions, and transitions for one stage of a multi-step conversation | | [SignalWire AI Gateway (SWAIG) functions](#swaig-functions) | When to call a function and how to collect its parameters | | [Conscience](#conscience) | Short, global standards that remain active throughout the conversation | | [Post-prompt](#post-prompt) | Summarizing or extracting data after the conversation ends | The main prompt, context steps, functions, and conscience affect the live conversation. The post-prompt runs after the conversation and cannot change how the agent responds during the call. ### Main prompt The main prompt provides shared context for the complete conversation. Organize it so the agent can distinguish its objective, facts, process, and speaking style. A useful starting structure is: * **Role and objective:** who the agent represents, what it does, and what completes the interaction * **Stable knowledge:** terminology, available services, and information required to complete a task * **Task structure:** the expected flow, written as outcomes rather than a script for every sentence * **Response guidelines:** the tone, response length, and application-specific formatting rules Keep stage-specific directions in context steps, function selection details in SWAIG function descriptions, global standards in the conscience, and after-call extraction in the post-prompt. ### Context steps Use context steps when a conversation has distinct stages. Each step can define: * `text`: what the agent should accomplish and how it should behave in that stage * `step_criteria`: the condition that completes the stage * `functions`: the functions available during the stage * `valid_steps`: the stages the agent may enter next For example, expose `get_quote` only in the quote step. This prevents the agent from quoting before it confirms the trip instead of relying on a sentence that asks it not to. ### SWAIG functions A function definition is also a prompt. Give each function a descriptive name, state when to call it, and describe each parameter precisely. Prefer `get_quote` with the description "Price a trip after the caller confirms both addresses" over `function_1` with the description "Check price." Parameter descriptions should also define the expected value, such as an address in the caller's words or one of the supported vehicle types. Return enough information for the agent to respond without guessing. A quoting function might return the fare, distance, and a caller-facing explanation when it rejects a trip. A response such as `ok` leaves the agent to invent the missing details. ### Conscience Use the conscience for a small set of standards that apply throughout the conversation, including after function calls. Write the behavior you want rather than a long list of prohibitions. For example: "Say what you know, be clear about what you don't know, and offer to find help instead of filling gaps. Collect only the information needed to serve the caller." Exact outcomes still belong in code. A conscience can tell the agent to respect a pricing policy; only the quoting function can enforce that policy on every call. ### Post-prompt Use the post-prompt to summarize the completed conversation or extract structured data for another system. Name the required fields, allowed values, and output format. For example, ask for a JSON object containing the final confirmed pickup address, destination, and an outcome limited to `quoted`, `declined`, or `unavailable`. ## Write focused instructions Use clear, specific language and consistent headings. Tell the agent what a successful interaction looks like, then give it enough flexibility to handle interruptions, corrections, and information provided out of order. Describe the behavior you want instead of trying to anticipate every failure. "Transfer fare disputes to a dispatcher" gives the agent a clear action. "Don't mishandle disputes" does not. ### Avoid overprompting Models already understand grammar, common conversational patterns, and how to structure a sentence. Use prompt space for your application's vocabulary, workflow, boundaries, and required response format. Don't embed large catalogs, rate tables, policies, or facts that change. They make the prompt harder to maintain and can still leave the model to calculate or choose an answer. Retrieve that data with a [tool call][tool-calling], then have the agent relay the result. The [complete example](#complete-example) applies these principles without putting fare data in the prompt. ## Complete example The two implementations below create the same fare-quote agent. The Server SDK example is first and runs its lookup in Python. The SDK generates a function webhook URL that routes calls to the `get_quote` handler. In the SWML example, replace the placeholder `web_hook_url` with an endpoint that performs the same lookup. Both versions include a main prompt, context steps, a `get_quote` function, a conscience, and a post-prompt. The small route table stands in for your dispatch system; replace it with your own current data source in production. #### Server SDK **`agent.py`** ```python title="agent.py" from signalwire import AgentBase, FunctionResult FARES = { ("123 gough street", "sfo"): 38.60, ("123 gough street", "embarcadero"): 10.25, } class QuoteAgent(AgentBase): def __init__(self): super().__init__(name="bayview-quote-agent") self.prompt_add_section( "Role and objective", "You are Ada, the dispatcher for Bayview Taxi. Give callers fare quotes. " "A call is complete when the caller receives a quote or learns that the " "route is unavailable.", ) self.prompt_add_section( "Stable knowledge", "A quote requires a pickup address and destination. Recognize SFO as " "San Francisco International Airport.", ) self.prompt_add_section( "Response guidelines", "Use a calm, efficient tone. Keep replies short enough for the caller to " "interrupt. Read the fare exactly as get_quote returns it.", ) self.set_params( { "conscience": ( "Say what you know and be clear about what you don't know. " "Collect only the information needed to quote the trip." ) } ) self.set_post_prompt( "Return JSON with pickup_address, destination, and outcome. " "Set outcome to quoted, declined, or unavailable." ) self.define_tool( name="get_quote", description=( "Return the fare after the caller confirms both addresses. " "Call again if either address changes." ), parameters={ "type": "object", "properties": { "pickup": { "type": "string", "description": "The confirmed pickup address", }, "destination": { "type": "string", "description": "The confirmed destination; use SFO for the airport", }, }, "required": ["pickup", "destination"], }, handler=self.get_quote, ) contexts = self.define_contexts() default = contexts.add_context("default") default.add_step("collect_trip") \ .set_text("Collect and confirm the pickup address and destination.") \ .set_step_criteria("Both addresses are confirmed.") \ .set_functions([]) \ .set_valid_steps(["quote"]) default.add_step("quote") \ .set_text("Call get_quote, read its result, and ask if the caller needs anything else.") \ .set_step_criteria("The caller has received the quote or learned it is unavailable.") \ .set_functions(["get_quote"]) def get_quote(self, args, raw_data): pickup = args.get("pickup", "").lower().strip() destination = args.get("destination", "").lower().strip() fare = FARES.get((pickup, destination)) if fare is None: return FunctionResult( "That route is unavailable. Say that you cannot quote it and ask " "whether the caller wants to try different addresses." ) return FunctionResult( f"The fare is ${fare:.2f}. Read this exact amount to the caller." ) if __name__ == "__main__": QuoteAgent().run() ``` #### SWML **`agent.yaml`** ```yaml title="agent.yaml" version: 1.0.0 sections: main: - answer: {} - ai: prompt: pom: - title: Role and objective body: >- You are Ada, the dispatcher for Bayview Taxi. Give callers fare quotes. A call is complete when the caller receives a quote or learns that the route is unavailable. - title: Stable knowledge body: >- A quote requires a pickup address and destination. Recognize SFO as San Francisco International Airport. - title: Response guidelines body: >- Use a calm, efficient tone. Keep replies short enough for the caller to interrupt. Read the fare exactly as get_quote returns it. contexts: default: steps: - name: collect_trip text: Collect and confirm the pickup address and destination. step_criteria: Both addresses are confirmed. functions: [] valid_steps: - quote - name: quote text: Call get_quote, read its result, and ask if the caller needs anything else. step_criteria: The caller has received the quote or learned it is unavailable. functions: - get_quote params: conscience: >- Say what you know and be clear about what you don't know. Collect only the information needed to quote the trip. SWAIG: functions: - function: get_quote description: >- Return the fare after the caller confirms both addresses. Call again if either address changes. parameters: type: object properties: pickup: type: string description: The confirmed pickup address destination: type: string description: The confirmed destination; use SFO for the airport required: - pickup - destination # Replace this URL with your endpoint. It receives the function # arguments and must return a SWAIG function response, such as: # {"response":"The fare is $38.60. Read this exact amount to the caller."} web_hook_url: https://example.com/swaig/get-quote post_prompt: text: >- Return JSON with pickup_address, destination, and outcome. Set outcome to quoted, declined, or unavailable. ``` ## Put guardrails in the right layer A prompt requests behavior; code enforces it. Use these options in order: 1. **Define success.** A clear objective handles many boundaries without extra prohibitions. 2. **Enforce exact rules in code.** Use functions for prices, discounts, eligibility, permissions, current data, and other outcomes that must be correct. 3. **Remove unavailable actions.** Limit functions and transitions with context steps so the agent cannot take an action at the wrong stage. 4. **Apply global standards through the conscience.** Reserve it for a short set of behavioral, safety, compliance, or brand standards that code cannot express. 5. **Add a prompt rule when needed.** Make it specific and describe the desired response. "Transfer fare disputes to a dispatcher" gives the agent an action; "don't mishandle disputes" does not. ## Test and refine Treat the first prompt as a draft. Test it against representative conversations, revise one behavior at a time, and keep the prompt under version control with its test cases. Test the common path first, then cover: * interruptions, corrections, and information provided out of order * missing, ambiguous, or unsupported requests * attempts to override the agent's instructions * function failures and handoffs * boundaries where the agent should say it doesn't know When a test fails, identify the right layer before adding prompt text. Fix incorrect data or hard rules in code, function timing in context steps, function selection in its description, and global behavior in the conscience. Trim after each successful revision. Remove one line, rerun the same tests, and keep the deletion if behavior stays the same. This prevents duplicated rules and general knowledge from hiding the instructions specific to your application. ## Next steps #### [Tool calling](/docs/platform/ai/tool-calling) Connect prompts to current data and rules your application can enforce. #### [AI agent best practices](/docs/platform/ai/best-practices) Apply prompt design alongside testing, latency, error handling, and observability practices. > Write clear, reliable prompts for SignalWire AI agents