跳到主要內容

AG Day 23 引用與出處:答案要能追溯

AG Day 23 引用與出處:答案要能追溯

執行需求:CPU+API key。昨天我們把檢索品質拉到可以信任的水準:AG Day 22 檢索品質:查詢改寫與 rerank(原文連結)用多路召回再精排,讓真正相關的段落有機會排進前五名。今天要處理的是「檢索之後」的那一步:把撈回來的段落變成可追溯的答案。所謂可追溯,不是把來源網址貼在文末就好,而是要能回答三個追問——這句話的依據是哪一段?那一段出自哪一份來源文本、哪一天抓的?如果原網址明天改版了,我們還找不找得回來?這三個問題分別對應來源識別、支撐關係與可驗證性,也是本篇要逐一補齊的三層能力。沒有這三層,一份看起來很專業的研究報告,在實務上其實無法交付。

引言

先設想一個很常見的場景。你請研究助理整理「邊緣運算的功耗設計趨勢」,它回了一份結構完整、語氣專業的三千字報告,文末列了五個網址。你拿去給同事看,同事問:「報告第三段說功耗下降四成,這是哪來的?」你無法回答,因為報告沒有標示。你回頭去翻那五個網址,其中一個已經 404,另外四個頁面很長,你根本不知道哪一句出自哪裡。這份報告的價值瞬間歸零——不是因為內容錯,而是因為無法查核。研究報告的可信度來自可查核性,而可查核性來自引用機制。

更麻煩的是另一種情況:報告「有」引用,但引用是假的。語言模型很擅長模仿引用的形式,它會生成看起來很合理的 [3],而那個編號可能根本不存在,或存在但內容跟那句話無關。這種現象叫幻覺引用(hallucinated citation),是 RAG 系統常見的失敗模式。所以我們的流程不能只有「產生引用」,還必須有「查核引用」——先驗證編號存在,再驗證被引用的段落是否真的支撐那句話。

今天會全部在 src/research_agent/report.py 與 storage.py 上實作,沿用昨天回傳的候選結構。--dry-run 模式下不呼叫模型,改走規則式示範,所有輸出都會清楚標示為示範,讓沒有金鑰的讀者也能把整條流程跑完。

原理/觀念

可追溯性的三個層次

第一層是來源識別(provenance):這份答案用到的每一段文字,都要能指回一份具體來源,而且那份來源要有可辨識的身分——標題、網址、抓取時間、內容雜湊。網址會變、內容會改,單靠網址不足以還原當時看到什麼,所以我們額外留下抓取時間與內容雜湊,日後才能判斷「是資料變了」還是「模型變了」。

第二層是支撐關係(attribution):答案裡的每一句敘述,要能對應到具體的段落編號,而不只是「這份報告參考了五份來源」。從「來源層級」降到「段落層級」是關鍵,因為同一份來源裡可能同時存在支撐與反對的論述。

第三層是可驗證性(verifiability):系統要能自己檢查引用是否成立,而不是把責任丟給讀者。這包括機械檢查(編號是否存在、有沒有段落完全沒被引用)與語意檢查(被引用的段落是否真的支撐那句話)。前者便宜且必須,後者昂貴但有價值。

引用編號是怎麼產生的

最穩定的做法是「先編號、後生成」。流程是這樣:檢索完成後,我們手上有一組精排後的段落;在組裝成模型輸入時,替每一段加上固定編號 [1] 到 [n],並同時建立「編號 → 段落 → 來源」的對照表。模型被要求在回答時,凡是引用某段內容就標上對應編號。因為編號是我們給的,模型只能選擇用或不用的,沒有機會憑空創造一個新編號——除非它不守規則,那就是查核機制要抓的。

這裡有個容易忽略的細節:對照表的生命週期必須跟答案綁在一起。如果生成過程中重新排序或重新編號,對照表就會錯位。因此我們把對照表視為輸出的一部分,跟答案一起回傳、一起寫檔,而不是流程中的臨時變數。

兩種引用產生策略

