從零開始: 前端轉型 AI agent 直到就業 第五天-第十一天

### 前言

接近 8.9 年的資深前端工程師,34 歲,雙非普本,座標廣州。25 年底被裁員,這三個月內也有投履歷、也有面試,有些推進到二面後就沒有下文,不禁感嘆現在的大環境實在不太好,而且在 AI 的衝擊下前端是受影響最大的職類之一。除了音視訊、圖形化方面還能有一些發揮空間外,AI 已經能完成 80–90% 的前端工作。在學歷及就業背景都不是特別強的情況下,一般前端即便技術不錯,也很缺乏競爭力。在失業的這三個月裡,經歷了持續學習、迷茫到看到曙光,決定轉型自學成為 AI agent;

大綱

  • 前一日心路歷程
  • 前一日的時間分配(不限於學習,也會有運動)
  • 前一日的知識總結(前期或許較少)

心路歷程

很久一段時間沒有更新了,並不是斷更了,而是慢慢地進入狀態,最近都是每天早上起來學習直到晚上。

時間分配

一般早上 8 點起來學習到中午 12 點,休息 2–3 個小時繼續學習到晚上 8 點,然後開始整理文件發到部落格。

知識總結

不知不覺間,已經整理了很多相關文件,對這個賽道的知識體系有一個粗略的認知並且有了一定的基礎。

image.png

今天把今天學到的知識體系整理一下:

RAG 檢索增強生成

第一章:RAG 思想與核心價值

1.1 什麼是 RAG?

通俗理解

想像一下:

你有一個非常聰明但有點健忘的朋友(大型語言模型,LLM)。他知道很多常識,但如果你問他:「我們上週三開會時說了什麼?」 他就傻眼了,因為他沒有那天的記憶。

RAG 就是給這個朋友配了一本筆記。每次你問問題,他先快速翻筆記(檢索),找到相關記錄,然後結合自己的理解來回答你(生成)。

  • 沒有 RAG:LLM 只靠訓練時記住的知識回答 → 容易「已讀亂回」(幻覺)或「已讀不回」(知識陳舊)
  • 有了 RAG:LLM 先查你給的知識庫,再基於這些知識回答 → 答案更準確、可溯源
官方定義

RAG(Retrieval-Augmented Generation,檢索增強生成)是一種將資訊檢索與大型語言模型生成能力相結合的技術架構。

核心公式:RAG = 檢索(Retrieval) + 增強(Augmented) + 生成(Generation)

環節作用:檢索從知識庫中找到與問題相關的資訊片段;增強把這些片段作為「上下文」注入到提示詞中;生成 LLM 基於增強後的提示詞生成最終答案。

RAG vs 微調(Fine-tuning)
對比維度 RAG 微調
知識更新 只需更新知識庫,無需重新訓練 需要重新訓練模型
可解釋性 答案可追溯到原始文件 難以追溯知識來源
實現成本 低,無需 GPU 訓練 高,需要訓練算力
即時性 秒級生效 小時/天級
適用場景 知識頻繁更新、私有文件問答 改變模型風格、行為或學習特定格式

建議一句話:想讓模型知道新事實 → 用 RAG;想改變模型行為方式 → 考慮微調。


1.2 RAG 能解決什麼問題?

問題說明 RAG 如何解決
知識截止日期 GPT-4 知識截止於 2023 年 10 月 → 注入最新文件(例如今天的新聞)
模型幻覺 LLM 會編造不存在的「事實」 → 強制基於檢索到的上下文回答
私有領域知識 公司內部文件、產品手冊、法律條文 → 將這些文件作為知識庫
動態更新 知識每天變化(如股價、政策) → 只需更新向量庫,秒級生效
答案可溯源 使用者想知道「你從哪裡知道的」 → 回傳答案時可附帶來源文件

1.3 RAG 工作流程全景圖

RAG 分為兩大階段:

