Tech Blog

ป้อนประวัติการสนทนาของ Copilot Chat และ Claude Code ลงใน ChromaDB และค้นหา "ตัวตนในอดีต" ของคุณ

GitHub Copilot Claude Code ChromaDB Ollama RAG AIアシスタント Python MCP

🔗 สารบัญซีรีส์: บทความนี้เป็น ฉบับการใช้งาน (2) ของซีรีส์ บันทึกการปฏิบัติงานของผู้ช่วย AI - บันทึกการปฏิบัติสำหรับการเพิ่มรหัส Copilot / Claude ในฐานะคู่หูของคุณ

สิ่งที่คุณสามารถเรียนรู้ได้จากบทความนี้

  • ตำแหน่งและโครงสร้าง ของบันทึกการสนทนา (JSONL) ที่ถูกบันทึกโดยอัตโนมัติโดย VSCode Copilot Chat และ Claude Code
  • ความแตกต่างในรูปแบบ JSONL แต่ละรูปแบบและ การออกแบบให้รองรับด้วยอินเทอร์เฟซทั่วไป
  • วิธีค้นหา “อดีตที่ผ่านมา” ในภาษาธรรมชาติโดยการใช้เวกเตอร์ด้วย ChromaDB + Ollama
  • ขั้นตอนในการส่งโดยอัตโนมัติเมื่อสิ้นสุดเซสชันโดยใช้ Stop hook ของ Claude Code
  • เอฟเฟกต์และประเด็นที่ควรทราบหลังจากสะสมเซสชันหลายสิบครั้ง

กลุ่มเป้าหมาย

  • ผู้ที่ต้องการ ใช้การสนทนากับผู้ช่วย AI เป็นความทรงจำถาวร
  • ผู้ที่อาศัยบันทึกการค้นหาทุกครั้งเพื่อถามว่า “ตอนนั้นแก้ปัญหายังไง
  • ผู้ที่ต้องการเพิ่ม ฟังก์ชันนำเข้าบันทึกการสนทนา ให้กับ RAG ของตนเอง
  • ผู้ที่ใช้ VSCode Copilot Chat / Claude Code หรือทั้งสองอย่าง

สภาพแวดล้อมการทำงาน

รายการเวอร์ชั่น
ระบบปฏิบัติการวินโดวส์ 11
หลาม3.13 (venv)
ChromaDBโหมดถาวร (PersistentClient)
การฝังโอลมะ nomic-embed-text (768 มิติ)
VSCode Copilot Chatบันทึกการถอดเสียง JSONL อัตโนมัติไปที่ workspaceStorage
รหัสคลอดบันทึกเซสชัน JSONL โดยอัตโนมัติไปที่ ~/.claude/projects/

1. บทนำ — “อะไรคือวิธีแก้ไขปัญหานั้น”ช่วงเวลาเช่นนี้กลายเป็นเรื่องปกติมากขึ้นเมื่อคุณทำงานร่วมกับผู้ช่วย AI ทุกวัน

วิธีแก้ปัญหาการเข้ารหัสอักขระของ PowerShell ที่ Copilot สอนฉันตอนนั้นคืออะไร

สัปดาห์ที่แล้ว ฉันได้พูดคุยกับ Claude Code และตัดสินใจเกี่ยวกับ “การออกแบบบายพาสการรับรองความถูกต้องโดยใช้โปรไฟล์ dev” ข้อสรุปคืออะไร

ฉันรู้สึกเหมือนฉันกำลังจะก้าวเข้าสู่กับดักที่ฉันเหยียบเมื่อเดือนที่แล้วอีกครั้ง…

ควรบันทึกการสนทนาไว้ที่ใดที่หนึ่งเป็นไฟล์ JSONL แต่ชื่อไฟล์คือ UUID และเนื้อหาเป็น JSON หลายแสนบรรทัด เป็นการยากที่จะค้นหาโดยใช้ grep และความปรารถนาเดิมคือ “ดึงวิธีแก้ปัญหาในอดีตโดยใช้ภาษาธรรมชาติ”

วันหนึ่งฉันก็ตระหนักได้

**บันทึกการสนทนาเป็น “เนื้อหาที่ดีที่สุดของ RAG” ที่สร้างขึ้นทุกวันไม่ใช่หรือ? **

