Skip to main content

机制 3:上下文注入 - CLAUDE.md vs 配置发现

项目规范如何传递给 AI?


核心问题

两个项目都需要注入项目上下文,但方式不同:

  • Claude Code:CLAUDE.md 文件
  • Codex:配置文件发现

各有什么优劣?


Claude Code 的方案

CLAUDE.md 发现机制

// src/utils/claudemd.ts
export async function findClaudeMd(startDir: string): Promise<string | null> {
let currentDir = startDir

// 向上查找,直到 Git 根目录
while (true) {
const claudeMdPath = path.join(currentDir, 'CLAUDE.md')

if (await fs.exists(claudeMdPath)) {
const content = await fs.readFile(claudeMdPath, 'utf-8')
return content
}

// 检查是否是 Git 根目录
const gitDir = path.join(currentDir, '.git')
if (await fs.exists(gitDir)) {
break // 到达 Git 根目录,停止查找
}

// 向上一级
const parentDir = path.dirname(currentDir)
if (parentDir === currentDir) {
break // 到达文件系统根目录
}
currentDir = parentDir
}

return null
}

查找顺序

当前目录: /Users/yangkunhai/buildagent/src/tools
查找: /Users/yangkunhai/buildagent/src/tools/CLAUDE.md ❌
查找: /Users/yangkunhai/buildagent/src/CLAUDE.md ❌
查找: /Users/yangkunhai/buildagent/CLAUDE.md ✅ 找到

CLAUDE.md 格式

# CLAUDE.md

This file provides guidance to Claude Code when working with code in this repository.

## Project Overview

This is a reverse-engineered version of Anthropic's Claude Code CLI tool.

## Commands

```bash
bun install
bun run dev
bun test

Architecture

  • Runtime: Bun
  • Language: TypeScript
  • Build: Bun.build
  • Test: bun:test

Rules

  • Don't try to fix all tsc errors
  • Feature flags default to false
  • Use import { feature } from 'bun:bundle'

### 上下文注入

```typescript
// src/context.ts
export async function buildContext(): Promise<ContextItem[]> {
const context: ContextItem[] = []

// 1. CLAUDE.md(项目规范)
const claudeMd = await findClaudeMd(process.cwd())
if (claudeMd) {
context.push({
type: "text",
text: `# claudeMd\n${claudeMd}`,
cache_control: { type: "ephemeral" } // Prompt Cache
})
}

// 2. Git 状态
const gitStatus = await getGitStatus()
if (gitStatus) {
context.push({
type: "text",
text: `# gitStatus\n${gitStatus}`
})
}

// 3. 当前日期
context.push({
type: "text",
text: `# currentDate\nToday's date is ${new Date().toISOString().split('T')[0]}.`
})

return context
}

Codex 的方案

配置文件发现

// codex-rs/core/src/config.rs
pub async fn find_config(start_dir: &Path) -> Result<Option<Config>> {
let mut current = start_dir.to_path_buf();

loop {
// 1. 查找 .codex/config.toml
let config_path = current.join(".codex").join("config.toml");
if config_path.exists() {
let content = tokio::fs::read_to_string(&config_path).await?;
let config: Config = toml::from_str(&content)?;
return Ok(Some(config));
}

// 2. 查找 codex.toml
let config_path = current.join("codex.toml");
if config_path.exists() {
let content = tokio::fs::read_to_string(&config_path).await?;
let config: Config = toml::from_str(&content)?;
return Ok(Some(config));
}

// 3. 检查 Git 根目录
if current.join(".git").exists() {
break;
}

// 向上一级
match current.parent() {
Some(parent) => current = parent.to_path_buf(),
None => break,
}
}

Ok(None)
}

配置文件格式

# .codex/config.toml

[project]
name = "buildagent"
language = "typescript"
runtime = "bun"

[rules]
max_line_length = 120
use_semicolons = true
no_any = true

[commands]
install = "bun install"
dev = "bun run dev"
test = "bun test"
build = "bun run build"

[architecture]
entry = "src/entrypoints/cli.tsx"
core = "src/query.ts"
tools = "src/tools/"

[conventions]
naming = "camelCase"
components = "PascalCase"
imports = "absolute"

上下文注入

