Files
val-blog/karpathy-wiki-compiler-design.md
T

18 KiB
Raw Blame History

Karpathy风格 Wiki 自动编译器设计文档

基于LLM的个人知识库自动构建系统

一、核心架构

┌─────────────────────────────────────────────────────────────────┐
│                    腾讯云 2核2G (编译服务器)                      │
│                                                                  │
│  /data/knowledge-base/                                           │
│  ├── 00-inbox/          ← 原始资料入口(手动/Web Clipper放入)    │
│  ├── 01-raw/            ← 已整理的原始资料(PDF/网页/图片)       │
│  ├── 02-compiler/       ← 编译器工作目录                         │
│  │   ├── scripts/       ← 编译脚本                              │
│  │   ├── queue/         ← 待处理队列                            │
│  │   └── logs/          ← 处理日志                              │
│  ├── 03-wiki/           ← 生成的WikiObsidian主目录)           │
│  │   ├── Concepts/      ← 概念文章(LLM生成)                    │
│  │   ├── Summaries/     ← 资料摘要                              │
│  │   ├── MOCs/          ← 内容地图                              │
│  │   └── Index.md       ← 主索引                                │
│  ├── 04-outputs/        ← 查询输出(问答/可视化)                │
│  └── 05-archive/        ← 归档                                  │
│                                                                  │
│  编译服务:                                                       │
│  ├── File Watcher (inotify)  ← 监控inbox新文件                   │
│  ├── Compiler Pipeline       ← 调用LLM处理                       │
│  └── WebDAV Server           ← 供Obsidian同步                    │
└─────────────────────────────────────────────────────────────────┘
                              ↑
                    WebDAV / HTTPS
                              ↑
        ┌─────────────────────┼─────────────────────┐
        │                     │                     │
   MacBook               手机/iPad             (其他设备)
   (Obsidian IDE)       (Obsidian App)
   (也可本地编译)

二、编译流程设计

2.1 阶段一:数据摄取(Ingest)

输入:任意格式的原始资料

  • 网页文章(通过Obsidian Web Clipper
  • PDF论文
  • 图片/截图
  • GitHub仓库
  • 数据集说明

处理

# 1. 文件放入 00-inbox/
# 2. File Watcher检测到新文件
# 3. 预处理:
#    - 网页 → Markdownreadability-lxml
#    - PDF → Markdownpymupdf4llm
#    - 图片 → OCR提取文字(可选)
# 4. 移动到 01-raw/,记录元数据

元数据记录JSON sidecar):

{
  "source": "https://example.com/article",
  "title": "原始标题",
  "author": "作者",
  "date_ingested": "2025-04-15",
  "file_type": "web_article",
  "word_count": 3500,
  "hash": "sha256:xxx",
  "status": "pending_compilation"
}

2.2 阶段二:编译(Compile

核心编译管道(每次处理一个文档):

# compiler.py 伪代码

def compile_document(raw_file):
    # 1. 读取原始内容
    content = read_raw(raw_file)
    metadata = read_sidecar(raw_file)
    
    # 2. LLM生成摘要(500字以内)
    summary = llm_call(
        model="claude-3-5-sonnet",
        prompt=f"""
        请为以下文章生成结构化摘要:
        - 一句话核心观点
        - 3-5个关键要点
        - 重要数据/事实
        - 与其他概念的潜在联系
        
        原文:{content[:8000]}  # 前8K字符
        """
    )
    
    # 3. LLM提取概念
    concepts = llm_call(
        prompt=f"""
        从以下文章中提取关键概念(实体、理论、方法、工具等)。
        对每个概念给出:名称、定义、重要性(1-5)、相关概念
        
        格式:YAML列表
        """
    )
    
    # 4. 生成或更新概念文章
    for concept in concepts:
        concept_file = f"03-wiki/Concepts/{concept.name}.md"
        if exists(concept_file):
            # 更新现有概念(添加新信息)
            update_concept(concept_file, concept, raw_file)
        else:
            # 创建新概念文章
            create_concept(concept_file, concept, raw_file)
    
    # 5. 生成资料摘要卡片
    summary_file = f"03-wiki/Summaries/{raw_file.basename}.md"
    create_summary_card(summary_file, summary, concepts, metadata)
    
    # 6. 更新主索引
    update_index()
    
    # 7. 生成反向链接
    create_backlinks(raw_file, concepts)
    
    return {
        "status": "compiled",
        "summary": summary_file,
        "concepts": [c.name for c in concepts],
        "links_created": len(concepts)
    }

概念文章模板LLM生成):

