FAQ for my AI Udemy Courses

I’ve put together this quick FAQ page to answer the most common questions that come up on my courses. I hope this sorts your problem – but if not, please do reach out in Udemy or by email to ed@edwarddonner.com.

1. Which order should I take your courses?

Aha! I get this question a lot, and so I wrote a full answer with a curriculum!

2. Can I take your courses with no Python / no programming experience?

I’ve tried to design my courses to appeal to the widest possible audience – there should be something here for everyone.

But for the AI Engineering courses, I’m gonna be straight up: the courses are easiest for people with at least python foundations. It’s entirely possible to take them with no python at all, and several people have completed everything and reported success and satisfaction. But it’s definitely a steep learning curve.

If you’re new to programming and decide to take the plunge with the AI Engineering courses, then:

  1. Please consider starting with the AI Builder course (first on the list of the curriculum) as this is the best starting point for those new to coding
  2. Please spend time on the self study guides, particularly guides 1-6
  3. Give yourself plenty of time: lots of practice, tons of patience
  4. Go through the labs slowly, adding print() statements and making tweaks
  5. If you don’t follow everything, it honestly doesn’t matter; if you pick up the general gist, then that’s all good! Some people have actually watched the entire course twice as a way to build understanding.
  6. I’m here to help! Always reach out if you’re stuck.
3. Will I be able to get a job as an AI Engineer after I take your course?

I’ve based the full AI curriculum of the courses off:

  • My knowledge of the industry
  • What I look for when hiring AI Engineers
  • A review of AI engineering job descriptions

But in my opinion, completing the courses is an important step, but on its own this will not qualify you for a job as an AI Engineer. What will qualify you: building, building, building. Create AI projects, particularly projects that tackle a personal or professional pain-point. If you share them on LinkedIn and tag me with some relevant content, then I’ll weigh in to amplify your accomplishment. This is the perfect way to build your experience and showcase your expertise to potential future employers.

And, I’ve built a recognition program for those who complete all my AI curriculum, to highlight your accomplishments to potential clients and employers. It links to your Digital Twin from the courses, and it has a flagship certificate that links to all your certificates. It’s called the Proficient AI Engineer directory and the full details are here. I hope this will be a valuable resource to boost your career.

4. I’m getting a Name Error

Name Errors are common in Python, but they are almost always instantly and trivially fixable. It’s good to build confidence so that you can always immediately address Name Errors whenever you find them, in this project and in any other.

The most common reason is that you’ve not run all the cells of your notebook in order, from the top down. If there are two cells, and the first cell has this:

favorite_fruit = "bananas"

And the next cell is this:

print(favorite_fruit)

Then if you execute the 2nd cell (Shift+Return) without executing the 1st cell, then you are trying to print “favorite_fruit” before you have told python what that is. And so, favorite_fruit isn’t defined, and you will get a Name Error. That’s it.

It doesn’t matter that the first cell is higher up in the notebook; all that matters is the order that you execute cells.

Try to use a variable before you’ve defined it? Name Error.
Try to use a python class before you’ve imported it? Name Error.
Try to call a function before you’ve defined it? Name Error.

Your action plan to build the foundations – 2 tutorials and exercises

Please look at the third section in this guide on Name Errors; complete both tutorials and all the exercises, all the way through. There’s some repetition but it’s good to take the time to get this right. And Name Errors should never give you a problem again.

I should mention: if you’re completely new to Python, or to any form of programming, then it might also be worth checking out the answer to Q2 above.

5. I’m getting a Module Not Found Error or Import Error – eg not able to import OpenAI or dotenv

Across all my courses, we use the package manager “uv” to make sure all the right Python packages are installed. It’s a bulletproof way to ensure that you have exactly the same packages as me and as all other students. As long as you’re working in the uv environment, you should never get an import error, and you should never need to install any packages, on any of my courses.

The most common reason for this error is that you don’t have the right ‘Kernel’ selected. But let’s take this step by step.

STEP 0: Pre-check – do you see the right folders in Cursor

Just to check: have you followed the instructions in the Setup guide; in Cursor, if you look at the top of the files in the side bar, do you see the project root (LLM_ENGINEERING or AGENTS or PRODUCTION) in block capitals? If not, see Q24 for instructions. Did you already install the Cursor Extensions for Python and Jupyter? If not, see the setup instructions. Ask me if unsure.

STEP 1: Recreate the uv environment

  1. You should have a folder called “.venv” showing in the side bar. Right click on it, and delete it completely.
  2. Open a New Terminal in Cursor by going to the View menu >> Terminal, then press the “+” button on the top right of the Terminal to make a new one
  3. Your current directory should be the “project root directory” like llm_engineering or agents. Run the command uv sync
  4. A new .venv directory should have appeared
  5. If anything went wrong during uv sync – any errors that look serious – then go to Q11 below to fully fix.

STEP 2: When running a notebook (a file with the .ipynb extension) you must ensure you’ve selected the right Kernel

  1. On the top right of the Notebook is a button which originally says “Select Kernel” and (after you’ve clicked it) has the name of a Python version. Click there now.
  2. If it prompts you to “Install recommended extensions” then you should do that. That should install the Python and Jupyter extensions, if you didn’t do it already.
  3. Now you should be able to choose a Python environment, and you should select the one called something like: .venv Python 3.12.12 or something of the form .venv Python 3.12.x. It’s usually the very first option and it’s usually marked as the recommended option.
  4. If you’re not able to select the right Kernel, or the Import related error persists, then please see Q25 for the detailed steps to fix Kernel related issues.

Extra note: When running python scripts (a file with a .py extension)

Any time on the course that you run a python script, you should always do:
uv run xxx.py
Rather than:
python3 xxx.py

If you follow exactly these steps, you should never experience an import error. But if you do, please send me screenshots:
Screenshot 1: Showing what happens when you run “uv sync”, and showing the .venv in the file explorer in the sidebar
Screenshot 2: Showing what happens when you click the Kernel button at the top right of the notebook to select a kernel.
And I should be able to sort it quickly. If you’re unsure how to take Screenshots, please see Q39 below.

6. My AI API key isn’t being set from my .env file or isn’t working or I’m getting “False” when I run load_dotenv(override=True)

Most of the time, it’s one of these annoying snafus:

  • Did you save the .env file after you edited it? You need to save the file (Ctrl+S on a PC, Command+S on a Mac)
  • Is the .env file named EXACTLY .env — just those 4 characters, not .env.txt, not env – it needs to be precisely .env, and it must be located in the project root directory not in a subdirectory: agents/.env or llm_engineering/.env
  • If you’re using OpenAI, is the key spelt exactly OPENAI_API_KEY (not OPEN_API_KEY). Please note: OPENAI_API_KEY has “AI” in it. However much I say this, I still get several frustrated students a week who have spelt it without the ‘AI’!

If none of that works, then please send me a screenshot with the .env file contents showing, and I should be able to fix it quickly. Send 1 screenshot with the .env file selected in the sidebar and the contents showing; and another screenshot with the load_dotenv(override=True) running. See Q39 if you’re not sure how to make screenshots. I should be able to sort this fast.

7. Why does Cursor show a stop sign with “AI features disabled..” by my .env file

First, it’s important to understand what is being communicated to you, and what is not being communicated to you.

What is Cursor communicating: AI auto-complete is disabled for your .env file.

What is Cursor NOT communicating: Cursor is not saying that there is an error, and Cursor is not telling you that the file isn’t being used. As long as you’ve saved the file, then the file is there.

So why has Cursor disabled AI features for this file?

Cursor is saying, “hey, I realize that the .env file contains secret information that might be linked to your credit card, and it probably wouldn’t be a good idea to send this to a third party vendor to do an AI prediction on it.. so I’m not going to use AI features with this file.”

It’s good to build an engineer’s instinct on security matters. Your guard should be up when entering private keys in a .env file. You might think, “I hope Cursor doesn’t send this to external AIs.” Then you should be pleased to see this symbol, and the message that AI features are disabled should make sense and be exactly the protection you’re expecting.

It’s for your security and protection, and you should require it from Cursor and any other AI tool that you use in the future.

8. Do I need to pay for APIs on this course? I’d like to use something instead of OpenAI or Anthropic – a free model, or Gemini, or OpenRouter, or something else..

Absolutely. You can use free models throughout all my courses. A large number of people have completed everything using free models.

Google Gemini has a free tier, and OpenRouter gives you access to many free models, as well as very cheap models.

The details are covered in the comprehensive write-up in Guide 9 in the guides folder in the repo. There’s a good explanation of how AI APIs work, followed by exact code to use for Gemini, OpenRouter, DeepSeek and local models via Ollama.