// codex-rs/core/src/context.rs
pub async fn build_context(&self) -> Result<Vec<ContextItem>> {
let mut context = Vec::new();

// 1. 配置文件
if let Some(config) = self.load_config().await? {
context.push(ContextItem::Config(config.to_string()));
}

// 2. Git 状态
let git_status = self.get_git_status().await?;
context.push(ContextItem::GitStatus(git_status));

// 3. 项目规范
if let Some(rules) = self.load_project_rules().await? {
context.push(ContextItem::Rules(rules));
}

// 4. 当前日期
let date = chrono::Local::now().format("%Y-%m-%d").to_string();
context.push(ContextItem::Date(date));

Ok(context)
}

对比分析

1. 文件格式

维度Claude CodeCodex
格式MarkdownTOML
文件名CLAUDE.md.codex/config.toml
可读性高(自然语言)中(结构化)
可解析性低(需要 LLM)高(标准格式)

Claude Code(Markdown)

# 项目规范
- 语言:TypeScript
- 禁止:any, var

优势

  • ✅ 自然语言,易读易写
  • ✅ 可以包含复杂说明
  • ✅ 不需要学习新格式

劣势

  • ❌ 不易解析(需要 LLM)
  • ❌ 没有类型检查
  • ❌ 难以验证

Codex(TOML)

[rules]
language = "typescript"
forbidden = ["any", "var"]

优势

  • ✅ 结构化,易解析
  • ✅ 可以类型检查
  • ✅ 可以验证

劣势

  • ❌ 需要学习 TOML 格式
  • ❌ 不如自然语言灵活
  • ❌ 复杂说明不方便

2. 查找策略

Claude Code

当前目录 → 父目录 → ... → Git 根目录
只查找 CLAUDE.md

Codex

当前目录 → 父目录 → ... → Git 根目录
查找 .codex/config.toml 或 codex.toml

相同点

  • 都是向上查找
  • 都在 Git 根目录停止

不同点

  • Claude Code:单一文件名
  • Codex:两个可能的位置

3. 内容组织

Claude Code(自由格式)

# CLAUDE.md

## Project Overview
描述项目...

## Commands
```bash
bun install

Rules

  • 规则 1
  • 规则 2

Architecture

描述架构...


**Codex(结构化)**:
```toml
[project]
name = "..."
description = "..."

[commands]
install = "..."
dev = "..."

[rules]
rule1 = true
rule2 = false

[architecture]
entry = "..."
core = "..."

4. 扩展性

Claude Code

# 可以随意添加新章节
## New Section
任何内容...

Codex

# 需要定义新的 section
[new_section]
key = "value"

Claude Code 更灵活

  • 可以添加任何内容
  • 不需要预定义结构

Codex 更规范

  • 需要预定义结构
  • 但更易于程序处理

Git 状态注入

Claude Code

// src/utils/git.ts
export async function getGitStatus(): Promise<string> {
const status = await exec('git status --porcelain')
const branch = await exec('git branch --show-current')
const commits = await exec('git log --oneline -5')

return `
Current branch: ${branch}

Modified files:
${status}

Recent commits:
${commits}
`.trim()
}

Codex

// codex-rs/core/src/git.rs
pub async fn get_git_status(&self) -> Result<String> {
let status = Command::new("git")
.args(&["status", "--porcelain"])
.output()
.await?;

let branch = Command::new("git")
.args(&["branch", "--show-current"])
.output()
.await?;

let commits = Command::new("git")
.args(&["log", "--oneline", "-5"])
.output()
.await?;

Ok(format!(
"Current branch: {}\n\nModified files:\n{}\n\nRecent commits:\n{}",
String::from_utf8_lossy(&branch.stdout).trim(),
String::from_utf8_lossy(&status.stdout),
String::from_utf8_lossy(&commits.stdout)
))
}

相同点

  • 都获取分支、修改文件、最近提交
  • 都用 git 命令

Memory 系统对比

Claude Code 的 Memory

// src/utils/memory.ts
export async function loadMemory(): Promise<string | null> {
const memoryDir = path.join(projectRoot, '.claude', 'memory')

if (!await fs.exists(memoryDir)) {
return null
}

// 读取所有 memory 文件
const files = await fs.readdir(memoryDir)
const memories: string[] = []

for (const file of files) {
if (file.endsWith('.md')) {
const content = await fs.readFile(
path.join(memoryDir, file),
'utf-8'
)
memories.push(content)
}
}

return memories.join('\n\n---\n\n')
}