---
concept: "概念名称"
aliases: ["别名1", "别名2"]
importance: 4
created: 2025-04-15
last_updated: 2025-04-15
sources:
  - "[[Summaries/原始文章1]]"
  - "[[Summaries/原始文章2]]"
related:
  - "[[概念A]]"
  - "[[概念B]]"
---

# 概念名称

## 定义
(LLM生成的简洁定义,50-100字)

## 核心要点
- 要点1
- 要点2
- 要点3

## 在资料中的出现
(自动聚合所有提到此概念的原始资料)
### [[Summaries/原始文章1]]
> 引用段落...
> 引用段落...

### [[Summaries/原始文章2]]
> 引用段落...

## 相关概念
- [[概念A]]:关系说明...
- [[概念B]]:关系说明...

## 待探索问题
LLM自动生成,供后续查询)
- 这个概念与X有什么区别?
- 在Y场景下如何应用?

2.3 阶段三:MOC生成(Map of Content

自动生成内容地图

def generate_mocs():
    # 1. 分析所有概念的关系
    concepts = load_all_concepts()
    graph = build_concept_graph(concepts)
    
    # 2. LLM识别主题集群
    clusters = llm_call(
        prompt=f"""
        以下是我知识库中的所有概念(共{len(concepts)}个)。
        请将它们组织成5-10个主题集群(MOC),每个集群包含:
        - 主题名称
        - 核心概念列表
        - 集群间的联系
        
        概念列表:{concepts}
        关系图:{graph}
        """
    )
    
    # 3. 为每个集群生成MOC文件
    for cluster in clusters:
        moc_file = f"03-wiki/MOCs/{cluster.name}.md"
        create_moc(moc_file, cluster)
    
    # 4. 更新主索引
    update_master_index(clusters)

MOC文件模板

---
moc: true
title: "人工智能研究"
description: "关于AI核心概念、方法论的集合"
concepts_count: 42
last_updated: 2025-04-15
---

# 🗺️ 人工智能研究

## 核心概念
Dataview查询自动生成)
```dataview
LIST
FROM "Concepts"
WHERE contains(mocs, "人工智能研究")
SORT importance DESC

重要资料

LIST
FROM "Summaries"
WHERE contains(topics, "AI")
SORT file.mtime DESC
LIMIT 10

最新探索

(手动或自动记录的研究问题)

  • 大模型微调的最佳实践?
  • RAG vs 长上下文,何时用哪个?

关联MOC


### 2.4 阶段四:问答与输出(Q&A

**基于Wiki的问答机制**

```python
def query_wiki(question):
    # 1. 确定相关概念
    relevant_concepts = llm_call(
        prompt=f"""
        用户问题:{question}
        
        请从以下概念列表中,找出最相关的5-10个概念:
        {all_concepts}
        
        返回概念名称列表。
        """
    )
    
    # 2. 读取相关概念文章
    context = ""
    for concept in relevant_concepts:
        context += read_concept_file(concept)
    
    # 3. 可选:读取原始资料(如果需要更多细节)
    if need_more_detail(question):
        raw_sources = find_raw_sources(relevant_concepts)
        context += extract_relevant_parts(raw_sources, question)
    
    # 4. LLM生成回答
    answer = llm_call(
        prompt=f"""
        基于以下Wiki内容,回答用户问题。
        如果信息不足,请明确说明。
        
        上下文:{context[:12000]}  # 限制长度
        
        问题:{question}
        
        要求:
        - 使用中文回答
        - 引用来源概念(如[[概念名称]])
        - 如果涉及多个方面,分点说明
        """
    )
    
    # 5. (可选)生成输出文档
    if user_wants_output:
        output_file = f"04-outputs/QnA-{timestamp}.md"
        create_qna_doc(output_file, question, answer, relevant_concepts)
    
    return answer

可视化输出(可选):

def generate_visualization(query_type):
    if query_type == "concept_graph":
        # 生成概念关系图(Mermaid或D3)
        pass
    elif query_type == "timeline":
        # 生成时间线
        pass
    elif query_type == "slides":
        # 生成Marp幻灯片
        pass

2.5 阶段五:健康检查与增强(Linting)

