跳到正文
DEV Community· Sonam Gupta·· 3 小時前AI 評分51

如何用 Telnyx Missions 建立可審計的 AI 電話工作流程

Build an Auditable AI Phone Workflow with Telnyx Missions

AI 導讀

開源 missions-audit 範例示範以 Python 和 Telnyx AI Missions 建立可審計的 AI 電話工作流程,將任務目標、計劃、步驟狀態及事件寫入持久紀錄。

正文

An AI phone workflow can return a successful response and still leave a difficult question unanswered: what actually happened?

If a clinic says an assistant called a patient, confirmed an appointment, and completed the outreach plan, a reviewer may need more than application logs. They need the objective, the approved plan, every observed state transition, the assistant associated with the run, and the final result in one durable record.

The open-source missions-audit sample shows one way to build that record with Python and Telnyx AI Missions. It creates a mission, starts a run, submits a plan, advances step statuses, logs typed events, optionally schedules a real AI-assisted phone call, and renders the resulting ledger as a timestamped audit report.

The important idea is simple: the audit trail is part of the workflow's API contract, not a story reconstructed later from process logs.

What is an AI mission audit trail?

A mission is the durable objective. A run is one execution of that objective. Each run has an ordered plan, and each plan contains steps whose statuses change as work progresses.

The sample combines five related resources:

Resource Purpose
Mission Stores the durable objective
Run Represents one execution and its final result
Plan and steps Describe the intended work and its progress
Typed events Record the chronological audit timeline
Agent link Identifies the Telnyx AI assistant associated with the run

This produces a queryable record of what was planned, what the worker observed, and how the run ended.

The five-script workflow

The project separates the workflow into five small programs:

  1. create_mission.py creates the durable mission.
  2. start_run.py starts one execution of that mission.
  3. plan_steps.py submits an ordered three-step plan.
  4. execute.py advances the run, steps, and event timeline.
  5. audit.py reads fresh server state and renders the report.

The default plan is:

provision_assistant
schedule_outreach_call
verify_outcome

Those names describe the live workflow, but the project is safe by default. More on that distinction shortly.

Creating a mission, run, and plan

The sample uses raw HTTP requests against https://api.telnyx.com/v2. Missions responses are wrapped in a data envelope and use identifiers such as mission_id and run_id.

Creating the durable objective starts with POST /ai/missions:

mission = post("/ai/missions", {
    "name": "Clinic outreach audit",
    "description": "Verify clinic contacts can be reached",
    "instructions": (
        "Verify the contact is reachable, confirm the appointment "
        "slot briefly, and stay polite."
    ),
    "execution_mode": "external",
})

The worker is external, so the application controls execution while the Missions API keeps the durable record.

One execution is created with:

run = post(f"/ai/missions/{mission_id}/runs", {})

The run begins in pending. The worker can advance it through running to succeeded or failed. The API also supports paused and cancelled states for operator intervention.

The plan uses client-selected step_id values, descriptions, and one-based sequence numbers:

steps = [
    {
        "step_id": "provision_assistant",
        "description": "Create or select the outreach assistant.",
        "sequence": 1,
    },
    {
        "step_id": "schedule_outreach_call",
        "description": "Create one scheduled phone-call event.",
        "sequence": 2,
    },
    {
        "step_id": "verify_outcome",
        "description": "Confirm the event reached a terminal status.",
        "sequence": 3,
    },
]

post(f"/ai/missions/{mission_id}/runs/{run_id}/plan", {"steps": steps})

Steps move from pending to in_progress, then to completed or failed. A skipped state is available for intentional no-ops.

The worker's honesty contract

The most useful pattern in the sample is not an endpoint. It is a rule enforced by execute.py:

Record every state change the worker observes, and never invent a platform outcome.

When a step starts, the worker updates the step and writes a step_started event. When it completes, the worker writes the corresponding status and step_completed event. Failures become both a failed step and a step_failed event.

