ป้อนประวัติการสนทนาของ Copilot Chat และ Claude Code ลงใน ChromaDB และค้นหา "ตัวตนในอดีต" ของคุณ
🔗 สารบัญซีรีส์: บทความนี้เป็น ฉบับการใช้งาน (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’singest_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
สองจุด:
- อ่าน
transcript_pathจาก stdin — Claude Code ส่งให้เราใน JSON - การเริ่มต้นพื้นหลังด้วย
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