階段一:索引階段(Indexing)—— 離線準備知識庫
原始文件 → 文件載入 → 文本拆分 → 文本塊 → 向量化 → 向量資料庫
   │           │          │         │        │         │
  PDF/Word     讀取     切分成塊   小片段   轉成向量   存儲檢索

這個階段不需要使用者等待,可以在背景定期執行(例如每晚更新一次)。

階段二:檢索與生成階段(Retrieval & Generation)—— 線上回答問題
使用者問題 → 向量化 → 問題向量 → 向量資料庫相似度搜尋 → Top-K 相關文本塊
                                                              ↓
最終答案 ← 大型語言模型 ← 構建 Prompt(上下文 + 問題) ← ────────┘
一個完整的例子

假設你上傳了一份《2024 年公司休假政策》文件:

步驟 階段發生了什麼
1 索引:文件被切分成塊 → 向量化 → 存入向量庫
2 檢索:你問:「春節放假幾天?」 → 問題被向量化
3 檢索:向量庫找到最相關的文本塊(含「春節假期 7 天」)
4 生成 Prompt =「根據上下文回答:春節放假幾天? 上下文:春節假期7天...」
5 生成:LLM 回答:「根據公司政策,春節放假 7 天。」

第一章小結

核心概念一句話總結:

  • RAG 先查資料,再回答問題,讓 LLM 有據可依
  • 索引階段:離線準備知識庫(文件 → 向量庫)
  • 檢索+生成階段:線上回答問題(問題 → 檢索 → 生成)
  • RAG vs 微調:RAG 提供知識,微調改能力

第二章:RAG 核心原理(純概念,無程式碼)

本章只講原理,不涉及任何程式碼或框架。所有 LangChain 實作放在第四章。

2.1 索引階段原理

2.1.1 文件載入

目標:將各種格式的原始文件讀取為程式可處理的純文字。

挑戰:不同格式有不同複雜度

格式 挑戰 原理說明
PDF 表格、影像、多欄版面需要解析器提取文字流
Word 複雜格式、內嵌物件需要解壓並提取文字
Markdown 標題層級需保留,標題可作為結構資訊
HTML 標籤噪音需移除標籤,保留正文
純文字 最簡單直接讀取
2.1.2 文本拆分(Chunking)

為什麼需要拆分?

  1. LLM 上下文視窗限制:模型一次能處理的文本長度有限
  2. 檢索精準度:小塊更容易精準匹配問題,大塊會引入噪音
  3. 成本控制:只傳送相關片段,節省 token 費用

核心概念:

概念 含義 示例
chunk_size 單個文本塊的最大長度 500 字元 或 200 tokens
chunk_overlap 相鄰塊之間的重疊長度 50 字元,保留上下文連續性
separators 優先切割的位置 段落 > 句子 > 詞語 > 字元

重疊的作用:

文檔: [A 段開頭...中間部分...B 段結尾]
                    ↓
塊1: [A 段開頭...中間部分]
塊2: [中間部分...B 段結尾]  ← 重疊部分防止資訊被切斷
2.1.3 文本向量化(Embedding)

什麼是向量化?
將文本轉換為固定維度的浮點數陣列(向量),語意相似的文本在向量空間中距離更近。

示例:
"蘋果很好吃" → [0.12, -0.34, 0.56, ..., 0.78](1536 維)
"水果很美味" → [0.11, -0.33, 0.55, ..., 0.79](距離很近,語意相似)
"汽車很快" → [-0.45, 0.23, -0.67, ..., 0.12](距離很遠,語意不同)

關鍵原則:索引階段和檢索階段必須使用同一個 Embedding 模型,否則向量空間不匹配,無法正確比較。

2.1.4 向量資料庫存儲

存儲的內容結構:

┌─────────────────────────────────────────────┐
│              向量資料庫中的一條紀錄         │
├─────────────────────────────────────────────┤
│  向量:[0.12, -0.34, 0.56, ..., 0.78]       │
│  原始文本:"春節假期共7天"                    │
│  元資料:{"source": "holiday.pdf", "page": 3}│
└─────────────────────────────────────────────┘

