Hotdry.
ai-systems

基于Claude Code构建书籍语义相似性图:从批量文本处理到交互式可视化前端的全栈工程实现

探索如何使用Claude Code处理100本非虚构书籍,构建语义相似性图,实现从EPUB解析、主题提取到交互式可视化的全栈工程流程。

在 AI 编程助手日益普及的今天,Claude Code 正从单纯的代码生成工具演变为复杂工作流的协调者。Pieter Maes 最近展示了一个引人注目的案例:使用 Claude Code 处理 100 本非虚构书籍,构建语义相似性图,并实现交互式可视化前端。这个项目不仅展示了 AI 在非编码任务中的潜力,更揭示了全栈语义图构建的工程化路径。

从代码助手到工作流协调者:Claude Code 的角色转变

传统上,Claude Code 被定位为 AI 编程助手,专注于代码生成、调试和重构。然而,在这个书籍语义图项目中,Claude Code 扮演了完全不同的角色:工作流协调者非编码任务执行者

Pieter Maes 最初尝试构建传统的多阶段 LLM 管道,每个阶段都有精心设计的手工组装上下文。但当他简单地向 Claude Code 提供调试工具和最小提示 "find something interesting" 时,结果令人惊讶。正如他在博客中所说:"It immediately did a better job at pulling in what it needed than the pipeline I was trying to tune by hand, while requiring much less orchestration."

这种转变的核心在于 Claude Code 能够:

  1. 自主推断工具调用序列:比人工回忆更快地推断出所需的 CLI 调用序列
  2. 处理非刚性任务:自动化那些传统脚本难以处理的非刚性任务
  3. 支持迭代优化:通过自然语言指令调整工作流,无需重写复杂逻辑

例如,当 Pieter 改变对摘录长度的偏好时,他只需向 Claude Code 传达新偏好,Claude Code 就会查看所有现有 trail 并进行相应编辑,平衡整体含义的变化。这种灵活性在传统编程范式中几乎不可能实现。

技术架构:从 EPUB 解析到主题树构建

构建 100 本书籍的语义图需要精心设计的全栈架构。以下是核心组件及其工程实现:

1. 文本处理流水线

# 伪代码展示核心处理流程
def process_epub_pipeline(epub_path):
    # 1. EPUB解析 - 使用selectolax替代BeautifulSoup
    html_content = parse_epub_with_selectolax(epub_path)
    
    # 2. 句子分割 - 使用wtpsplit的sat-6l-sm模型
    sentences = split_sentences_with_wtpsplit(html_content)
    
    # 3. 文本分块 - 500词块,尽量不打断段落
    chunks = create_chunks(sentences, target_words=500)
    
    # 4. 主题提取 - Gemini 2.5 Flash Lite
    topics = extract_topics_with_gemini(chunks)
    
    # 5. 嵌入存储 - sqlite-vec
    store_embeddings_in_sqlite(topics, chunks)

关键工程决策

  • 选择 selectolax 而非 BeautifulSoup:基于速度和更简单的 API
  • 500 词分块策略:平衡 token 节省与上下文完整性
  • 实时清理而非预处理:仅在显示摘录时清理 EPUB 伪影,节省大量 token

2. 主题提取与去重

主题提取使用 Gemini 2.5 Flash Lite 模型,每个块提取 3-5 个主题。处理 100 本书籍消耗约 60M 输入 token,总成本约 £10。Pieter 发现提取的主题具有惊人的稳定性:"similar chunks often shared some of the exact same topic labels."

去重处理通过合并距离低于阈值的主题对来处理近义词,如 "Startup founder"、"Startup founders" 和 "Founder of startups"。

3. 图构建与树形聚类

主题集合通过以下步骤转换为可探索的图结构:

def build_semantic_graph(topics):
    # 1. 创建图 - 使用igraph
    graph = create_igraph_from_topics(topics)
    
    # 2. 添加边 - 基于嵌入相似性和点互信息
    add_edges_based_on_similarity(graph, topics)
    
    # 3. 递归聚类 - Leiden算法
    tree = apply_leiden_partitioning_recursive(graph)
    
    # 4. 标签生成 - Gemini为每个聚类生成描述性标签
    labels = generate_cluster_labels_with_gemini(tree)
    return tree