And, ahem, I have an appeal. Guide 9 is linked frequently in all my courses. For the Agent course, it’s in the README, the Setup instructions, and by a huge STOP sign at the top of Lab 1. I’m always happy to answer questions and I try to be fast. But you’ll get the very fastest answers by keeping a watchful eye on the docs. They’re really good. It’s your path to greatest success with the course.

Side-note: the approach for Bedrock, Azure and Vertex AI is very similar – just google for the details from the providers.

9. I’m getting an Archive Error while setting my environment

Please search for ‘Archive’ in SETUP-PC.md for the full explanation and fix!

10. I’m getting an error trying to run CrewAI

a. Double check you followed the README setup

Please check out the section on CrewAI in the README.md in the project root directory (agents/README.md) – make sure you’ve installed the CrewAI tool. If you’re on a PC be sure to have installed Microsoft Build tools as described in the README, otherwise you’ll get a strange error involving Chroma.

Here is the command to have run in a Cursor Terminal in the project root directory (agents):

uv tool install crewai==0.130.0 --python 3.12

And in case you’ve used Crew before, it might be worth doing this to make sure you have the latest:

uv tool upgrade crewai==0.130.0 --python 3.12

This command pins Crew to the same version that I use on the course. If you have any problems with Crew, you could try using the latest version instead, by running this command:

uv tool upgrade crewai --python 3.12

You could also try fixing python to 3.11 instead, either with the 0.130.0 version of CrewAI or the latest version.

uv tool upgrade crewai==0.130.0 --python 3.11
uv tool upgrade crewai --python 3.11

At any point, you can see which version of Crew you have installed with this:

uv tool list

b. If CrewAI has created an inner .env file, then remove it

If you’re having a problem with API keys apparently not being read, then check inside the Crew project subdirectory to see if Crew has created a new .env file inside that directory. If so, it will override your own .env file in the agents directory! Please delete Crew’s inner .env file.

c. Intel Mac onnxruntime fix

On an Intel Mac, be sure that you’re using crewai version 0.130.0 with python 3.12 as specified above. Next, try going back to an earlier python:

uv tool upgrade crewai==0.130.0 --python 3.11

If you still get a problem with “onnxruntime” then try running this within the Crew project directory:

uv add onnxruntime==1.19.0

11. I’m having problems with uv

I’m so sorry! Normally uv is very reliable. The good news is, this is usually quick to fix. Please review these common problems and solutions. Sections (f) and (g) usually fix any situation, but you should check the earlier sections first!

(a) Before we start – checking the “gotchas” like file length

Be sure to have reviewed the list of “gotchas” near the top of the Setup instructions. Windows PC people, you need to have enabled file paths longer than 260 characters, and you need to have installed Microsoft Build Tools. Mac people, you need to have installed or upgraded XCode tools.

Also, confirm that your projects directory is NOT within OneDrive (PC) or iCloud (Mac). This can be problematic; replicating your projects directories to the cloud can cause glitches. If so, please move your projects directory somewhere else, like C:\Users\username\projects

(b) Any errors related to permissions (but not certificates – see lower down for that!)

PC people: please read the permissions tutorial at the top of the setup instructions, or ask ChatGPT!
Mac people, please see Q14 below for how to fix common permissions problems with profile files — this is typically a configuration issue on your Mac.

(c) uv isn’t found when you run the command after installation

Aha! Most likely you just need to open a new Terminal window to pick up the changes. Terminal menu >> New Terminal. If that doesn’t work, try restarting (sorry!). And if that doesn’t work, pick another approach from the uv installation page, such as Winget for Windows users. If you’re on a Mac and you got an error related to permissions when you tried to install uv, then check Q14 below.

(d) Anaconda interference

If you’ve used Anaconda before, make sure that your Anaconda environment is deactivated: conda deactivate
And if you still have any problems with conda and python versions, it’s possible that you will need to run this too: conda config --set auto_activate_base false

(e) Issues with SSL / Certificates

If you get an error about “invalid certificate” while running uv sync: typically this is caused by a Corporate Security environment. This is covered fully in Q15 below with the fixes that will address this for the entire course. If you want to work around this quickly, then one of these commands should work for you:
uv --native-tls sync
or
uv --allow-insecure-host github.com sync

(f) Issues with older computers, such as Intel Macs, perhaps with an error related to “av” or pytorch / torch, or any error about ‘missing wheels’

This solution should work for you:

  • Edit the file pyproject.toml in the project root and change the python version specified version near the top from 3.12 to 3.11 (it might already be 3.11):
    requires-python = ">=3.11"
  • Change the file .python-version so that it contains 3.11 and save it
  • If the error was specifically about pytorch / torch, then run this:
    uv add torch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2
  • If there are numpy errors, add this: uv add numpy<2
  • Specifically for Intel Macs, add this to the end of pyproject.toml:

    [tool.uv]
    required-environments = [ "sys_platform == 'darwin' and platform_machine == 'x86_64'",]

  • Delete the entire directory .venv
  • Then rerun uv sync
  • If that doesn’t work, then delete uv.lock and the .venv directory, and rerun uv sync again
  • and if that doesn’t work, repeat with 3.10 instead of 3.11

(g) Other issues with your uv environment, such as an “ipykernel” related issue, or any other issue!

Most other problems are caused by a super-subtle issue:

Explanation of the problem:
– You have an existing python 3.12 on your system from a prior project
– That python has global packages from a ‘pip install’ that you did in the past
– These global packages are conflicting with our project

Here is the fix, and this has worked well for many students:

  1. Run this command: uv python list
  2. See which versions of python (“cpython”) you already have on your computer.
  3. Choose a version of 3.12 which is not already shown as installed, preferably 3.12.10 or 3.12.11 or 3.12.9.
    So for example, if 3.12.10 is either not on the list, or if 3.12.10 is showing as ‘download available’, then it would be a good choice.
  4. Delete the entire folder .venv in the project root
  5. Change the file .python-version so that it contains the full name of the new python version you’ve picked, like 3.12.10, and save it
  6. Run uv sync
  7. Run uv python list again to satisfy yourself that you now have a new, fresh version of python and it’s being used by this project. If in doubt, go back to step 1!

This should install a new, fresh python and install the packages fresh – that should be bulletproof.

12. Agentic course: I’m having problems deploying to HuggingFace Spaces

First, within the directory that you are deploying, see if there’s a file called “README.md”. If so, delete it.

Also: delete the entire folder “community_contributions” from within the week1 directory. It uses up a lot of space and might interfere with your deployment. You can always get it back via git later.

In HuggingFace, make sure your API key was created with WRITE permissions (not FINE GRAINED).

In the Terminal / Powershell, run:

uv tool install 'huggingface_hub[cli]'

hf auth login

Or even pass in your token explicitly like this:

hf auth login token=hf_xxxxx

And make sure it logs in OK with your WRITE API key. There is an occasional problem pasting keys from HuggingFace into the Terminal; you may need to experiment with ways to do this to ensure that your key is pasted exactly in.

Check that you have a file called requirements.txt that lists the packages that you need (like openai, openai-agents). Then:

uv run gradio deploy

If necessary, from the HuggingFace Space itself, click on the Settings, and scroll down to Variables and Secrets, and add in Secrets for OPENAI_API_KEY and PUSHOVER_USER and PUSHOVER_TOKEN so everything is there.

Then from that Settings screen, be sure to restart the Space to pick up the changes.

13. Should I be concerned about privacy when sending my data to Frontier LLMs?

Any time you send data to a third party, you should carry out an appropriate information security review. OpenAI and Anthropic have extremely clear and thorough policies, as do the other providers.

OpenAI: https://openai.com/security-and-privacy/
Anthropic: https://trust.anthropic.com/

Both these policies are crystal clear that for any paid services, data is not used for training purposes. Both are certified at SOC2 level for most business purposes. There may be circumstances in which certain employees could see your data, such as in a production crisis, which is typical and expected.

There’s a lot of hand-wringing about data privacy with model providers. Some of it is justified – it is important to understand what’s happening with your data, and there have been some incidents. But the bottom line: you should treat OpenAI, Anthropic, Google and others as you would any other third party dependency. Carry out a rigorous review, understand the risks and security protections, make a balanced decision.

I had one student who was training Gemini with data from his personal emails on his gmail, and he was very concerned that Gemini would have access to his personal email data. “But this will give Google access to many of my personal emails..” I had to point out the slight flaw in his thinking!!

But the corollary applies: if you’ve never trusted Gmail, and you use an email provider like Proton Mail for extra privacy, then for sure you should carefully review the security guarantees before training Gemini on your email..

Many companies have all their email on Microsoft servers, and their employee HR data on Workday, and so on. Dependencies on Cloud Providers introduce security risks that need to be considered and reviewed, to decide if the benefits outweigh the risks. I recommend the same approach with LLM APIs.