Memory 文件格式

---
name: user_preferences
type: user
description: User's coding preferences
---

# User Preferences

- Prefers TypeScript over JavaScript
- Uses Bun as runtime
- Likes concise code

Codex 的 Memory

// codex-rs/core/src/memory.rs
pub async fn load_memory(&self) -> Result<Option<String>> {
let memory_dir = self.project_root.join(".codex").join("memory");

if !memory_dir.exists() {
return Ok(None);
}

let mut memories = Vec::new();
let mut entries = tokio::fs::read_dir(&memory_dir).await?;

while let Some(entry) = entries.next_entry().await? {
if entry.path().extension() == Some("toml".as_ref()) {
let content = tokio::fs::read_to_string(entry.path()).await?;
memories.push(content);
}
}

Ok(Some(memories.join("\n\n---\n\n")))
}

Memory 文件格式

[metadata]
name = "user_preferences"
type = "user"
description = "User's coding preferences"

[content]
language = "typescript"
runtime = "bun"
style = "concise"

性能对比

文件读取

操作Claude CodeCodex差距
查找配置~10ms~5ms2 倍
读取内容~5ms~3ms1.7 倍
解析内容不需要~2ms-
总计~15ms~10ms1.5 倍

Codex 更快

  • Rust I/O 性能好
  • TOML 解析快

内存占用

项目配置大小内存占用
Claude Code~5KB~50KB
Codex~3KB~20KB

你应该怎么选择?

选择 CLAUDE.md(Markdown)

适合

  • ✅ 需要灵活的自然语言描述
  • ✅ 复杂的项目规范
  • ✅ 团队不熟悉配置文件
  • ✅ 快速上手

示例

# CLAUDE.md

## 重要决策

2024-03-01: 我们决定不用 Redux,因为太重了。
团队讨论后选择 Zustand,更轻量。

## 特殊约定

文件命名:
- 组件:PascalCase.tsx
- 工具函数:camelCase.ts
- 类型定义:types.ts

## 历史教训

曾经尝试过 GraphQL,但后端团队不支持,
最后还是用 REST API。不要再提 GraphQL。

选择配置文件(TOML)

适合

  • ✅ 需要程序化处理
  • ✅ 需要类型检查
  • ✅ 需要验证
  • ✅ 结构化数据

示例

[decisions]
state_management = "zustand"
reason = "Redux too heavy"
date = "2024-03-01"

[naming]
components = "PascalCase"
utils = "camelCase"
types = "types.ts"

[forbidden]
technologies = ["redux", "graphql"]
reasons = ["too heavy", "backend not support"]

混合方案

最佳实践

.codex/
├── config.toml # 结构化配置
├── RULES.md # 自然语言规范
└── memory/ # 历史记忆
├── decisions.md
└── lessons.md

优势

  • 结构化数据用 TOML
  • 复杂说明用 Markdown
  • 两者结合

可复用模板

CLAUDE.md 模板

# CLAUDE.md

## Project Overview
[项目简介]

## Tech Stack
- Language: [语言]
- Framework: [框架]
- Runtime: [运行时]

## Commands
```bash
# 安装依赖
[命令]

# 开发模式
[命令]

# 测试
[命令]

Code Conventions

  • [约定 1]
  • [约定 2]

Architecture

[架构说明]

Important Decisions

Forbidden


### config.toml 模板

```toml
[project]
name = "project-name"
language = "typescript"
framework = "react"
runtime = "bun"

[commands]
install = "bun install"
dev = "bun run dev"
test = "bun test"
build = "bun run build"

[conventions]
naming_components = "PascalCase"
naming_utils = "camelCase"
imports = "absolute"

[rules]
no_any = true
no_var = true
max_line_length = 120

[forbidden]
technologies = ["redux", "graphql"]
patterns = ["class-components"]

关键要点

  1. Claude Code:CLAUDE.md(Markdown)
  2. Codex:config.toml(TOML)
  3. Markdown:灵活、自然语言、易读
  4. TOML:结构化、可解析、可验证
  5. 查找策略:向上查找到 Git 根目录
  6. 性能:Codex 快 1.5 倍
  7. 选择:灵活性 vs 结构化

记住:Markdown 适合复杂说明,TOML 适合结构化数据。


字数:约 4000 字
阅读时间:约 10 分钟