บล็อกทางเทคนิค เอกสารการออกแบบ คำตอบ Stack Overflow มี “ความรู้ของผู้อื่น” มากมาย แต่วิธีแก้ปัญหาเดียวที่คุณจะพบในบริบทของคุณเอง ด้วยคำพูดของคุณเอง และสำหรับปัญหาของคุณเองนั้นอยู่ในบันทึกการสนทนาของคุณเอง หากคุณใส่สิ่งนี้ลงใน RAG คุณสามารถสร้างเครื่องมือค้นหาที่ช่วยให้คุณสามารถค้นหา “ตัวตนในอดีต” ของคุณได้อย่างแท้จริง

บทความนี้จะอธิบายวิธีการสร้างกลไกนี้สำหรับทั้ง Copilot Chat และ Claude Code


2. การถอดเสียงสองประเภท

ขั้นแรก จัดระเบียบว่าแต่ละไฟล์จะบันทึกที่ไหนและในรูปแบบใด

2.1 VSCode Copilot Chat

บันทึกไปที่:

C:\Users\<ผู้ใช้>\AppData\Roaming\Code\User\workspaceStorage\<workspace-hash>\GitHub.copilot-chat\transcripts\<uuid>.jsonl
````มีไดเร็กทอรีที่แฮชสำหรับแต่ละพื้นที่ทำงาน (โฟลเดอร์ที่เปิดใน VSCode) และโฟลเดอร์ **`transcripts/`** ถูกขุดในไดเร็กทอรีนั้น และ JSONL ที่มีชื่อ UUID จะถูกซ้อนกัน

โครงสร้าง JSONL (ประยุกต์):

```jsonl
{"type": user.message "data": {"content": วิธีเขียน Virtual Threads ใน Java 21?"}, "timestamp": "..."}
{"type":assistant.message"", "data":{"content":`Thread.ofVirtual().start(() -> { ... })`..."},"การประทับเวลา":..."}
{"type":assistant.turn_end","data":{...}}

1 แถว = 1 กิจกรรม ข้อความผู้ใช้/ผู้ช่วยสามารถระบุได้ด้วยฟิลด์ type และ data.content มีข้อความเป็นสตริง เรียบง่าย.

2.2 รหัสคลอดด์

บันทึกไปที่:

C:\Users\<ผู้ใช้>\.claude\projects\<project-slug>\<session-id>.jsonl

มีโฟลเดอร์สำหรับแต่ละโปรเจ็กต์ (กระสุนที่แปลงจากเส้นทางแบบเต็มของที่เก็บ) และในโฟลเดอร์นั้นจะมี JSONL พร้อมชื่อ ID เซสชัน

โครงสร้าง JSONL (ประยุกต์):```jsonl {“type”: “user”, “ข้อความ”:{“role”:“user”, “content”:[{“type”: “ข้อความ”, “ข้อความ”: ”}]}} {“type”:assistant”,“message”:{“role”:assistant""content”:[{“type”:text""text"":” กรอกtype”การคิด” “การคิด” ”…” กรอกtype:tool_use”,“name”:Read""input”:{…}}]}} {“type”:การเข้าคิว”,“การทำงาน""เข้าคิว”,…} {“ประเภท”:ไฟล์แนบ”,…} {“type”:file-history-snapshot”,…}


**อันนี้ซับซ้อนนิดหน่อย**:

- `type` ฟิลด์คือ `"user"` / `"assistant"` แต่ **เนื้อหาซ้อนอยู่ในอาร์เรย์ของ `message.content`**
- แต่ละองค์ประกอบของอาร์เรย์เป็น **บล็อกเนื้อหา** และ `type` แบ่งออกเป็น `"text"` / `"thinking"` / `"tool_use"` เป็นต้น
- ข้อความของผู้ใช้มี **แท็กอัตโนมัติ** เช่น `<system-reminder>` `<ide_opened_file>`
- **เหตุการณ์ Meta** เช่น `queue-operation` `attachment` `file-history-snapshot` ก็อยู่ใน JSONL เดียวกันเช่นกัน