14. Permission denied on a Mac trying to install

If you’re on a Mac, you’re trying to install something (like uv), and you get and error message like this:

/Users/xxx/.bash_profile: Permission denied
/Users/xxx/.zshrc: Permission denied

This means that your Mac has a problem. There are special files called “profile files” associated with your account that have important information. These files have been misconfigured so that you no longer have permission to edit them. This isn’t a great state of affairs; you should be able to edit your own profile files.

How did I get in this state?

The most common reason is that at some point in the past, you used the word “sudo” to install something else with administrator permissions. This should be used with care; improper use of sudo can change permissions of files to be only editable by an administrator.

How to fix this

The easy way is to use an end-user tool which can fix disk permissions. I believe CleanMyMac (not the App Store version, but the version from their website) has a Disk Permissions Repair function.

But it’s pretty quick to do it yourself:

For those familiar with this, you need to: “use chown to change permissions of the file .zshrc so that it can be edited by your user again”. If you’re not familiar with this, ask ChatGPT or Claude to walk you through this:

  1. Explain the basics about Mac file permissions
  2. Explain hidden files on a Mac that begin with a period
  3. Explain the meaning of the “.zshrc” file on a Mac
  4. I believe I no longer have permissions to edit my own .zshrc file – please tell me the command that will show that, and illustrate the problem (don’t fix it yet – I want to be sure I understand the problem)
  5. Please now give me the simple “chown” command that will allow me to edit my own .zshrc file in the future, and give me a test to check it worked

And all will be sorted!

15. I’m getting an SSL error, Certificate error, network error, failure to download uv or Anaconda files, or API Connection failure related to SSL – maybe like this:
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1028)
requests.exceptions.SSLError: HTTPSConnectionPool(...)
ConnectionError: HTTPSConnectionPool(...)

Aha! This is quite common, and is often related to your network security infrastructure, either because you’re running in a Corporate Environment or because you’re behind a VPN or Firewall.

As a first step, if you’re able to, please disable VPNs, firewalls, etc. If you have a phone hotspot, you could try using that to help understand the problem.

Then here is a really comprehensive guide to working through and resolving these problems, courtesy of you-know-who. There are workarounds in Part 6 that will likely quickly get you through this situation, but it’s definitely preferable to fix it properly if you can.

https://chatgpt.com/share/69934d95-85dc-8012-af71-497d17fcb0a6

From this write-up, the most common solution is:
uv --native-tls sync
And this often works as a last resort:
uv --allow-insecure-host github.com sync

It might also be worth trying uv add pip_system_certs – I’ve heard that this fixed it for some.

If your problem is with downloading Ollama models (a CloudFlare error) then the solution is simpler: you need to add 1.1.1.1 (CloudFlare) and/or 8.8.8.8 (Google) to your computer’s DNS entries. You can ask ChatGPT for instructions for your system – this is a pretty common activity.

16. Cursor is not giving me AI Autocomplete suggestions

First up, navigate to Cursor Settings:

On a PC: File menu >> Preferences >> Cursor Settings
On a Mac: Cursor menu >> Settings >> Cursor Settings

And click through and make sure all the settings look reasonable – nothing important is switched off.

If that doesn’t help, then most likely Cursor has incorrectly decided that you’ve already completed their free trial. (I believe Cursor has software called ‘fingerprinting’ to stop people for signing up for multiple free trials, but sometimes it makes mistakes.) A quick email to hi@cursor.com to say that AI Autocomplete isn’t working should quickly get this fixed.

In the meantime, you can also use VS Code (with GitHub Copilot) for a very similar experience. Cursor is just a fork of VS Code, so everything on the course will be almost identical.

17. I’ve completed my Cursor free trial and I don’t wish to pay for it

That’s no problem at all! You can continue using Cursor without the paid features (Frontier Model AI autocomplete) and it will work great – it’s essentially VS Code.

Also, you should be able to configure Cursor to use open-source models for free – but the experience isn’t as compelling as Cursor + Frontier Models.

Another alternative is to use the free trials for products like Github Copilot and Windsurf. But eventually, they will need your credit card too..

If you feel frustrated about ‘yet another API cost’, it’s worth doing a quick reality check. AI Autocomplete involves seriously heavy lifting behind-the-scenes; each chunk of code involves perhaps 1,000,000,000,000,000 floating point calculations. If you’d asked me 5 years ago to predict how much this would cost, I would have said “don’t be ridiculous – that won’t be possible in our lifetimes”. If you said, “Actually, Ed, it will be! In 5 years! How much will it cost?” I would have guessed $2,000 – $20,000 a month, for a super-computer.

With that in mind, the fact that you can have this for $20 / month is a minor miracle! I get it — all these costs are annoying — but it’s good to keep in mind the extraordinary capability that we get for our $20.

18. OpenAI API (or another AI) isn’t working

First up, check out the error message you’re getting from OpenAI – it should be the last line of the error message. This should allow you to determine if the problem is (a) an incorrect key was provided (b) there’s an API connection error or (c) there’s a quota, billing or rate limiting error. If you can’t tell, then go through each of these steps in turn.

(a) Incorrect key provided

If it indicates that an incorrect API key was provided, then please check out Q6 above. In particular, work through the troubleshooting lab to get to the bottom of it.

(b) API connection error

Please see Q15 above to address. This is most likely a restriction from your Corporate Security infrastructure or your VPN; there are fixes, and a workaround if this doesn’t work.

(c) Insufficient quota, billing or rate limiting error

If it indicates that there’s a billing problem (“Insufficient quota”, “Rate Limiting” or “insufficient funds” errors) then please follow this bulletproof 8 step resolution action plan!

If you’re not sure how to take a screenshot (it’s not the same as a photo of your screen), see this: https://chatgpt.com/share/681f691b-6644-8012-b07d-207c68f259d5

Here we go:

  1. Visit https://platform.openai.com/settings/organization/billing/overview and make sure that a positive balance shows and you have a valid payment method on file. Take a screenshot of this and call it Screenshot1.
  2. Next, visit https://platform.openai.com/settings/organization/api-keys and press the red trash button next to every key, so that you delete all the keys on the screen.
  3. Next, click “Create New Secret Key” on the top right. For Project, select “Default project” and for permissions, All. Take a screenshot of the next screen with your key (call it Screenshot2), and also copy it direct to your clipboard.
  4. After you’ve created the key, go back to https://platform.openai.com/settings/organization/api-keys and take another screenshot (call it Screenshot3) showing that the new key is your only key.
  5. Then edit your .env file and paste in the new key directly from your clipboard, so that your env file contains something like:
    OPENAI_API_KEY=sk-proj-xxxx
    After you’ve saved it, take a screenshot of the saved file (call it Screenshot4). If you’re using Cursor, take a screenshot of Cursor with the .env file selected on the left and the contents showing on the right. If you’re not using Cursor, then bring up a new Terminal or Powershell, go to the project root, and run either cat .env or type .env depending on your system (try both) to list the contents, and then take Screenshot4.
  6. Next, visit https://platform.openai.com/chat/edit?models=gpt-4.1-nano and on the pane on the right hand side, say “Hi there” and make sure it answers. Take a screenshot (call it Screenshot5) showing the answer.
    • If this doesn’t work, then your problem is with OpenAI accessing your credit card. Please contact OpenAI through the chat at help.openai.com or by email to support@openai.com.
    • For what it’s worth, most of the time this does end up being a problem with your credit card company not accepting OpenAI’s charge.. OpenAI is usually happy to take your money if you let them!
  7. Also take a screenshot of this: https://platform.openai.com/settings/organization/limits including scrolling down on that webpage and including every section. Call it Screenshot6a, Screenshot6b, etc.
  8. Finally, restart your computer to clear any environment variables. Go back to the troubleshooting lab. Ensure you run load_dotenv(override=True). Run the cell that prints the OpenAI API key, and when that’s displayed in your notebook, take a screenshot and call it Screenshot7. Then run the call to OpenAI, and take a screenshot of the result including full error trace, and call it Screenshot 8.

And finally, send me an email to ed@edwarddonner.com and include all 8 screenshots. I believe I will be able to spot the problem fast.

At the same time, email support@openai.com; only send them Screenshot 8, not the other screenshots. Inform them:

  1. You have 1 API key with full permissions
  2. The playground at platform.openai.com is working
  3. You have the OPENAI_API_KEY set in your python code, and it matches
  4. When you call the OpenAI Chat Completions API, it is failing – see attached screenshot

Between OpenAI and I, we will have this sorted extremely fast!

19. MCP isn’t working in Agentic AI course – typically a Connection Refused or a timeout error

If this is the ‘sneak preview’ of MCP in Week 5 – then please wait until Week 6!