2.2 檢索與生成階段原理

2.2.1 問題向量化

將使用者問題用與索引階段相同的 Embedding 模型轉換為向量。

2.2.2 相似度搜尋

常用相似度演算法:

演算法 直觀理解 公式
餘弦相似度 關注向量方向是否一致(最常用) cos(θ) = (A·B) / ( A B )
歐氏距離 關注絕對距離遠近 d = √Σ(Ai - Bi)^2
點積 向量已歸一化時等同於餘弦 A·B

Top-K 檢索:返回與問題向量最相似的 K 個文本塊。

2.2.3 構建 Prompt

核心思想:將檢索到的文本塊作為「上下文」注入到提示詞中。

標準 RAG Prompt 範本結構:

你是一个基於以下上下文回答問題的助手。

上下文:
{這裡放檢索到的相關文本塊}

問題:{使用者的問題}

請基於以上上下文回答。如果上下文中沒有相關資訊,請說「我不知道」。

2.2.4 LLM 生成

大型語言模型接收包含「上下文 + 問題」的 Prompt,基於上下文生成答案,而不是依賴自己的訓練記憶。


第二章小結

概念一句話解釋:

  • 文本拆分把長文件切成小塊,便於檢索
  • chunk_size:每塊多大
  • chunk_overlap:塊之間重疊多少
  • 向量化:把文字轉成數字陣列
  • 相似度搜尋:找最接近問題向量的文本塊
  • Top-K:返回最相似的 K 個塊
  • Prompt:把「上下文 + 問題」打包發給 LLM

第三章:向量資料庫選型

3.1 為什麼需要向量資料庫?

傳統資料庫(如 MySQL)無法高效進行向量相似度搜尋:

能力 傳統資料庫 向量資料庫
精確匹配 ✅ 快 ❌ 不支援
模糊搜尋 ⚠️ 慢 ❌ 不支援
向量相似度 ❌ 不支援 ✅ 支援(快)
標量過濾 ✅ 支援 ✅ 支援(多數向量資料庫)

向量資料庫專為向量搜尋設計,提供:

  • 高效索引:HNSW、IVF 等演算法實現毫秒級搜尋
  • 相似度計算:內建餘弦、歐氏距離等
  • 混合搜尋:向量 + 標量過濾

3.2 常用向量資料庫比較

資料庫 類型 性能 易用性 擴展性 最適場景
Chroma 嵌入式 中等 ⭐⭐⭐⭐⭐ 學習原型、小專案
FAISS 函式庫 ⭐⭐⭐ 本地研究、無需持久化
Pgvector PostgreSQL 擴充 中高 ⭐⭐⭐⭐ 已有 PostgreSQL 棧
Milvus 雲原生 極高 ⭐⭐ 極高 十億級向量生產環境
Redis 內存資料庫 極高 ⭐⭐⭐⭐ 超低延遲場景
Elasticsearch 搜尋引擎 ⭐⭐⭐ 需要關鍵字+向量混合搜尋
各資料庫詳解

Chroma

  • 輕量級,純 Python,API 極其簡單
  • 資料持久化到本地磁碟
  • 適合:學習 RAG、原型驗證、小規模應用

FAISS

  • Facebook 開源,C++ 核心,效能強悍
  • 本質是函式庫而非完整資料庫(無持久化,需自行管理)
  • 適合:學術研究、本地實驗、對效能要求高但不需分散式

Pgvector

  • PostgreSQL 官方擴充,使用 SQL 操作向量
  • 可复用現有 PG 基礎設施(備份、高可用、權限)
  • 適合:團隊已有 PostgreSQL,不想引入新元件

Milvus

  • 雲原生架構,支援十億級向量
  • 功能最全:混合搜尋、動態 schema、多副本
  • 適合:大規模生產系統、需要分散式擴展

