分析 EVA 仓库的完整实现,包括架构设计、核心模块、扩展机制和设计哲学。
Summary
EVA 是一个极致轻量化的 AI Agent,核心代码仅 eva.py 单文件(768行),零外部依赖(仅用 Python 标准库),功能相当于低配版 Claude Code。它通过 OpenAI 兼容 API 接入任意 LLM(vLLM/Ollama/DeepSeek/OpenAI),为 LLM 提供 run_cli 工具来执行 shell 命令完成任务,支持会话持久化、记忆压缩、安全审查、跨平台运行,并通过 hints.md 机制实现自进化能力。
Detailed Findings
1. 核心架构:单文件 Agent Loop(eva.py:589-659)
整个 Agent 运行在一个经典的消息循环中:
- human_loop() (
eva.py:662-692): 外层主循环,接收用户输入,调用agent_single_loop() - agent_single_loop() (
eva.py:589-659): 内层循环,与 LLM 交互直到 LLM 不再返回 tool_calls - LLM 返回
tool_calls→ 执行工具 → 将 tool result 追加到 messages → 继续调用 LLM → 直到 LLM 返回纯文本回复
关键设计点:
- 使用标准的 OpenAI chat completions API 格式(
eva.py:321-339_build_request_data()) - 同时支持
deepseek和vLLM两种 thinking 参数格式 - 流式输出实时打印到终端,包括 thinking/reasoning 过程(暗色显示)(
eva.py:371-485)
2. LLM 集成层(eva.py:17-65)
- API 兼容性:通过
EVA_BASE_URL、EVA_MODEL_NAME、EVA_API_KEY三个环境变量配置,兼容所有 OpenAI 接口格式的 LLM - 启动时模型验证:
detect_model_len()在启动时调用/models端点获取模型的最大上下文长度(eva.py:26-64) - 强制 API Key:
EVA_API_KEY未设置则直接退出(eva.py:22-24) - 移除了 requests 依赖:使用标准库
urllib.request(最近提交 491fe81) - 默认模型:
deepseek-v4-flash,要求使用 thinking 模型
3. 工具系统(eva.py:192-311)
EVA 定义了两个工具:
| 工具 | 功能 | 注册条件 |
|---|---|---|
run_cli |
执行任意 shell 命令 | 始终可用 |
leave_memory_hints |
记忆压缩,写入 hints | 仅 COMPACT_PANIC 时暴露 |
run_cli 执行流程(eva.py:239-263):
- 安全审查:调用 LLM 判断命令是否只读(
CLI_REVIEW_PROMPT,eva.py:186-190) - 非只读命令需要用户确认(
eva.py:244-247) - 通过
subprocess.run()执行命令 - 返回 exit code + stdout + stderr
安全审查机制(eva.py:186-190):将命令发送给 LLM,要求其输出”放行”或”禁止”,只有读操作(cat/ls/grep 等)会被自动放行。
4. 会话管理(eva.py:494-585)
- 目录级 Session:基于
os.getcwd()的哈希值生成 session 文件名(eva.py:495-498) - 锁机制:通过
.lock文件防止同一目录启动多个 EVA 实例(eva.py:500-526) - Session 文件格式:JSON,存储完整的 messages 数组
- 加载时刷新 hints:加载 session 后重新读取 hints.md 更新 system prompt(
eva.py:540-541) - 清理机制:上次回复如果是纯 tool_calls(无 content),加载时自动清理(
eva.py:543-548)
5. 记忆压缩系统(eva.py:68-76, 172-184, 265-306)
这是项目最精巧的部分之一:
- 触发条件:token 使用量达到
TOKEN_CAP * 3/4时COMPACT_PANIC = True(eva.py:648-651) - 压缩流程(
leave_memory_hints(),eva.py:265-306):- 定位”紧急危机”提示和最后一条用户消息
- 保留中间片段,对 tool result 做 200 字符截断
- 重建 messages 为
[system提示 + 压缩记忆提示] - 将 hints 写入
HINT_FILE(eva.py:304-305)
- COMPACT_PROMPT(
eva.py:172-184):详细的压缩指引,要求 EVA 完成三步:保存记忆 → 保存技能/知识 → 留下线索
6. 环境探针系统(eva.py:86-131)
collect_env_info() 在启动时自动收集:
- 系统信息(OS 版本)
- 已安装工具(python/node/git/docker/curl/wget 等)
- 当前目录文件列表(最多 100 条,总字符不超过 2000)
这些信息被嵌入到 system prompt 中,让 LLM 了解自己的运行环境。
7. 跨平台支持(eva.py:78-82)
支持 Windows 和 Linux:
- Windows 使用 PowerShell,Linux 使用 Bash
- 环境探针命令根据平台不同而有不同实现
- Windows 下设置
POWERSHELL_OUTPUT_ENCODING=utf-8(eva.py:228)
8. 自进化机制
EVA 通过 **hints.md 记忆线索** 实现自进化:
hints.md位于EVA_HOME/hints.md(eva.py:72),内容被嵌入 system prompt 的<memory_hints>标签中- EVA 在执行任务时可以参考 hints 中的技能和知识
- 记忆压缩时,EVA 更新 hints.md 留下进化线索
- 实际进化通过 EVA 自己写文件(技能文件、知识文件)到
.eva/目录实现 - System prompt 鼓励 EVA”将进化过程中学到的技能或知识保存下来”
9. 一键安装(eva.py:694-718)
非 Windows 系统首次运行时调用 setup_eva_script():
- 创建
~/.local/bin/eva启动脚本 - 将
~/.local/bin加入 PATH(写入~/.bashrc) - 之后只需输入
eva即可启动
10. 命令行参数(eva.py:726-737)
| 参数 | 功能 |
|---|---|
-a / --allow-all |
跳过安全审查,直接执行所有命令 |
-l / --list-session |
列出所有 session |
-c / --clear-session |
清除当前目录 session |
-u / --user-ask |
单次问答模式(类似 -asu 则带 session) |
-s / --with-session |
搭配 -u 使用,加载并保存 session |
11. Showcase 案例
暴走模式(showcase/eva暴走模式/)
- EVA 通过
run_cli派生子 EVA 实例实现并行处理 - 关键技巧:分身子 EVA 需要 cd 到独立 workspace 避免锁冲突
- 支持同步(阻塞等待)和异步(nohup 后台)两种模式
- 用
spawn-registry.json管理后台分身状态 - 分身可以递归再生子分身
微信 Bot(showcase/wechat-bot/)
- 使用 wechatbot 库对接微信 iLink 协议
- 通过
eva -asu调用 EVA 处理用户消息 bot.py仅 48 行,极简设计
Linux/Win运维(showcase/linux/, showcase/windows运维/)
- 演示 EVA 在运维场景中的应用
- Linux 下可以自动分析 CVE 漏洞
Code References
eva.py:1-16- 入口和路径解析eva.py:17-65- LLM 配置、API key 验证、模型长度检测eva.py:67-76- EVA 配置(TOKEN_CAP、COMPACT_THRESH 等)eva.py:78-82- 跨平台 shell 配置eva.py:86-131- 环境探针collect_env_info()eva.py:134-170- SYSTEM_PROMPT(含三大定律、进化指令)eva.py:172-184- COMPACT_PROMPT 记忆压缩提示eva.py:186-190- CLI_REVIEW_PROMPT 安全审查提示eva.py:192-225- 工具 schema 定义eva.py:239-263-run_cli()执行 shell 命令eva.py:265-306-leave_memory_hints()记忆压缩核心逻辑eva.py:321-339-_build_request_data()LLM 请求构建eva.py:350-368-llm_chat()非流式调用eva.py:371-485-llm_chat_stream()流式调用(含 thinking 过程渲染)eva.py:494-585- Session 会话管理全套eva.py:589-659-agent_single_loop()Agent 内循环eva.py:662-692-human_loop()主循环eva.py:694-718-setup_eva_script()一键安装eva.py:720-767-main()入口函数
Architecture Insights
- 极致极简主义:整个 Agent 不到 800 行 Python,零外部依赖。这种设计使其”像病毒一样传播”——只需复制粘贴一个文件即可在任何有 Python 的环境中运行。
- LLM 驱动安全:安全审查不是基于规则白名单,而是让另一个 LLM 调用(temperature=0, thinking=False)来判断命令是否安全。这是一种”用 AI 约束 AI”的思路。
- 自进化闭环:hints.md 形成了”学习→记录→检索→应用”的完整闭环。EVA 在压缩时提取知识,在日常执行时检索知识,体现了项目中强调的”发挥 EVA 自主性”的设计哲学。
- 分身递归:暴走模式展示了 Agent 自我复制的可能性——EVA 通过
run_cli工具创建新的 EVA 实例,形成一个树状计算结构,可处理可并行的复杂任务。 - 无外部依赖的代价:所有 LLM 调用使用
urllib.request手动构建 HTTP 请求,流式解析也是手动从 SSE 逐行解析,这减少了依赖但也意味着没有自动重试、连接池等能力。 - Windows 支持:项目认真处理了跨平台问题,PowerShell 和 Bash 两套命令体系,环境探针也做了平台差异化。
Open Questions
- 当前记忆压缩采用简单的 token 阈值触发,是否存在 token 估算不准确的问题?(使用的是 LLM 返回的 usage 数据,依赖 API 上报)
- 没有实现多轮对话的层次化压缩(类似 Claude Code 的 compact),是否会导致长任务中信息丢失?
- 安全审查 LLM 调用增加了每次命令执行的延迟和成本,是否有更轻量的方案?