> ## Documentation Index
> Fetch the complete documentation index at: https://anaconda.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Example project

export const TroubleshootSolution = ({children}) => <>
    <hr className="my-3 w-full" />
    <details className="mt-3">
      <summary className="cursor-pointer font-semibold text-base mb-1">
        Solution
      </summary>
      <div className="mt-2 ml-4" data-component-part="step-content">
        {children}
      </div>
    </details>
  </>;

export const TroubleshootCause = ({children}) => <details className="mt-3 mb-2">
    <summary className="cursor-pointer font-semibold text-base mb-1">
      Cause
    </summary>
    <div className="mt-2 ml-4" data-component-part="step-content">
      {children}
    </div>
  </details>;

export const TroubleshootTitle = ({children}) => <>
    <p className="m-0 font-semibold text-xl leading-tight mb-2" role="heading" aria-level={3}>
      {children}
    </p>
    <hr className="my-3 w-full" />
  </>;

export const Troubleshoot = ({children}) => <div className="callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border troubleshoot-admonition dark:troubleshoot-admonition" data-callout-type="troubleshoot">
    <div className="mt-0.5 w-4">
      <svg width="14" height="14" viewBox="0 0 640 640" fill="currentColor" className="w-4 h-4" aria-label="Troubleshoot">
        <path d="M541.4 162.6C549 155 561.7 156.9 565.5 166.9C572.3 184.6 576 203.9 576 224C576 312.4 504.4 384 416 384C398.5 384 381.6 381.2 365.8 376L178.9 562.9C150.8 591 105.2 591 77.1 562.9C49 534.8 49 489.2 77.1 461.1L264 274.2C258.8 258.4 256 241.6 256 224C256 135.6 327.6 64 416 64C436.1 64 455.4 67.7 473.1 74.5C483.1 78.3 484.9 91 477.4 98.6L388.7 187.3C385.7 190.3 384 194.4 384 198.6L384 240C384 248.8 391.2 256 400 256L441.4 256C445.6 256 449.7 254.3 452.7 251.3L541.4 162.6z" />
      </svg>
    </div>
    <div className="prose min-w-0 w-full">{children}</div>
  </div>;