If this is in Week 6, Day 1…

First, if you’re on a Windows PC, please be sure to have completed everything in SETUP-wsl.md.

Then both PC and Mac people need to have completed all of SETUP-node.md, too.. (And PC people, you need to ensure node and playwright are installed in your Ubuntu box, not Windows..)

Next up, try all of the MCP servers in week 6 lab 1, to establish which situation you’re in:

(a) No MCP servers work!
(b) uvx MCP servers work (Python-based) but npx ones do not (node based)
(c) uvx MCP severs work, some npx servers do, but Playwright does not!

Theory 1: Multiple WSL installations

This is the most common issue with PCs!

If you’re in situation (a) or (b) and you’re on Windows WSL, then one possibility is that your computer has multiple WSL “instances” (different installations) and Cursor is attaching to the wrong one. This often happens if you already have a WSL for a Docker Desktop installation. Let’s see.
From a powershell, run this command:

wsl --list --verbose

Do you have multiple? Try a command like this to ensure you set the right one as your default distribution:
wsl --setdefault Ubuntu
And then again:
wsl --list --verbose

And to start the right wsl version:
wsl -d Ubuntu

You could try to shutdown, or even uninstall, the ones you’re not using. And in Cursor when you attach to WSL, be sure that you’re selecting the right distribution.

Theory 2: node path

If you’re in situation (b), then please try running this in the same lab:

!npx --version

And if that doesn’t work, then go to a terminal in Cursor (which should be Ubuntu for WSL people) and run:

which npx

That should give you a full path; use the full path in the lab:

!/path/from/prior/command/npx --version

And if that works, then replace “npx” with the full path version in your params.

Theory 3: Playwright / Chromium installation issues

If you’re in scenario (c), then please have another bash at installing Playwright and Chromium. And if you can’t get it to work, I suggest moving on and leaving out the Playwright MCP server! You can use “fetch” instead for all this course – it will work great!

Theory 4: Timeout issue due to network settings with WSL

OK this is a rare issue that at least 2-3 students have hit related to DNS and timing, and here is a solution discovered by Eliza Z. (Thank you Eliza!)

First, shutdown wsl by running from Powershell:
wsl --shutdown

Then create or edit C:\Users\<username>\.wslconfig and add the following 2 lines:

networkingMode=mirrored
dnsTunneling=true

And then start wsl again with wsl -d Ubuntu

20. I’m getting a permission denied error trying to git push to the repo

Aha – yes, that’s expected, and is usual for Open-Source development. With 250,000 students on the courses, it could get chaotic if everyone had the ability to push to the repo. Instead, the common practice is for you to make a fork of the repo, push to your fork on your account, and then submit a “Pull Request” (known as a PR) so that I can merge your change in to the main repo.

Please see this guide on Git and Github – the third section – for detailed instructions on submitting a PR. Be sure that all your changes are in the community contributions directories and that notebook outputs are cleared. I’m excited to see your work and share it with everyone!

21. What does “opinionated” and “batteries included” mean when describing frameworks and libraries?

Yes – you often hear people describe frameworks as being opinionated and batteries included. Here’s what that means:

An opinionated framework is one that’s prescriptive about how things should be done. It often comes with a predefined structure, architecture, terminology, abstractions, conventions.

This allows you to move fast, have consistency, and reuse.

The disadvantage is that it’s less flexible; you need to do things its way, otherwise you can find yourself fighting the framework. Also, the abstractions can hide some of what’s going on, making debugging harder.

You might say: in frontend-land, React is opinionated, vs JQuery.

So CrewAI is a great example. You type “crewai create crew my_project” and it generates a complete starter project for you with about 10 files and 5 folders. It creates special config files in yaml format. Instead of a System Prompt, you provide fields like “role” and “backstory”. The Crew dev team have a strong opinion that this is a good way to craft a system prompt, and their opinion is baked into the framework — hence “Opinionated”.

And “Batteries-included” is a related expression — overlapping but not identical — meaning, comes with many capabilities out of the box, all-in.

Many frameworks are both or neither: CrewAI is both. For example, CrewAI comes with tons of useful features, like Tools for executing code in a secure Docker container, and many forms of memory, all built in to the framework.

But for example:

React is opinionated but not particularly batteries-included. It doesn’t include routing, state management, etc. You get to pick and choose.

Conversely, you could say that the Python Standard Library is batteries-included but not particularly opinionated. There are so many libraries like ‘csv’ and ‘concurrent.futures’ that bring tons of functionality, with much flexibility on how you use them.

This is all totally subjective and relative. You might consider React opinionated compared with JQuery, but arguably it’s less opinionated compared with Angular. And I think of FastAPI as un-opinionated, especially compared with Django, but it’s more opinionated than Flask.

22. I’m using VS Code (or another IDE) instead of Cursor, and I’m having problems setting the Kernel or environment, and getting import errors

Yes – VS Code can be a bit more complex than Cursor for selecting the right Kernel. Here are instructions from a friend; you can also google to learn more about choosing the right Kernel and python environment in VS Code: https://chatgpt.com/share/68f2762d-3254-8012-9526-3544b17811d4

For other IDEs, ask ChatGPT or Claude for instructions. Tell ChatGPT that you’re using uv, that you have a .venv folder in your project root created by uv sync, that you’re using a particular IDE and you need to set the Kernel for a notebook. It should get you on track quickly, and import errors should be history!

23. I’m getting an Internal Server Error or other error trying to deploy my application to Vercel in the production course

So look, don’t hate me, but I gotta point out that the overwhelming likelihood is that you’ve made a mistake somewhere along the way. It’s always possible that there’s a Vercel outage (that did impact some students), but more likely that you have an error. The most common issues are with the OPENAI_API_KEY not set in all environments. One student spelled vercel.json wrong, one had requirements.txt in the wrong place, several had typos in the package names.

Here’s the thing: you are uniquely positioned to solve this, and it’s hard for anybody else to do it because they don’t have access to your environment. There are techniques for problem-solving environment issues. The single most important skill to pick up on this course is the personal toolkit for how to dig in to problems like this. And this is a great opportunity.

Step 1: Gather information

It is essential to start by trying to identify the underlying problem, rather than guessing fixes. LLMs love to guess fixes and put bandaids in everywhere; sometimes that gets through the problem, but then introduces new problems later.

Look at the Javascript console in your browser (if you’re not sure how, then google it for your browser). Click around the Vercel screens to find the Server logs. Also find the Environment variables and check they look right. Check the status pages like https://status.vercel.com and https://status.openai.com – gather all information to get the best possible view of the problem.

Step 2: Simplify, simplify, simplify for a working baseline

This is the dark art to environment problem solving. It’s tedious, but it almost always gets to the problem. Remove functionality until it works. Getting an internal server error? Replace your server with:

return "bananas"

If that doesn’t work, keep simplifying down until it does work. You should get to a point that you can get a response from the server.

Step 3: Gradually add functionality, a tiny step at a time

Now add in functionality very slowly. For example, start with something like this:

import os
api_key = os.getenv("OPENAI_API_KEY")
if api_key:
return f"API key is set and begins {api_key[:8]}"
else:
return "API key is NOT set"

Take it very slowly. The problem will almost always be revealed.

Final thoughts

It’s good to ask an LLM for help, but be wary of the response. When an LLM doesn’t know, it will often confidently give you LLM slop. They tend to jump to conclusions, and give nonsense answers.

LLMs tend to confidently tell you that you need to set the OpenAI API key, because they’re not aware that Vercel already set this environment variable.
And if you have this line:
openai = OpenAI()
Then LLMs love to tell you that you need to replace it with:
openai = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
Which makes no difference at all! Those 2 lines are functionally identical.

Always instruct an LLM like Cursor Agent or Claude Code to identify and prove the root cause of a problem before it proposes solutions.

And please do post your investigation on Udemy with as much detail as possible, including Javascript Console and information from logs, so that other students and I can help. But nobody is in a better position than you to get to the bottom of this. It’s painful, but usually there ends up being a simple explanation.

24. In Cursor or VSCode, I’m not seeing the same folders and files as you

First up, be sure that you’re looking at the File Explorer on the left. If not, go to View menu >> Explorer.

Now you you see the name of the project (like AGENTS or LLM_ENGINEERING or PRODUCTION) in block capitals at the top left. If not, you need to open the project properly. Go to File menu >> New Window, then Open Project, then find your projects folder, and double click into the project root folder (like llm_engineering or agents or production).

You should now see the contents of the repo, with folders like “guides” and others. Now click the OPEN button to open this project in Cursor. You should now be set!

25. I’m not able to Select the Kernel

