ข้ามไปยังเนื้อหา
19/30บทที่ 19 จาก 30

RAG ใน Production: Chunking, Retrieval และการอ้างอิงที่ซื่อสัตย์

ตัดทื่อที่ 512 ตัวอักษร คำตอบ 4 จาก 32 หายก่อนถึง retriever แค่แก้ chunker ก็พา rank 115 ขึ้นเป็น 3

ในหน้านี้

นี่คือคำถามจริงจากผู้ใช้จริงของ assistant จริง: eval set ของฉันมี 20 รายการ พอให้เชื่อคะแนนได้ไหม corpus มีคำตอบอยู่ — เป็นทั้ง section หนึ่งเลย นี่คือ fragment สี่ชิ้นที่ retriever ใส่เข้าไปใน prompt จริง ๆ

four fragments, chunked blind at 512 charactersTEXT
[1] d=0.578  ship — that set has been used for fitting, and its score stops being
             unbiased. Measured on this belt: sweeping the threshold on the
             validation set picks 0.196, and the model then scores F1 = 0.4122…

[2] d=0.602  ng when the model is confidently **wrong**. Evaluate both at a few
             scores, for an example whose true label is 1: | score | p | …

[3] d=0.613  ard and watch both numbers: | | reward model's score | true quality
             | length produced | … The reward went up by a factor of 2.5. The…

[4] d=0.617  | 0.6 | +0.97 | +1.00 | +0.27 | … The reward model is working
             perfectly. It has faithfully learned the preferences it was shown…

สามในสี่ชิ้นเริ่มกลางคำ สองชิ้นมาจาก chapter อื่นที่พูดคนละเรื่อง และ fragment ที่ตอบคำถามได้ — ชิ้นที่มีข้อความ Seventeen out of twenty cannot distinguish an 85 % model from a 65 % one — กลับมาอยู่ที่ rank 115

ตอนนี้ใช้คำถามเดิม embedding model เดิม prompt template เดิม เปลี่ยนแค่อย่างเดียว: วิธีตัดเอกสาร

four fragments, cut on section boundaries with a contextual headerTEXT
[1] d=0.594  [Classification, Cross-Entropy… > How many test examples do I need?]
             Read it backwards, which is how you will use it: ±5 points needs
             about 200 examples. ±2 points needs about 1,230…

[2] d=0.598  [Classification, Cross-Entropy… > Three splits, and the leak…]
             Why three splits and not two? Because the moment you use a set of
             examples to *choose* anything…

[3] d=0.600  [Classification, Cross-Entropy… > How many test examples do I need?]
             The honest reading of 17/20 is *somewhere between 64 % and 95 %*.
             …Seventeen out of twenty cannot distinguish an 85 % model from a 65 % one.

[4] d=0.605  [Classification, Cross-Entropy… > How many test examples do I need?]
             Suppose you score a model on 20 examples and it gets 17 right. You
             report 85 %. …Wilson 95% CI : [0.6396, 0.9476]

จาก rank 115 เป็น rank 3 ไม่มีใครแตะ model, prompt, threshold หรือจำนวนช่อง บทนี้ว่าด้วยช่องว่างนั้น และอีกสี่จุดที่ระบบ retrieval แอบโกหกคุณอย่างเงียบ ๆ

แสดงรายละเอียด

สิ่งที่บทนี้ต้องใช้จากบทก่อนหน้า และจุดเดียวที่เปลี่ยนภาษา

  • บทที่ 1 นิยาม dot product และ L2 norm แล้ว ส่วน threshold ด้านล่างคือสองอย่างนี้เท่านั้น ไม่มีอย่างอื่น
  • บทที่ 8 แยก embedding table ของ language model ออกจาก retrieval embedding model ที่ฝึกแบบ contrastive บนคู่ข้อมูล วัด cosine similarity และจบด้วยสัญญาว่าบทที่ 19 จะให้ cut-off ที่จับต้องได้ สัญญานั้นมาถึงตรงนี้แล้ว และจะไม่ทวนซ้ำ
  • บทที่ 4 สร้าง Wilson interval; บทที่ 15 สร้าง evaluation harness ตารางทั้งหมดด้านล่างใช้ตัวแรกและถูกผลิตด้วยตัวที่สอง
  • บทที่ 16 คิดราคาของ context window prompt ที่ประกอบตอนท้ายบทนี้ใช้ 591 tokens และนั่นคือ budget ที่ fragment ต่าง ๆ ต้องแข่งขันกัน

ทุกอย่างที่นี่เป็น TypeScript เช่นเดียวกับตั้งแต่ บทที่ 14 และบทนี้คือจุดที่กฎนั้นพิสูจน์คุณค่าของตัวเอง: ingestion คือ queue และ storage, search คือ network call และการประกอบ prompt พร้อม citation คือหน้าที่ของ server การวัดคือ code ชุดเดิมที่มี scoreboard ครอบไว้โดยตั้งใจ — retriever ที่ถูกให้คะแนนด้วย implementation ชุดที่สองคือเลขเกี่ยวกับ software ที่คุณไม่ได้ ship และ cosine threshold ด้านล่างน่าเชื่อได้ก็เพราะคุณเห็นมันถูก sweep โดย chunker ตัวเดียวกับที่จะรันใน production

corpus และอะไรนับว่าเป็นคำตอบที่ถูก

ลิงก์ไปยังส่วน: corpus และอะไรนับว่าเป็นคำตอบที่ถูก

ทุกอย่างด้านล่างวัดกับ corpus เดียว: สิบสามบทแรกของคอร์สนี้ — 13 เอกสาร, 359,067 ตัวอักษร, 127 sections โดยตัด front matter และ bibliography ออกแล้ว นี่คือ corpus เชิงเทคนิคจริง มี prose, table, formula และ code block อยู่ในนั้น และเป็นของประเภทเดียวกับที่คนมักโหลดเข้า knowledge base แล้วค่อยบ่นทีหลัง

ground truth คือคำถาม 32 ข้อ แต่ละข้อจับคู่กับ needle: ประโยคสั้น ๆ แบบ verbatim จาก corpus ที่ตอบคำถามนั้น needle แต่ละอันปรากฏครั้งเดียวเท่านั้นใน 359,067 ตัวอักษร และไม่มีอันไหนเป็น section heading — การตรวจนี้สำคัญ เพราะไม่อย่างนั้น chunker ที่คัดลอก heading เข้าไปในทุก chunk จะให้คะแนนตัวเองได้ คำถามแต่ละข้อถูกถามสองครั้ง ครั้งหนึ่งเป็นภาษาอังกฤษแบบในคอร์ส และอีกครั้งเป็นสำนวนแบบ support ticket: 64 queries บน 32 ground truths