定期检查任务(每日/每周):

def health_check():
    # 1. 查找孤立概念(无链接的概念)
    orphans = find_orphan_concepts()
    
    # 2. 查找矛盾信息
    contradictions = llm_call(
        prompt="扫描所有概念文章,标记矛盾或过时的信息"
    )
    
    # 3. 建议新文章
    gaps = llm_call(
        prompt="基于现有概念,建议缺失的链接文章或MOC"
    )
    
    # 4. 生成健康报告
    report = generate_report(orphans, contradictions, gaps)
    save_to("04-outputs/health-reports/weekly.md")

三、技术实现细节

3.1 文件监控与触发

使用inotifyLinux

# file_watcher.py
import inotify.adapters

def watch_inbox():
    i = inotify.adapters.Inotify()
    i.add_watch('/data/knowledge-base/00-inbox')
    
    for event in i.event_gen(yield_nones=False):
        (_, type_names, path, filename) = event
        
        if 'IN_CLOSE_WRITE' in type_names:
            # 新文件写入完成,触发编译
            queue_for_compilation(os.path.join(path, filename))

备选:定时扫描

# cron任务,每分钟检查
* * * * * /usr/bin/python3 /data/knowledge-base/compiler/scan_inbox.py

3.2 队列管理

使用Redis或简单文件队列

# queue.py - 简化版文件队列
import json
import os
from datetime import datetime

QUEUE_DIR = "/data/knowledge-base/02-compiler/queue"

def enqueue(file_path, priority=5):
    task = {
        "id": generate_id(),
        "file": file_path,
        "priority": priority,  # 1-10, 10最优先
        "status": "pending",
        "created": datetime.now().isoformat(),
        "attempts": 0
    }
    
    queue_file = os.path.join(QUEUE_DIR, f"{task['id']}.json")
    with open(queue_file, 'w') as f:
        json.dump(task, f)

def dequeue():
    """按优先级取出任务"""
    tasks = []
    for f in os.listdir(QUEUE_DIR):
        if f.endswith('.json'):
            with open(os.path.join(QUEUE_DIR, f)) as fp:
                task = json.load(fp)
                if task['status'] == 'pending':
                    tasks.append(task)
    
    # 按优先级排序
    tasks.sort(key=lambda x: x['priority'], reverse=True)
    return tasks[0] if tasks else None

3.3 LLM调用封装

统一接口,支持多后端

# llm_client.py
import os
from typing import Literal

Backend = Literal["claude", "openai", "kimi", "local"]

class LLMClient:
    def __init__(self, default_backend: Backend = "claude"):
        self.default = default_backend
        self.api_keys = {
            "claude": os.getenv("ANTHROPIC_API_KEY"),
            "openai": os.getenv("OPENAI_API_KEY"),
            "kimi": os.getenv("MOONSHOT_API_KEY"),
            "local": "http://localhost:11434"  # Ollama
        }
    
    def call(self, prompt: str, backend: Backend = None, **kwargs):
        backend = backend or self.default
        
        if backend == "claude":
            return self._call_claude(prompt, **kwargs)
        elif backend == "openai":
            return self._call_openai(prompt, **kwargs)
        elif backend == "kimi":
            return self._call_kimi(prompt, **kwargs)
        elif backend == "local":
            return self._call_ollama(prompt, **kwargs)
    
    def _call_claude(self, prompt, model="claude-3-5-sonnet-20241022", max_tokens=4000):
        import anthropic
        client = anthropic.Anthropic(api_key=self.api_keys["claude"])
        
        message = client.messages.create(
            model=model,
            max_tokens=max_tokens,
            messages=[{"role": "user", "content": prompt}]
        )
        return message.content[0].text
    
    # ... 其他后端的实现

3.4 WebDAV配置

使用WsgiDAV

# 安装
pip install wsgidav cheroot

# 配置 /etc/wsgidav.yaml
host: 0.0.0.0
port: 5232
root: /data/knowledge-base/03-wiki

auth:
  type: htpasswd
  htpasswd_filename: /etc/wsgidav/users.htpasswd
  
# SSL(通过Nginx反向代理)

Nginx反向代理配置

