上周Anthropic又翻车了。Claude Code v2.1.88的npm包里留了一个59.8MB的.map文件,1900个源文件、512000行TypeScript代码直接裸奔。13个月内第二次犯同样的错误——.npmignore没写对。

吃瓜归吃瓜,但51万行生产级Agent代码摆在眼前,不看白不看。我花了两天翻完核心模块,记录了几个值得借鉴的架构决策。

整体架构:比想象中复杂得多

先看目录结构:

claude-code/
├── coordinator/    # 多Agent协调(29000+行)
├── assistant/      # KAIROS自主模式
├── query-engine/   # 查询引擎(46000行,最大模块)
├── tools/          # 40个工具实现
├── plugins/        # 插件系统
├── skills/         # 技能按需加载
├── memory/         # 持久化记忆
├── security/       # 安全检查(2500行bash)
├── voice/          # 语音交互
├── ide-bridge/     # IDE桥接(VS Code, JetBrains)
└── system-prompt/  # 系统提示词

512000行代码分成了十几个模块。query-engine一个模块就46000行,tools目录29000行。这不是一个"套壳ChatGPT"——是一个完整的Agent平台。

技术栈也有意思:运行时用Bun(Anthropic 2025年底收购了Bun),终端UI用React + Ink做组件化渲染,数据校验全部用Zod v4。

第一个值得学的设计:用Prompt编排Agent,不用框架

coordinator/目录实现了多Agent协调,但没用LangChain、CrewAI这类框架。多个Agent各自有独立的上下文窗口和工具权限,在各自的沙箱里跑,通过Prompt来协调分工。

社区有个评论很到位:"这让LangChain看起来像是在寻找问题的解决方案。"

实际项目中我也碰过类似的选择。用框架写Agent,调试的时间比写业务的时间还长——框架的抽象层和你的业务逻辑经常打架。Anthropic的做法说明了一件事:对LLM来说,Prompt本身就是最好的编排语言。

如果你也在做Agent,先试试纯Prompt编排:

# 简化示例:Prompt驱动的Agent分工
coordinator_prompt = """
你是协调者。现在有三个任务需要并行处理:
1. 读取项目目录结构 → 交给 agent_file
2. 检查依赖版本 → 交给 agent_deps  
3. 运行测试 → 交给 agent_test

每个agent完成后向你汇报,你汇总结果。
如果agent_test报告失败,让agent_file检查相关代码。
"""

核心思路:Prompt定义角色和规则,代码只负责消息传递和沙箱隔离。

第二个值得学的设计:技能按需加载

skills/目录实现了一套动态技能加载系统,工作流程是这样的:

用户输入 → 意图识别 → 技能匹配 → 按需加载领域知识 → 注入上下文 → 执行

这解决了一个实际问题:Agent的上下文窗口有限,不可能把所有领域知识都塞进去。技能系统的做法是——用到哪个加载哪个。

举个例子,你让Claude Code帮你调试Docker问题,它不会一开始就把Docker、Kubernetes、CI/CD的知识全部加载。它先识别"这是Docker相关",然后只加载Docker的技能包,把有限的上下文用在刀刃上。

这个思路可以直接搬到自己的项目里:

# 技能加载示例
skills_dir = "./skills/"

def load_skill(intent: str) -> str:
    """根据意图加载对应技能的提示词"""
    skill_map = {
        "docker": "docker/SKILL.md",
        "git": "git/SKILL.md",
        "database": "database/SKILL.md",
    }
    skill_file = skill_map.get(intent)
    if skill_file:
        with open(f"{skills_dir}/{skill_file}") as f:
            return f.read()
    return ""

# 使用时
user_input = "我的docker容器启动报错了"
intent = detect_intent(user_input)  # → "docker"
skill_prompt = load_skill(intent)
# 把skill_prompt拼到系统提示词里,再调用LLM

第三个值得学的设计:44个功能标志管理未发布特性

泄露代码里有44个Feature Flag,控制着20多个未发布功能。KAIROS自主模式、语音交互、Undercover Mode——全部通过功能标志开关控制。