Leiden 算法参数

  • 使用 Surprise 质量函数:无需调整参数
  • 递归分区直到达到最小规模
  • 每个聚类基于包含的所有主题由 Gemini 生成标签

新颖性搜索算法:工程实现细节

"有趣性" 难以量化优化,Pieter 借鉴了《Why Greatness Cannot Be Planned》中的观点,将新颖性作为搜索指导。实现方式包括:

1. 算法层面新颖性搜索

def calculate_novelty_score(topic_embedding, all_embeddings, k=10):
    # 计算与k个最近邻的平均距离
    distances = calculate_distances(topic_embedding, all_embeddings)
    nearest_distances = sorted(distances)[:k]
    novelty_score = np.mean(nearest_distances)
    return novelty_score

def rank_search_results(query, topics, embeddings):
    # 结合相关性和新颖性进行排序
    relevance_scores = calculate_relevance(query, topics)
    novelty_scores = calculate_novelty_scores(embeddings)
    
    # 加权综合评分
    combined_scores = 0.7 * relevance_scores + 0.3 * novelty_scores
    return topics_sorted_by(combined_scores)

2. 提示层面新颖性引导

在提示层面,Claude Code 在构思阶段会查看所有现有 trail,并被要求避免任何概念重叠。Pieter 观察到:"This works fairly well, though it is often distracted by any topics related to secrecy, systems theory, or tacit knowledge."

交互式可视化:D3.js 前端工程实践

可视化前端采用 D3.js 构建,支持用户探索主题连接。关键工程考虑包括:

1. 数据流设计

// 简化版数据流架构
class SemanticGraphVisualizer {
    constructor() {
        this.graphData = null;
        this.zoom = d3.zoom();
        this.simulation = d3.forceSimulation();
    }
    
    async loadData() {
        // 从Claude Code后端API获取数据
        const response = await fetch('/api/semantic-graph');
        this.graphData = await response.json();
        this.initializeVisualization();
    }
    
    initializeVisualization() {
        // 力导向图布局
        this.simulation
            .force('link', d3.forceLink().id(d => d.id))
            .force('charge', d3.forceManyBody())
            .force('center', d3.forceCenter());
        
        // 交互功能
        this.addZoomPanInteraction();
        this.addNodeClickHandlers();
        this.addSearchFunctionality();
    }
}

2. 性能优化策略

  • 增量渲染:仅渲染视口内的节点和边
  • Web Workers:将力计算卸载到工作线程
  • 虚拟化:对大型图使用虚拟滚动
  • 缓存策略:本地存储常用查询结果

3. 用户体验设计

  • 渐进式披露:从高级主题树开始,逐步深入细节
  • 上下文保持:导航时保持相关上下文可见
  • 多视图协调:同步图视图、列表视图和详情视图

CLI 工具设计:半 XML 格式与服务器架构

Claude Code 通过 CLI 工具与系统交互,这些工具采用独特的半 XML 输出格式:

<topics query="deception" count="1">
  <topic id="47193" books="7" score="0.0173" label="Deception">
    <chunk id="186" book="1">
      <topic id="47192" label="Business deal"/>
      <topic id="47108" label="Internal conflict"/>
      <topic id="46623" label="Startup founders"/>
    </chunk>
  </topic>
</topics>

设计优势

  • 嵌套结构:支持相关内容的自然导航
  • 上下文丰富:显示块包含的其他主题,提供额外探索线索
  • 人类可读:虽然可能不是最 token 高效的格式,但从未达到上下文窗口限制

服务器架构优化

# 资源加载优化
class CLIServer:
    def __init__(self):
        self.embedding_model = None
        self.connection = None
        
    def start_server(self):
        # 首次调用时透明启动服务器进程
        if not self.connection:
            self.embedding_model = load_embedding_model()
            self.connection = start_multiprocessing_server()
        
    def handle_request(self, command):
        # 通过multiprocessing.connection复用已加载资源
        return self.connection.send_command(command)

工程挑战与解决方案

1. 成本控制

处理 100 本书籍的成本约为 £10,主要来自:

  • Gemini 2.5 Flash Lite 调用:60M 输入 token
  • 嵌入模型推理:google/embeddinggemma-300m
  • 重排序模型:BAAI/bge-reranker-v2-m3

优化策略

  • 选择性处理:仅在实际显示时清理摘录
  • 缓存重用:复用已计算的嵌入和主题
  • 批量处理:优化 API 调用批处理大小