第一種是生成時夾帶(inline citation):在 system prompt 要求模型「每個事實性語句後面加上 [n]」,模型一次產出答案與引用。優點是引用位置精準、實作單純;缺點是模型可能漏標或亂標,而且沒有引用支撐的句子會混在裡面。第二種是事後對齊(post-hoc attribution):先請模型寫出答案,再逐句送去與候選段落比對,找出每句最可能的來源。優點是分工清楚、可以只對事實句做昂貴的查核;缺點是對齊本身也是模型判斷,可能出錯,而且多一輪成本。

實務上我建議混合:以「生成時夾帶」為主,因為它讓模型有意識地在寫每個句子時就想到出處;再以「事後查核」為輔,專門用來抓幻覺引用與無引用句。這樣成本集中在查核,而不是全篇重做一次對齊。

查核要驗什麼

查核分成三種不同性質的檢查。第一種是結構檢查:答案中出現的每個 [n] 是否都落在有效範圍內;有沒有段落從頭到尾沒被引用(可能是召回雜訊);有沒有句子完全沒有引用(必須決定政策:是允許常識性敘述,還是要求全部標註)。第二種是支撐檢查:對每個「句子+其引用段落」配對,請模型判斷段落是否支撐該句,輸出相符、部分相符、不相符三種結果,並附理由。第三種是穩定性檢查:同一份來源在不同時間抓取是否內容一致,這靠內容雜湊比對即可。

結構檢查免費、決定性、應該每次執行;支撐檢查要花模型費用,適合在報告產出前執行一輪,或在評估流程中抽樣執行(AG Day 33、AG Day 34 會把這件事納入評估集)。今天兩種都會實作,但把支撐檢查設計成可開關的選項。

出處清單的呈現方式

最後一層是呈現。常見格式有兩種:編號式,例如正文寫「功耗較前一代下降約四成 [2]」,文末列「[2] 標題(網址,抓取於 2026-08-05)」;以及 Markdown 連結式,直接在段落中嵌入連結。編號式的優點是簡潔、可程式化檢查、適合大量引用;連結式的優點是閱讀器可以直接點。實務上我會兩者並用:正文用編號,文末同時列出編號、標題、可點連結、抓取時間與段落位置,讓讀者能一路回到原始文本。

完整實作

今天從資料層往上長。第一步,先把來源中介資料存進 SQLite;欄位比昨天多,但都是為了可追溯性而存在:

# research-agent/src/research_agent/storage.py(節錄)
import sqlite3
from pathlib import Path
from typing import Any

DB_PATH = Path("data/knowledge.db")

SCHEMA = """
CREATE TABLE IF NOT EXISTS documents (
    id           TEXT PRIMARY KEY,
    url          TEXT NOT NULL,
    title        TEXT,
    fetched_at   TEXT NOT NULL,
    content_hash TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS chunks (
    id     TEXT PRIMARY KEY,
    doc_id TEXT NOT NULL REFERENCES documents(id),
    ord    INTEGER NOT NULL,
    text   TEXT NOT NULL
);
"""


def connect() -> sqlite3.Connection:
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    conn.executescript(SCHEMA)
    return conn


def get_document(conn: sqlite3.Connection, doc_id: str) -> dict[str, Any] | None:
    row = conn.execute("SELECT * FROM documents WHERE id = ?", (doc_id,)).fetchone()
    return dict(row) if row else None

段落說明:content_hash 是內容快照的指紋,只要抓到的文本沒變,雜湊就不變;它讓我們能偵測「同一網址內容已改」。row_factory = sqlite3.Row 讓查詢結果可以用欄位名稱存取,比位置索引可讀得多。若你前幾篇的欄位命名不同,請以本節的欄位為準調整查詢,資料模型是全系列共用的。

第二步,建立「編號 → 段落 → 來源」的對照表。這是整篇最關鍵的資料結構:

# research-agent/src/research_agent/report.py
import sqlite3
from dataclasses import dataclass, field
from typing import Any

from . import storage