Redis

  • 記憶體級速度,毫秒級回應
  • 支援向量搜尋作為輔助功能
  • 適合:超低延遲場景、已有 Redis 基礎設施

Elasticsearch

  • 老牌搜尋引擎,現支援向量
  • 最大優勢:關鍵字搜尋 + 向量搜尋混合
  • 適合:需要同時支援精確關鍵字匹配與語意匹配

3.3 選型決策樹

開始

├─ 只是學習/原型 → Chroma

├─ 已有 PostgreSQL → Pgvector

├─ 十億級向量 / 雲原生 → Milvus

├─ 需要超低延遲 → Redis

├─ 需要關鍵字+向量混合 → Elasticsearch

└─ 本地研究/高效能 → FAISS


第四章:LangChain 實戰(精簡版)

本章只講核心常用程式碼,次要內容簡要帶過。

4.1 環境準備

pip install langchain langchain-community chromadb openai tiktoken
# 按需安裝:unstructured pypdf docx2txt jq redis dashscope

4.2 核心元件速覽

元件 作用 常用類別
文件載入器 讀取各種格式文件 TextLoader, PyPDFLoader, CSVLoader, Docx2txtLoader, JSONLoader
文本分割器 切分長文件 RecursiveCharacterTextSplitter(首選)
Embedding 模型 文本向量化 OpenAIEmbeddings, HuggingFaceEmbeddings, DashScopeEmbeddings
向量資料庫 存儲與檢索 Chroma(學習), Redis(生產), FAISS(本地)
檢索器 查詢相關文件 as_retriever(k=4)
LLM 生成 生成答案 ChatOpenAI, init_chat_model(阿里千問)
Prompt 模板 組裝提示詞 PromptTemplate, ChatPromptTemplate

4.3 文件載入器(常用示例)

# 純文字
from langchain_community.document_loaders import TextLoader
loader = TextLoader("file.txt", encoding="utf-8")
docs = loader.load()

# PDF
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("file.pdf", extraction_mode="plain")
docs = loader.load()

# Word
from langchain_community.document_loaders import Docx2txtLoader
loader = Docx2txtLoader("file.docx")
docs = loader.load()

# CSV
from langchain_community.document_loaders.csv_loader import CSVLoader
loader = CSVLoader(file_path="file.csv")
docs = loader.load()

# JSON
from langchain_community.document_loaders import JSONLoader
loader = JSONLoader(file_path="file.json", jq_schema=".", text_content=False)
docs = loader.load()

其他載入器(Markdown、HTML、目錄批次等)用法類似,按需查閱文件。

4.4 文本分割器

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # 每塊最大字元數
    chunk_overlap=50,    # 塊間重疊
)
chunks = splitter.split_documents(docs)

4.5 Embedding 模型

# OpenAI
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# HuggingFace 本地(中文推薦)
from langchain_community.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh")

# 阿里千問
from langchain_community.embeddings import DashScopeEmbeddings
embeddings = DashScopeEmbeddings(model="text-embedding-v3", dashscope_api_key=api_key)

4.6 向量資料庫

# Chroma(學習推薦)
from langchain_community.vectorstores import Chroma
vector_store = Chroma.from_documents(chunks, embeddings, persist_directory="./db")
vector_store.persist()

# Redis(生產推薦)
from langchain_community.vectorstores import Redis
vector_store = Redis.from_documents(docs, embeddings, redis_url="redis://localhost:6379", index_name="my_index")

# FAISS(本地快速)
from langchain_community.vectorstores import FAISS
vector_store = FAISS.from_documents(chunks, embeddings)
vector_store.save_local("./faiss_index")

4.7 檢索器

retriever = vector_store.as_retriever(search_kwargs={"k": 4})  # 返回 Top-4

4.8 LLM 模型