def run_step(step_id, work):
    update_step(step_id, "in_progress")
    log_event("step_started", f"Step {step_id} started", step_id)

    try:
        result = work()
    except Exception as exc:
        update_step(step_id, "failed")
        log_event(
            "step_failed",
            f"Step {step_id} failed: {exc}",
            step_id,
        )
        raise

    update_step(step_id, "completed")
    log_event("step_completed", f"Step {step_id} completed", step_id)
    return result

In live mode, the worker polls the scheduled-event API every five seconds, with a hard ten-minute cap. Each newly observed event status becomes a status_change mission event. A request failure or timeout becomes an explicit error event and causes the verification step to fail.

That means the ledger never reports that a call completed merely because the worker expected it to complete.

Demo mode and live-call mode are deliberately different

DEMO_MODE=true is the default.

In demo mode, the sample exercises the mission, run, plan, step, and event APIs without creating an assistant, creating a scheduled event, placing a call, or linking an agent. It records a custom event with this meaning:

{
  "demo_mode": true,
  "note": "no assistant created, no call placed"
}

The run's result summary says the same thing. The simulated walk is useful for learning the ledger mechanics, but it cannot be mistaken for evidence of a real phone call.

Set DEMO_MODE=false to exercise the live path. Live mode requires placeholder-backed environment variables for a destination you control and a phone number attached to the assistant:

TELNYX_API_KEY=YOUR_TELNYX_API_KEY
DEMO_MODE=false
DESTINATION_NUMBER=<E164_TEST_NUMBER>
ASSISTANT_PHONE_NUMBER=<E164_ASSISTANT_NUMBER>

The live worker can create or reuse an assistant, schedule the phone-call event, poll its status, record the latest call attempt, and link the assistant to the mission run. The Assistants and scheduled-events APIs return bare JSON objects rather than the Missions API's data envelope, so the sample handles those response shapes separately.

Rendering the report from fresh state

audit.py waits for the run to reach succeeded, failed, or cancelled, with the same ten-minute safety cap. It then performs fresh reads of the mission, run, plan, events, and linked agents.

The report is therefore built from the server's current view rather than from the worker's in-memory state:

MISSIONS AUDIT REPORT

Mission
  mission_id: ...
  name: Clinic outreach audit

Run
  status: succeeded
  result_summary: ...

Steps
  provision_assistant     completed
  schedule_outreach_call  completed
  verify_outcome          completed

Event timeline
  step_started
  step_completed
  status_change
  custom

Linked Telnyx agents
  run_id=... telnyx_agent_id=...

If the report reaches its polling cap before the run becomes terminal, it prints a warning, performs a final fresh read, renders the current state, and exits unsuccessfully. It does not silently present an unfinished run as complete.

Run the sample

Clone the repository and install its dependencies:

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/missions-audit

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env

Add your Telnyx API key to .env. Keep DEMO_MODE=true for the side-effect-free walkthrough.

Run the included smoke test first:

pytest smoke_test.py -q

Then run the workflow, passing the identifiers printed by each step:

python create_mission.py
python start_run.py --mission-id <MISSION_ID>
python plan_steps.py --mission-id <MISSION_ID> --run-id <RUN_ID>
python execute.py --mission-id <MISSION_ID> --run-id <RUN_ID>
python audit.py --mission-id <MISSION_ID> --run-id <RUN_ID>

For live mode, use only test numbers you control and configure the required assistant phone number before running execute.py.

Where this pattern fits

The clinic example is only one use case. The same structure works anywhere an automated workflow must be replayable:

  • Collections outreach where each contact attempt needs a durable record
  • Appointment reminders that require proof of execution and outcome
  • Incident-response agents whose plans and tool calls need review
  • Sales workflows where a human must approve or audit each stage
  • Operational automations that can pause, resume, fail, or be cancelled

The reusable design is a durable objective, an explicit plan, typed events, independently queryable state, and a final report assembled from fresh reads.

Resources

An audit trail is most useful when it is designed before the first action runs. With Missions, plans, typed events, and agent linkage treated as API resources, "what happened?" becomes a query instead of an investigation.

來源:DEV Community · dev.to