I’m assuming you’re using Cursor. If you’re in VS Code, please see Q22. I’m also assuming that you see the name of this project (like LLM_ENGINEERING or AGENTS or PRODUCTION) in block capitals on the top left. If not, see Q24.

Context: what is a Kernel?

We’re using this neat interactive playground called a “Jupyter Notebook”. Jupyter is the company that first made these playgrounds – they were originally called iPython Notebooks, and you still see that name in some places. These Notebooks are great for running experiments.

With a Notebook, code is divided into cells, and you can execute each cell in turn by pressing Shift+Enter. What’s really going on: there’s a version of Python running behind the scenes. Each time you press Shift+Enter, the code in that cell is sent to that Python program, and run.

This Python program running behind the scenes is called “The Kernel”. We need to be using the right version of python with the right packages installed, otherwise we’ll get import errors and other issues.

First up – making sure Cursor is set up right

Let’s start by making sure you have the right Cursor Extensions installed. Go the View menu >> Extensions to see extensions on the left sidebar. Search for “Python”. You’ll see Python extensions from Anysphere (the makers of Cursor) and ms-python (Microsoft!) Make sure one of them is installed; either is great. Then search for ‘Jupyter’, and make sure the one from ms-toolsai is installed.

Sidenote: when installing extensions, it’s always worth checking that it looks legit. You’ll see that both of these extensions have millions of downloads and are from reputable companies – so all is good.

Now it’s time to Select the Kernel

Now go to the View menu >> Explorer to bring back the files. Go back to your lab. Click the “Select Kernel” button. If it prompts you with something like “Install Recommended Extensions”, then select that. If you see an option, “Select Another Kernel…” then choose that. You might see 2 options: “Python Environments” and “Existing Jupyter Server”; if so, choose “Python Environments”.

OK, at this point, you should be looking at a list of possible Python kernels that you can pick from, like this:

The Kernel you’re looking for is that top one for me. It will usually be the top one. It will usually be the Recommended one. But the key is that it should be of this form:

.venv (Python 3.12.x) .venv/bin/python

Importantly, the path (the part in lighter emphasis) should start “.venv”. It should be referring to the folder “.venv” in the current folder.

If you don’t see this Environment as one of the available Environments

We will need to force Cursor to see this Kernel. If you’re surprised by step 1, I should mention that Cursor is itself built from VS Code..

  1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select VSCode Settings not Cursor Settings)
    Or on Windows PC: From the File menu, choose Preferences >> VS Code Settings (NOTE: be sure to select VSCode Settings not Cursor Settings)
  2. In the Settings search bar, type “venv”
  3. In the field “Path to folder with a list of Virtual Environments” put the path to the project root, like C:\Users\username\projects\llm_engineering (on a Windows PC) or /Users/username/projects/llm_engineering (on Mac or Linux). Or substitute “llm_engineering” for “agents” or “production” for those courses.
    And then try to select the Kernel again.

And if that doesn’t work, let me know! I’m standing by with even more aggressive ways to force Cursor to pick the right Kernel as a last resort…

26. I’m having problems with MCP server mcp-memory-libsql

Perhaps you’re getting an error like: “Error initializing MCP server: MCP error -32603: Initialization failed: CustomEvent is not defined”

Please change this MCP server to the simpler reference MCP server from Anthropic for memory; it uses json files rather than a SQL database. These are the params:

 params = {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-memory"
      ]
    }
27. My python script won’t output Markdown – perhaps with error 'NoneType' object has no attribute 'display_id'

Aha! So you have something nicely running in a Lab with Markdown showing. Then you converted it into a python module, and you’re running the module and hoping to see the output in your Terminal, and it’s failing. Why’s that?

First up, we should cover some foundations. When you run a python script at the command line, you are running code as a “CLI” (Command Line Interface). Your terminal launches your process, and it sees whether your process has any output. If it has output, then the characters are displayed in your Terminal window. This is known as “standard out” – you could google to learn more about it.

When you run a notebook, you’re actually running a web application (“Jupyter”). The web application manages a python process (“the Kernel”). This web application, Jupyter, takes chunks of code (the cells) and lets you run them. Based on the results, Jupyter displays stuff in the area below each cell — the cell output.