@dataclass
class Citation:
    number: int
    chunk_id: str
    doc_id: str
    title: str
    url: str
    fetched_at: str
    text: str

    @property
    def label(self) -> str:
        return f"[{self.number}] {self.title}({self.url},抓取於 {self.fetched_at})"


@dataclass
class CitationTable:
    by_number: dict[int, Citation] = field(default_factory=dict)

    def add(self, citation: Citation) -> None:
        self.by_number[citation.number] = citation

    def numbers(self) -> list[int]:
        return sorted(self.by_number)


def build_context(hits: list[dict[str, Any]], conn: sqlite3.Connection,
                  max_chars: int = 600) -> tuple[str, CitationTable]:
    """把精排後的候選組成帶編號的 context,並回傳對照表。"""
    table = CitationTable()
    blocks: list[str] = []
    for number, hit in enumerate(hits, start=1):
        doc = storage.get_document(conn, hit["meta"]["doc_id"])
        if doc is None:
            continue
        citation = Citation(
            number=number,
            chunk_id=hit["id"],
            doc_id=doc["id"],
            title=doc["title"] or "(未命名來源)",
            url=doc["url"],
            fetched_at=doc["fetched_at"],
            text=hit["text"],
        )
        table.add(citation)
        blocks.append(f"[{number}] {citation.text[:max_chars]}\n來源:{citation.title}")
    return "\n\n".join(blocks), table

段落說明:max_chars 限制每段送進模型的長度,控制 prompt 預算。編號從 1 開始且與清單順序一致,讓「第幾段」與「第幾號引用」在視覺上對齊,減少模型錯位。若某筆候選查不到對應來源,我們選擇跳過並在紀錄中留下警告,而不是編一個空來源——這是資料完整性優先的取捨。

第三步,設計要求模型夾帶引用的 system prompt。這段的措辭直接決定引用品質,值得多花心思:

ANSWER_SYSTEM = (
    "你是研究報告撰寫者。只能依據提供的來源段落作答,不要使用來源以外的知識。\n"
    "規則:\n"
    "1. 每個事實性敘述後面都要標注來源編號,格式為 [1]、[2]。\n"
    "2. 一句話可以引用多個來源,例如 [1][3]。\n"
    "3. 若來源不足以回答某個子問題,明確寫出『來源不足』,不要臆測。\n"
    "4. 不要編造編號;只能使用上下文中出現過的編號。\n"
    "5. 輸出使用繁體中文 Markdown,不要加開場問候語。"
)


def generate_answer(question: str, context: str, dry_run: bool | None = None) -> str:
    if dry_run:
        from .dryrun import mock_answer

        return mock_answer(question, context)
    from .llm import complete_text  # AG Day 3 建立的通用對話介面

    user = f"研究問題:{question}\n\n來源段落:\n{context}"
    return complete_text(system=ANSWER_SYSTEM, user=user)

段落說明:「不要使用來源以外的知識」這句約束看似理所當然,但少了它,模型很容易混入訓練資料中的舊資訊而不自知,這也是開源 RAG 系統常見的問題。要求它「明確寫出來源不足」則是為了避免模型為了通順而硬答——一份誠實標示不確定性的報告,遠比一份流暢但無依據的報告有價值。

第四步,實作結構檢查。這是零成本、必須每次都跑的一關:

import re

CITE_PATTERN = re.compile(r"\[(\d+)\]")


def parse_citations(answer: str) -> set[int]:
    return {int(m.group(1)) for m in CITE_PATTERN.finditer(answer)}


def check_structure(answer: str, table: CitationTable) -> dict[str, Any]:
    """回傳結構檢查結果,包含越界編號與未被引用的來源。"""
    used = parse_citations(answer)
    valid = set(table.numbers())
    invalid = sorted(used - valid)
    unused = sorted(valid - used)
    return {
        "cited": sorted(used),
        "invalid": invalid,
        "unused": unused,
        "ok": not invalid,
    }

段落說明:invalid 非空代表模型編造了編號,這是嚴重問題,必須在寫檔前處理(重新生成或標記);unused 通常代表召回進來了雜訊段落,屬於提示訊號而非錯誤,可以留給人工檢視。把兩者分開,才不會讓小問題淹沒大問題。

