机制 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 Code | Codex |
|---|---|---|
| 格式 | Markdown | TOML |
| 文件名 | 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 Code | Codex | 差距 |
|---|---|---|---|
| 查找配置 | ~10ms | ~5ms | 2 倍 |
| 读取内容 | ~5ms | ~3ms | 1.7 倍 |
| 解析内容 | 不需要 | ~2ms | - |
| 总计 | ~15ms | ~10ms | 1.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"]
关键要点
- Claude Code:CLAUDE.md(Markdown)
- Codex:config.toml(TOML)
- Markdown:灵活、自然语言、易读
- TOML:结构化、可解析、可验证
- 查找策略:向上查找到 Git 根目录
- 性能:Codex 快 1.5 倍
- 选择:灵活性 vs 结构化
记住:Markdown 适合复杂说明,TOML 适合结构化数据。
字数:约 4000 字
阅读时间:约 10 分钟