server {
    listen 443 ssl http2;
    server_name knowledge.yourdomain.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # WebDAV
    location / {
        proxy_pass http://localhost:5232;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # WebDAV方法
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_ext_methods PROPFIND OPTIONS;
    }
    
    # API网关(编译器API
    location /api/ {
        proxy_pass http://localhost:8000;
        proxy_set_header Authorization $http_authorization;
    }
}

四、Obsidian配置

4.1 必需插件

  1. Remotely Save - WebDAV同步
  2. Dataview - 查询和索引
  3. Templater - 模板自动化
  4. QuickAdd - 快速捕获
  5. Obsidian Web Clipper(浏览器扩展)- 剪藏网页

4.2 快捷键配置

{
  "快速捕获到inbox": "Ctrl+Shift+I",
  "触发编译当前文件": "Ctrl+Shift+C",
  "打开概念图谱": "Ctrl+Shift+G",
  "查询Wiki": "Ctrl+Shift+Q"
}

4.3 模板示例

新资料模板(放入00-inbox时自动添加):

---
ingest_date: {{date}}
source: {{source_url}}
status: pending
priority: 5
tags: [raw]
---

# {{title}}

[原始链接]({{source_url}})

## 内容

{{content}}

## 待处理
- [ ] 编译成摘要
- [ ] 提取概念
- [ ] 链接到相关MOC

五、部署清单

5.1 腾讯云初始化

# 1. 系统更新
sudo apt update && sudo apt upgrade -y

# 2. 安装依赖
sudo apt install -y python3 python3-pip nodejs npm redis-server nginx

# 3. 安装Python包
pip3 install anthropic openai pymupdf4llm readability-lxml markdown pyyaml watchdog

# 4. 创建目录结构
sudo mkdir -p /data/knowledge-base/{00-inbox,01-raw,02-compiler,03-wiki,04-outputs,05-archive}
sudo chown -R $USER:$USER /data/knowledge-base

# 5. 配置环境变量
export ANTHROPIC_API_KEY="your-key"
export OPENAI_API_KEY="your-key"
export MOONSHOT_API_KEY="your-key"

# 6. 启动服务
# - WebDAV
# - File Watcher
# - Compiler Worker

5.2 服务脚本

systemd服务:编译器工作器

# /etc/systemd/system/wiki-compiler.service
[Unit]
Description=Wiki Compiler Worker
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/data/knowledge-base
ExecStart=/usr/bin/python3 /data/knowledge-base/compiler/worker.py
Restart=always
RestartSec=10
Environment="ANTHROPIC_API_KEY=xxx"
Environment="OPENAI_API_KEY=xxx"

[Install]
WantedBy=multi-user.target

六、使用工作流

日常流程

1. 发现资料
   ↓
2. Web Clipper剪藏 → 自动放入00-inbox/
   ↓
3. File Watcher检测 → 触发编译队列
   ↓
4. Compiler Worker处理:
   - 预处理 → 01-raw/
   - LLM生成摘要 → 03-wiki/Summaries/
   - LLM提取概念 → 03-wiki/Concepts/
   - 更新MOC → 03-wiki/MOCs/
   - 建立反向链接
   ↓
5. Obsidian自动同步(通过WebDAV
   ↓
6. 在Obsidian中查看生成的Wiki

查询流程

1. 在Obsidian中打开查询面板(或命令行)
   ↓
2. 输入问题
   ↓
3. 系统:
   - 识别相关概念
   - 读取Concepts/和Summaries/
   - 组装上下文
   - 调用LLM生成回答
   ↓
4. 输出:
   - 直接回答(聊天形式)
   - 或生成文档 → 04-outputs/
   - 或生成可视化(图表/幻灯片)

七、扩展思路

7.1 未来增强

  1. 语义搜索:用向量数据库存储embedding,支持模糊查询
  2. 多模态:处理图片、音频、视频内容
  3. 协作:多人共享Wiki,权限管理
  4. 版本控制Git集成,追踪概念演变
  5. AI Agent:让LLM主动提出问题、建议研究方向

7.2 与OpenClaw集成

可以设计一个OpenClaw Skill

  • /wiki ingest <url> - 让Val帮你剪藏和编译
  • /wiki query <question> - 查询你的知识库
  • /wiki stats - 查看知识库统计

Val可以直接调用腾讯云的编译API,帮你管理个人知识库。


核心原则:LLM维护Wiki,人负责提出问题和判断价值。

这是一个活的系统,随着使用它会越来越懂你的知识结构和兴趣方向。