第五步,實作支撐檢查。對每個「句子+引用」配對,請模型判斷是否支撐:

VERIFY_SYSTEM = (
    "你是引用查核員。判斷『來源段落』是否支撐『敘述句』的內容。\n"
    "判定標準:相符(可直接推出)、部分相符(需補充或弱化措辭)、不相符。\n"
    '只回傳 JSON,格式為 {"verdict": "相符|部分相符|不相符", "reason": "..."}。'
)

SENTENCE_SPLIT = re.compile(r"(?<=[。!?])")


def split_sentences(answer: str) -> list[str]:
    return [s.strip() for s in SENTENCE_SPLIT.split(answer) if s.strip()]


def verify_support(sentence: str, sources: list[str], dry_run: bool | None = None) -> dict[str, Any]:
    from .retrieval import call_model_json  # AG Day 22 建立的薄封裝

    body = "\n\n".join(sources)
    user = f"敘述句:{sentence}\n\n來源段落:\n{body}"
    return call_model_json(VERIFY_SYSTEM, user, dry_run=dry_run)


def verify_all(answer: str, table: CitationTable, dry_run: bool | None = None) -> list[dict[str, Any]]:
    reports: list[dict[str, Any]] = []
    for sentence in split_sentences(answer):
        numbers = [int(m.group(1)) for m in CITE_PATTERN.finditer(sentence)]
        sources = [table.by_number[n].text for n in numbers if n in table.by_number]
        if not sources:
            reports.append({"sentence": sentence, "verdict": "無引用", "reason": "句子沒有引用任何來源"})
            continue
        result = verify_support(sentence, sources, dry_run=dry_run)
        reports.append({"sentence": sentence, **result})
    return reports

段落說明:中文句子切分用標點後的正向查找,簡單但不完美——引號、括號內的句號也會被切斷。若要更嚴謹,可改用專門的分句工具或請模型輸出結構化句子清單,這是以官方文件為準的取捨。另外,verify_all 是「每句一次模型呼叫」,句子多時成本會快速上升,實務上務必加快取或抽樣。

第六步,把答案、引用與查核結果渲染成 Markdown 報告,並寫入 reports/:

from datetime import datetime, timezone
from pathlib import Path

REPORT_DIR = Path("reports")


def render_report(question: str, answer: str, table: CitationTable,
                  checks: dict[str, Any], support: list[dict[str, Any]] | None = None) -> str:
    lines = [
        f"# 研究報告:{question}",
        "",
        f"產出時間:{datetime.now(timezone.utc).isoformat(timespec='seconds')}",
        "",
        "## 分析",
        "",
        answer.strip(),
        "",
        "## 參考來源",
        "",
    ]
    for number in table.numbers():
        lines.append(f"- {table.by_number[number].label}")
    lines += ["", "## 引用查核(自動產出)", ""]
    lines.append(f"- 越界編號:{checks['invalid'] or '無'}")
    lines.append(f"- 未被引用的來源:{checks['unused'] or '無'}")
    if support:
        bad = [r for r in support if r["verdict"] in ("不相符", "無引用")]
        lines.append(f"- 未被支撐的敘述句:{len(bad)} 句")
        for row in bad:
            lines.append(f"  - {row['sentence'][:40]}…→ {row['verdict']}")
    return "\n".join(lines)


def write_report(question: str, markdown: str) -> Path:
    REPORT_DIR.mkdir(exist_ok=True)
    stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
    path = REPORT_DIR / f"report-{stamp}.md"
    path.write_text(markdown, encoding="utf-8")
    return path

段落說明:把查核結果寫進報告本身,是一個刻意的設計決定。它讓讀者一眼看到「這份報告有幾句沒有依據」,也讓後續的評估有檔案可稽。實務上你可以把自動查核區塊放在報告附錄,或只在內部版本保留,但不要讓它消失在流程裡。

第七步,串接指令列,把整條鏈路跑起來:

def build_report(question: str, dry_run: bool | None = None, verify: bool = True) -> Path:
    from .retrieval import retrieve

    hits = retrieve(question, dry_run=dry_run)
    conn = storage.connect()
    try:
        context, table = build_context(hits, conn)
    finally:
        conn.close()
    answer = generate_answer(question, context, dry_run=dry_run)
    checks = check_structure(answer, table)
    support = verify_all(answer, table, dry_run=dry_run) if verify else None
    markdown = render_report(question, answer, table, checks, support)
    return write_report(question, markdown)


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="產生帶引用的研究報告")
    parser.add_argument("question", help="研究問題")
    parser.add_argument("--dry-run", action="store_true", help="離線模式,輸出為示範")
    parser.add_argument("--no-verify", action="store_true", help="跳過支撐檢查以節省成本")
    args = parser.parse_args()
    path = build_report(args.question, dry_run=args.dry_run, verify=not args.no_verify)
    print(f"報告已寫入:{path}")

段落說明:build_report() 把七個步驟收斂成一個入口,方便後面 Day 28 接上 MCP 或 Day 38 包成 API 時直接複用。執行方式與示範輸出如下:

uv run python -m research_agent.report "邊緣運算的功耗設計有什麼趨勢?" --dry-run
報告已寫入:reports/report-20260806-041233.md

接下來的報告內容片段是 dry-run 的示範輸出,由規則式 mock 產生,不代表真實模型的引用品質,也不代表任何真實資料來源:

## 分析

根據檢索到的來源,邊緣裝置的功耗設計正朝向動態電壓調整與異質運算分工發展 [1]。
部分來源指出,散熱限制與成本壓力仍是導入的主要障礙 [2]。

## 參考來源

