Chapter 63 — Licenses, Data Ethics, and Law: The Rules of the Game That Are Often Forgotten
Series A4 — Generative AI and AI Agents
All scripts in this chapter are in the folder kode/ch63/. In the web version you can also download them all at once (zip).
Imagine you borrow your neighbor’s tractor. One neighbor says “just use it”, another says “fine, as long as you refill the fuel and do not rent it out”, another says “fine, but you share the harvest”. Software, data, and imagery also have “borrowing terms”: licenses. This chapter is not pure programming; it is about keeping your work from causing trouble for others and for you.
This chapter is NOT legal advice. The author is not a lawyer. All quotes and summaries below were read from official sources on 2026-10-10 (the source is named each time); rules and terms can change, and how they apply to your case depends on the facts. For a product that is sold, shared, or contains people’s data, ask a legal consultant. The tables and code here are learning tools, not a substitute for reading the actual license texts and laws.

Software licenses
Without a license, other people’s code may not be used, changed, or shared by you (copyright attaches automatically to its authors; see the copyright section below). A license is written permission from the owner, with its conditions. There are two big families:
- Permissive (MIT, Apache-2.0, BSD): the permissions are loose; the conditions are mainly to include the license text and copyright notice.
- Copyleft (GPL, AGPL): derived works that you share must use the same license and their source code must be available. “Copyleft” is a play on the word copyright.
The table below quotes the site choosealicense.com (read 2026-10-10), which summarizes common licenses in three columns: Permissions, Conditions, Limitations. The official license text is what binds. We keep the English terms so that you can match them with the site.
02_license_table.py
# Chapter 63 - Table of software licenses and the combined obligations of a project
# Source of the contents: https://choosealicense.com/licenses/ (MIT, Apache-2.0, GPL-3.0, AGPL-3.0 pages; read 2026-10-10)
# This is a study summary, NOT legal advice. The official license text is what binds you.
LICENSES = {
"MIT": {
"permissions": ["Commercial use", "Distribution", "Modification", "Private use"],
"conditions": ["License and copyright notice"],
"limitations": ["Liability", "Warranty"],
},
"Apache-2.0": {
"permissions": ["Commercial use", "Distribution", "Modification", "Patent use", "Private use"],
"conditions": ["License and copyright notice", "State changes"],
"limitations": ["Liability", "Trademark use", "Warranty"],
},
"GPL-3.0": {
"permissions": ["Commercial use", "Distribution", "Modification", "Patent use", "Private use"],
"conditions": ["Disclose source", "License and copyright notice", "Same license", "State changes"],
"limitations": ["Liability", "Warranty"],
},
"AGPL-3.0": {
"permissions": ["Commercial use", "Distribution", "Modification", "Patent use", "Private use"],
"conditions": ["Disclose source", "License and copyright notice", "Network use is distribution",
"Same license", "State changes"],
"limitations": ["Liability", "Warranty"],
},
}
EXPLANATION = {
"License and copyright notice": "include the license text and the copyright notice",
"State changes": "mark it when you change the files",
"Disclose source": "the source code must be available to recipients",
"Same license": "derived works use the same license",
"Network use is distribution": "serving it over a network counts as distributing it",
}
print(f"{'license':<12} {'conditions':>10} list")
for name, d in LICENSES.items():
print(f"{name:<12} {len(d['conditions']):>10} {', '.join(d['conditions'])}")
project = ["MIT", "Apache-2.0", "MIT", "AGPL-3.0"]
combined = sorted({c for name in project for c in LICENSES[name]["conditions"]})
print("\nThe project uses:", sorted(set(project)))
print("Combined obligations to review:")
for c in combined:
print(f" - {c}: {EXPLANATION[c]}")
copyleft = [n for n in set(project) if "Same license" in LICENSES[n]["conditions"]]
print("\nLicenses with the 'Same license' condition:", sorted(copyleft))
print("Meaning: ask a legal expert before combining it into a product that is shared or served.")license conditions list
MIT 1 License and copyright notice
Apache-2.0 2 License and copyright notice, State changes
GPL-3.0 4 Disclose source, License and copyright notice, Same license, State changes
AGPL-3.0 5 Disclose source, License and copyright notice, Network use is distribution, Same license, State changes
The project uses: ['AGPL-3.0', 'Apache-2.0', 'MIT']
Combined obligations to review:
- Disclose source: the source code must be available to recipients
- License and copyright notice: include the license text and the copyright notice
- Network use is distribution: serving it over a network counts as distributing it
- Same license: derived works use the same license
- State changes: mark it when you change the files
Licenses with the 'Same license' condition: ['AGPL-3.0']
Meaning: ask a legal expert before combining it into a product that is shared or served.
Look at the “conditions” column: MIT has only 1 (include the license and copyright), Apache-2.0 has 2 (adding State changes, mark it when you change the files), GPL-3.0 has 4 (adding Disclose source and Same license), AGPL-3.0 has 5 (adding Network use is distribution). According to that site, MIT and Apache-2.0 both give permission for commercial use, distribution, modification, and private use; Apache-2.0 adds Patent use permission and the Trademark use limitation. All of these licenses limit liability and warranty (Liability and Warranty): the software is given “as is”.
The lower part of the output computes the combined obligations of a project that uses MIT, Apache-2.0, and AGPL-3.0: five conditions must be reviewed, and AGPL brings the Same license and Network use is distribution conditions. This means, according to the choosealicense page, that with AGPL serving a program over a network is treated like distributing it. That is relevant if you serve the agent or assistant from Chapters 60 and 62 as a web service with an AGPL-licensed component. How exactly that condition applies to your case is a legal question.
Checking the licenses of installed packages
Your Python project depends on dozens of packages. The standard library importlib.metadata reads installed packages’ metadata, including their license.
01_package_licenses.py
# Chapter 63 - Checking the licenses of installed packages with importlib.metadata
from importlib import metadata
PACKAGES = ["numpy", "pandas", "geopandas", "scikit-learn", "torch", "transformers", "peft",
"langgraph", "mcp", "chromadb", "faiss-cpu", "matplotlib", "httpx"]
def read_license(name):
"""Return (license, where_from). Package metadata can keep the license in three different places."""
m = metadata.metadata(name)
expression = m.get("License-Expression")
if expression:
return expression, "License-Expression"
text = (m.get("License") or "").strip()
if text and len(text) <= 40 and "\n" not in text:
return text, "License"
classifiers = [c.split(" :: ")[-1] for c in m.get_all("Classifier", []) if c.startswith("License ::")]
if classifiers:
return classifiers[0], "Classifier"
return "(unclear: read the LICENSE file)", "-"
def family(license_name):
lower = license_name.lower()
if "agpl" in lower or "gpl" in lower:
return "COPYLEFT: read its terms"
return "permissive"
print(f"{'package':<14} {'license':<46} {'metadata source':<19} family")
for name in PACKAGES:
license_name, source = read_license(name)
shown = license_name if len(license_name) <= 44 else license_name[:41] + "..."
print(f"{name:<14} {shown:<46} {source:<19} {family(license_name)}")
# Demonstration of the copyleft flag on a made-up list (not real packages)
print("\nDemonstration with an example list:")
for name, license_name in [("library-a", "MIT"), ("library-b", "GPL-3.0-or-later"), ("library-c", "AGPL-3.0-only")]:
print(f" {name}: {license_name:<18} -> {family(license_name)}")package license metadata source family
numpy BSD-3-Clause AND 0BSD AND MIT AND Zlib AN... License-Expression permissive
pandas BSD License Classifier permissive
geopandas BSD-3-Clause License-Expression permissive
scikit-learn BSD-3-Clause License-Expression permissive
torch Apache-2.0 AND Apache-2.0 WITH LLVM-excep... License-Expression permissive
transformers Apache 2.0 License License permissive
peft Apache License permissive
langgraph MIT License-Expression permissive
mcp MIT License permissive
chromadb Apache Software License Classifier permissive
faiss-cpu MIT License-Expression permissive
matplotlib Python Software Foundation License Classifier permissive
httpx BSD-3-Clause License permissive
Demonstration with an example list:
library-a: MIT -> permissive
library-b: GPL-3.0-or-later -> COPYLEFT: read its terms
library-c: AGPL-3.0-only -> COPYLEFT: read its terms
The output shows something that often surprises: license metadata is not uniform. Some packages (numpy, geopandas, langgraph) use the modern License-Expression field holding an SPDX identifier, for example BSD-3-Clause; numpy and torch even list several licenses at once because they carry components under different licenses. Other packages (transformers, peft, mcp, httpx) write in the License field with free text (“Apache 2.0 License”, “Apache”). Some (pandas, chromadb, matplotlib) only have a classifier such as Python Software Foundation License. That is why the function read_license tries three places in order. The result is only a first hint: “Apache” alone does not tell you the license version, and a metadata field can simply be wrong. For serious decisions read the package’s LICENSE file. All thirteen rows count as permissive by our simple filter; the demonstration at the end shows how an example list containing GPL and AGPL would be flagged as copyleft. Note also: the license of a package differs from the license of the model weights and data the package uses.
For a project you will share, keep a list of dependency licenses in the repository (and update it when you add a package). If one package is under a copyleft license, note it and decide consciously; do not discover it after the product is out.
Data and imagery licenses
Data has terms too, and in many shapes. Three examples relevant to this book:
Sentinel imagery (Copernicus). The Sentinel data license (Copernicus Sentinel data licence, rev. 1, read at ads.atmosphere.copernicus.eu/licences/ec-sentinel on 2026-10-10) states free, full, and open access to Sentinel data, with permitted uses including reproduction, distribution, communication to the public, adaptation, and combination with other data, as long as the use is lawful. The condition: when you communicate or distribute it, state the source with the sentence “Copernicus Sentinel data [Year]”, and if you modified the data, “Contains modified Copernicus Sentinel data [Year]”. The data is provided without warranty, and users waive damage claims against the European Union and the data providers. There are also access restrictions that EU law allows in rare cases (security, third-party rights, risk of service disruption).
Landsat imagery (USGS). The Landsat data-access page on usgs.gov (read 2026-10-10) states that Landsat products in the USGS EROS archive have been free since 2008. That page does not state citation terms or usage restrictions, and advises reading each application’s guide. Because the page is silent about terms, we do not conclude that there are none; read USGS’s official policy before redistributing. That is an example of the “unclear” status that will appear in our project.
Indonesian government data. Government data governance is regulated among others by Presidential Regulation Number 39 of 2019 on One Data Indonesia (enacted 12 June 2019, State Gazette 2019 Number 112; metadata at peraturan.bpk.go.id, read 2026-10-10). We have not read its articles, so we say nothing about data usage rights. In practice: each portal or agency (for example a data portal or geoportal) has its own terms of use; read them on the portal where you download, and note the date you read them.
The same habit for all data: record the source, address, download date, and terms; credit the source when required; never assume “it is on the internet” means “free to use”. In this book we use fictional data of our own making for exactly that reason.
Privacy and the Personal Data Protection Law
The systems we build (assistants, agents, field notes) easily touch personal data: names of extension officers, phone numbers, addresses of farmers, even home coordinates. In Indonesia the basis is Law Number 27 of 2022 on Personal Data Protection (“PDP Law”), enacted 17 October 2022 (source: peraturan.bpk.go.id/Details/229798; the official PDF copy on the same site was read on 2026-10-10; the PDF text was read through machine extraction, so the statements below are paraphrases, not word-for-word copies). Some provisions we read directly:
| Provision | Short content (paraphrase) |
|---|---|
| Article 1 point 1 | Personal Data is data about an individual who is identified or identifiable, alone or combined with other information, directly or indirectly, through electronic or non-electronic systems |
| Article 2 paragraph (2) | The law does not apply to the processing of personal data by an individual in personal or household activities |
| Article 4 paragraphs (1) and (2) | Personal Data consists of specific data (among others health data and information, biometric, genetic, criminal records, and children’s data) and general data |
| Article 20 paragraphs (1) and (2) | A Personal Data Controller must have a basis for processing, for example valid explicit consent for specific purposes, fulfilling an agreement, a legal obligation, vital interests, a public-interest or public-service task, or other legitimate interests |
| Article 57 | Violating a number of articles is met with administrative sanctions: a written warning, temporary suspension of processing, deletion or destruction of data, and/or an administrative fine of at most 2% of annual income or revenue relative to the violation variable |
| Articles 65 and 67 | Unlawfully obtaining or collecting personal data that is not yours to benefit yourself or others is prohibited; the penalty (Article 67 paragraph 1) is imprisonment of at most 5 years and/or a fine of at most Rp5 billion; disclosing personal data that is not yours (paragraph 2) is imprisonment of at most 4 years and/or a fine of at most Rp4 billion |
Note that we only list the articles we have read. The PDP Law contains many other provisions (data subject rights, duties of controllers and processors, cross-border transfers, the implementing institution) that we do not interpret here, and there are implementing regulations. Sensible practical consequences (the author’s opinion, not legal advice): collect as little personal data as possible; have a lawful reason for each item; mask or delete it when not needed; secure its storage; and do not send people’s data to an outside LLM service without a clear basis.
The next script shows the technical side on fictional data: recognizing personal-data patterns with regular expressions (Chapter 22) and masking them, and making pseudonyms (a fixed code replacing an identity).
03_personal_data.py
# Chapter 63 - Recognizing and masking personal data in text (FICTIONAL data)
import hashlib
import re
NOTES = [
"Extension officer Budi Santoso, NIK 3201010101010001, phone 081234567890, inspected pond B.",
"The report was sent to siti.rahayu@contoh.test on Monday.",
"Farmer Agus (+62 812-3456-7890) reported a sick cow in pen 2.",
"Pond C was harvested at 720 kg without problems.",
]
PATTERNS = {
"NIK": re.compile(r"\b\d{16}\b"),
"EMAIL": re.compile(r"\b[\w.+-]+@[\w-]+\.[\w.]+\b"),
"PHONE": re.compile(r"(?:\+62\s?|0)8[\d\s-]{8,14}\d"),
}
def mask(text):
found = []
for kind, pattern in PATTERNS.items():
for _ in pattern.findall(text):
found.append(kind)
text = pattern.sub(f"[{kind}]", text)
return text, found
def pseudonym(value, salt="example-salt"):
"""Replace an identity with a fixed code (salted hash): it can be recomputed but not read directly."""
return hashlib.sha256((salt + value).encode()).hexdigest()[:8]
total = 0
for line in NOTES:
clean, found = mask(line)
total += len(found)
print(f"{len(found)} found {found}\n -> {clean}")
print("\nTotal personal data found:", total)
print("Same pseudonym for the same person:", pseudonym("Budi Santoso") == pseudonym("Budi Santoso"))
print("Budi's pseudonym:", pseudonym("Budi Santoso"), "| Siti's:", pseudonym("Siti Rahayu"))
print("\nText patterns only catch the shapes we guessed. Names, home addresses, and home coordinates are not caught.")2 found ['NIK', 'PHONE']
-> Extension officer Budi Santoso, NIK [NIK], phone [PHONE], inspected pond B.
1 found ['EMAIL']
-> The report was sent to [EMAIL] on Monday.
1 found ['PHONE']
-> Farmer Agus ([PHONE]) reported a sick cow in pen 2.
0 found []
-> Pond C was harvested at 720 kg without problems.
Total personal data found: 4
Same pseudonym for the same person: True
Budi's pseudonym: 0f418416 | Siti's: e434b1e5
Text patterns only catch the shapes we guessed. Names, home addresses, and home coordinates are not caught.
Four pieces of personal data are found and masked: one NIK (a 16-digit pattern in this example), two phone numbers (with the prefixes 08 and +62 8...), and one email. The phone pattern at first did not catch the form +62 812-... with a space after +62; after fixing it (\s?), both are caught. The lesson: test a pattern with many shapes. A pseudonym uses a salted SHA-256 hash: the same person always gets the same code (True), so the data can still be analyzed per person without showing the name. But a pseudonym is not anonymous: anyone holding the salt and a list of names can recompute it. The last line of the output gives an important warning: text patterns do not catch people’s names, home addresses, or home coordinates. Masking personal data properly takes more than a regex.
Scraping ethics
Scraping (collecting data from web pages automatically; see Chapter 41) is legal or not depending on the site, the kind of data, and the applicable law; that is a legal question, not a technical one. But there is clear technical etiquette. The robots.txt file is how a site states which parts it asks crawlers not to fetch. According to RFC 9309 (Robots Exclusion Protocol, September 2022, rfc-editor.org, read 2026-10-10), the rules are not a mechanism for permission or security, but requests that crawlers are expected to honor; to protect forbidden content a site needs real protection such as authentication. So obeying robots.txt is ethics and good faith, not a guarantee that you are free of trouble; the site’s terms of service must still be read.
04_scraping_ethics.py
# Chapter 63 - Scraping ethics: reading robots.txt and limiting the request rate (no internet)
from urllib.robotparser import RobotFileParser
# A robots.txt made up by the author for the fictional site contoh.test
ROBOTS = """User-agent: *
Disallow: /admin/
Disallow: /private-data/
Crawl-delay: 5
User-agent: bad-bot
Disallow: /
"""
reader = RobotFileParser()
reader.parse(ROBOTS.splitlines())
AGENT = "book-learning-bot"
for path in ["/news/harvest-2026", "/admin/login", "/private-data/officers.csv"]:
allowed = reader.can_fetch(AGENT, "https://contoh.test" + path)
print(f"{path:<38} -> {'ALLOWED' if allowed else 'FORBIDDEN'} for {AGENT}")
print("bad-bot to /news/ ->", "ALLOWED" if reader.can_fetch("bad-bot", "https://contoh.test/news/") else "FORBIDDEN")
print("Delay requested by the site (seconds):", reader.crawl_delay(AGENT))
class RateLimiter:
"""Guarantees a minimum gap between requests. The clock here is fake so the output stays fixed."""
def __init__(self, delay):
self.delay, self.last, self.clock = delay, None, 0.0
def wait(self):
if self.last is not None and self.clock - self.last < self.delay:
self.clock = self.last + self.delay # wait just long enough
self.last = self.clock
return self.clock
limiter = RateLimiter(reader.crawl_delay(AGENT))
print("\nTimes (seconds) of 4 requests in a row:", [limiter.wait() for _ in range(4)])
print("Without the limiter, all 4 requests happen at second 0: a small site can be overwhelmed.")
print("\nRemember: robots.txt is technical etiquette, NOT legal permission. The site's terms of service must still be read.")/news/harvest-2026 -> ALLOWED for book-learning-bot
/admin/login -> FORBIDDEN for book-learning-bot
/private-data/officers.csv -> FORBIDDEN for book-learning-bot
bad-bot to /news/ -> FORBIDDEN
Delay requested by the site (seconds): 5
Times (seconds) of 4 requests in a row: [0.0, 5.0, 10.0, 15.0]
Without the limiter, all 4 requests happen at second 0: a small site can be overwhelmed.
Remember: robots.txt is technical etiquette, NOT legal permission. The site's terms of service must still be read.
The script uses Python’s built-in urllib.robotparser on a robots.txt written by the author (a fictional site, no internet). The agent book-learning-bot may open /news/... but is forbidden from /admin/ and /private-data/; the agent bad-bot is forbidden everywhere. The site asks for a 5-second pause between requests (Crawl-delay; the Python library supports it, but not every crawler understands it). The RateLimiter class guarantees that pause: four requests fall at seconds 0, 5, 10, and 15 (the clock is fake so the output stays fixed). Without the limiter they all land at second 0 and a small site can be overwhelmed. Other good habits: introduce yourself with a clear User-Agent, store results so you do not download again, avoid personal data, and prefer an official API when one exists.
AI ethics
AI ethics is not a list to memorize, but a habit of asking. Questions worth asking of every system in Series A3 and A4 (this is the author’s framework, not an official standard):
- Who can be harmed if the system is wrong? An extension officer who receives wrong advice, a farmer who loses livestock because an early warning was missed, a resident whose data leaks.
- Is a human checking important decisions? (Chapter 62: human approval for write actions.)
- Is the data fair? A model trained on data from one region can do badly in another (Chapter 58 covers bias).
- Do users know they are talking to an AI, and where the answer came from? (Source citations in Chapter 60.)
- What are the audit trail and the route for objections? (Call logging in Chapter 60.)
- Is there a simpler alternative that carries no risk?
Copyright and AI output
In Indonesia, copyright is governed by Law Number 28 of 2014 on Copyright (enacted 16 October 2014; peraturan.bpk.go.id/Details/38690; its PDF text was read 2026-10-10). In Article 1, copyright is defined as the exclusive right of the creator that arises automatically under the declarative principle once a work is realized in tangible form (point 1); a creator is one or more persons who produce a work that is distinctive and personal (point 2); and a work is a creation in the fields of science, art, and literature produced from inspiration, ability, thought, imagination, dexterity, skill, or expertise (point 3). Because the definition of creator speaks of “persons” and “distinctive and personal”, the legal status of purely AI-generated output in Indonesia is unclear to the author: we did not find or search for an Indonesian ruling or regulation that answers it specifically, so it is not verified. Ask a legal expert.
For comparison from another country: the report of the United States Copyright Office, Copyright and Artificial Intelligence, Part 2: Copyrightability (January 2025, copyright.gov/ai, read 2026-10-10), concludes that existing law is adequate to answer this case by case; using AI as an assistive tool does not remove protection for the output; copyright protects the original expression made by a human author even if the work contains AI-generated parts; while prompts alone, at this stage, are not enough to make someone the author of the output. That is United States law, not Indonesian, and not advice for you.
There is another side: training data and outputs that copy. A model can produce text or images resembling existing works. Safe habits: do not present AI output as your own work without saying AI was used, check similarity with existing works for important things, do not use AI images to imitate particular people or works, and read the model provider’s terms on ownership and use of output. Because the license status of AI images is uncertain, this book does not use AI-made images.
Running Project A4: license and data-ethics review
We close project A4 with a full review: a list of sources used (or possibly used) with their terms and the decision.
05_project_review.py
# Chapter 63 - Project A4: license and data-ethics review of the whole project
# The status of each row comes from reading the official sources on 2026-10-10 (see the chapter text); not legal advice.
SOURCES = [
{"name": "Fictional core data (ponds, trees, livestock)", "owner": "project", "terms": "written by the author",
"attribution_required": False, "redistribute": True, "personal": False},
{"name": "Fictional SOP documents (Chapter 60)", "owner": "project", "terms": "written by the author",
"attribution_required": False, "redistribute": True, "personal": False},
{"name": "Home-made random test model (Chapter 61)", "owner": "project", "terms": "built from a configuration",
"attribution_required": False, "redistribute": True, "personal": False},
{"name": "Sentinel imagery (Copernicus), if used", "owner": "EU/ESA",
"terms": "free, full and open; credit the source as 'Copernicus Sentinel data [Year]'",
"attribution_required": True, "redistribute": True, "personal": False},
{"name": "Landsat imagery (USGS), if used", "owner": "USGS",
"terms": "free to download; the data-access page states no citation terms",
"attribution_required": None, "redistribute": None, "personal": False},
{"name": "Downloaded test model tiny-random-gpt2 (dropped)", "owner": "outside party",
"terms": "the model page does not state a license",
"attribution_required": None, "redistribute": None, "personal": False},
{"name": "Extension officer contact list (if any)", "owner": "the people concerned",
"terms": "personal data: needs a lawful basis for processing",
"attribution_required": False, "redistribute": False, "personal": True},
]
def review(s):
if s["personal"]:
return "HOLD: needs a processing basis and masking (see the PDP Law, Article 20 paragraph 2)"
if s["redistribute"] is None:
return "UNCLEAR: check the official source before redistributing"
if s["attribution_required"]:
return "ALLOWED, with credit to the source"
return "ALLOWED"
count = {}
for s in SOURCES:
result = review(s)
kind = result.split(":")[0].split(",")[0]
count[kind] = count.get(kind, 0) + 1
print(f"- {s['name']}\n {result}")
print("\nSummary:", count)
print("Project decision: sources with status UNCLEAR are not put into files that are shared.")- Fictional core data (ponds, trees, livestock)
ALLOWED
- Fictional SOP documents (Chapter 60)
ALLOWED
- Home-made random test model (Chapter 61)
ALLOWED
- Sentinel imagery (Copernicus), if used
ALLOWED, with credit to the source
- Landsat imagery (USGS), if used
UNCLEAR: check the official source before redistributing
- Downloaded test model tiny-random-gpt2 (dropped)
UNCLEAR: check the official source before redistributing
- Extension officer contact list (if any)
HOLD: needs a processing basis and masking (see the PDP Law, Article 20 paragraph 2)
Summary: {'ALLOWED': 4, 'UNCLEAR': 2, 'HOLD': 1}
Project decision: sources with status UNCLEAR are not put into files that are shared.
Of seven rows, four are allowed (three home-made, one Sentinel with the condition to credit the source), two are unclear, and one is on hold. The two unclear ones deserve attention. Landsat: its data-access page states no terms, and “not stated” is not the same as “free”. The downloaded test model tiny-random-gpt2: this is a real event from writing Chapter 61. The model was downloaded (454 KB), but its page states no license, so it was dropped and replaced by a home-made model. The policy is simple and worth copying: a source whose status is unclear does not go into files that are shared until it is checked. One row is on hold because it contains personal data: it needs a basis for processing (PDP Law Article 20 paragraph 2) and masking. In your own project, make a review like this part of the checklist before release.
Exercises
- Run
01_package_licenses.pyand add two other packages you use to thePACKAGESlist. Which package’s license is unclear from the metadata? Open itsLICENSEfile. - Change
projectin02_license_table.pyso that it holds only MIT and Apache-2.0. How many combined conditions are there? Is there still a license with the Same license condition? - Add a pattern for vehicle plate numbers or bank account numbers to
03_personal_data.pyand test it with three ways of writing. How many forms are caught? - Challenge: list all the data sources and models you will use in this book’s final project. Fill them in as in
05_project_review.pywith terms from official sources (note the date you read them), then decide which are allowed, unclear, and on hold.
Chapter Summary
- A license determines what you may do. Permissive ones (MIT, Apache-2.0) ask little; copyleft (GPL, AGPL) requires derived works to use the same license, and AGPL treats serving over a network like distribution. The official text is what binds.
- Package license metadata is not uniform;
importlib.metadatagives only a first hint. - Data has its own terms: Sentinel is free and open with a duty to credit the source; Landsat is free but its citation terms are not stated on the page we read; government data follows each portal’s terms.
- PDP Law (Law 27/2022): the articles we read cover the definition, types of data, the basis for processing, administrative sanctions, and criminal penalties. Collect as little as possible, mask, and have a lawful basis. This is not legal advice.
- Scraping: obey robots.txt and limit the rate, but remember robots.txt is not legal permission.
- AI ethics: ask who is harmed, who checks, and what the source is. The copyright of AI output in Indonesia is not verified by us; the US has its own official report.
- Sources whose status is unclear do not go into files that are shared.
Closing Series A4
Congratulations! You have finished Series A4: Generative AI and AI Agents. You can now build a document question-answering assistant with RAG, embeddings, and a vector store (Chapter 60); understand the inside of a language machine: attention, tokens, VRAM, LoRA, and quantization (Chapter 61); make agents that use tools, including an MCP server, a LangGraph graph, and GIS tools with safety guards (Chapter 62); and review licenses, personal data, and ethics before sharing your work (Chapter 63). You ran all of it for real on your own computer, with limits stated plainly: a mock language model, a small test model, and fictional data.
All the systems are running. Series A5 understands Python in depth and prepares the career steps: how Python works behind the scenes, how to speed it up, how to process big data with many workers, and how to build a portfolio.
Next, in Chapter 64: metaprogramming, that is descriptors, metaclasses, __init_subclass__, introspection, and the AST.