กล่าวโดยสรุป **Copilot Chat เป็นรูปแบบง่ายๆ โดยที่ "1 ข้อความ = 1 ข้อความ" และ Claude Code เป็นรูปแบบที่มีโครงสร้างโดยที่ "อาร์เรย์บล็อกเนื้อหา + เหตุการณ์เมตาผสมกัน"** แม้จะเรียกว่า "ประวัติการสนทนา" เดียวกัน แต่รูปแบบก็แตกต่างกัน

---

## 3. ความท้าทายและการออกแบบทั่วไป

ฉันต้องการจัดการทั้งสองอย่างในอินเทอร์เฟซเดียว ดังนั้นฉันจึงจัดระเบียบมันแบบนี้

### ขั้นตอนการประมวลผลทั่วไป````
[ไฟล์ JSONL]
   ↓ การแยกวิเคราะห์ (ขึ้นอยู่กับรูปแบบ)
[คู่ข้อความผู้ใช้ / ผู้ช่วย (= รอบ)]
   ↓ การแบ่งส่วน (ทั่วไป)
[id/ข้อความ/ข้อมูลเมตาที่ตั้งค่าไว้สำหรับ ChromaDB]
   ↓ upsert (ทั่วไป)
[คอลเลกชัน ChromaDB]

การออกแบบเป็นเช่นนั้นเฉพาะส่วนที่ขึ้นอยู่กับรูปแบบ (การแยกวิเคราะห์) เท่านั้นที่จะถูกนำไปใช้แยกกัน และทุกอย่างหลังจากการแยกชิ้นส่วนจะได้รับมาตรฐานอย่างสมบูรณ์ โดยเฉพาะ:

  • ingest_conversation.py — Parser สำหรับ Copilot Chat + เรียกกระบวนการป้อนข้อมูลทั่วไป
  • ingest_claude_code.py — Parser สำหรับ Claude Code + เรียกกระบวนการอินพุตทั่วไปแบบเดียวกัน
  • src/db/store.py’s ingest_chunks() — ChromaDB ที่เติมตรรกะที่เรียกโดยทั้งคู่

กฎการแบ่งส่วน

หน่วยการสนทนาตามธรรมชาติคือคู่ “ผู้ใช้ 1 ตา + ผู้ช่วย 1 คำตอบ” สิ่งนี้เรียกว่า turns

ขีดจำกัดสูงสุดของการฝังในครั้งเดียวจะอยู่ที่ MAX_CHUNK_CHARS = 1200 เป็นโซนปลอดภัยสำหรับ nomic-embed-text และเราตัดสินใจรวม 3 รอบเป็น 1 ชิ้น

TURNS_PER_CHUNK = 3
MAX_CHUNK_CHARS = 1200def Turn_to_chunks (เปลี่ยน: รายการ [dict], session_id: str, date_str: str) -> รายการ [dict]:
    ชิ้น = []
    สำหรับฉันอยู่ในช่วง (0, เลน (เปลี่ยน), TURNS_PER_CHUNK):
        กลุ่ม = รอบ[i:i + TURNS_PER_CHUNK]
        เส้น = []
        สำหรับ t ในกลุ่ม:
            lines.append(f"[ผู้ใช้] {t['ผู้ใช้']}")
            lines.append(f"[AI] {t['ผู้ช่วย']}")
            เส้น.ผนวก("")
        text = "\n".join(เส้น)
        ถ้า len (ข้อความ) > MAX_CHUNK_CHARS:
            ข้อความ = ข้อความ[:MAX_CHUNK_CHARS]
        chunk_index = i // TURNS_PER_CHUNK
        ชิ้น. ผนวก ({
            "id": f"{session_id}::chunk_{chunk_index}",
            "ข้อความ": ข้อความ
            "ข้อมูลเมตา": {
                "source_type": "การสนทนา",
                "source_file": session_id,
                "title": f"เซสชันการสนทนา {date_str} (ส่วน {chunk_index + 1})",
                "วันที่": date_str,
                "tags": "conversation,copilot", # หรือ "conversation,claude-code"
                "chunk_index": chunk_index,
            },
        })
    คืนชิ้น