retrieval ถือว่าถูกเมื่อ chunk ที่ถูกส่งกลับมี needle อยู่ ครบทั้งประโยค นี่คือ definition เดียวที่ตรงกับสิ่งที่ generator ต้องการ: ครึ่งประโยคใน prompt ไม่ใช่คำตอบ แต่เป็นความเสี่ยง

embedding model คือ all-MiniLM-L6-v2 — 384 dimensions, mean-pooled และ normalised เป็น model ที่ฝึกแบบ contrastive ที่บทที่ 8 วัดไว้ การ index corpus ใช้เวลา 20.8 วินาทีบน CPU, 22 ms ต่อ chunk; embedding query หนึ่งข้อใช้ 13 ms

หก strategies จากส่วนประกอบอิสระสามอย่าง Blind ตัดทุก 512 ตัวอักษรโดยไม่ดูข้อความ Boundaries ไม่ตัดกลาง paragraph และ fallback ไปที่ sentence boundary เฉพาะเมื่อ paragraph เดียวเกิน budget Header เติม document title และ section path นำหน้า chunk แต่ละชิ้น Overlap คัดลอก 64 ตัวอักษรสุดท้ายของ chunk ก่อนหน้าเข้าไปใน chunk ถัดไป

strategychunksanswers destroyedR@1R@4R@8R@20MRR
A blind 5127084 / 320.1250.2970.4220.5940.241
B blind + overlap80900.1720.3910.4530.6250.286
C boundaries94000.1560.4220.5310.6720.293
D boundaries + overlap94000.1560.3590.5160.6560.277
E boundaries + header94000.0940.4220.5780.8280.280
F boundaries + header + overlap94000.1560.3910.5620.7660.298

ด้วย 64 queries ช่วง 95 % Wilson interval บน R@20 คือ [0.471, 0.705] สำหรับ A และ [0.718, 0.901] สำหรับ E — สองช่วงนี้ไม่ overlap กัน แต่ column อื่นส่วนใหญ่ overlap และ table แบบ unpaired แยกไม่ได้ ทุก strategy ตอบ queries ชุดเดียวกัน ดังนั้น test ที่ซื่อสัตย์คือ paired: นับ win และ loss ของแต่ละ strategy เทียบกับอีกตัว แล้วรัน sign test บน discordant pairs มีสามผลลัพธ์ที่รอดจากการทดสอบนี้

Blind chunking ทำลายคำตอบสี่ข้อจากสามสิบสองข้อทิ้งไปเลย ไม่ใช่จัด rank แย่ — ทำลายทิ้ง needle คร่อม boundary 512 ตัวอักษร ดังนั้นไม่มี chunk ใดใน index ที่มีมัน และ recall ceiling สำหรับ queries เหล่านั้นคือศูนย์ ไม่มี reranker กู้คืนได้ ไม่มี threshold ช่วยได้ ไม่มี model ที่ใหญ่กว่าช่วยได้ คุณ retrieve ข้อความที่ไม่มีอยู่เป็นชิ้นเดียวที่ไหนเลยใน index ไม่ได้ นี่คือ failure ที่ถูกพูดถึงน้อยที่สุดใน RAG เพราะมันดูเหมือน retriever แย่ทุกประการ

Overlap แก้เรื่องนั้น และไม่แก้อย่างอื่น ทุก strategy ที่มี overlap เสียคำตอบเป็นศูนย์ ซึ่งก็คือเหตุผลที่มี overlap มันไม่ได้ปรับ ranking ให้ดีขึ้น: B เทียบกับ A ที่ R@8 คือ +8/−6, p = 0.79; ที่ R@20 คือ +9/−7, p = 0.80 แย่กว่านั้น การเพิ่ม overlap บน header กลับทำร้ายผลลัพธ์ — F เทียบกับ E คือ +2/−6 ที่ R@20 — และเหตุผลเป็นเชิงกล vector ของ chunk คือ mean เหนือ tokens ของมัน ดังนั้น 64 ตัวอักษรจาก chunk ก่อนหน้าจะลาก mean นั้นไปทาง topic ของเพื่อนบ้าน Overlap คือประกันต่อคำตอบที่ถูกผ่าแบ่ง จ่ายด้วย precision

contextual header คือสิ่งที่ซื้อ retrieval มาให้ E เทียบกับ A คือ +18/−3 ที่ R@20, p = 0.0015 และ ablation บอกว่าไม่ใช่เพราะ boundaries: E เทียบกับ C — การตัดเหมือนกัน ต่างกันแค่ header — คือ +12/−2, p = 0.0129 การเติม prefix "Classification, Cross-Entropy, and How Not to Fool Yourself > How many test examples do I need?" ให้ paragraph บอก embedding model ว่า paragraph นี้ เกี่ยวกับอะไร ซึ่งตัว paragraph เองมักไม่ได้พูด มันคือ pronoun resolver สำหรับเอกสาร

นั่นให้รูปทรงแก่ chunker และให้กฎหนึ่งข้อที่พลาดได้ง่าย:

chunk.tsTS
export interface Chunked {
  /** What gets EMBEDDED: contextual header + this chunk's own content. */
  text: string;              
  /** ONLY this chunk's own content: what is quoted back to the user. */
  content: string;           
  section: string;
  /** Character range in the document's canonical text. Sliceable. */
  from: number;
  to: number;
}

