Skip to content

Commit 26347d2

Browse files
authored
Merge pull request #3455 from jaidev17/ai-agent-cpu-orchestration
Add Learning Path for CPU-orchestrated local AI agent
2 parents 09d6964 + d1e238a commit 26347d2

12 files changed

Lines changed: 1236 additions & 0 deletions

File tree

assets/contributors.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ author,company,github,linkedin,twitter,website
22
Jason Andrews,Arm,jasonrandrews,jason-andrews-7b05a8,,
33
Doug Anson,Arm,DougAnsonAustinTx,douganson,,
44
Pareena Verma,Arm,pareenaverma,pareena-verma-7853607,,
5+
Jaidev Singh Chadha,Arm,jaidev17,jaidevsinghchadha,,
56
Ronan Synnott,Arm,,ronansynnott,,
67
Florent Lebeau,Arm,,,,
78
Brenda Strech,Remote.It,bstrech,bstrech,@remote_it,www.remote.it
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
title: Understand the agent and the CPU/GPU split
3+
weight: 2
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## What you'll build
10+
11+
In this Learning Path, you'll build and run a *local concierge agent*: a terminal application that answers research-style questions, such as finding restaurants, comparing products, or summarizing a topic, by searching the web, reading multiple pages, and writing a fact-checked summary.
12+
13+
Everything runs on your own Arm machine. The agent uses an LLM served locally by [Ollama](https://ollama.com/), so your prompts and the pages you browse never leave the device. The same code runs on an Apple silicon MacBook, an Arm Linux laptop, or an NVIDIA DGX Spark.
14+
15+
16+
The agent is interesting not just because it runs locally, but because of *how the work is divided*. A common assumption is that an AI agent is "just the model." In practice, the model is only one stage in a longer pipeline, and most of the surrounding work runs on the CPU.
17+
18+
## The agentic loop
19+
20+
Each time you ask the agent a question, it runs through a chain of steps rather than a single model call:
21+
22+
```text
23+
Your question
24+
-> Decide what to search for (GPU: model reasoning)
25+
-> Expand into several search queries (CPU: orchestration)
26+
-> Run web searches in parallel (CPU: network I/O)
27+
-> Choose which pages to open (GPU: model reasoning)
28+
-> Scrape those pages in parallel (CPU: network I/O)
29+
-> Rank, deduplicate, extract data (CPU: text processing)
30+
-> Write a fact-checked summary (GPU: model reasoning)
31+
```
32+
33+
The model is called several times, but between every model call the CPU does a large amount of orchestration: generating query variants, dispatching parallel network requests, merging and deduplicating results, scoring pages for relevance, and extracting structured data.
34+
35+
## Why the CPU matters in an agentic workflow
36+
37+
It's easy to focus only on token generation, because that's the visible "thinking" part. But in an agent, the CPU is responsible for turning a single user request into useful, structured context for the model:
38+
39+
| Stage | Runs on | Example work |
40+
|---|---|---|
41+
| Reasoning and summarization | GPU | Choosing search terms, selecting URLs, writing the final answer |
42+
| Orchestration | CPU | Expanding queries, scheduling parallel tasks, merging results |
43+
| Web I/O | CPU | Calling the search API, downloading and parsing web pages |
44+
| Text processing | CPU | TF-IDF ranking, deduplication, entity extraction, indexing |
45+
46+
This division is a natural fit for Arm platforms. On an Apple silicon MacBook or an Arm Linux machine, the CPU handles all orchestration and I/O while the GPU accelerates the model. On an NVIDIA DGX Spark, the Arm CPU coordinates the workflow while the GPU runs inference. In every case, the model is only as good as the context the CPU prepares for it.
47+
48+
## How the agent shows you the split
49+
50+
The program is instrumented so you can see this division directly. As it runs, it prints:
51+
52+
- <code style="color:#00aaaa"><strong>[CPU]</strong></code> log lines for every orchestration and processing step
53+
- <code style="color:#aaaa00"><strong>[GPU]</strong></code> log lines for every model call
54+
- A timing breakdown of CPU time versus GPU input-token processing and token generation
55+
- A text-based timeline that visualizes when the CPU and GPU are each active
56+
57+
By the end of this Learning Path, you'll be able to look at a single query and see exactly how much of the work happened on the CPU before and after the model produced its answer.
58+
59+
## What's next
60+
61+
In the next section, you'll set up your environment: create a Python virtual environment, install the required packages, and get a Serper API key for web search.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
title: Set up your environment
3+
weight: 3
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Set up your environment
10+
11+
In this section, you'll prepare everything the agent needs before running it: a Serper API key for web search, a Python virtual environment, the required packages, and the agent script itself.
12+
13+
These steps are identical on an Apple silicon MacBook, an Arm Linux machine, and an NVIDIA DGX Spark.
14+
15+
## Get a Serper API key
16+
17+
The agent searches the web through [Serper](https://serper.dev/), a Google Search API. The free tier is enough to complete this Learning Path.
18+
19+
1. Go to [serper.dev](https://serper.dev/) and create a free account.
20+
2. Open your dashboard and copy your API key.
21+
22+
You'll set this key as an environment variable later in this section, so the agent can read it without the key being written into the code.
23+
24+
## Create a project directory and virtual environment
25+
26+
A virtual environment keeps the agent's dependencies isolated from your system Python, so you always run against the right package versions.
27+
28+
Create and enter a project directory:
29+
30+
```bash
31+
mkdir concierge-agent
32+
cd concierge-agent
33+
```
34+
35+
Create and activate a virtual environment:
36+
37+
```bash
38+
python3 -m venv .venv
39+
source .venv/bin/activate
40+
```
41+
42+
Your shell prompt now shows `(.venv)`, which confirms the environment is active.
43+
44+
## Install the required packages
45+
46+
The agent uses `requests` for HTTP calls and `beautifulsoup4` to parse web pages.
47+
48+
Install the two packages:
49+
50+
```bash
51+
pip install requests beautifulsoup4
52+
```
53+
54+
## Set your Serper API key
55+
56+
Export your API key as an environment variable in the same terminal session:
57+
58+
```bash
59+
export SERPER_API_KEY="your-serper-api-key"
60+
```
61+
62+
{{% notice Tip %}}
63+
This variable only lasts for the current terminal session. To keep it across sessions, add the same line to your shell profile, for example `~/.zshrc` on macOS or `~/.bashrc` on Linux.
64+
{{% /notice %}}
65+
66+
## Download the agent script
67+
68+
Download the complete agent script, <a href="/learning-paths/cross-platform/ai-agent-cpu-orchestration/concierge_agent.py" download>concierge_agent.py</a>, and move it into your `concierge-agent` directory. You'll run it at the end of this Learning Path, and the next sections walk through how the important parts work.
69+
70+
Alternatively, download it directly from the command line:
71+
72+
```bash
73+
curl -O https://raw.githubusercontent.com/ArmDeveloperEcosystem/arm-learning-paths/main/content/learning-paths/cross-platform/ai-agent-cpu-orchestration/concierge_agent.py
74+
```
75+
76+
Confirm the file is in your project directory:
77+
78+
```bash
79+
ls concierge_agent.py
80+
```
81+
82+
With the environment ready and the script in place, the next step is to serve a model locally with Ollama.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
title: Serve a model locally with Ollama
3+
weight: 4
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Run AI models locally with Ollama
10+
11+
The agent's reasoning steps, such as choosing search terms, selecting URLs, and writing the final summary, are handled by an LLM. Instead of calling a cloud API, this Learning Path serves the model locally with [Ollama](https://ollama.com/).
12+
13+
Ollama is a lightweight runtime that downloads open models, loads them into memory, and exposes a local HTTP API at `http://localhost:11434`. When the agent calls that endpoint, the model runs directly on your machine: the CPU and GPU on your MacBook, Arm Linux laptop, or NVIDIA DGX Spark. Nothing is sent to an external service or cloud.
14+
15+
Running locally has three benefits that matter for an agent:
16+
17+
- **Privacy**: your prompts and the web content the agent reads stay on the device.
18+
- **Cost**: there are no per-token API charges, so you can run many queries freely.
19+
- **Control**: you choose the exact model and keep it resident in memory for fast repeated calls.
20+
21+
## Install and start Ollama
22+
23+
Install [Ollama](https://ollama.com/) and start its server using one of the following options. The server exposes a local API at `http://localhost:11434` that the agent connects to.
24+
25+
{{< tabpane code=true >}}
26+
{{< tab header="Homebrew (Recommended)" language="bash">}}
27+
# Install Ollama
28+
brew install ollama
29+
30+
# Start the server as a background service (no terminal to keep open)
31+
brew services start ollama
32+
{{< /tab >}}
33+
{{< tab header="Install script" language="bash">}}
34+
# Install Ollama
35+
curl -fsSL https://ollama.com/install.sh | sh
36+
37+
# Start the server (leave this running, and open a second terminal)
38+
ollama serve
39+
{{< /tab >}}
40+
{{< /tabpane >}}
41+
42+
With [Homebrew](https://brew.sh/), `brew services` runs Ollama in the background, so you can use the same terminal throughout. With the install script, `ollama serve` runs in the foreground, so keep that terminal open and use a second terminal for the remaining commands.
43+
44+
## Pull the Gemma model
45+
46+
This Learning Path uses [Gemma 3](https://ai.google.dev/gemma), Google's family of open models, in its 4-billion-parameter size (`gemma3:4b`). This size is a good default: it's capable enough for the agent's reasoning steps and small enough to run on a laptop.
47+
48+
Download the model:
49+
50+
```bash
51+
ollama pull gemma3:4b
52+
```
53+
54+
Confirm the model is available:
55+
56+
```bash
57+
ollama list
58+
```
59+
60+
You'll see `gemma3:4b` in the list of installed models.
61+
62+
## Choose a different model (optional)
63+
64+
The agent reads the model name from the `OLLAMA_MODEL` environment variable, so you can switch models without editing the code. The default in the script is `gemma3:4b`.
65+
66+
| Model | Size | Best for |
67+
|---|---|---|
68+
| `gemma3:4b` | 4B | Laptops and modest hardware; the default |
69+
| `gemma3:27b` | 27B | High-memory systems such as DGX Spark, for stronger reasoning |
70+
71+
To use a larger model on a DGX Spark, pull it and set the environment variable before running the agent:
72+
73+
```bash
74+
ollama pull gemma3:27b
75+
export OLLAMA_MODEL="gemma3:27b"
76+
```
77+
78+
{{% notice Note %}}
79+
Larger models produce stronger summaries but use more memory and generate tokens more slowly. Start with `gemma3:4b` to confirm everything works, then experiment with larger models if your hardware allows.
80+
{{% /notice %}}
81+
82+
## Test the model
83+
84+
Before wiring it into the agent, confirm the model responds:
85+
86+
```bash
87+
ollama run gemma3:4b
88+
```
89+
90+
Enter a short prompt:
91+
92+
```text
93+
Summarize what a local AI agent does in one sentence.
94+
```
95+
96+
Type `/bye` to exit the model session.
97+
98+
With Ollama serving `gemma3:4b`, you're ready to look at how the agent code uses it.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: Walk through the agent code
3+
weight: 5
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## How the code is organized
10+
11+
The `concierge_agent.py` script you downloaded earlier is organized into four parts. This section walks through the important pieces so you understand what runs on the CPU, what runs on the GPU, and how the agent chains them together.
12+
13+
| Part | Responsibility | Runs on |
14+
|---|---|---|
15+
| Part 1: Tools | Web search, page scraping, optional email | CPU |
16+
| Part 2: The brain | Calls the local Gemma model through Ollama | GPU |
17+
| Part 3: Orchestration pipeline | Query expansion, parallelism, ranking, deduplication, extraction | CPU |
18+
| Part 4: The agentic chain | Ties everything together into one query workflow | CPU + GPU |
19+
20+
## Part 1: The tools
21+
22+
The tools are the agent's connection to the outside world. `search_web()` calls the Serper API and returns the top five results; `browse_website()` downloads a page and strips it down to clean text. Both run entirely on the CPU and both log their activity with `log_cpu()`:
23+
24+
```python
25+
def search_web(query: str) -> str:
26+
"""Use the Serper.dev API to perform a web search."""
27+
log_cpu(f"Searching web: '{query}'")
28+
...
29+
30+
def browse_website(url: str) -> str:
31+
"""Scrape and clean the text content of a given URL."""
32+
log_cpu(f"Browsing website: '{url}'")
33+
...
34+
return text[:8000]
35+
```
36+
37+
`browse_website()` uses BeautifulSoup to remove `<script>` and `<style>` tags, collapse whitespace, and cap the result at 8,000 characters so a single large page can't dominate the pipeline.
38+
39+
## Part 2: The brain
40+
41+
A single function, `call_gemma_ollama()`, is the only place the model is used. It sends a prompt to the local Ollama API and streams the response back token by token:
42+
43+
```python
44+
def call_gemma_ollama(prompt, output_format="json", timing=None,
45+
label="ollama", timeline_events=None):
46+
log_gpu("Thinking with local Gemma model...")
47+
payload = {
48+
"model": OLLAMA_MODEL,
49+
"prompt": prompt,
50+
"stream": True,
51+
"keep_alive": -1, # Keep model weights in GPU memory
52+
}
53+
...
54+
```
55+
56+
Two details are worth highlighting:
57+
58+
- `keep_alive: -1` tells Ollama to keep the model resident in memory between calls. The agent calls the model several times per query, so this avoids reloading the weights each time.
59+
- The function records timing as it streams. The time until the *first* token arrives is the model's input-token processing (prefill); the time spent streaming the rest is token generation. Separating these two values is what lets the agent show you the GPU breakdown later.
60+
61+
Because every model call goes through this one function, the agent only ever uses the GPU in clearly marked places.
62+
63+
## Part 3: The CPU orchestration pipeline
64+
65+
Between the model calls, the CPU prepares the context. Each stage below runs entirely on the CPU:
66+
67+
| Stage | Function | What it does |
68+
|---|---|---|
69+
| Expand queries | `generate_search_queries()` | Turns one query into several variants |
70+
| Search in parallel | `parallel_search()` | Runs all searches concurrently |
71+
| Browse in parallel | `parallel_browse()` | Fetches up to ten websites at once |
72+
| Rank by relevance | `rank_and_filter_content()` | Scores pages with TF-IDF |
73+
| Deduplicate | `deduplicate_sentences()`, `find_near_duplicates()` | Removes repeated sentences and near-duplicate pages |
74+
| Extract data | `extract_entities()` | Pulls out phone numbers, hours, and prices |
75+
| Build index | `build_keyword_index()` | Indexes the most frequent terms |
76+
77+
## Part 4: The agentic chain
78+
79+
`run_concierge_agent()` ties the pipeline together. Reading it top to bottom shows how CPU and GPU steps alternate:
80+
81+
1. **GPU**`call_gemma_ollama()` turns your question into a search query.
82+
2. **CPU**`generate_search_queries()` and `parallel_search()` expand and run the searches.
83+
3. **GPU** – the model selects which URLs to open.
84+
4. **CPU**`parallel_browse()`, `rank_and_filter_content()`, `deduplicate_sentences()`, `extract_entities()`, and the indexing stages prepare the context.
85+
5. **GPU** – the model writes the final, fact-checked summary.
86+
87+
Now that you understand the structure, you'll run the agent and watch the CPU and GPU work in real time.

0 commit comments

Comments
 (0)