88行代码跑通AI智能体:从消息流到工具调用的极简设计真相
你可能听说过 Claude Code——Anthropic 推出的 AI 编程助手。但它的原始代码有 51万行 TypeScript,光是 QueryEngine 就占了 4.6 万行。别说读完,打开文件夹都卡顿。
可真相是:一个真正能工作的 AI 智能体(Agent),核心逻辑只需要 88 行代码。
这不是夸张,而是真实发生的事——社区开发者 Sigrid Jin 用 Rust 重写了整个系统,把 51 万行压缩到 2 万行,并完整保留了所有关键能力:自动调用工具、权限控制、多轮对话、子智能体、MCP 协议支持……全部在线。
为什么能砍掉 96% 的代码?因为原始版本里塞满了工程细节(打包、兼容、监控、UI 渲染),而真正驱动 AI 工作的「心跳」,其实非常简单。
下面带你直击最硬核的部分:那个只有 88 行的 run_turn() 循环——它就是 AI 智能体的“心脏”。
🧠 核心思想:消息即状态(Messages = State)
传统程序要维护一堆变量:当前步骤、历史记录、工具权限、是否超时……
Claude Code(Rust 版)只留一样东西:
// AgentRuntime 结构体里,唯一需要持久化的状态就是:
session.messages: Vec<Message> // 所有对话 + 工具结果都存在这里
✅ 用户输入 → 存为 UserMessage
✅ AI 回复 → 存为 AssistantMessage
✅ 工具执行结果 → 存为 ToolResult
✅ 权限拒绝提示 → 也存为 ToolResult(is_error: true)
没有状态机,没有流程图,没有全局变量。只要把这条消息链保存下来,整个对话过程就能随时恢复、调试、压缩、回放。
就像微信聊天记录——删掉 App,只要备份还在,你就没丢任何上下文。
⚙️ 看懂这 88 行:run_turn() 循环(Python 风格伪代码,已加中文注释)
def run_turn(user_input):
# 1️⃣ 把用户问题加到消息队列末尾
session.messages.append(UserMessage(user_input))
# 2️⃣ 开始循环:让 AI 反复思考、调用工具、直到自己决定停下
while True:
# 防止死循环:最多执行 max_iterations 次
if iterations > max_iterations:
raise Error("AI 思考太多次,强制终止")
# 3️⃣ 调用大模型(如 Claude),传入系统提示词 + 全部历史消息
response = api_client.stream(system_prompt, session.messages)
# 4️⃣ 解析模型返回的回复(可能是纯文字,也可能含工具调用指令)
assistant_message = parse_response(response)
session.messages.append(assistant_message) # 记录 AI 的“发言”
# 5️⃣ 提取 AI 想调用的工具(例如:bash、read_file、search_web)
tool_calls = extract_tool_uses(assistant_message)
# 6️⃣ 如果 AI 没想调工具 → 直接结束本轮,等待下一次用户输入
if not tool_calls:
break
# 7️⃣ 否则,挨个执行每个工具调用
for tool_name, input in tool_calls:
# 先检查权限:这个工具,当前 AI 有没有资格用?
permission = authorize(tool_name, input)
if permission == Allow:
# ✅ 有权限 → 执行工具(比如运行 shell 命令)
result = tool_executor.execute(tool_name, input)
session.messages.append(ToolResult(result)) # 把结果也记进消息流
else:
# ❌ 没权限 → 不报错、不中断,而是把“拒绝理由”当一条普通消息返回给 AI
session.messages.append(
ToolResult(deny_reason, is_error=True)
)
💡 关键洞察:
– AI 自己决定什么时候停(不是程序员写 if 判断);
– 错误不中断流程,而是变成消息反馈给 AI → 它会立刻调整策略(比如换工具、改提问);
– 所有中间结果都留在消息里 → 下一轮调用模型时,自然就“记得刚才发生了什么”。
🔧 工具怎么加?3 步搞定(以 bash 命令为例)
- 定义工具长啥样(JSON Schema)——直接喂给 AI 看
{
"name": "bash",
"description": "执行 Linux/macOS 命令(如 ls -l, git status)",
"input_schema": {
"command": "string", // 必填:要运行的命令
"timeout": "number?" // 可选:超时秒数,默认 30
},
"required_permission": "DangerFullAccess" // 运行前必须获得高危权限
}
- 写一个执行函数(Rust 或 Python 都行)
fn run_bash(command: String, timeout: u64) -> Result<String, String> {
// 实际调用 std::process::Command 执行命令
}
- 在调度器里加一行映射
match name {
"bash" => run_bash(input),
"read_file" => read_file(input),
"search_web" => search_web(input),
// ... 其他 15 个工具,每加一个只多 1 行
}
✅ 新工具上线:不用改模型、不用改循环、不用动权限系统——只加 3 处,5 分钟搞定。
🛡️ 权限系统:不是“开/关”,而是“问/等/拒”
它不搞一刀切。比如 AI 想运行 bash(高危操作),但当前只有 WorkspaceWrite 权限:
- 权限差 1 级 → 弹窗问用户:“AI 想执行命令,允许吗?”
- 权限差 2 级以上 → 直接拒绝,并把理由写进消息流(AI 看得见!)
- 完全匹配 → 静默通过
这样既安全,又不打断思考流。
子智能体(sub-agent)也沿用同一套规则:它权限很高,但不能主动向用户要新权限——彻底堵住递归失控风险。
💡 一句话总结 Part 1 的硬核收获:
一个能干活的 AI 智能体,不需要框架、不需要状态机、不需要复杂配置。
它只需要:
– 一个循环(88 行)
– 一套工具接口(JSON Schema + 执行函数)
– 一个渐进式权限判断(5 级 + 人机确认)
– 和一个信念:消息流,就是全部状态。
剩下的 50 万行?那是让它“更好用、更稳定、更易维护”的工程层——不是让它“能工作”的必要条件。
下一期我们拆解:AI 是怎么记住上下文的?Prompt 怎么拼?历史消息太多怎么自动压缩?哪些设计模式真正在项目里救过命?
直达网址:https://claw-code.codes/