export function chunkDocument(doc: string, docTitle: string, target = 512): Chunked[] {
  const out: Chunked[] = [];
  const heads = [...doc.matchAll(/^## (.+)$/gm)].map((m) => ({ at: m.index!, title: m[1].trim() }));
  const spans = heads.length
    ? heads.map((h, i) => ({ ...h, end: i + 1 < heads.length ? heads[i + 1].at : doc.length }))
    : [{ at: 0, title: "", end: doc.length }];

  for (const s of spans) {
    const header = s.title ? `${docTitle} > ${s.title}` : docTitle;     
    const skip = /^## .+\n/.exec(doc.slice(s.at, s.end))?.[0].length ?? 0;
    const body = doc.slice(s.at + skip, s.end);
    const origin = s.at + skip;

    // The offset is FOUND in the document, never accumulated: adding up
    // lengths drifts by a character wherever a separator was normalised,
    // and a citation anchor off by one points at the wrong line.
    const emit = (from: number, to: number) => {
      const raw = body.slice(from, to);
      const lead = raw.length - raw.trimStart().length;
      const content = raw.trim();
      if (!content) return;
      out.push({ text: `[${header}]\n${content}`, content, section: s.title,
                 from: origin + from + lead, to: origin + from + lead + content.length });
    };

    let open: [number, number] | null = null;
    for (const m of body.matchAll(/[^\n]([^\n]|\n(?!\n))*/g)) {          // paragraphs
      const [pf, pt] = [m.index!, m.index! + m[0].length];
      if (pt - pf > target) {                                            // one huge paragraph
        if (open) { emit(open[0], open[1]); open = null; }
        let cur: [number, number] | null = null;
        for (const sm of body.slice(pf, pt).matchAll(/[^.!?]*[.!?]*\s*/g)) {
          if (!sm[0]) continue;
          const [sf, st] = [pf + sm.index!, pf + sm.index! + sm[0].length];
          if (cur && st - cur[0] > target) { emit(cur[0], cur[1]); cur = null; }
          cur = cur ? [cur[0], st] : [sf, st];
        }
        if (cur) emit(cur[0], cur[1]);
        continue;
      }
      if (open && pt - open[0] > target) { emit(open[0], open[1]); open = null; }
      open = open ? [open[0], pt] : [pf, pt];
    }
    if (open) emit(open[0], open[1]);
  }
  return out;
}

สองข้อความ ไม่ใช่ข้อความเดียว text คือสิ่งที่ถูก embedded ทั้ง header และทุกอย่าง content คือเฉพาะคำของ chunk นี้เอง และเป็นสิ่งที่ quote กลับไปหาผู้ใช้ ถ้า quote text citation จะแสดง header ที่ไม่ได้อยู่ในเอกสาร ณ จุดนั้น — และเมื่อมี overlap ก็จะแสดง tail ที่ซ้ำและเป็นของ fragment ก่อนหน้า จากนั้นมันแสดงข้อความที่ไม่ได้อยู่ในตำแหน่งที่มันอ้างว่าอยู่ ซึ่งแย่กว่าการไม่แสดงอะไรเลย

header ไม่ได้ฟรี ใน 940 chunks มันใช้ 24,213 จาก 114,275 embedded tokens ของ index: 21.2 % ของสิ่งที่คุณจ่ายเพื่อ embed คือ header ที่คุณเขียนเอง มันยังดัน chunks ให้ชน window ของ encoder ด้วย all-MiniLM-L6-v2 รับ 256 word-pieces; strategy E มี 17 chunks ที่เกินเส้นนั้น และ F มี 28 ทุกอันถูก truncate เงียบ ๆ โดยไม่มี warning จากอะไรเลย ขนาด chunk ที่ใช้ได้จริงของคุณไม่ใช่เลขใน config — แต่คือค่าที่เล็กกว่าระหว่างเลขนั้นกับ window ของ encoder

Dense retrieval มีจุดอ่อนเชิงระบบหนึ่งอย่าง และมันไม่ละเอียดอ่อนเลย: มัน match ความหมาย ดังนั้นมันไม่สนว่า string ที่คุณพิมพ์คืออะไร แบบเป๊ะ ๆ หมายเลขชิ้นส่วน error code acronym นามสกุล — สิ่งเหล่านี้ไม่มีความหมายที่มีประโยชน์ให้ embed และ nearest neighbour ของ error code หนึ่งก็คือ error code อื่นทุกตัวใน corpus ของคุณ

คำตอบแบบดั้งเดิมเก่ากว่าทั้งหมดนี้และใช้ยี่สิบบรรทัด BM25 ให้คะแนนเอกสารจากความถี่ที่ terms ของ query ปรากฏในนั้น โดยลดแรงของแต่ละ term เมื่อ frequency สูงขึ้น และลงโทษเอกสารยาวที่สะสม matches ได้เพราะความยาวล้วน ๆ1 Term tt contributes

idf(t)ft,d(k1+1)ft,d+k1(1b+bdd)\mathrm{idf}(t)\cdot\frac{f_{t,d}\,(k_1+1)}{f_{t,d} + k_1\left(1 - b + b\,\frac{|d|}{\overline{|d|}}\right)}

โดยที่ ft,df_{t,d} คือจำนวนครั้งของ term ในเอกสาร, d|d| คือความยาว, d\overline{|d|} คือความยาวเฉลี่ย และ k1=1.2k_1 = 1.2 กับ b=0.75b = 0.75 คือ constants ตามธรรมเนียมสองตัว — k1k_1 กำหนดว่าการซ้ำหยุดช่วยเร็วแค่ไหน, bb กำหนดว่าความยาวถูกลงโทษแรงแค่ไหน

bm25.tsTS
const toks = (s: string) => s.toLowerCase().match(/[a-z0-9]+/g) ?? [];

export class BM25 {
  private tf: Map<string, number>[] = [];
  private len: number[] = [];
  private idf = new Map<string, number>();
  private avg = 0;
  private k1: number; private b: number;
  constructor(docs: string[], k1 = 1.2, b = 0.75) {
    this.k1 = k1; this.b = b;
    const df = new Map<string, number>();
    for (const d of docs) {
      const t = new Map<string, number>(); const ws = toks(d);
      for (const w of ws) t.set(w, (t.get(w) ?? 0) + 1);
      for (const w of t.keys()) df.set(w, (df.get(w) ?? 0) + 1);
      this.tf.push(t); this.len.push(ws.length);
    }
    this.avg = this.len.reduce((a, b) => a + b, 0) / this.len.length;
    const N = docs.length;
    for (const [w, n] of df) this.idf.set(w, Math.log(1 + (N - n + 0.5) / (n + 0.5)));
  }
  scores(query: string): number[] {
    const q = toks(query);
    return this.tf.map((tf, i) => {
      const L = this.len[i]; let s = 0;
      for (const w of q) {
        const f = tf.get(w); if (!f) continue;
        s += (this.idf.get(w) ?? 0) * (f * (this.k1 + 1)) /
             (f + this.k1 * (1 - this.b + (this.b * L) / this.avg));
      }
      return s;
    });
  }
}

บน 940 chunks มันให้คะแนน query หนึ่งข้อใน 1.14 ms โดยไม่มี index ใด ๆ นอกจาก hash maps สองตัว และมันไม่ใช่ของโชว์ในพิพิธภัณฑ์:

retrieverR@1R@4R@8MRRcost per query
dense (cosine)0.0940.4220.5780.28013 ms เพื่อ embed + 0.3 ms เพื่อ scan
lexical (BM25)0.2190.3750.4690.3131.14 ms
hybrid (RRF)0.2030.4840.6090.346ทั้งสองอย่าง
hybrid + cross-encoder0.3120.5780.7030.447+ 569 ms

BM25 เพิ่มความแม่นยำ top-1 ของ dense retriever บน corpus นี้มากกว่าสองเท่า และแพ้หนักเมื่อถึง rank 8 ทั้งสองล้มเหลวกับ queries คนละชุด ซึ่งเป็นเหตุผลทั้งหมดของการรันทั้งคู่

การ fuse ทั้งสองเป็นจุดเดียวที่วิธีที่ดู obvious กลับผิด Cosine distances และ BM25 scores ไม่ได้อยู่บน scale เดียวกัน ไม่ได้ bounded แบบเดียวกัน และการ normalise ต่อ query ทำให้ weight ขึ้นกับว่า hit ที่ดีที่สุดบังเอิญดีแค่ไหน Reciprocal rank fusion ทิ้ง scores แล้วเก็บแค่ ranks:2

RRF(d)=lists1k+rank(d),k=60\mathrm{RRF}(d) = \sum_{\text{lists}} \frac{1}{k + \mathrm{rank}(d)}, \qquad k = 60
retrieve.tsTS
/** Reciprocal rank fusion: ranks, not scores. Nothing to calibrate. */
export function rrf(lists: number[][], k = 60): number[] {
  const acc = new Map<number, number>();
  for (const list of lists)
    list.forEach((id, r) => acc.set(id, (acc.get(id) ?? 0) + 1 / (k + r + 1)));
  return [...acc.entries()].sort((a, b) => b[1] - a[1]).map(([id]) => id);
}

และตรงนี้ การอ่าน table อย่างซื่อสัตย์สำคัญกว่า table เอง Hybrid ชนะ BM25 ที่ R@4 ด้วย +10/−3, p = 0.09 มันชนะ dense ด้วย +10/−6, p = 0.45 บน corpus นี้ ด้วย 64 queries, hybrid retrieval แยกไม่ออกจาก dense retrieval อย่างมีนัยสำคัญ มันดีกว่าใน point estimates ทั้งสองและทุก recall column แต่หลักฐานยังไม่ถึง significance แทบทุก blog post เรื่อง hybrid search บนอินเทอร์เน็ตรายงาน table แบบข้างบนและไม่มี interval; นี่คือสิ่งที่ interval บอก

ทุกอย่างจนถึงตอนนี้คือ bi-encoder: query ผ่าน model ลำพัง chunk แต่ละชิ้นผ่านมันลำพังเมื่อหลายเดือนก่อน และทั้งสองไม่เคยเจอกันนอกจากเป็น dot product นั่นคือสิ่งที่ทำให้ index เป็นไปได้ — embed ครั้งเดียว ใช้ซ้ำตลอดไป — และมันก็คือเพดานด้วย model ไม่เคยมอง query กับ chunk พร้อมกัน

cross-encoder ทำแบบนั้นพอดี: มันรับคู่ข้อมูลเป็น input เดียวและคืน relevance score ไม่มีอะไร precompute ได้ ดังนั้นมัน rank index ไม่ได้ — แต่มัน rerank shortlist ได้ การ reranking hybrid top 25 ด้วย ms-marco-MiniLM-L-6-v2 ขยับ R@1 จาก 0.094 (dense) เป็น 0.312 และ MRR จาก 0.280 เป็น 0.447: improvement เดี่ยวที่ใหญ่ที่สุดในบทนี้ และเป็นอันเดียวที่แตะหัว list แทนที่จะเป็น tail

มันใช้ 569 ms ต่อ query บน CPU เทียบกับ 1.14 ms สำหรับ BM25 และ 0.3 ms สำหรับ vector scan ประมาณสองพันเท่าของ retrieval cost สำหรับเอกสารยี่สิบห้าชิ้น นี่คือ trade-off ระหว่าง bi-encoder/cross-encoder ทั้งหมดในเลขเดียว และเป็นเหตุผลที่ architecture มีรูปทรงเดิมเสมอ: retriever ราคาถูกที่ recall กว้าง แล้วตามด้วย scorer ราคาแพงบน shortlist ที่คุณจ่ายไหว ColBERT อยู่กลางระหว่างทั้งสอง โดย precompute vectors ต่อ token และทำ late interaction ที่ถูกกว่า cross-encoder และคมกว่า dot product3

Vector databases รายงาน distances และ distance แบบไหนเป็น option ใน configuration บน vectors ที่ normalised แล้ว ตัวเลือกนี้เป็นเรื่อง cosmetic และ identity นี้คุ้มที่จะทำหนึ่งครั้ง เพราะทุกอย่างหลังจากนี้ขึ้นกับการที่ vectors เป็น unit จริง ๆ สำหรับ a=b=1\lVert a \rVert = \lVert b \rVert = 1:

ab2=a2+b22ab=22cosθ\lVert a - b \rVert^2 = \lVert a \rVert^2 + \lVert b \rVert^2 - 2\,a \cdot b = 2 - 2\cos\theta

ดังนั้น cosine distance 1cosθ1 - \cos\theta จึงเท่ากับ d2/2d^2/2 พอดี นี่คือ dot product และ norm จากบทที่ 1 ที่ถูกนำมาใช้ ตรวจบน chunk vectors จริงสองตัวจาก index ข้างบน แล้วต่อด้วย pairs 40,000 คู่:

TEXT
||a|| = 1.000000   ||b|| = 1.000000
L2 = 0.795183   L2^2/2 = 0.316158   1 - cos = 0.316158   diff = 7.66e-08
max |L2^2/2 - (1 - cos)| over 200 x 200 pairs = 8.3e-07

แม่นเท่ากับ floating-point noise — และ เฉพาะเพราะ vectors ถูก normalised ข้าม normalisation ไป identity นี้จะเป็นเท็จ threshold ของคุณจะไม่มีความหมาย และ distance ที่เอกสารรายงานจะขึ้นกับความยาวของข้อความ

ทีนี้มาถึงเลขที่ไม่มีใคร derive retriever คืนบางอย่างเสมอ: มัน sort ทั้ง index แล้วส่งหัว list ให้คุณ ไม่ว่าคำตอบจะอยู่ใน corpus หรือไม่ threshold คือส่วนเดียวของระบบที่พูดว่า ไม่ ได้ — และการตั้งมันต้องมี queries ที่ ควร ไม่ได้อะไรกลับมา นี่คือสามสิบข้อ: ยี่สิบเอ็ดข้อเกี่ยวกับสิ่งที่ corpus นี้ไม่ได้ครอบคลุมจริง ๆ — streaming, rate limits, prompt caching, JSON schemas, agent loops, vector databases, prompt injection, image generation — และอีกเก้าข้อเกี่ยวกับปาเอยา passport และ refund policies เทียบกับ index เดิม:

top-1 cosine distance
in-domain queries ทั้ง 64mean 0.445, range 0.270 – 0.721
in-domain, top-1 ถูกจริงmean 0.370
in-domain, top-1 ผิดmean 0.452
out-of-domain ทั้ง 30mean 0.699, range 0.497 – 0.867

distributions แยกกัน และ overlap กัน query in-domain ที่แย่ที่สุดอยู่ไกลจากคำตอบของมัน (0.721) มากกว่า query out-of-domain ที่ ดีที่สุด อยู่จาก paragraph ที่ไม่เกี่ยวข้อง (0.497) ดังนั้นไม่มี threshold ใดทำให้ทั้งสองถูกพร้อมกัน Sweep มันบน gate จริง — เก็บไม่เกินสี่ chunks และเก็บเฉพาะชิ้นที่อยู่ใต้ cut:

thresholdin-domain answeredof which the answer was inout-of-domain answered
0.40017 / 6460 / 30
0.45038 / 64130 / 30
0.50050 / 64191 / 30
0.52552 / 64202 / 30
0.55055 / 64213 / 30
0.60060 / 64255 / 30
0.67562 / 642710 / 30
0.80064 / 642726 / 30
none64 / 642730 / 30

อ่าน column สุดท้ายว่าเป็น การ bluff ไม่มี threshold assistant จะสร้างคำตอบที่มั่นใจและอ้างอิงดีให้กับ "how do I renew my Spanish passport" จาก corpus เกี่ยวกับ backpropagation สามสิบครั้งจากสามสิบครั้ง ที่ 0.675 มันทำสิบครั้งจากสามสิบ ที่ 0.525 มันทำสองครั้ง และยอมแพ้กับคำถามสิบสองข้อที่มันตอบได้

trade-off นั้นเป็นการตัดสินใจเชิง product และปลายที่ถูกต้องขึ้นกับว่าคำตอบผิดทำให้คุณเสียอะไร สิ่งที่ ต่อรองไม่ได้ คือการมี column สุดท้ายอยู่จริง ถ้าคุณไม่เคยวัด retriever กับคำถามที่มันควรปฏิเสธ คุณไม่มี threshold — คุณมีแค่ตัวเลข

สองจากสิบ bluffs ที่ 0.675 แสดงสองวิธีที่สิ่งนี้พัง

the two shapes of a confident wrong retrievalTEXT
query: "how much does prompt caching save on a long conversation"
  [1] d=0.497  13-inference-optimization > Prefill and decode are two different machines
  [2] d=0.532  13-inference-optimization > The cache is also the bill

query: "what is the capital of france"
  [1] d=0.671  12-reasoning > The model does not think. It computes for longer.
      "…it is why 'think step by step' does nothing for what is the capital of France."

อันแรกคือ near miss: corpus อธิบาย KV cache อย่างละเอียด query ถามเรื่อง prompt cache คำที่ใช้คือคำชุดเดียวกัน และ 0.497 ใกล้กว่าการ retrieval แบบ in-domain ที่ถูกส่วนใหญ่ในทั้งการทดลอง embedding ไม่รู้ว่า caches สองแบบที่มีชื่อเหมือนกันเป็นเครื่องจักรคนละชนิด อันที่สองคือ literal match with no answer: corpus มีวลีตรงตัว "what is the capital of France" ใช้เป็นตัวอย่างของคำถามที่ไม่ต้องใช้ reasoning retriever ถูกแล้ว แต่คำตอบไม่ได้อยู่ตรงนั้น ระบบใดก็ตามที่อ่าน "ฉันเจอบางอย่างคล้ายกัน" เป็น "ฉันเจอคำตอบ" จะ assert Paris จากหลักฐานนั้น — หรือแย่กว่านั้น คือจะไม่ assert

model ไม่มีคณะทำงานแยกสำหรับ facts การสร้างประโยคจริงและการสร้างประโยคที่ดูน่าเชื่อคือ operation เดียวกัน — next-token prediction จากบทที่ 8 — และไม่มีอะไรใน operation นั้นทำเครื่องหมายว่าอันไหนคืออันไหน งานวิเคราะห์ปี 2025 ที่ reframe เรื่องนี้โต้แย้งว่า training และ evaluation pipeline ให้รางวัลกับการเดาอย่างจริงจัง: benchmarks ให้คะแนนด้วย binary accuracy และไม่ให้ credit กับ abstention ดังนั้น model ที่ตอบเสมอจะทำคะแนนสูงกว่า model ที่เหมือนกันทุกอย่างแต่พูดว่า "I don't know" เมื่อมันไม่รู้ และ post-training ก็ optimise ไปตามนั้น6 Hallucination ตามการอ่านแบบนี้ไม่ใช่ defect ลึกลับ มันคือสิ่งที่คุณได้เมื่อให้คะแนนข้อสอบ multiple-choice โดยไม่มี penalty สำหรับคำตอบผิด

ดูรูปทรงของมัน เมื่อถูกขอ papers แปดฉบับเกี่ยวกับ contrastive sentence embeddings พร้อม identifiers, Qwen2.5-0.5B-Instruct สร้างแปดบรรทัดใน format สมบูรณ์แบบ identifiers ทั้งแปด well-formed ทั้งแปด resolve ไปยัง papers จริงบน arXiv ศูนย์จากแปดตรงกับ paper ที่อ้าง

8 references, checked one by one against the arXiv APITEXT
claimed  arXiv:1907.06432 - Contrastive Sentence Embeddings for Text Retrieval
actual   A Neural Turing~Machine for Conditional Transition Graph Modeling

claimed  arXiv:1809.08669 - Contrastive Learning of Sentence Representations…
actual   Collapsing Superstring Conjecture

claimed  arXiv:1807.08669 - Contrastive Learning of Sentence Representations…
actual   Automatic Speech Recognition for Humanitarian Applications in Somali

นี่เป็น model ขนาดเล็กและ rate เป็นของมันเอง frontier model ประดิษฐ์น้อยกว่ามาก mechanism นี้ generalise และเป็นเหตุผลของกฎที่ตามมา validator ที่ตรวจว่า "identifier นี้มีอยู่ไหม" ผ่านทั้งแปด และผู้ใช้ที่คลิกอันหนึ่งจะไปถึงหน้าจริงจาก archive จริงโดยไม่มีทางรู้ว่า mapping ถูกประดิษฐ์ failure ไม่ได้อยู่ใน identifier หรือ format แต่อยู่ใน association — สิ่งที่ language model ผลิตจาก plausibility พอดี

ดังนั้น: model เขียน [1] และ [2] และไม่เคยเขียน link ตัวเลขอ้างถึง fragments ที่ server retrieve มา และ server — ซึ่งรู้แน่ชัดว่าแต่ละหมายเลขมาจากเอกสารไหนและ offsets ไหน — เป็นคนแนบ document, label และ URL ภายหลัง ไม่มีอะไรให้ model ประดิษฐ์ เพราะมันไม่เคยถูกขอให้สร้างสิ่งเดียวที่มันจะประดิษฐ์

prompt.tsTS
export function buildContext(question: string, hits: Scored[]) {
  const citations: Citation[] = hits.map((h, i) => ({
    index: i + 1,
    documentId: h.chunk.documentId,
    documentName: h.chunk.documentName,
    locatorLabel: label(h.chunk),
    fragment: `#char=${h.chunk.locator.flow.from},${h.chunk.locator.flow.to}`,  
    quote: h.chunk.content,          // the OWN content, never `text`
    cosineDistance: h.cosineDistance,
  }));
  const blocks = citations
    .map((c) => `[${c.index}] ${c.documentName} - ${c.locatorLabel}\n${c.quote}`)
    .join("\n\n");
  const prompt =
    `Answer using ONLY the numbered sources below. Cite every claim as [n].\n` +
    `If the sources do not contain the answer, say so and stop.\n\n` +
    `SOURCES\n${blocks}\n\nQUESTION\n${question}`;
  return { prompt, citations };
}

รันกับคำถามเปิดบท แล้ว chunks สี่ชิ้นกลายเป็น prompt 591-token และ table ที่ model ไม่เคยเห็น:

TEXT
[1] 04-classification  How many test examples do I need?      #char=28215,28701  d=0.594
[2] 04-classification  Three splits, and the leak…            #char=20329,20839  d=0.598
[3] 04-classification  How many test examples do I need?      #char=25873,26272  d=0.600
[4] 04-classification  How many test examples do I need?      #char=25554,25871  d=0.605

locator คือส่วนที่คนข้ามแล้วภายหลังเติมไม่ได้ #char=25873,26272 คือ range ใน canonical text ของเอกสาร; สำหรับ PDF equivalent คือ #page=12, สำหรับ audio หรือ video คือ #t=132.4,158.9, สำหรับ spreadsheet คือ sheet และ A1 range สองอย่างนั้นไม่ใช่สิ่งประดิษฐ์ — #page= คือ PDF Open Parameters และ #t= คือ W3C Media Fragments ที่ browser รองรับแบบ native บน video และ audio elements citation ที่ไม่มี locator คือชื่อเอกสาร และชื่อเอกสารไม่ใช่ citation; มันคือคำแนะนำให้ผู้ใช้ไปหาเอง

และเมื่อไม่มีอะไรผ่าน threshold pipeline จะไม่ไปถึง model เลย:

TEXT
NO ANSWER: nothing under cosine distance 0.675 for "what is the offside rule in football"
NO ANSWER: nothing under cosine distance 0.675 for "how do i renew my spanish passport"
NO ANSWER: nothing under cosine distance 0.675 for "how do i build an agent loop with tools"

นั่นคือ refusal ที่ถูกกว่าและเชื่อถือได้กว่าคำสั่งใด ๆ ใน system prompt เพราะมันคือการเปรียบเทียบระหว่างตัวเลขสองตัว ไม่ใช่คำขอถึงระบบ probabilistic

การวัดทุกอย่างในบทนี้ให้คะแนน retriever และไม่เคยขอให้ model เขียนคำตอบเลยสักครั้ง นี่ตั้งใจ และเป็นชิ้นที่ทีมส่วนใหญ่ข้าม

ระบบ RAG มี failure modes สองแบบที่ดูเหมือนกันจากภายนอก retriever ไม่เจอ passage; หรือมันเจอแล้วแต่ generator เมิน ขัดแย้ง หรือผสมกับสิ่งที่มันเชื่ออยู่แล้ว ถ้าคะแนนเฉพาะคำตอบสุดท้าย สองอย่างนี้แยกไม่ออก ดังนั้นคุณจะ tune prompts กับปัญหาที่อยู่ใน chunker ของคุณ Recall@k, MRR และ answer-destroyed count ไม่ต้องใช้ generation call เลย ราคาถูกพอจะรันทุก deploy และมันคือ harness จากบทที่ 15 ที่เปลี่ยน scoring function — request, deadline, concurrency และ tally เดิม บน question set คงที่แทน live conversation

รายงานพร้อม intervals เลขคณิตจากบทที่ 4 ใช้ได้เหมือนเดิม: ที่ 64 queries recall 0.5 มี 95 % Wilson interval ประมาณ ±0.12 ดังนั้น strategy ที่นำอีกตัวสี่ points ยังไม่ได้บอกอะไรคุณ ใช้ paired test เมื่อ strategies ทั้งสองตอบคำถามชุดเดียวกัน ซึ่งที่นี่เป็นอย่างนั้นเสมอ — มันคือสิ่งที่เปลี่ยน "E ดูดีกว่า A" เป็น p = 0.0015

และความซื่อสัตย์สุดท้าย: RAG ลด hallucination และไม่ได้เอามันออกไป การใส่ passage ที่ถูกต้องใน prompt ไม่ได้บังคับให้ model ใช้มัน และ literature พูดแบบนั้นมาตั้งแต่ paper ต้นฉบับ7 มีสองอย่างที่ทำให้แย่ลงใน production context ยาว degrade — model หา information ที่ต้นและท้าย prompt ยาวได้เชื่อถือกว่าตรงกลาง ดังนั้น chunks ยี่สิบชิ้นแทนที่จะเป็นสี่ชิ้นอาจลด accuracy พร้อมเพิ่มบิล ผลกระทบนี้วัดใน บทที่ 24 และ retrieval อาจ ถูก แต่ยังไม่พอ อย่างที่ caches สองแบบด้านบนแสดง SelfCheckGPT flag claims ที่ไม่รอดจาก resampling;8 Self-RAG ฝึก model ให้ emit retrieve-and-critique tokens ของตัวเอง;9 TruthfulQA ทำให้ failure mode นี้อ่านออกตั้งแต่แรก10 ไม่มีอันไหนปิดช่องว่างนี้ และระบบที่นำ retrieved text มาเสนอเป็น proof สับสนระหว่าง มีแหล่งอ้างอิง กับ จริง

ครึ่งหนึ่งของระบบที่รันก่อนมี query ใด ๆ

ลิงก์ไปยังส่วน: ครึ่งหนึ่งของระบบที่รันก่อนมี query ใด ๆ

retriever คือส่วนที่มองเห็นได้ของ pipeline ที่ failures ทั้งหมดเกิดก่อนหน้านั้น ในความมืด มีสามอย่างที่เกิดซ้ำ

Extraction คือจุดที่ content ตาย PDF ไม่ใช่ text; มันคือ drawing instructions layout สอง column interleave, tables กลายเป็น word soup, page headers ซ้ำเข้าไปในทุก chunk และหน้าที่ scan ไม่มี text เลยจนกว่า OCR จะให้มาบ้าง พร้อม confidence ทุกอย่างที่วัดด้านบน assume ว่า extractor ทำหน้าที่ของมันแล้ว; ใน production มันมักไม่ทำ และ symptom โผล่เป็น retrieval แย่ที่อยู่ห่างออกไปสามชั้น

index ถูกประทับตราด้วย model ที่สร้างมัน Embeddings จาก models สองตัวเทียบกันไม่ได้ — ไม่ใช่ "แม่นน้อยกว่า" แต่เทียบกันไม่ได้ เพราะมันเป็น points ใน spaces คนละอัน เปลี่ยน embedding model แล้ว vector ทุกตัวใน store เป็นขยะจนกว่าจะ rebuild ดังนั้น model name, dimension count, pipeline version และ extractor version ต้องถูกเขียนไว้ข้างเอกสารแต่ละฉบับตอน index หากไม่มีสิ่งเหล่านี้ ในวัน upgrade คุณจะบอกไม่ได้ว่าเอกสารไหน stale และเอกสารไหน current และ index ที่ migrate ไปครึ่งหนึ่งจะคืน nonsense ที่มั่นใจโดยไม่มี error ที่ไหนเลย

เอกสารที่พังหนึ่งฉบับต้องไม่ทำ folder พัง และ counters ต้องนับสิ่งที่เกิดขึ้นจริง เอกสารที่ extraction ล้มเหลวจบในสถานะ failed พร้อมเหตุผล มองเห็นได้และ retry ได้ ขณะที่อีกเก้าสิบเก้าฉบับยัง searchable; และจำนวน chunks ที่ indexed ถูกเขียนโดย server เมื่อทำเสร็จ ไม่ใช่ประกาศโดย client ตอน upload folder ที่รายงาน 400 fragments แต่มี 40 คือคำโกหกที่โผล่ให้เห็นเฉพาะในรูปของคำถามที่ตอบไม่ได้

ระบบในบทนี้ตอบคำถามที่คำตอบถูกเขียนไว้แล้ว มัน retrieve, rank, ปฏิเสธเมื่อทำไม่ได้ และ cite ว่ามันดูที่ไหน นั่นคือส่วนใหญ่ของสิ่งที่คนต้องการจาก assistant เหนือเอกสารของตัวเอง และมันถูกจำกัดในแบบเฉพาะอย่างหนึ่ง: retrieval คืนได้เฉพาะสิ่งที่มีใครบางคนเขียนไว้

จึงเหลืออีกครึ่งหนึ่ง บางสิ่งที่คุณอยากให้ model ทำไม่ใช่ fact ในเอกสารเลย — format ที่ต้องรักษาไว้, tone, taxonomy ที่มี labels สี่ร้อยอัน, วิธีตัดสินใจที่อยู่ในตัวอย่างเก่าหมื่นรายการและไม่อยู่ใน paragraph ไหนเลย Retrieval ส่งสิ่งเหล่านั้นไม่ได้ เพราะไม่มีอะไรให้ retrieve; prompt ที่ยาวขึ้นแค่จ่ายบิลของบทที่ 16 เพื่อ description ของ skill แทนที่จะเป็น skill

บทที่ 20 คือการตัดสินใจนั้น — fine-tune, retrieve หรือ prompt — และ finding ของมันคือการตัดสินใจนี้เป็นเศรษฐศาสตร์ก่อนเป็นเทคนิค: ทั้งสามถูกคิดราคา end to end บนคำถามเดียวกัน และ crossover คือ token count คำถามที่เปิดบทนั้นคือคำถามที่บทนี้ตอบไม่ได้ ไม่ใช่ คำตอบถูกเขียนไว้ที่ไหน แต่เป็น คุณทำอะไรเมื่อมันไม่เคยถูกเขียนไว้เลย


ทุกอย่างที่วัดในบทนี้ใช้ corpus หนึ่งชุดและ instrument หนึ่งตัว และทั้งคู่ reproducible corpus คือบทที่ 1 ถึง 13 ของคอร์สนี้ตามสภาพเมื่อ 7 กันยายน 2026 — 13 เอกสาร, 359,067 ตัวอักษร, 127 sections, ตัด front matter และ bibliography ออกแล้ว บทเหล่านั้นยังถูกแก้ไขอยู่ ดังนั้นถ้าใช้ rule เดียวกันวันนี้จะนับตัวอักษรเพิ่มขึ้นอีกสองสามพัน: section count ไม่เปลี่ยนและ conclusion ทุกอย่างด้านล่างก็ไม่เปลี่ยน แต่ character total เป็น snapshot และถูก label ไว้เช่นนั้น ground truth คือคำถาม 32 ข้อ แต่ละข้อจับคู่กับประโยค verbatim ที่เกิดขึ้นครั้งเดียวเท่านั้นใน corpus และไม่เคยเป็น section heading ถูกถามสองสำนวนเป็น 64 queries Retrieval embeddings คือ sentence-transformers/all-MiniLM-L6-v2 (384 dimensions, mean-pooled, L2-normalised, 256-token window); reranking คือ cross-encoder/ms-marco-MiniLM-L-6-v2 บน top 25; ตัวอย่าง generation คือ Qwen/Qwen2.5-0.5B-Instruct พร้อม greedy decoding timings ทั้งหมดเป็น single-threaded CPU ไม่มี paid API ถูกเรียกเพื่อผลิตบทนี้ ซึ่งเป็นเหตุผลด้วยว่าทุก latency ที่นี่เป็น local latency และถูก label ไว้แบบนั้น

chunker ที่แสดงใน TypeScript คือ chunker ที่ถูกวัด: Python instrument ที่ implement rule เดียวกันและ ts/chunk.ts ถูกเทียบ chunk ต่อ chunk ทั่วทั้ง corpus และตรงกันทั้ง 940 chunks ทั้ง texts และ offsets Intervals คือ Wilson ที่ 95 %; paired comparisons คือ two-sided exact sign tests บน discordant pairs

identifiers ทั้งสิบสี่รายการที่ cite ด้านบนถูก resolve กับ arXiv API และตรวจ title ทีละรายการเมื่อ 7 กันยายน 2026 — ซึ่งเมื่อดูจากแปดรายการที่ไม่ใช่แล้ว ก็ดูเหมือนเป็นอย่างน้อยที่สุดที่บทนี้ควรทำ

  1. Robertson, S. and Zaragoza, H. The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval 3(4), pp. 333–389 (2009). แหล่งที่มาของ saturation function และ constants สองตัวที่ใช้ด้านบน และเป็นที่สำหรับอ่านว่าทำไม bb จึงมีอยู่ตั้งแต่แรก

  2. Cormack, G. V., Clarke, C. L. A. and Büttcher, S. Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods. SIGIR 2009. k=60k = 60 เป็นของพวกเขา และประเด็นของ method นี้คือมันไม่ต้องการ calibration ระหว่าง score scales ที่กำลัง fuse

  3. Khattab, O. and Zaharia, M. ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT. arXiv:2004.12832 (2020). จุดกึ่งกลางระหว่าง dot product และ cross-encoder Reimers, N. and Gurevych, I., Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks, arXiv:1908.10084 (2019), คือ bi-encoder ที่ index ของบทนี้สร้างขึ้นและถูกวัดในบทที่ 8

  4. Malkov, Yu. A. and Yashunin, D. A. Efficient and Robust Approximate Nearest Neighbor Search using Hierarchical Navigable Small World Graphs. arXiv:1603.09320 (2016). graph index ที่อยู่เบื้องหลัง vector databases ส่วนใหญ่ที่ขายกันตอนนี้

  5. Johnson, J., Douze, M. and Jégou, H. Billion-scale Similarity Search with GPUs. arXiv:1702.08734 (2017). FAISS และ reference implementation ของ IVF ที่วัดใน box ด้านบน

  6. Kalai, A. T., Nachum, O., Vempala, S. S. and Zhang, E. Why Language Models Hallucinate. arXiv:2509.04664 (2025). argument ว่า hallucination ถูกผลิตโดยการให้คะแนน binary-accuracy ที่ไม่เคยให้รางวัล abstention และดังนั้นจึงเป็นปัญหา evaluation ก่อนเป็นปัญหา modelling

  7. Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goyal, N., Küttler, H., Lewis, M., Yih, W., Rocktäschel, T., Riedel, S. and Kiela, D. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. arXiv:2005.11401 (2020). paper ที่ตั้งชื่อ pattern นี้และเป็น paper ที่ควรอ่านเพื่อรู้ว่ามันแก้และไม่แก้อะไร Guu et al., REALM: Retrieval-Augmented Language Model Pre-Training, arXiv:2002.08909 (2020), คืองานร่วมสมัยที่ฝึก retriever ร่วมกับ model แทนที่จะ bolt on; Karpukhin et al., Dense Passage Retrieval for Open-Domain Question Answering, arXiv:2004.04906 (2020), คือที่มาของ two-encoder dense retriever ที่ใช้ตลอดบทนี้; และ Izacard and Grave, Leveraging Passage Retrieval with Generative Models for Open Domain Question Answering, arXiv:2007.01282 (2020), คือ fusion-in-decoder arrangement สำหรับป้อน passages จำนวนมากให้ generator ตัวเดียว Gao et al., Retrieval-Augmented Generation for Large Language Models: A Survey, arXiv:2312.10997 (2023), คือแผนที่ของทุกอย่างที่ตามมา รวมถึง HyDE (Gao et al., Precise Zero-Shot Dense Retrieval without Relevance Labels, arXiv:2212.10496, 2022), ซึ่ง embed คำตอบสมมติแทนคำถาม

  8. Manakul, P., Liusie, A. and Gales, M. J. F. SelfCheckGPT: Zero-Resource Black-Box Hallucination Detection for Generative Large Language Models. arXiv:2303.08896 (2023). การตรวจจับด้วย resampling โดยไม่ต้องเข้าถึง internals ของ model และไม่มี external knowledge base

  9. Asai, A., Wu, Z., Wang, Y., Sil, A. and Hajishirzi, H. Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection. arXiv:2310.11511 (2023). ฝึก model ให้ตัดสินใจว่าเมื่อไรควร retrieve แทนที่จะ retrieve ทุก turn

  10. Lin, S., Hilton, J. and Evans, O. TruthfulQA: Measuring How Models Mimic Human Falsehoods. arXiv:2109.07958 (2021). benchmark ที่สร้างจากคำถามซึ่งคำตอบที่น่าเชื่อกับคำตอบจริงต่างกัน ซึ่งคือความยากทั้งหมดในประโยคเดียว


สร้างโดย

David Vicente Campos

ผู้ก่อตั้ง NeuraLIA Labs และผู้ร่วมก่อตั้ง MyRealFood

ผมเป็นวิศวกรคอมพิวเตอร์ที่จบจากมหาวิทยาลัยเลออน ผมร่วมก่อตั้ง MyRealFood ที่ที่ผมในฐานะ CTO ได้สร้างแอปซึ่งผู้คนหลายล้านคนใช้เพื่อกินให้ดีขึ้น และผมก่อตั้ง NeuraLIA Labs ที่ที่ผมสร้างผลิตภัณฑ์ AI ที่นี่ผมเขียนถึงสิ่งที่ผมต้องทำความเข้าใจระหว่างทาง ในแบบที่ผมเคยหวังว่าจะมีใครสักคนอธิบายให้ผมฟัง

เพิ่มเติมเกี่ยวกับผู้เขียน

เผยแพร่โดย NeuraLIA Labs

รับโพสต์ใหม่ในกล่องจดหมาย

ข่าว AI คู่มือ และอัปเดตผลิตภัณฑ์ — อีเมลสั้น ๆ เมื่อเรามีสิ่งที่คุ้มเวลาของคุณ

ชอบแบบข้อความมากกว่าไหม รับเนื้อหาเดียวกันได้ที่นี่:คอมมูนิตี้ WhatsApp (เปิดในแท็บใหม่)ช่อง Telegram (เปิดในแท็บใหม่)

ดัชนีคอร์ส

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jevอ่าน 5 นาที

โมเดล AI Jev สร้างมาเพื่อการตัดสินใจ ไม่ใช่การเขียนความเรียง

Jev ของ TypeSafe AI กำลังได้รับความสนใจ เพราะมองความฉลาดของซอฟต์แวร์เป็นปัญหาความน่าจะเป็น: เลือกกิ่งที่ถูกต้อง แนบความมั่นใจ และหลีกเลี่ยงการจ่ายเงินให้ LLM เขียนข้อความเมื่อโค้ดต้องการการตัดสินใจ

Abstract legal research workspace with documents, search nodes and governance controls.
openaiอ่าน 4 นาที

Astra for Law ของ OpenAI คือระบบ AI ด้านกฎหมาย ไม่ใช่โมเดลใหม่

การเปิดตัวด้านกฎหมายของ OpenAI ไม่ได้เน้นโมเดลฐานรากใหม่เท่ากับระบบที่ล้อมรอบโมเดลนั้น: การค้นคืนเฉพาะโดเมน เครื่องมือที่เชื่อถือได้ สิทธิ์ เบนช์มาร์ก และเส้นทางการตรวจทาน

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineeringอ่าน 4 นาที

วิศวกรรมบริบทสำหรับเอเจนต์ AI ที่ทำงานระยะยาว

เอเจนต์ที่ทำงานต่อเนื่องไม่ได้ล้มเหลวเพียงเพราะหน้าต่างบริบทเล็กเกินไป แต่ล้มเหลวเมื่อไฟล์ ผลลัพธ์จากเครื่องมือ และประวัติที่ค้างเก่าบดบังงานที่เอเจนต์ควรทำให้เสร็จ

พร้อมให้ LIA เลือกโมเดลให้แล้วหรือยัง?

สร้างงานด้วยโมเดล AI ทุกตัวในที่เดียว เริ่มฟรีวันนี้