The cell output is actually basically web content. Jupyter can put text, images, audio – or even raw HTML if you want. If your code has standard output, then Jupyter shows it. Jupyter has some helper code that can allow you to display graphical content in there.. like display(Markdown(“# Heading!!”))

Hopefully with that foundation, the situation is clear to you. A python process running at the command line can’t display web content in the Terminal window – imagine how strange it would be if graphics and sound started coming in the Terminal…

Markdown is a form of shorthand for HTML content, and Jupyter can show it because Jupyter is a web-based platform.

So what’s the solution?

Well, the simplest is to use a proper UI platform like Gradio, which we cover on all my courses. Gradio (week 2 in the Core Track) will allow you to display HTML content in a Browser window – which is where HTML content works, not in a Terminal..

And in the meantime, stick to outputing text in the Terminal!

If you want a stopgap solution, there is a middle ground. The Terminal does allow some limited graphics – based on special control characters. You can install a package called “rich”:

https://rich.readthedocs.io/en/latest/introduction.html

And then ask your LLM to output in the right format to print suitable for “rich”. You’ll see text that looks a bit like command line tools like Claude Code – semi-formatted, but still using characters.

But I recommend wait for Gradio! And for a pure python module to run at the Command Line, stick with plain text with print().

28. I’m having problems with n8n (from the Agentic Track course rather than the AI Builder with n8n course)

Ah – I have bad news and good news for you. Let’s start with the bad.

This course is not actually about n8n, and I don’t cover n8n beyond this simple demo on this course. I show you n8n at the start, and I encourage you to give it a try, to give you a feel for what an Agentic solution looks like. I want you to experience “autonomy” in action. I use a Philips Hue Lightbulb; a Smart bulb that connects to the internet and can be controlled by API. But the idea is for you to pick something else from the list that resonates with you, like Google Calendar, or anything that you want to control.

n8n is primarily intended for AI Users and Builders – people who want to use AI to build workflows, either with no code at all, or a low amount of code, and don’t want to get too technical.

The course is primarily intended for Aspiring AI Engineers – people who want to build industry-ready Agentic AI software. The course is a step towards becoming an Agentic AI engineer in industry; an exciting and in-demand space. This course is for up-and-coming AI professionals. By the end of this course, you would be able to create your own n8n — you are a CREATOR of AI, rather than a USER of AI.

So if you’re having problems with n8n, then I’d ask you to watch the rest of the Week 1, Day 1 lectures: I quickly explain that this is not the purpose of this course, and we move on to the foundational work at the forefront of AI.

I’m sorry if that’s not what you expected from the course – please do check out the full curriculum and objectives (see Q1 and Q3 above).

And the good news? I have another course that’s intended for AI Builders and focuses entirely on n8n, and delivers tremendous impact in a very short time! It’s the first course listed on my curriculum here.

29. I’m having problems sending email with SendGrid

Many students have reported frustrations registering with SendGrid or getting emails reliably sent in the Agent course. There are many alternatives, and there’s some important commercial context to explain.

First alternative – use Resend

Many students have successfully used “Resend” as an alternative. There’s an implementation in the community_contributions directory called 2_lab2_with_resend_email. You can set up your account at https://resend.com/ and then use code like this:

@function_tool
def send_html_email(subject: str, html_body: str) -> str:
"""Send out an email with the given subject and HTML body to all sales prospects using Resend"""
from_email = "ed@edwarddonner.com"
to_email = "ed.donner@gmail.com"
RESEND_API_KEY = os.environ.get("RESEND_API_KEY")
headers = {
"Authorization": f"Bearer {RESEND_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"from": f"Ed Donner <{from_email}>",
"to": [to_email],
"subject": subject,
"html": html_body
}
response = requests.post("https://api.resend.com/emails", json=payload, headers=headers)
if response.status_code == 202:
return "Email sent successfully"
else:
return f"Email failed to send due to {response.text}"

Second alternative – use PushOver

In Week 1 we created a function to send a push notification; you could simply reuse that here.

Third alternative – just print or output a file

The purpose of this lab is to demonstrate Agentic AI, not to actually send emails. It’s totally good just to print() the contents of the email, or write to a local file, to prove that it’s working.

Crucial commercial context

Several students have been frustrated by SendGrid’s controls around this. One student commented in the platform: “I would suggest the course probably shouldn’t be giving free advertising to a blatantly anti-consumer website.”

It might be helpful if I give you the broader context.

Sending bulk email is a delicate topic. There are strict regulations and penalties in some regions against sending unsolicited sales emails. Companies like SendGrid are in a difficult position: if they send lots of emails that are marked as Spam, they can get punished by providers like Google who will stop delivering emails from SendGrid, which would destroy their business.

SendGrid is run by Twilio, a well-regarded company, common in the industry. They need to maintain their status as a trusted provider of email.

Let’s face it: the reality is, in this example, that we are crafting Sales Emails with our Automated SDR. We are basically generating emails that could be considered Spam! We’re sending it to ourselves, and the assumption is that this would be sent to opt-in email distributions, so everything we’re doing is above board. But frankly, it’s completely reasonable that SendGrid would raise an eyebrow – this is a grey area.

I wanted to use SendGrid because it is an industry leader, but it’s understandable that in some regions there might be some hoops to jump through, and there are good alternatives if SendGrid doesn’t work out.

I hope that makes sense!

30. I’m getting an error from CrewAI: AttributeError: module ‘signal’ has no attribute ‘SIGHUP’

This is an error from the latest version of CrewAI that they will hopefully fix soon. If you search for ‘SIGHUP’ in the Udemy Q&A, you’ll see the other students that have hit this.

Until CrewAI fixes it, the temporary solution is to downgrade the CrewAI tool to the prior working version:

uv tool install --upgrade crewai==1.6.0

If you’ve created a CrewAI project with the broken version, you will need to recreate the project (with crewai create crew …) after doing this downgrade. This should resolve the issue.

31. Will you cover “Skills” in addition to MCP in the Agentic AI course?

Before we dive in, let me give you some important context to help answer the question.

I’d like to distinguish between two types of people: AI Users and AI Engineers.

AI Users are end-users of AI products. Here are some products that they use:

  • They use products like ChatGPT and Claude.ai to chat with an LLM. These are software products that handle memory, conversation history, and calling LLMs with Tools, to give the illusion of an ongoing conversation.
  • They use products like Claude Code, Codex, Antigravity and Cursor to write software with Agents.

AI Engineers are engineers and data scientists that create AI products. An AI Engineer could make their alternative to ChatGPT, or an alternative to Claude Code, or a business application like DuoLingo Premium with AI chat.

Typically AI Engineers write software that makes calls to LLMs, and adds in memory, conversation history, tools (potentially via MCP) and structured outputs.

OK, now to answer the question:

Skills, originally invented by Anthropic for Claude Code, is a feature of the Claude Code product. It’s now also been added to many other products. It effectively allows you to add in tools in a very efficient way – you describe a kind of tool, and let the LLM decide that it wants to learn more about that tool if it seems relevant.

As an AI User, you can make use of Skills in Claude Code, in Claude.ai, and in Codex.

As an AI Engineer, you get to decide what Tools you equip your LLM with. You can implement the ideas behind Skills yourself in your application; decide in what circumstances you provide which Skills to which LLM. Decide how to add triggers. It would probably make less sense to read and use a “SKILLS.md” file; you are writing code directly yourself to decide which Tools to use. You are building the product.

In summary, Skills are a feature of some AI products. As an AI Engineer, you might build a similar feature into yours.

Hopefully that makes sense. This course is for AI Engineers rather than AI Users, so it makes less sense to directly cover SKILLS.md or AGENTS.md or other features of end-user products. However, I do have a course that specifically covers Coding Agents, Claude Code and Skills – the AI Coder course on my curriculum here.

32. Shouldn’t we use the OpenAI Responses API rather than the Chat Completions API? It’s recommended by OpenAI.

Yes, OpenAI has the Responses API which they offer as an alternative to the Chat Completions API, and indeed their documentation recommends that users adopt the Responses API.

But there’s a catch.

The Chat Completions API was originally devised by OpenAI, but it’s become an industry standard. It’s ubiquitous across all major LLM providers. If you work with the Chat Completions API, then you can seamlessly switch between OpenAI, Anthropic, Gemini, DeepSeek, OpenRouter and more.

The Responses API is a proprietary OpenAI API that makes use of OpenAI specific functionality, such as paid managed tools. Using the Responses API locks you in to the OpenAI ecosystem. If you build against the Responses API, you will not be able to seamlessly switch provider – you will need to rewrite your AI code.

It’s no surprise that OpenAI advocates for their Responses API! It brings you in to their ecosystem, and it keeps you there.

So I strongly recommend sticking with the Chat Completions API. That’s what I teach in all my courses. And that is what’s used across the industry. Companies do not want to be locked in to an ecosystem, so they favor standard approaches.

One exception to this: many of the abstraction frameworks (including OpenAI Agents SDK) provide an abstraction over both the Responses API and Chat Completions API. When you’re calling OpenAI models, it uses the Responses API, and for other models it uses Chat Completions. That gives you the best of both worlds: access to the Responses API functionality without lock-in. This is one situation when I happily recommend the Responses API!

33. I’m getting inconsistent performance from an LLM, such as tools not being called reliably, unreliable Agent flows, poor RAG performance, or inaccurate results

Well, I have two pieces of advice here. First up:

Keep in mind that being an AI Engineer involves wearing 2 hats: a Software Engineer, and a Data Scientist. It’s common for people from a software engineering background to raise questions like this. The key is to take off your Software Engineer hat, and firmly put on your Data Scientist hat.

Getting reliable performance from LLMs requires comprehensive R&D and experimentation. Often this isn’t measured in hours; it’s measured in days, or even weeks. It involves running many experiments: experiment with prompts, experiment with models, experiment with tools and techniques. Be guided by an evaluation metric with curated test data.

There are no shortcuts to this work. If you’re getting unreliable results, I can’t tell you “the solution is to use this model” or “the solution is to use this Tool”. There’s only one way to solve it: through R&D. Ensure that you properly plan for this experimentation in your project plan; this is what it takes to build a reliable AI system.

And my other piece of advice is more concrete:

Particularly with Agentic flows, it’s more reliable and bulletproof to orchestrate with code wherever possible. Rather than providing a number of Tools to your Orchestration Agent, have it respond with Structured Outputs, and use python code to make the next call. For those on the Agentic course, this is the approach we use for the Deep Research Agent in Week 2 (by contrast with the SDR in Week 2). This still allows for autonomy, but results in a more controlled and predictable flow. In my experience, it is most common to work this way in real-world production systems.

34. Please can we have a meeting (eg to pitch idea, debug an issue, speak to team, coaching, …)

Ahh. It’s been my great joy that there are many people on my courses. I try to respond to every question I get, and right now I’m fielding about 500 questions a day. I enjoy doing it, but it takes all my time to stay on top of the flow. Plus, I have a hectic day job, and I barely have the time to meet my own team!

So at least for the time being, regrettably, I’m completely out of time for more meetings. I’ll always try to respond quickly by email (ed@edwarddonner.com) or in Udemy. I’ll update this if my situation changes.

In the spirit of never saying an outright “No” — if you were comfortable with a paid engagement situation, then for sure, please send me an email. But to be upfront.. if I take on anything new, I’d need to postpone something on my plate (and I have a team to support). Which is my subtle way of saying, I’m not exactly a cheap date!

Just to emphasize again: I’m always available by email, and I try to respond to everything. I’m sorry that I don’t have time to meet, but I’ll make up for it by replying fast. Hope that all makes sense.

35. Please fix this problem with 100 lines of code or more

Please do see the Debugging Guide in the Guides folder!

The very best way to debug problems is to simplify your code down to the easiest way to reproduce the problem consistently, with a minimal example. That’s how technical people typically work together.

If I tried to work on this, my first step would be to recreate, and then reduce it down to 5-10 lines of code that reproduces the problem. And that might take me 2 hours. It’s hard work, but it’s necessary. Usually when it’s reduced to 5-10 lines of code, it’s quick to fix, and that is satisfying.

Here’s the thing: I don’t have the capacity to spend 2 hours on one problem – I wouldn’t be able to help anybody else!

So my ask to you: please work on simplifying your problem down. Aim for 5-10 lines of code that reproduces the problem. If possible, recreate it using Google Colab, then share it with me – my colab account is ed.donner@gmail.com. We can then quickly collaborate and fix it.

One student sent me several hundred lines of code, and pushed back on me that it was only possible to get the problem with the full example! I spend some time working on it, and I sent him back the minimal example.. just a couple of lines of code. And then the fix was obvious.

I do want to emphasize – I enjoy solving problems, and I’m here to help you. This is just the most effective way for us to work together. I’m excited to see your colab and work with you to fix your problem.

36. But can’t I just use ChatGPT for that?

I do sometimes get this question, often when making the webpage summarizer app in the first week of the Core Track. Before you read my answer, I would challenge you to think deeply how you would answer it yourself.

And now; my answer.
To set the stage:

ChatGPT itself is a consumer product, created by a team of AI Engineers at OpenAI. The ChatGPT product makes calls to the GPT model, and it adds on functionality like conversation history, long term memory, web search, and more, with carefully crafted System Prompts for general activities, and features for downloading and sharing. It provides an overall conversational AI experience to end-users for a subscription price with rate limits.

And now the answer:

My courses teach you how to become an AI Engineer. As an AI Engineer, you would be able to create a product like ChatGPT yourself, with much the same capability. You would also be able to offer subscription plans to end-users, and you would make a profit from the difference between the subscription revenue and the underlying API costs. The course is teaching you how to build products like this, potentially to monetize.

But it’s more than that. ChatGPT is a generic product, designed to service a general user. The key is that you can make a business-specific product, that solves a problem in a way that is optimized and specialized for your domain. You perfect the prompts to apply to your industry. You add expertise using RAG, and perhaps fine-tuning. You test your Agentic flows against business objectives so that you maximize commercial performance. You take advantage of proprietary data that only you have, to outperform others in your vertical.

For example, DuoLingo, the AI language tutor, has a Premium feature for you to chat directly in a foreign language. Behind the scenes, it simply uses the GPT API. So perhaps you’re thinking – but can’t anyone just use ChatGPT for that? Well actually, yes, you can. But DuoLingo worked carefully to optimize the AI chat experience for their users.

No doubt some skeptics working at DuoLingo said: “Why are we building this? Can’t people just use ChatGPT?” Yet, as a direct result of this Premium AI feature, DuoLingo had record revenues.

This is the job of an AI Engineer. It’s about selecting, applying and deploying LLMs to optimize for a commercial objective. You may have people say to you “but couldn’t you just use ChatGPT?” Just point them to DuoLingo, or any of the other commercial and consumer products that have added value with generative AI functionality.

37. Your courses come with projects already set up, and packages selected. How do I create my own project from scratch, and how do I know which packages to install for my project?

Excellent question – first I’ll tackle creating your project, and then selecting packages.

You’d start by creating a repo for your project; you can do this starting from Github or from your computer. I’d suggest it’s easiest to start from Github:

  1. In Github, press the green New button to create a new repo
  2. Give it a pleasing name, choose sensible defaults (the Python .gitignore is a good one; the MIT license is a great, permissive open-source license); make it private initially
  3. When it’s created, press the green code button and copy the link. On your local box, in a Terminal or Powershell, go to your projects directory and do git clone paste_link_here

Then open Cursor, click File >> New Window, select Open Project, and navigate your way inside the new directory that you just created, and click to open your project in Cursor.

Now it’s time to set up your uv project. Take a browse through the uv docs; they’re really good. You can follow 2 approaches:

APPROACH 1: just copy the same environment from one of my courses that you like. Simply copy across the files pyproject.toml and .python-version and uv.lock from one of my course repos into your new project. Then run uv sync. That’s it.

APPROACH 2: build your own from scratch. Run these commands:
uv init --bare
(This sets up a new uv project without unnecessary boilerplate files)
uv python pin 3.12
(This fixes the python version to 3.12, which is safest for data science; you could also do 3.13, or 3.11 on an Intel Mac).
uv add python-dotenv requests ipykernel openai
(This adds some of the basic packages that you’re bound to need).
If you wish: uv add openai-agents for OpenAI Agents SDK and uv add litellm for LiteLLM and uv add gradio for the wonderful Gradio.
And then uv sync and you’re done!

And finally, to the actual question you were probably asking me: how do I know which packages to install for my project? If I want to parse a PDF document, how do I know to use pypdf?

Well you’re not going to like my answer. We are all tremendously lucky to have access to vast numbers of Open-Source libraries that give us this extraordinary head-start when it comes to our projects. Selecting the right Open-Source library for your project simply takes one thing: work. There isn’t a simple answer “Want to parse a PDF? Use PyPDF!” But rather, you need to reserve about one hour of time for each library that you might want to select:

  1. Define the problem you need to solve (“parse a PDF”)
  2. Use Google and ChatGPT (etc) to research: “python pdf parsing library” and develop a shortlist (PyPDF is the first hit for me)
  3. Next, look up each of your shortlisted libaries at https://pypi.org – this is where packages are registered for you to pip install or uv add. Review the documentation to assess how well it fits your needs
  4. From pypi.org, navigate to the repo and look at the number of Github stars, the number of open issues, how frequently it’s updated, the license, and how recently it was last changed

Based on this research, you should weigh up your contenders based on:

  • How well does it solve your project needs? Consider how opinionated and batteries-included it is (see Q21 for the definition) – is that what you’re looking for?
  • How popular is it, how often is it updated, how well is it tested, how active is the community, are there any concerning open issues?
  • Does the license suit your requirements?
  • How good is the documentation – is it clear how you can use it?

And coming out of this analysis, you should be able to make your decision.

When it comes to parsing PDFs, pypdf emerges quickly. For most vanilla HTML tasks, BeautifulSoup is the winner. But many other requirements will take deeper research.

Some people think there’s just a “right answer” to which packages to use. In practice, selecting the right packages for your project takes sleeves-rolled-up work. In time everyone gets to have their favorites that they’re familiar with; but ideally you’d go through this exercise every time, with every project, because project needs are different and open-source packages change frequently.

Keep in mind: it’s absolutely incredible that we are so spoilt for choice! It’s well worth putting in the homework to select the best packages for your project.

38. On the Core Track course, I can’t get Claude Code to work (typing Claude at the command line)

First up, please can I point you at the section in the course README that covers this – please click here. You’ll always get the very fastest answers by reading the README; the materials are very thorough and position you for success on the course. It’s in the section headed “Special Note” – here it is:

Early on in the course (on Day 2), I give a demo of a very cool, popular product called Claude Code. It’s an AI coding tool, similar to Cursor that we use on the course. I’m only showing this as an example of Agentic AI in action; it’s not a tool that’s covered explicitly on this course, particularly as we’re in Cursor. But if you want to use Claude Code yourself, the Quick Start guide from Anthropic is here.

But I have good news: I have a different course that is about using Coding Agents to write code and includes full installation and usage of Claude Code! It’s the AI Coder course on my curriculum here.

39. How do I take a screenshot?

Technical people often collaborate with screenshots. It’s good to take the time to make them high quality with all the key information, so we can work together effectively. You probably know this, but a screenshot is not the same as a camera photo of your screen! Photos are often blurry, dusty, poorly lit and at an angle, making it harder to troubleshoot.

Here is a good explanation of taking a Screenshot on Windows or Mac: https://chatgpt.com/share/681f691b-6644-8012-b07d-207c68f259d5

In a nutshell:
On a Windows PC, it’s Windows Key + PrtScn button. The screenshot is saved to Pictures > Screenshots.
On a Mac, it’s command+shift+4, then press Space, then click on the screen you want to capture. The file appears on your desktop.

It’s worth taking the time to gather all the evidence you can. Also include the full stack trace of an error; that’s the error message and all the other information printed along with the error message. Then I’m best equipped to help you.

40. Which AI Architecture and Frameworks should I use?

I get this question a lot, and it’s one of my pet peeves, so forgive me for being a bit direct with you..

Being an AI Engineer involves wearing two hats: a Software Engineering hat, and a Data Science hat. Many people come into this field from a Software Engineering background (like me!) and lean in to the Software Engineering concerns. But often these are not the highest priority challenges for building an AI system.

In the past, we Software Engineers focused a lot on framework choices. A Java developer might become an expert in Spring Boot (or, remember the good old J2EE days) and would splash it all over their resume. A framework decision to select an App Server would be pivotal to the project.

But when it comes to a successful Gen AI or Agentic AI implementation, here are some examples of big problems that will hugely impact your success:

  1. How will you measure the performance of your AI system in a quantitative way that aligns with commercial goals?
  2. How will trade-off model accuracy, cost and speed? How will you observe this in production?
  3. What data do you have and what data do you need? How will you curate proprietary data to provide a competitive advantage?

Here are some examples of questions that you may face, but in my opinion are all quite reversible decisions and will not materially affect your project’s success:

  1. Which abstraction layer to use? Like, LangChain or LiteLLM or OpenAI Agents SDK or Google ADK or CrewAI?
  2. How to deploy – like, Lambda or App Runner or Vercel or Cloud Run or ACA
  3. How should agents communicate? Where should the MCP servers run?

If you decide to use LangChain, you can swap it out for something else in an afternoon. If you deploy to Lambda, you can update your TF scripts and deploy somewhere else. You can replace your MCP with direct Tools if you wish. In my opinion, these are not the essential questions. Pick the one that ties best to the skills of your team and has strong community support, and move on. You can change it later.

I also find people often get embroiled in debates about the ‘Agent Architecture’, giving human-like responsibilities to Agents and drawing pictures of boxes with org charts. We’ve been conditioned to think we need boxes and lines!

But it’s usually better to consider Agent Architecture with a Data Science hat on:

  1. Start as simple as possible (a single LLM call is usually good)
  2. Develop a quantitative metric to measure performance with a curated test set
  3. Divide up into multiple Agents in order to improve your metric; test and demonstrate improvement. Repeat.

Come up with an Agent Architecture in order to improve on measurable performance outcomes.

OK, end of rant!

More on this in Guide 12 in the repos, with my definitive guide to starting your own project.

And when it comes to Agent architectures, I cover a lot in my Agentic Track course. And I cover deployment architectures in my Production Track. Full explanations and curriculum here. But please put the Data Science challenges first!

41. Which RAG technique should I use? In the Core Track, why can’t I get an answer to “Who went to Manchester University”?

First up, please read my answer to Q40 above for my favorite rant. With RAG, I recommend less focus on Software Engineering and more on Data Science.

There’s been this cottage industry of techniques and frameworks built around RAG, resulting in an explosion in terminology and complexity.

If your RAG isn’t working, there are 3 questions you should ask:

  1. Do I have information in my knowledge base that answers the user’s question?
  2. If so, am I selecting this information in my Retrieval?
  3. If so, is my LLM able to answer the question?

But rather than digging into these 3 simple questions, I often find that people dive straight into techniques, frameworks and solutioning. Reranking, different encoders, graph databases, pre-processing, using LangChain, LlamaIndex, …

I recommend focusing on measuring the problem. Use the techniques as reference approaches; experiment with something like reranking or preprocessing if it helps you achieve a goal, once you understand the problem.

For the Manchester University problem, let’s face it: if you had a chunk “Jane Doe went to Manchester University” and a query “Who went to Manchester University” then there’s no doubt it would find the chunk and answer the question. That gives you a working starting point. Now find out what’s different, and fix it!

42. I’m getting Rate Limit or Quota errors calling models using Bedrock

AWS changed this recently, and it’s really quite tiresome. A lot of people are annoyed on the internet about the complexity of calling Bedrock.

First, to set the scene

There’s no longer a need to Request Access to a model. You now have access to all models, by default. But you might have limitations on quotas for models, or no quota allowed at all (which basically means you don’t have access!)

Models can be used in 2 different modes:

“On-demand” with a Foundation Model ID – use a model name like amazon.nova-2-lite-v1:0 and it will be run in a specific region.

“Inference profile” with a Cross-Region Inference Profile ID – use a prefix to the name and it will be automatically routed to wherever there’s capacity.

Model IDDescription
amazon.nova-2-lite-v1:0Region-specific “on demand” profile – usually has quota problems!
us.amazon.nova-2-lite-v1:0Cross-region inference profile for US
eu.amazon.nova-2-lite-v1:0Cross-region inference profile for EU
ap.amazon.nova-2-lite-v1:0Cross-region inference profile for AP
global.amazon.nova-2-lite-v1:0Global cross-region inference profile

The Bedrock Region needs to support the Inference Profiles

In addition, as a final complication: when you call Bedrock, you need to specify a region for Bedrock, and that region needs to support that inference profile. Your Bedrock region doesn’t need to match the region for your other services. If you get any errors related to Bedrock region, then use us-east-1 or us-west-2 or eu-west-1 as your Bedrock region.

Which model to use?

The right course of action most of the time is to use the global inference profile name: global.amazon.nova-2-lite-v1:0

If that gives you quota errors, try your region specific inference profile.

The proper way to check your quotas and request quota

If you still get quota errors, then it’s time to dig into the Quota side of AWS. I’m sorry to have to put you through this! Instructions:

  1. Sign in to AWS as your root user
  2. Go to the “Service Quotas” service
  3. Under “Manage Quotas” on the top right, choose “Amazon Bedrock” and press View Quotas
  4. Filter on “nova 2 lite”
  5. Look for the “Tokens per minute” and “Max tokens per day” and “requests per minute”, both for “Global cross-region” and for “Cross-region” – you should get a good sense for what you already have access to.
  6. As necessary, click in a check box to the left of a quota, and click “Request increase at account level”

To use models other than Nova Lite, like Nova Pro or Claude

First, check out the latest models on Bedrock here: https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html

You need to use the filter to navigate this – so type “Claude” to see the latest Claude models.

After picking your model, go back to the Quota page above and filter to see your quota for that model and request an increase.

Let me know if this doesn’t work for you. AWS changes this often, so please let me know if anything needs to be updated..

43. On the Core Track: what’s the difference between using LLMs like OpenAI in weeks 1-2, and using LLMs with HuggingFace pipelines/transformers in week 3?

It’s really important that you’re crystal clear on this. If this confuses you, you might want to take 2 hours to work through my answer below and build a couple of examples so that you’re very comfortable with this.

There are two very different situations to explain:

SITUATION 1: USING SOMEONE’S WEB API

Suppose you want to use software that another company has written. Let’s say it’s a rather simple utility that adds two numbers together. The company has written the software, it’s their secret confidential software (!), and they have it running in the cloud. They’ve provided a Web API to it.

This is how it works:

[Your code] —> [The internet] —> [Their adding code on a server]

Your code is responsible for making a network connection and receiving the results. Your code is basically all about networking. Your code does not actually add 2 numbers; that happens on their computers.

SITUATION 2: USING SOMEONE’S CODE

And now the company decides that they will generously reveal their secret confidential code to the world! The code is in a python module adder.py:

def complex_add_algorithm(x, y):
return x + y

They publish this code; they use a public service at pypi.org which means that you can now simply do uv add adder and then you have direct access to their code. That means you can do this in your code:

from adder import complex_add_algorithm
x = 2
y = 3
result = complex_add_algorithm(x, y)
print(result)

And you are using their code within your code. The internet is not involved. It looks like this:

[Your code -> their code]

Everything is happening in the same process and the same python interpreter. You could trace through everything line by line. Most importantly: the adding of 2 numbers is happening on your computer, not their machine on the cloud! Your computer is adding numbers, not making an internet connection.

If this distinction isn’t super clear to you, then can I suggest you take 1-2 hours to dig into this. Get the Cursor Agent to help you build an adder service both ways – via an API, and via code.

In weeks 1-2 we are using Web APIs; Situation 1

We are calling OpenAI in the cloud:

  • Our code is responsible for making a web request to an endpoint on the internet
  • The LLM is written by OpenAI and it is secret; it cost $100 million to train and it is their core IP and they would never let you access it!
  • The calculations happen on OpenAI’s servers; trillions of floating point calculations on their high-end servers. Your computer is waiting on the internet connection. OpenAI charges your API key for the compute.

In week 3 we are calling code; Situation 2

We are using Open-Source models via HuggingFace code

  • Our code imports code to run an LLM
  • The LLM is open-source and is not secret, and we can load the code and parameters in to our machine
  • The calculations happen on our machine! Trillions of floating point calcs. The memory and GPU are slammed.

So hopefully it’s now clear to you how very different these are! Your machine is doing trillions of calculations, not waiting on an internet connection!

A few complications to keep in mind:

  • Because most of us don’t have powerful enough computers for this, we are renting Google computers using Google Colab. That’s a distraction: just think of Google Colab as “your computer” for this purpose
  • HuggingFace provides the higher level pipelines code; think of this as a ‘wizard’ wrapper code that keeps things simple for the most common tasks. Your computer is still running the full calculations.

Sidenote: There is another feature in HuggingFace that we do not use on the course, called HuggingFace Inference Client. This is actually running LLMs on HuggingFace’s cloud, so it is basically “Situation 1”. That’s confusing and I suggest you ignore it for now!

Let me know if the difference between Weeks 1/2 (OpenAI API) and Week 3 (HuggingFace code) isn’t completely clear now.

44. I’m having problems with Semgrep

You and me both, unfortunately.

In mid-Feb 2026, Semgrep made some changes to their MCP server which seems to have broken it. Initially the Semgrep MCP wasn’t being deployed at all; I’ve now fixed that, but we’re getting back an error related to how the tool is called. I’ve not been able to fix that error yet.

Semgrep also prints a warning about not having Semgrep Pro, but I believe this is an informational message that can be ignored – that’s not related to the error.

If you’re able to dig in to fix this, I would be hugely grateful. I think one student mentioned in the Q&A that he might have fixed it? First up, we need to investigate the new tools available in the Semgrep MCP and check whether our prompts are consistent with the new tools.

But regardless – the great thing is, the LLM should say to you something like, “The Semgrep tool gave an error due to incorrect file paths, but I’ve identified errors anyway.” And while that’s not ideal, it shows you that MCP is working and your LLM is recovering. So if you wish, you could press on and deploy this to GCP and Azure! I’m traveling at the moment, but I hope to get this fixed asap.