This example project fetches the latest comic strip from [xkcd.com](https://xkcd.com), updates a data asset, and triggers a flow that explains the comic using a small visual language model running locally. You can also browse past comics through a deployed app and choose to explain any of them.

<GitHub.Repo repo="outerbounds/ob-project-starter" />

<Frame>
  <img src="https://mintcdn.com/anaconda-29683c67/TXgc_5FN8574WLab/images/platform/migrated/xkcd-example.png?fit=max&auto=format&n=TXgc_5FN8574WLab&q=85&s=9576a19ef7c586f7bd13c395a96d468a" alt="The example project overview page showing the XKCD comic explanation workflow" width="1190" height="646" data-path="images/platform/migrated/xkcd-example.png" />
</Frame>

The example is not a gold standard for performant, accurate AI. Explaining one comic as a batch process with a small model on a CPU instance is deliberately modest, but it offers a good baseline. You can improve the example by adding a compute pool with GPUs, running larger batches of comics through the model, or upgrading the model itself.

More importantly, the example demonstrates concisely how [all the elements of a project](/docs/platform/guides/projects/project-structure) (flows, deployments, code, data, and models) come together to create a complete, live AI system powered by a local model. You can use the project as inspiration and as a template for your own projects.

## Deploying the example

Deploy the project following the steps in [Setting up a new project](/docs/platform/guides/projects/setting-up-a-new-project):

1. Clone the repository:

   ```sh theme={null}
   git clone https://github.com/outerbounds/ob-project-starter.git
   ```

2. Change `platform` in [`obproject.toml`](https://github.com/outerbounds/ob-project-starter/blob/main/obproject.toml#L2) to match your platform URL.

3. Create a corresponding CI/CD machine user, as described in [Programmatic access with machine users](/docs/platform/guides/security/programmatic-access-via-machine-users).

4. Open a pull request and make a commit to trigger a project update.

When the deploy completes, the project overview page looks like this, without the highlight cards, which appear only after the workflows have run:

<Frame>
  <img src="https://mintcdn.com/anaconda-29683c67/VD0yQ0tXYWIdTsBU/images/platform/plat_projects_example_overview.png?fit=max&auto=format&n=VD0yQ0tXYWIdTsBU&q=85&s=d2b89df58a8cbf166e1a711cad4eef04" alt="The example project overview page on the platform showing workflows, deployments, and assets" width="1866" height="1082" data-path="images/platform/plat_projects_example_overview.png" />
</Frame>

### Testing highlight cards

The full explanation pipeline takes several minutes per comic. Before getting to XKCD, run a faster flow to confirm the project deployed correctly and to see how highlight cards work:

1. Select the project in the context picker.
2. Navigate to **Workflows** and select `HighlightTester`.
3. Click **Actions**, then click **Trigger a run**. The `style` field is pre-filled with `animals`; leave it as-is or enter another style (`nyan`, `image`, `small_square`, `tall_image`, `wide_image`, `revenue`, `busy`).
4. Click **Trigger**.

After the run completes, a highlight card appears on the project overview page:

<Frame>
  <img src="https://mintcdn.com/anaconda-29683c67/VD0yQ0tXYWIdTsBU/images/platform/plat_projects_example_highlight_card.png?fit=max&auto=format&n=VD0yQ0tXYWIdTsBU&q=85&s=5713b5e6351265f6559d3b31fe4a7b56" alt="The project overview page showing the Zoo highlight card with animal emojis, alongside the Code, Data Assets, Model Assets, and Workflows panels" width="1866" height="1035" data-path="images/platform/plat_projects_example_highlight_card.png" />
</Frame>

See [the source code of `HighlightTester`](https://github.com/outerbounds/ob-project-starter/blob/main/flows/highlight-tester/flow.py) to learn how you can render highlights of different styles for your own projects.

<Tip>
  Any flow can define a `@highlight` card to make the system readily observable. The overview page reflects the highlights produced by the latest successful runs, so you can use them to surface KPIs and health indicators as a project dashboard. Highlights are concise by design; click a highlight to view the more detailed `@card` behind it.
</Tip>

## View a comic and trigger an explanation

The project includes a deployed app, `xkcd-viewer`: [a Streamlit app](https://github.com/outerbounds/ob-project-starter/blob/main/deployments/xkcd-viewer/app.py) that lets you browse past XKCD comics and trigger an explanation for any of them.

1. Navigate to **Deployments** to see the deployed app:

2. On the deployment's card, click the URL shown under the deployment name to open the viewer in a new tab.

   <Frame>
     <img src="https://mintcdn.com/anaconda-29683c67/VD0yQ0tXYWIdTsBU/images/platform/plat_projects_example_deployment.png?fit=max&auto=format&n=VD0yQ0tXYWIdTsBU&q=85&s=e68a025a4f7d86d8f218e59265137f85" alt="The Deployments view showing the xkcd-viewer deployment details, including its status, project branch, and the 'available at' link to open the viewer" width="1866" height="597" data-path="images/platform/plat_projects_example_deployment.png" />
   </Frame>

3. In the viewer, browse to a comic and click **Trigger analysis**. This triggers a run of the `XKCDExplainer` flow.

4. Navigate to **Workflows → XKCDExplainer** to watch the run start, and follow its progress through logs and the run card.

While the explanation runs, click **Models** to see the model asset the flow uses. The model is defined in [`asset_config.toml`](https://github.com/outerbounds/ob-project-starter/blob/main/models/explainer-vlm/asset_config.toml), which keeps it decoupled from the code and explicitly visible, an important property for evaluation. In real AI projects, teams often iterate across multiple models, which makes tracking their performance crucial.

<Tip>
  Instantiating a local VLM and prompting it takes 3-5 minutes on the small CPU instance the example uses by default. If you have a GPU compute pool configured, you can speed up prompting by [adding `gpu=1` to the `@resources` decorator of the `prompt_vlm` step](https://github.com/outerbounds/ob-project-starter/blob/main/flows/xkcd-explainer/flow.py#L86).
</Tip>

<Troubleshoot>
  <TroubleshootTitle>
    ### The xkcd-viewer deployment does not appear
  </TroubleshootTitle>

  <TroubleshootCause>
    The **Deployments** view is empty because the deployment was not deployed to the perimeter or branch currently selected in the context picker, or the CI/CD deploy did not complete.
  </TroubleshootCause>

  <TroubleshootSolution>
    Confirm that the context picker shows the project and the perimeter where the CI/CD machine user deploys, and that you are viewing the branch you pushed to. Check your CI/CD run logs for errors during the deploy step; flows and deployments deploy together, so if `XKCDData` appears under **Workflows** but `xkcd-viewer` does not appear under **Deployments**, the deploy step likely failed partway.
  </TroubleshootSolution>
</Troubleshoot>

## Trigger a data asset update

The `XKCDData` flow fetches the latest comic daily at midnight and creates the `xkcd` data asset. Until it runs, the **Data** view shows only an empty asset. You can trigger an update manually instead of waiting:

1. Navigate to **Workflows → XKCDData**.
2. Click **Actions**, then click **Trigger a run**.
3. Click **Trigger**.
4. After the run completes, return to the **Data** view to see the populated `xkcd` asset.

Whenever `XKCDData` finds a new comic, it triggers a run of `XKCDExplainer` automatically. You can observe the `explain` events facilitating this in the **Events** view.

## Connecting the dots

At this point you have touched all the parts of the system:

* The `XKCDData` and `XKCDExplainer` flows
* The `xkcd-viewer` app
* The `models`, `data`, `code`, and `events` that make the system work

<Frame>
  <img src="https://mintcdn.com/anaconda-29683c67/TXgc_5FN8574WLab/images/platform/migrated/xkcd-diagram.png?fit=max&auto=format&n=TXgc_5FN8574WLab&q=85&s=1a03bdc60933c19dc030a8178cee5d5b" alt="Diagram showing how the XKCD flows, app, and assets connect" width="2000" height="914" data-path="images/platform/migrated/xkcd-diagram.png" />
</Frame>

Note how the project uses [a small shared library, `xkcd_utils`](https://github.com/outerbounds/ob-project-starter/tree/main/src/xkcd_utils), to encapsulate logic used across flows and deployments.

## Developing and testing locally

A key strength of Metaflow is how easily it supports local development and testing, even when flows demand substantial compute resources. Project flows are no different.

For instance, to test `XKCDData` locally, run the following at the project root:

```sh theme={null}
python flows/xkcd-data/flow.py run
```

To test the explainer flow, which requires more computational resources including GPUs:

```sh theme={null}
python flows/xkcd-explainer/flow.py --environment=fast-bakery --with kubernetes run --xkcd_url https://imgs.xkcd.com/comics/every_data_table_2x.png
```

To avoid setting the environment option repeatedly, export it:

```sh theme={null}
export METAFLOW_ENVIRONMENT=fast-bakery
```

<Note>
  Local testing takes place in the **default project**, outside Git branches. Use the context picker to switch to the default project to observe locally started runs.
</Note>

### Using assets during development

By default, `XKCDExplainer` fetches the latest data asset. During local development, you can configure which branch to *read* assets from while your *writes* remain isolated to your user namespace.

In `obproject.toml`, define:

```toml theme={null}
[dev-assets]
branch = 'main'
```

This lets you consume production assets from `main` while any assets you register go to your Metaflow user branch, such as `user.alice`, which prevents local experiments from contaminating production data.

## Iterate and evaluate

A key benefit of projects is that they let you iterate quickly and safely on every part of a production-grade system: code, data, and models across both offline and online components. For a deeper look at this pattern, see [Building standout AI](https://www.anaconda.com/blog/building-standout-ai-on-outerbounds).

To see how this works in practice, create a new branch for `ob-project-starter`, change any aspect of the system, test it locally, and open a pull request.

You can then observe your branch alongside the existing version, safely running in [its own isolated namespace](https://docs.metaflow.org/scaling/tagging), and compare the results. Colleagues can do the same at the same time, without interfering with each other's work.