````มีเพียง `tags` ของข้อมูลเมตาเท่านั้นที่ถูกแยกความแตกต่างด้วย "`copilot`" หรือ "`claude-code`" และที่เหลือก็เหมือนกันโดยสิ้นเชิง ตอนนี้ เมื่อคุณ `rag search` การสนทนากับ Copilot และ Claude Code จะปรากฏในผลการค้นหาโดยไม่คำนึงถึง

---

## 4. รวม Copilot Chat

ตัวแยกวิเคราะห์นี้เป็นแกนหลักของ Copilot Chat (`ingest_conversation.py`)

```หลาม
def parse_conversation(jsonl_path: เส้นทาง) -> รายการ [dict]:
    """แยกคู่ข้อความผู้ใช้/AI ออกจาก JSONL"""
    รอบ = []
    user_msg = ไม่มี

    ด้วย open(jsonl_path, encoding="utf-8") เป็น f:
        สำหรับบรรทัดใน f:
            เส้น = line.strip()
            ถ้าไม่ใช่บรรทัด:
                ดำเนินการต่อ
            ลอง:
                บันทึก = json.loads (บรรทัด)
            ยกเว้น json.JSONDecodeError:
                ดำเนินการต่อ

            rtype = record.get("ประเภท", "")
            การประทับเวลา = record.get("การประทับเวลา", "")ถ้า rtype == "user.message":
                เนื้อหา = บันทึก ["ข้อมูล"].get ("เนื้อหา", "").แถบ ()
                หากเนื้อหา:
                    user_msg = {"เนื้อหา": เนื้อหา, "การประทับเวลา": การประทับเวลา}

            elif rtype == "ผู้ช่วยข้อความ":
                เนื้อหา = บันทึก ["ข้อมูล"].get ("เนื้อหา", "").แถบ ()
                ถ้าเนื้อหาและ user_msg:
                    เปลี่ยน.ผนวก({
                        "ผู้ใช้": user_msg["เนื้อหา"],
                        "ผู้ช่วย": เนื้อหา
                        "ประทับเวลา": user_msg["ประทับเวลา"],
                    })
                    user_msg = ไม่มี

    กลับมา

เพียงเลือกโฟลว์ user.message → Assistant.message ตามลำดับและโหลดลงใน turns type ความเรียบง่ายของ parser สามารถใส่ได้ 30 บรรทัด ฉันชื่นชมความซื่อสัตย์ในส่วนของ Copilot Chat

ตรวจจับการถอดเสียงล่าสุดโดยอัตโนมัติได้อย่างง่ายดาย:

TRANSCRIPT_BASE = เส้นทาง (r "C:\Users\<ผู้ใช้>\AppData\Roaming\Code\User\workspaceStorage")def find_latest_transcript() -> เส้นทาง:
    """ตรวจหาข้อความถอดเสียงล่าสุดโดยอัตโนมัติในทุกพื้นที่ทำงาน"""
    ผู้สมัคร = []
    ถ้า TRANSCRIPT_BASE.exists():
        สำหรับ jsonl ใน TRANSCRIPT_BASE.rglob("*.jsonl"):
            ถ้า "การถอดเสียง" ใน jsonl.parts:
                ผู้สมัครผนวก (jsonl)
    หากไม่ใช่ผู้สมัคร:
        เพิ่ม FileNotFoundError(...)
    กลับสูงสุด (ผู้สมัคร, key=lambda p: p.stat().st_mtime)

ค้นหาพื้นที่ทำงานทั้งหมดซ้ำด้วย rglob และส่งคืน JSONL ล่าสุดด้วย mtime ฉันเผยแพร่สิ่งนี้ให้กับผู้ใช้ผ่านคำสั่ง rag conv

# แทรกการสนทนาล่าสุดด้วยคำสั่งเดียว
เศษผ้า Conv

# ป้อนข้อมูลโดยระบุเซสชันที่ผ่านมาโดยเฉพาะ
rag conv "C:\path\to\special.jsonl"

5. ผสมผสานรหัส Claude — ต่อสู้กับเสียงรบกวน

นี่คือสิ่งที่น่าสนใจเล็กน้อย Claude Code ผสมกับเหตุการณ์เมตามากมายนอกเหนือจากการสนทนา ดังนั้นจึงจำเป็นต้องมีตัวกรอง

5.1 สิ่งของที่ต้องนำออก/ทิ้ง| type | บางสิ่งบางอย่าง | นำเข้า |

|---|---|---| | user | ข้อความผู้ใช้ | ✅ อย่างไรก็ตาม เฉพาะบล็อกข้อความเท่านั้นที่ถูกแยกออกจากอาร์เรย์ content | | assistant | การตอบสนองของ AI | ✅ ในทำนองเดียวกัน ให้แยกเฉพาะบล็อกข้อความ | | queue-operation | การดำเนินการคิว (เหตุการณ์การประมวลผลภายใน) | ❌ | | attachment | ข้อมูลไฟล์แนบ | ❌ | | file-history-snapshot | ประวัติไฟล์ | ❌ | | ai-title | การกำหนดชื่อเซสชัน | ❌ | | last-prompt | เมตาดาต้าพร้อมท์สุดท้าย | ❌ |

5.2 กำลังประมวลผลอาร์เรย์บล็อกเนื้อหา

User/AI ทั้งข้อความ message.content เป็นอาร์เรย์และมีประเภทบล็อกผสม```หลาม def _extract_text_blocks (เนื้อหา) -> str: """เชื่อมต่อและส่งคืนเฉพาะบล็อกข้อความจาก message.content""" ถ้าเป็นอินสแตนซ์ (เนื้อหา, str): กลับ content.strip() หากไม่ใช่อินสแตนซ์ (เนื้อหา รายการ): กลับ "" ชิ้นส่วน = [] สำหรับการบล็อกเนื้อหา: หากไม่ใช่อินสแตนซ์ (บล็อก, dict): ดำเนินการต่อ ถ้า block.get(“type”) == “text”: txt = block.get(“ข้อความ”, "") ถ้า isinstance(txt, str) และ txt.strip(): ชิ้นส่วนผนวก (txt.strip()) กลับ “\n\n”.join(บางส่วน)


