The last lesson called a general-purpose AI model. This lesson connects AI to your own data and chains several steps together — the pattern behind most real AI applications.
Some AI models accept an image alongside text. Sent together with the right instruction, this can automate genuinely tedious work — like writing product descriptions from photos for an online shop:
import base64
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_KEY_HERE")
with open("product.jpg", "rb") as image_file:
image_data = base64.b64encode(image_file.read()).decode("utf-8")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}},
{"type": "text", "text": "Describe this product in 2 sentences for an online listing, then list 3 key features as bullet points."}
]
}]
)
print(response.choices[0].message.content)A small shop adding new products regularly could use exactly this to turn a batch of photos into draft listings in minutes instead of an evening of manual writing.
A regular chatbot only knows what it was trained on — it has never seen your specific PDF, brochure, or FAQ document. Retrieval-Augmented Generation (RAG), covered conceptually in the next lesson, fixes this. Here's what actually building one looks like using a toolkit called LangChain:
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import FAISS
from langchain.chains import RetrievalQA
# 1. Load the document
loader = PyPDFLoader("course-brochure.pdf")
documents = loader.load()
# 2. Split it into small overlapping chunks
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(documents)
# 3. Convert chunks into searchable vectors and store them
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(chunks, embeddings)
# 4. Build a chain that retrieves relevant chunks, then asks the AI
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o-mini", temperature=0),
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
)
# 5. Ask a question — answered only from the document's contents
result = qa_chain.invoke({"query": "What are the course fees?"})
print(result["result"])The AI now answers using only information that actually exists in your document — not its general training data. This exact pattern is how a hospital could let staff ask natural-language questions against its own protocols, or how a company builds a support bot that only ever answers from its own documentation.
Not every automation needs custom code. Tools like n8n and Make.com let you visually connect AI to other apps — "when a new form response arrives, send it to an AI model to summarise, then post the summary to a messaging channel" — built by dragging and connecting blocks rather than writing a script. These are worth knowing about specifically because they let a non-developer build real automations that would otherwise need custom code.
The open-source, run-it-yourself option covered earlier in this section — Ollama for local models — applies here too: for privacy-sensitive documents or high-volume automation where API costs add up, running an open model on your own hardware avoids sending data to an outside service entirely.
You've now connected AI to real documents and chained multiple steps together — genuinely production-shaped skills. The next lesson steps back to the concepts underneath all of this: machine learning fundamentals.