# OpenAI
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# 阿里千問
from langchain.chat_models import init_chat_model
llm = init_chat_model(model="qwen-plus", model_provider="openai", api_key=api_key, base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")

4.9 Prompt 模板

from langchain_core.prompts import PromptTemplate

template = """基於以下上下文回答問題:
上下文:{context}
問題:{question}"""
prompt = PromptTemplate(template=template, input_variables=["context", "question"])

4.10 完整 RAG Chain

from langchain_core.runnables import RunnablePassthrough

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
)

result = rag_chain.invoke("你的問題")
print(result.content)

4.11 完整問答範例(阿里千問 + Redis)

# complete_rag_example.py
import os
from langchain.chat_models import init_chat_model
from langchain_community.document_loaders import Docx2txtLoader
from langchain_core.prompts import PromptTemplate
from langchain_classic.text_splitter import CharacterTextSplitter
from langchain_core.runnables import RunnablePassthrough
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores import Redis

# 1. 初始化 LLM
llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

# 2. Prompt 模板
prompt_template = """
請使用以下提供的文本內容來回答問題。僅使用提供的文本資訊,
如果文本中沒有相關資訊,請回答 "抱歉,提供的文本中沒有這個資訊"。

文本內容:{context}
問題:{question}
回答:
"""
prompt = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

# 3. Embedding
embeddings = DashScopeEmbeddings(model="text-embedding-v3", dashscope_api_key=os.getenv("aliQwen-api"))

# 4. 載入文件
loader = Docx2txtLoader("alibaba-java.docx")
documents = loader.load()

# 5. 分割文件
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

# 6. 建立 Redis 向量庫
vector_store = Redis.from_documents(
    documents=documents,
    embedding=embeddings,
    redis_url="redis://localhost:6379",
    index_name="my_index",
)

# 7. 檢索器
retriever = vector_store.as_retriever(search_kwargs={"k": 2})

# 8. RAG Chain
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
)

# 9. 提問
result = rag_chain.invoke("00000和A0001分別是什麼意思")
print(result.content)

第五章:參數調優與最佳實務

5.1 核心參數調優指南

chunk_size 選擇
文檔類型 推薦值 原因
產品問答 200–300 字元 每個問答短小精悍
技術文件 500–800 字元 段落通常較長
法律條文 300–500 字元 條款需保持獨立
長篇文章 1000–1500 字元 保持上下文連貫
chunk_overlap 設定
chunk_overlap = chunk_size × (10% ~ 20%)
Top-K 選擇
K 值 適用場景 優點 缺點
3 答案集中在少數段落 精準 可能遺漏資訊
5 通用推薦 平衡
10 需要廣泛上下文信息 全面 噪音增多、成本增加

5.2 進階優化技術(簡介)

技術 一句話說明
多查詢檢索 將問題改寫成多個角度,分別檢索後合併
父文檔檢索 存小塊(精準匹配),返回大塊(完整上下文)
自查詢檢索 從問題中提取語意條件 + 元資料過濾條件
重排序 檢索更多結果,用更強模型重新排序取 Top-K

5.3 常見問題排查

問題 可能原因 解決方案
答案不相關 chunk 太大含噪音 減小 chunk_size
丟失關鍵資訊 chunk 太小切斷上下文 增大 chunk_size 或 overlap
檢索不到問題 表述與文件不匹配 使用多查詢檢索
回答「不知道」但有文件 Embedding 模型不適合中文 改用 BAAI/bge-large-zh
速度慢 向量庫太大 添加索引、使用 GPU
成本高 Top-K 或 chunk 太大 減小 K 和 chunk_size

5.4 推薦起步配置

splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
retriever = vector_store.as_retriever(search_kwargs={"k": 4})

目標

成為 AI agent 工程師並且就業

幫助

需要大家的關注跟按讚,你們的關注按讚就是對我最大的鼓勵。或許等我技術成熟時,你們當中的大佬還可以幫我一把,感謝。


原文出處:https://juejin.cn/post/7628081158655688710


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝5   💬6   ❤️3
430
🥈
我愛JS
📝1   💬6   ❤️2
122
🥉
💬1  
4
#4
Gigi
2
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登