**บล็อกอื่นที่ไม่ใช่ `text` (`thinking` / `tool_use` / `tool_result`) จะถูกข้ามไป** `thinking` เป็นการคิดภายในของ AI ดังนั้นจึงเป็นการดีกว่าที่จะไม่รวมไว้ในโฟลว์ของการสนทนา เนื่องจากเสียงรบกวนในการค้นหาจะลดลง และ `tool_use` เป็น JSON ที่มีโครงสร้าง ดังนั้นจึงไม่เหมาะสำหรับการค้นหาข้อความ

### 5.3 การลบแท็กที่แทรกอัตโนมัติ

ข้อความของผู้ใช้ผสมกับแท็กที่ Claude Code แทรกโดยอัตโนมัติ```html
<system-reminder>เครื่องมือ TodoWrite ไม่ได้ถูกใช้เมื่อเร็วๆ นี้...</system-reminder>
<ide_opened_file>ผู้ใช้เปิดไฟล์ ...</ide_opened_file>
<คำสั่งข้อความ>...</คำสั่งข้อความ>

หากคุณป้อน RAG โดยมีจำนวนที่เหลือ เมื่อคุณค้นหา “สิ่งที่พูดในการสนทนานั้น” เนื้อหาแท็ก (เสียงรบกวนทั่วไป) จะถูกโจมตี งั้นฉันจะเอามันออก

NOISE_TAG_RE = คอมไพล์ใหม่ (
    r"<(system-reminder|ide_opened_file|ide_selection|command-message|command-name|command-args|local-command-stdout)>.*?</\1>",
    อีกครั้ง DOTALL,
)

def _strip_noise (ข้อความ: str) -> str:
    ถ้าไม่ใช่ข้อความ:
        กลับ ""
    ทำความสะอาด = NOISE_TAG_RE.sub ("", ข้อความ)
    cleaned = re.sub(r"\n{3,}", "\n\n", cleaned) # ล้างบรรทัดว่าง
    กลับ cleaned.strip()

DOTALL ตัดข้ามตัวแบ่งบรรทัดพร้อมกัน เพียงทำเช่นนี้ ฉันก็พบว่ามีความแม่นยำในการค้นหาเพิ่มขึ้น 30%

5.4 ตัวพาร์เซอร์

เมื่อรวมสิ่งเหล่านี้เข้าด้วยกัน parser สำหรับ Claude Code จะมีลักษณะดังนี้:```หลาม def parse_conversation(jsonl_path: เส้นทาง) -> รายการ [dict]: เปลี่ยน: รายการ[dict] = [] pending_user: dict | ไม่มี = ไม่มี

