花 3 个月从零写了个 AI Coding Agent:HumanEval 跑出 87.8% 和 GPT-4o 持平

作者:袖梨 2026-07-13

从零构建 AI Agent:6 个让我重新理解"智能体"的工程决策

先看结果

在 HumanEval 基准(164 道 Python 函数补全题)上,OmniAgent 驱动的 deepseek-v4-pro 跑出了 144/164 (87.8%) 的成绩:

花 3 个月从零写了个 AI Coding Agent,HumanEval 跑出 87.8%,和 GPT-4o 持平

对比HumanEval pass@1
Claude 3.5 Sonnet~92%
GPT-4o~90%
OmniAgent + v4-pro87.8%
GPT-4~87%
DeepSeek V3~87%
CodeLlama 34B~56%

但分数只是结果。更值得讲的是:这个 Agent 是怎么设计的。

为什么写这篇文章

2026 年,AI Agent 框架已经多到让人麻木。LangChain、CrewAI、AutoGPT……每个都号称"几行代码搭 Agent"。但当你真正想理解一个 Agent 的内部机制——不是怎么用,而是怎么造——会发现大多数框架把复杂度藏在了抽象层后面。

我花了几个月从零写了一个终端 AI Agent(OmniAgent),不是为了跟 Claude Code 竞争,是为了把 Agent 核心机制做透、看懂。这篇文章分享 6 个关键设计决策。

决策 1:引擎是类,不是 Prompt

直觉做法:切换 Agent 行为 = 换 system prompt。加一句"你要先做计划再执行"就行。

实际问题:Prompt 只能影响 LLM 输出内容,不能改变控制流。

ReAct 需要 observe → think → act → observe 循环,Plan-Execute 需要 decompose → topological_sort → parallel_execute → synthesize,Reflection 需要 execute → critic_review → revise → critic_review 双模型多轮。

这三个的代码结构完全不同。把它们压在 prompt 里,等于让 LLM 自己管理控制流——而 LLM 最不擅长的就是保持状态一致性。

omniagent/engine/├── direct_engine.py# 一条路走到黑├── react_engine.py # 循环直到 finish├── plan_execute_engine.py # DAG + 拓扑排序├── reflection_engine.py# 双模型互审├── novel_engine.py # 创意写作专用├── plan_react_engine.py# 计划 → ReAct├── plan_reflection_engine.py# 计划 → 审查└── react_reflection_engine.py # ReAct → 审查

每个引擎是独立的类,有自己的 run() 方法。切范式不是改 prompt 文本——是 EngineFactory.create(mode) 换一个类。

决策 2:断路器不应该"一次挂、次次挂"

直觉做法:工具调用失败 → 捕获异常 → 返回错误给 LLM → LLM 决定下一步。

实际问题:LLM 会反复用同样的参数调同一个失败的工具。你见过 LLM 在循环里连续 5 次 read_file("不存在的路径") 吗?我见过。

标准答案是加个重试装饰器。但 Agent 的真正问题是:有些失败是可恢复的(网络抖动),有些是终局性的(文件不存在)。重试机制不分青红皂白统一退避,浪费 token 和时间。

我做的:

# 每个工具有独立的断路器实例breaker = CircuitBreaker(failure_threshold=3, # 3 次连续失败 → 熔断cooldown_seconds=30, # 30s 后进 half_openbackoff_multiplier=2, # 再失败冷却翻倍,上限 600s)

关键不是"熔断"本身——是这个状态跨 LLM 轮次持久化。GLOBAL_BREAKERS 字典在 Agent 的整个生命周期存活。

决策 3:上下文压缩不是"取最后 N 条"

直觉做法:对话太长 → 只保留最近 10 条消息。

实际问题:最近 10 条可能全是工具输出(read_file 返回的 200 行代码),而 15 条之前的 user prompt 才是真正需要的语义信息。

我实现的是 6 步压缩流水线:

1. 摘要 → LLM 生成对话摘要2. 精简 → 工具输出超过 20 行部分用 [N lines truncated] 替换3. 去重 → 连续相同的工具调用合并为 "[N repeated calls]"4. 评分 → 按语义密度给每条消息打分5. 裁剪 → 保留 Top-N 条高分消息6. 重组 → 摘要 + 裁剪结果拼回 messages 列表

触发条件不是"消息数 > N",而是 Token 窗口达 80% 。

决策 4:花钱也要分阶段

Agent 在任务尾声还在调 list_filessearch_files——Token 被浪费在无意义的浏览上。

解决:三阶段软预算。

阶段Token 配额可用工具
EXPLORE25%全部 20 个
EXECUTE50%全部 20 个
CONVERGE25%禁用 7 个纯探索工具

决策 5:MCP 子进程管理不是 subprocess.run

  1. readline() 会无限阻塞
  2. 进程崩溃变僵尸
  3. 守护进程挂了 Agent 不知道

解决:select + 墙钟超时 + terminate() + 兜底 kill()。守护进程崩溃自动重启(最多 3 次)。

决策 6:空输入检测不能只靠 LLM

LLM 有时候会返回:

——实际什么都没做。HollowDetector 用 15 个正则 + 组合判定来识别空洞回答,靠模式匹配 + 工具调用记录交叉校验。

这些决策的通用性

上面 6 个设计决策不绑定 OmniAgent。无论你用什么框架——LangChain、CrewAI、自研——只要你的 Agent 需要:

  • 多种推理模式 → 把范式差异放在控制流层,不要全压在 prompt 上
  • 可靠执行 → 加断路器,且状态要跨轮次持久化
  • 长对话 → 上下文压缩要按语义密度裁剪,不是简单截断
  • 预算控制 → 分阶段限制可用工具,收束阶段裁掉探索型工具
  • MCP 集成 → 用 select + 超时而不是 readline
  • 输出质量 → 空洞检测基于模式匹配,不要靠 LLM 自检

项目地址

GitHub: github.com/xianyu-shen…

19K 行 Python,MIT 开源,1000+ 测试,HumanEval 87.8%。

如果你也在学习 Agent 架构,欢迎 ⭐Star、读源码、提 issue。

相关文章

精彩推荐