2. 主题提取稳定性

虽然 Gemini 提取的主题具有良好稳定性,但仍需处理:

  • 近义词合并:基于嵌入距离阈值
  • 质量过滤:过滤索引条目、致谢等无用内容
  • 一致性检查:定期验证主题标签的一致性

3. 新颖性偏差

Pieter 观察到新颖性搜索可能过度关注特定主题领域:"It's as if the very act of finding connections in a corpus summons the spirit of Umberto Eco and amps up the conspiratorial thinking."

缓解策略

  • 多样性约束:强制探索不同主题领域
  • 时间衰减:降低频繁出现主题的权重
  • 人工引导:定期人工审查和调整搜索方向

可落地参数与监控要点

1. 生产部署参数

# 配置示例
processing:
  chunk_size_words: 500
  max_topics_per_chunk: 5
  embedding_model: "google/embeddinggemma-300m"
  reranker_model: "BAAI/bge-reranker-v2-m3"
  
graph:
  similarity_threshold: 0.85
  pmi_threshold: 0.1
  leiden_resolution: 1.0
  min_cluster_size: 10
  
search:
  novelty_weight: 0.3
  relevance_weight: 0.7
  k_nearest_neighbors: 10

2. 监控指标

  • 处理吞吐量:书籍 / 小时,块 / 秒
  • 成本效率:£/ 百万 token,£/ 本书
  • 主题质量:一致性得分,人工评估准确率
  • 搜索效果:新颖性 - 相关性平衡,用户满意度
  • 系统性能:API 响应时间,内存使用率

3. 回滚策略

  • 版本化存储:所有中间结果版本化
  • 检查点机制:定期保存处理状态
  • A/B 测试:新旧算法并行运行比较
  • 渐进式部署:逐步增加处理书籍数量

未来方向与工程启示

这个项目展示了几个重要的工程趋势:

1. AI 作为工作流协调者

Claude Code 的成功表明,AI 代理可以超越简单的任务执行,成为复杂工作流的智能协调者。这种模式特别适合:

  • 非刚性任务:难以用传统脚本精确描述的工作
  • 迭代优化:需要频繁调整和优化的流程
  • 探索性任务:目标不明确,需要发现性学习

2. 全栈语义系统架构

从 EPUB 解析到 D3.js 可视化的完整堆栈展示了构建语义系统的工程复杂性。关键洞察包括:

  • 数据流设计:平衡实时处理与批量处理的权衡
  • 工具抽象:为 AI 代理设计直观的工具接口
  • 可视化集成:将后端语义分析与前端交互深度集成

3. 成本感知的 AI 工程

£10 处理 100 本书的成本效益展示了 AI 工程在成本控制方面的成熟。工程团队需要:

  • 精细化成本核算:按组件、按任务跟踪成本
  • 优化策略组合:结合模型选择、批处理和缓存
  • 价值导向设计:确保 AI 投入产生可衡量的业务价值

结语

Pieter Maes 的书籍语义图项目不仅是技术演示,更是 AI 工程实践的典范。它展示了如何将 Claude Code 从代码助手转变为工作流协调者,如何构建从文本处理到可视化展示的全栈系统,以及如何在成本控制下实现有意义的语义分析。

正如 Pieter 所反思的:"My mental model of the AI component changed: from a function mapping input to output, to a coworker I was assisting." 这种思维转变 —— 从将 AI 视为函数到视为协作伙伴 —— 可能是这个项目最重要的启示。

对于工程团队而言,这个案例提供了具体的参数、架构模式和最佳实践,可用于构建自己的语义分析系统。从 500 词分块策略到 Leiden 聚类参数,从半 XML CLI 格式到 D3.js 可视化模式,这些工程细节构成了可复用的知识资产。

在 AI 日益融入工程工作流的今天,这种全栈、成本感知、用户中心的 AI 系统设计方法,为未来的智能应用开发指明了方向。


资料来源

  1. Pieter Maes, "Reading across books with Claude Code" - https://pieterma.es/syntopic-reading-claude/
  2. SynapseFlow 项目参考 - https://github.com/mrkingsleyobi/synapseflow
  3. D3.js 与 AI 代理集成模式 - https://relevanceai.com/agent-templates-software/d3-js
查看归档