ด้วย open(jsonl_path, encoding="utf-8") เป็น f:
    สำหรับบรรทัดใน f:
        เส้น = line.strip()
        ถ้าไม่ใช่บรรทัด:
            ดำเนินการต่อ
        ลอง:
            บันทึก = json.loads (บรรทัด)
        ยกเว้น json.JSONDecodeError:
            ดำเนินการต่อ

        rtype = record.get("ประเภท")
        msg = record.get("ข้อความ", {}) หรือ {}

        ถ้า rtype == "ผู้ใช้":
            content = msg.get("content") if isinstance(msg, dict) else ไม่มี
            ข้อความ = _strip_noise(_extract_text_blocks(เนื้อหา))
            ถ้าข้อความ:
                pending_user = {"เนื้อหา": ข้อความ "ประทับเวลา": record.get("ประทับเวลา", "")}elif rtype == "ผู้ช่วย":
            content = msg.get("content") if isinstance(msg, dict) else ไม่มี
            text = _extract_text_blocks(content) # การตอบสนองของ AI ไม่จำเป็นต้องลดสัญญาณรบกวน
            ถ้าข้อความและ pending_user:
                เปลี่ยน.ผนวก({
                    "ผู้ใช้": pending_user["เนื้อหา"],
                    "ผู้ช่วย": ข้อความ
                    "ประทับเวลา": pending_user["ประทับเวลา"],
                })
                pending_user = ไม่มี

กลับมา

มันเป็นตรรกะ "การซ้อนคู่" แบบเดียวกับสำหรับ Copilot แต่ประเด็นสำคัญคือ **การแยกบล็อกและการกำจัดสัญญาณรบกวนจะถูกประกบกันในขั้นตอนก่อนหน้า**

---

## 6. รหัส Claude จะถูกป้อนโดยอัตโนมัติด้วย Stop hook

สำหรับ Copilot Chat นั้น `rag conv` จะถูกดำเนินการด้วยตนเอง แต่ Claude Code มีกลไกที่เรียกว่า **Stop hook** คำสั่งใดๆ ก็สามารถดำเนินการได้ทันทีที่เซสชันสิ้นสุดลง ซึ่งจะทำให้คุณสามารถ **ทำให้เป็นอัตโนมัติ** ได้อย่างเต็มที่