- [1] (示範來源 A)(https://example.invalid/a,抓取於 2026-08-05)
- [2] (示範來源 B)(https://example.invalid/b,抓取於 2026-08-05)

## 引用查核(自動產出)

- 越界編號:無
- 未被引用的來源:[3]
- 未被支撐的敘述句:0 句

段落說明:上面的網址刻意用 example.invalid,提醒讀者這是示範而非真實來源。執行 dry-run 之後請打開產生的 Markdown 檔,逐項對照「編號、來源清單、查核結果」是否一致;這一步很多人會跳過,但它是驗證整條引用鏈路是否正確最快的方法。

常見錯誤與踩雷

第一個錯誤是編號在流程中被重排。有些實作會在精排後才編號、又在渲染前依網址去重再編號一次,兩次編號不一致,引用就全部錯位。請把「編號」當成不可變的識別,只在 build_context() 裡發生一次,後續所有階段都沿用同一張對照表。

第二個錯誤是引用粒度太粗。寫成「本文參考 [1][2][3]」等於沒有引用;讀者無法判斷哪句話對應哪個來源。務必要求引用貼在句子層級,並在查核時統計「無引用句」的數量,把它當成報告品質指標之一。

第三個錯誤是只檢查編號存在,就宣稱引用正確。編號存在只證明模型守格式,不證明內容相關。要做支撐檢查,並對「不相符」的句子安排補救流程:重新檢索、補充提示、或直接刪掉該句。刪句是最保守但最可靠的做法,因為一句沒有依據的結論,會拖累整份報告的可信度。

第四個錯誤是沒有留下抓取時間與內容雜湊。當來源頁面改版、內容改變時,你會無法判斷是「原始資料就這樣」還是「後來被改過」。這兩個欄位在第一天寫入時幾乎零成本,事後補救卻極困難,請務必在儲存階段就寫進去。

第五個錯誤是把 Markdown 連結當成唯一出處,而沒有段落位置。連結失效是常態,尤其是新聞頁面;保留段落編號與原文片段,即使連結失效,也還能靠雜湊與片段在快取或存檔中找回。若你有合規要求,甚至可以留存原文快照,但要注意授權條款與儲存空間。

第六個錯誤是忽略授權與合理使用。引用網頁內容時,請留意來源的授權條款與 robots 規範,長篇轉載與短句引用在法律上的界線不同。本系列是教學示範,正式產品請諮詢法務,不要以「技術上做得到」當成「可以這樣做」。

效能與實務提醒

先說成本結構。生成答案是一次模型呼叫,支撐檢查則是「每句一次」,假設一份報告三十句,那就是三十次呼叫再加一次生成。這在示範規模可以接受,但在正式服務會很快變成主要開銷。三個實用做法:只對「含數值、含因果詞、含比較詞」的句子做支撐檢查;把同一組來源的查核結果依 內容雜湊 快取起來;把完整查核放到離線批次,請求路徑只做結構檢查。

再談延遲與體驗。使用者送出問題後,最不想等的是「看到第一段答案」。因此建議先串流輸出帶引用的答案,查核結果稍後補在文末(AG Day 19 談過的串流輸出可以直接沿用)。這樣使用者先拿到可用內容,系統再默默補上可驗證性。如果查核發現嚴重問題,再以醒目標記更新報告,而不是讓使用者對著空白畫面等待。

第三個提醒是顯示層。編號式引用要讓讀者能「一眼跳到來源」。若是在網頁呈現,把 [1] 做成可點的元素,點選後展開該段落的原文與連結;若是在終端機輸出,至少在文末列出編號與可複製的網址。引用不是給機器看的格式,而是給人查核的路徑,呈現方式本身就是功能的一部分。

最後是與後續章節的銜接。今天我們假設所有來源都已經在知識庫裡,但真實的研究流程需要「先去網路上找」。目前的檢索只能查已擷取的內容,還不能主動發現新來源。這件事會由搜尋工具補上,也是明天的主題。

小結

今天我們把「答案」升級成「可追溯的答案」。重點整理如下:

  • 資料層:documents 表記錄網址、標題、抓取時間與內容雜湊;chunks 表記錄段落與所屬來源,這是可追溯性的地基。
  • 對照層:build_context() 產生「編號 → 段落 → 來源」對照表,編號只產生一次且不可變。
  • 生成層:system prompt 明確要求逐句夾帶引用、限制只能用提供的編號、來源不足要坦白說明。
  • 查核層:結構檢查(越界編號、未引用來源)免費且必跑;支撐檢查(相符/部分相符/不相符)昂貴但可開關。
  • 呈現層:正文用編號,文末列出編號、標題、網址、抓取時間,並附上自動查核結果。

一句話總結今天的原則:沒有出處的結論,等於沒有結論。把這句話寫進你的提示詞與你的流程檢查,就抓住了報告品質的核心。

結語

到這裡,檢索區塊(Day 20 到 Day 23)算是完整了:我們能擷取網頁、切塊、向量化、改寫查詢、精排,最後產出帶引用的報告。但整條流程目前還有一個明顯的天花板——來源只能來自「已經被丟進知識庫的那些頁面」。如果讀者問的是昨天才發布的新聞,或知識庫裡根本沒收錄的主題,系統會誠實地說「來源不足」,然後就停在這裡。

明天,我們會進入「AG Day 24 搜尋工具:讓 Agent 查網路」,把網路搜尋與內容擷取包成代理可以呼叫的工具,讓研究助理第一次能夠「主動發現新來源」,而不只是被動查詢既有知識庫。我們會處理搜尋 API 的選用、結果清洗、失敗重試,以及如何把新抓到的內容接回今天的引用鏈路。

延伸資源

  • Model Context Protocol 官方規格(2025-06-18 版):https://modelcontextprotocol.io/。工具與資源的標準化介面,Day 25 起會正式使用。
  • W3C PROV 概觀:https://www.w3.org/TR/prov-overview/。資料來源與推導關係的標準模型,理解 provenance 概念的好起點。
  • SQLite 官方說明:https://sqlite.org/lang.html。本專案 documents/chunks 的查詢語法以這份說明為準。
  • LangChain 相關說明:https://python.langchain.com/。Document 的 metadata 欄位設計與 retriever 回傳格式,可與本篇的候選結構互相對照。
  • Markdown 規格:https://commonmark.org/。報告輸出格式以 CommonMark 為基準,確保在不同檢視器都能正確呈現。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...