这在大型项目里很常见,但在Agent项目里用得好的不多。功能标志的好处是:代码已经在主分支上了,随时可以灰度发布,不需要维护一堆长期分支。

我在自己的Agent项目里也开始用类似的方式:

// 功能标志配置
const FEATURE_FLAGS = {
  ENABLE_VOICE: false,        // 语音交互
  ENABLE_MULTI_AGENT: true,   // 多Agent模式
  ENABLE_MEMORY: true,        // 持久记忆
  ENABLE_AUTO_PLAN: false,    // 自主规划
} as const;

// 运行时检查
if (FEATURE_FLAGS.ENABLE_MULTI_AGENT) {
  await coordinator.dispatch(task);
} else {
  await singleAgent.run(task);
}

好处很直接:新功能可以一边开发一边合并到主分支,不影响线上稳定性。想灰度测试?改个标志就行。

第四个值得学的设计:2500行bash做安全检查

security/目录里有2500行bash代码,专门做输入验证和命令注入防护。

为什么用bash不用TypeScript?因为Agent执行shell命令时,验证逻辑离执行层越近越安全。用bash写安全检查,直接在命令执行前拦截,少一层语言转换就少一个出错的环节。

Claude Code的安全模型分成几层:

用户输入
  ↓
权限检查(这个操作需要授权吗?)
  ↓
命令解析(提取要执行的命令)
  ↓
bash安全检查(命令注入检测、路径穿越检测)
  ↓
沙箱执行(隔离环境里跑)
  ↓
结果返回

如果你在做允许LLM执行命令的Agent,至少要做到两件事:

  1. 白名单机制:列出允许执行的命令,其他一律拒绝
  2. 参数检查:防止命令注入(比如用户输入里带分号、管道符)
#!/bin/bash
# 简单的命令安全检查
check_command() {
    local cmd="$1"
    # 检查危险字符
    if echo "$cmd" | grep -qE '[;&|`$()]'; then
        echo "BLOCKED: 检测到危险字符"
        return 1
    fi
    # 白名单检查
    local allowed="ls cat grep find head tail wc"
    local first_word=$(echo "$cmd" | awk '{print $1}')
    if ! echo "$allowed" | grep -qw "$first_word"; then
        echo "BLOCKED: 命令不在白名单中"
        return 1
    fi
    return 0
}

第五个值得学的设计:Source Map的教训

最后说说这次泄露本身的技术教训,因为这个错误太低级了。

Bun默认生成Source Map文件。Source Map的sourcesContent字段直接包含了所有原始源码——拿到.map文件就等于拿到了全部代码。

Anthropic的.npmignore里没排除*.map文件,package.json的files字段也没配对。一行配置就能防住的事情,两次都没做。

如果你在发布npm包,发布前跑一下这个检查:

# 发布前自查脚本
echo "=== 检查打包文件 ==="
npm pack --dry-run 2>&1 | head -30

echo ""
echo "=== 检查是否包含.map文件 ==="
npm pack 2>/dev/null | tar -tz | grep '\.map$'
if [ $? -eq 0 ]; then
    echo "警告:包中包含.map文件!"
    echo "修复:在.npmignore中添加 *.map"
else
    echo "通过:没有.map文件"
fi

echo ""
echo "=== 检查包大小 ==="
npm pack 2>/dev/null | xargs ls -lh
# 如果包大小异常(比如超过10MB),大概率有问题

还有Bun用户注意一下构建配置:

# bunfig.toml
[build]
sourcemap = "none"  # 生产环境禁用Source Map

总结

从Claude Code的51万行代码里,值得带走的几个实操经验:

  1. Agent编排用Prompt就够了,别急着上框架
  2. 技能按需加载,省上下文窗口
  3. 用功能标志管理未发布特性,别开长期分支
  4. Agent执行命令前必须做安全检查,白名单+参数过滤
  5. 发npm包之前,检查有没有.map文件混进去

Anthropic在安全上翻了车,但他们的Agent架构设计确实有东西。代码泄了不可怕,模型能力和工程迭代速度才是真正的护城河。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