เขียนสิ่งนี้ลงใน `~/.claude/settings.json````.json
{
  "ตะขอ": {
    "หยุด": [
      {
        "ตัวจับคู่": "",
        "ตะขอ": [
          {
            "type": "คำสั่ง",
            "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\Users\\<user>\\git\\my-rag-brain\\scripts\\claude_code_stop_hook.ps1\""
          }
        ]
      }
    ]
  }
}

สคริปต์ PowerShell ที่เรียกว่ามีลักษณะเช่นนี้

# อ่าน JSON จาก stdin แยก transcript_path และเริ่มนำเข้า
$stdin = [คอนโซล]::In.ReadToEnd()
$เพย์โหลด = $stdin | แปลงจาก-Json
$transcript = $payload.transcript_path

ถ้า (-ไม่ใช่ (Test-Path $transcript)) { return }# เริ่มต้นในพื้นหลัง (อย่าปิดกั้น Stop hook เป็นเวลานาน)
$env:PYTHONIOENCODING = 'utf-8'
เริ่มกระบวนการ -FilePath "<venv>\python.exe" `
    -ArgumentList @("<my-rag-brain>\src\pipeline\ingest_claude_code.py", $transcript) `
    -WindowStyle ซ่อนอยู่ `
    -RedirectStandardOutput $stdoutLog `
    -RedirectStandardError $stderrLog

สองจุด:

  1. อ่าน transcript_path จาก stdin — Claude Code ส่งให้เราใน JSON
  2. การเริ่มต้นพื้นหลังด้วย Start-Process — เพื่อหลีกเลี่ยงการบล็อก Stop hook เป็นเวลานาน

ตอนนี้ เมื่อคุณปิด Claude Code เซสชันจะถูกแทรกลงใน ChromaDB โดยอัตโนมัติ ไม่มีอะไรที่ต้องทำด้วยตนเอง


7. มีอะไรเปลี่ยนแปลงบ้าง? ความรู้สึกปลอดภัยที่ช่วยให้คุณสามารถค้นหา “ตัวตนในอดีต” ของคุณ

หลังจากผ่านไปหลายสิบครั้ง เหตุการณ์เช่นนี้ก็เริ่มเกิดขึ้น

7.1 “คุณแก้ปัญหานั้นได้อย่างไร” ปัญหาหายไป

เมื่อฉันพบข้อผิดพลาดในการเข้ารหัสอักขระใน PowerShell:

ค้นหาเศษผ้า "อักขระที่อ่านไม่ออกของ PowerShell UTF-8"

จากนั้นโซลูชัน 5 อันดับแรกที่สร้างขึ้นในอดีตเมื่อพูดถึงปัญหาเดียวกันกับ Copilot และ Claude Code จะถูกส่งคืน คุณสามารถจำได้ทันทีว่า “โอ้ ฉันใส่ PYTHONIOENCODING=utf-8 ไว้ในตัวแปรสภาพแวดล้อม”

7.2 ข้อสรุปของการโต้แย้งถูกทำให้ถาวรในเซสชันที่มีการหารือเกี่ยวกับการตัดสินใจในการออกแบบและนโยบายการดำเนินงาน การเลี้ยวทั้ง 3 ครั้งก่อนและหลังการสรุปจะถูกบันทึกเป็นชิ้นเดียว หลังจากนั้น หากคุณค้นหาว่า ปัจจัยในการตัดสินใจที่อยู่เบื้องหลังการตัดสินใจออกแบบนั้นคืออะไร'' ลำดับการสนทนาทั้งหมดจะปรากฏขึ้น **บริบทของ ทำไมเราถึงทำ” ที่ไม่สามารถเขียนลงในเอกสารการออกแบบได้** มักถูกทิ้งไว้ข้างหลัง

7.3 คุณจะมีโอกาสทำผิดพลาดน้อยลง

กับดักที่ “ตัวตนในอดีต” ของฉันก้าวเข้าไปนั้นถูกซ่อนไว้อย่างลึกลับใกล้กับตัวตนปัจจุบันของฉัน เมื่อคุณค้นหาบันทึกที่ผ่านมาด้วย rag search "<関連キーワード>" ก่อนที่จะเริ่มงานใหม่ คุณมักจะได้รับการเตือนถึง บันทึกที่ถูกลืม

โดยเฉพาะอย่างยิ่งหลังจากติดตั้งการแทรกตะขอหยุดอัตโนมัติของ Claude Code ความทรงจำของฉันก็สะสมโดยที่ฉันไม่รู้ตัว ดังนั้นประสบการณ์การค้นหาของฉันจึงดีขึ้นเรื่อยๆ


8. ข้อควรทราบ/ข้อจำกัด

8.1 ความเสี่ยงจากการปะปนข้อมูลที่เป็นความลับ

การสนทนาอาจรวมถึง ข้อความที่ตัดตอนมาจากโค้ด, คีย์ API, URL และข้อมูลส่วนบุคคล ไม่มีปัญหาตราบใดที่ปิดไปยัง ChromaDB ในเครื่อง แต่จำเป็นอย่างยิ่งที่คุณจะต้องไม่ส่งไดเรกทอรี ChromaDB ไปยังพื้นที่เก็บข้อมูลสาธารณะโดยใช้ Git ฉันมี chroma_db/ ใน .gitignore

8.2 กฎการกำจัดเสียงรบกวนต้องมีการบำรุงรักษา

แท็กการแทรกอัตโนมัติของ Claude Code อาจเพิ่มขึ้นในอนาคต รายการแท็กที่ถูกลบโดย NOISE_TAG_RE ครั้งนี้มาจากช่วงที่ฉันสังเกตเห็น มีค่าใช้จ่ายในการดำเนินการ เพิ่มแท็กใหม่ตามที่ปรากฏ

8.3 การฝังการสนทนาแบบเก่าอาจสูญเสียความแม่นยำหากไม่สร้างใหม่

เมื่อคุณอัปเดตโมเดลการฝังของ Ollama การฝังของก้อนเก่าและการฝังของก้อนใหม่จะอยู่ใน ช่องว่างที่แตกต่างกันเล็กน้อย จำเป็นต้องฝังใหม่ทั้งหมด เพื่อความเข้ากันได้อย่างสมบูรณ์ ซึ่งทำได้โดยใช้ rag bulk เพื่อส่งรายการทั้งหมดอีกครั้ง### 8.4 ความแม่นยำในการค้นหาอาจลดลงหากคุณ “ใส่ทุกอย่างลงไป”

ข้อผิดพลาดที่ไม่คาดคิดคือการรวม เซสชันเริ่มต้นที่มีเสียงดังรบกวน และ เซสชันที่จบลงด้วยข้อผิดพลาด จะลดความแม่นยำในการค้นหา source_file ด้วยการกรองด้วยข้อมูลเมตาหรือโดยการแบ่ง source_type ออกเป็นส่วนเล็กๆ คุณสามารถจำกัดการค้นหาของคุณให้แคบลงได้


9. สรุป

  • VSCode Copilot Chat บันทึกประวัติการสนทนาไปที่ workspaceStorage/.../transcripts/*.jsonl โดยอัตโนมัติ และ Claude Code บันทึกประวัติการสนทนาไปที่ ~/.claude/projects/<slug>/<session-id>.jsonl โดยอัตโนมัติ
  • JSONL ทั้งสองมีโครงสร้างที่แตกต่างกัน (Copilot นั้นเรียบง่าย Claude Code มีบล็อกอาร์เรย์ + เหตุการณ์เมตาผสมกัน) แต่สามารถดูดซับได้โดยใช้พาร์เซอร์แยกกัน + การกำหนดมาตรฐานและต่อมา
  • การกำจัดเสียงรบกวน (การลบแท็ก เช่น <system-reminder>) ส่งผลให้ความแม่นยำในการค้นหาแตกต่างกัน 30%
  • ด้าน Claude Code สามารถทำงานอัตโนมัติได้อย่างสมบูรณ์ด้วย Stop hook + การเริ่มต้นพื้นหลัง
  • หลังจากสะสมเซสชันได้หลายสิบเซสชัน คุณจะสามารถค้นหา “ตัวตนในอดีต” ในภาษาธรรมชาติได้ ฉันจำวิธีแก้ปัญหานั้น การตัดสิน และกับดักนั้นได้ทันที
  • การจัดการข้อมูลที่เป็นความลับ การบำรุงรักษากฎการกำจัดสัญญาณรบกวน การสร้างใหม่เมื่ออัปเดตโมเดลการฝัง — แม้ว่าจะมีค่าใช้จ่ายในการดำเนินการ แต่ผลตอบแทนที่ได้รับก็มีจำนวนมากอย่างท่วมท้นบทความที่เกี่ยวข้อง:
  • [กลไกเพื่อป้องกันไม่ให้ Copilot ทำผิดพลาดซ้ำสองครั้ง — ออกแบบให้มี “หน่วยความจำของการสนทนา” ด้วย RAG + MCP] (/blog/copilot-memory-rag-mcp) — การใช้งานเซิร์ฟเวอร์ MCP สำหรับ Copilot เพื่อค้นหาอัตโนมัติแบบเรียลไทม์ที่ป้อนประวัติการสนทนาในบทความนี้
  • ฉันใช้ GitHub Copilot เป็นเวลา 1 เดือน และใช้ Claude Code เป็นเวลา 2 วัน — พันธมิตรด้านการเขียนโค้ดและตัวแทนเป็นคนละเรื่องกัน — ความแตกต่างในบุคลิกของ Copilot และ Claude Code

แม้ว่า AI จะลืมฉันก็จะบันทึกมันไว้ จากนั้นเมื่อจำเป็นให้ “เตือน” จากด้านนี้ นี่คือทัศนคติพื้นฐานของฉันเมื่อทำงานกับ AI

ส่งข้อความได้ตามสบาย

กรุณาส่งข้อความ หากมีคำปรึกษาด้านเทคนิค ความคิดเห็น หรือคำถาม