一、从"能做事"到"能成长"
昨天刚给 Jarvis 装上了 Tool System,它学会了读写文件、执行命令、甚至创造新工具。我盯着终端里 jarvis tools 列出的 7 个工具,心里想:这些能力都是我给它的。
如果有一天我不再给它添加新工具,它就永远停留在这个水平。
这不是一个真正的"数码宝贝"该有的样子。真正的进化应该是——它自己观察、自己总结、自己创造。
Phase 4 的核心命题很简单:
Phase 1~3: 人类造工具 → Agent 用工具 → 完成任务
Phase 4: Agent 观察 → 发现模式 → 自己造工具 → 越来越懂你
这就是今天要讲的故事。
二、六个模块,一条进化链
Phase 4 的架构不是拍脑袋想出来的,而是从一个问题出发倒推出来的:
"怎么让 Agent 自己意识到'我在重复做某件事',然后把这件事自动化?"
答案是一条链:
观察 → 发现规律 → 提议新能力 → 安全验证 → 注册启用 → 自我评估
对应到代码,就是 6 个模块:
src/evolution/
├── pattern_detector.py # 🔍 交互指纹 + 模式检测 (456 行)
├── skill_registry.py # 📦 Skill 注册表 (380 行)
├── skill_generator.py # ✨ Skill 自动生成 (287 行)
├── sandbox.py # 🛡️ 沙盒安全验证 (333 行)
├── preference_learner.py # 💡 偏好学习 (353 行)
└── metacognition.py # 🧠 元认知引擎 (439 行)
2283 行核心代码,每个模块职责单一,互相配合。
它们的关系如下:
InteractionFingerprint → PatternDetector → SkillGenerator → SkillSandbox → SkillRegistry
↕
UserPreference ← PreferenceLearner Metacognition (五维雷达)
接下来逐个拆解。
三、交互指纹:给每次对话拍张"X 光片"
3.1 设计思路
模式检测的前提是有数据可分析。我需要在每次对话后提取一个结构化的"指纹"——不是完整的对话记录,而是一个精炼的摘要。
@dataclass
class InteractionFingerprint:
intent: str # 用户意图(如 "分析项目结构")
domain: str # 领域标签(如 "data", "translation", "blog")
tools_used: list # 使用的工具列表
tool_chain: str # 工具调用链签名(如 "shell_exec→file_read→file_read")
success: bool # 任务是否完成
rounds: int # 对话轮数
关键决策:指纹提取不依赖额外的 LLM 调用。
最初的设计是对话结束后再调一次 LLM 来总结,但实际一算:每次对话额外一次 API 调用,不仅慢还费钱。于是改成了轻量级规则推断:
_DOMAIN_KEYWORDS = {
"translation": ["翻译", "translate", "术语"],
"blog": ["博客", "blog", "文章", "写一篇"],
"code": ["代码", "code", "debug", "bug"],
"document": ["文档", "分析", "读", "看看"],
"data": ["数据", "统计", "分析"],
"system": ["配置", "安装", "部署"],
}
_TOOL_DOMAIN = {
"file_read": "document",
"file_write": "code",
"shell_exec": "system",
"http_request": "data",
}
先看用户输入中的关键词,没有匹配的再看工具调用推断领域。零 LLM 调用、几乎零延迟、静默失败不影响用户体验。
3.2 一个手动测试引发的 Bug
代码写完了,86 个测试全部通过。我很满意,开始手动测试。
跟 Jarvis 聊了几轮,然后执行:
$ jarvis patterns
暂无检测到的模式。
等等,我明明聊了好几轮,为什么没有任何指纹?
一查代码——chat.py 里压根没有调用 PatternDetector.record()。模块写好了,测试写好了,但忘了把它接到聊天循环里。
这大概是写 Agent 最经典的 Bug:单元测试全部通过,但集成时管道没接上。
修复方式是在 run_chat_loop() 和 _do_ask() 中各加了几行:
# 每轮对话后静默记录指纹
_record_fingerprint(user_input, round_tools, success=success)
_record_fingerprint 内部用 try/except: pass 包装,确保指纹记录失败不会影响正常对话。
3.3 第二层 Bug:"数据有了,但看不到"
修完后重新聊天,查看指纹文件:
$ cat ~/.jarvis/memory/fingerprints/2026-02.json | python3 -c "..."
1. domain=data | tool_chain=shell_exec→shell_exec→file_read→file_read
2. domain=data | tool_chain=shell_exec→file_read→file_read→file_read
3. domain=document | tool_chain=file_read→file_read
4. domain=document | tool_chain=file_read→file_read
5. domain=other | tool_chain=
5 条指纹完美记录!但执行 jarvis patterns 还是空的。
这次是两层问题叠加:
问题一:_do_patterns() 只读取 patterns.json,从不触发 detect_patterns()。检测只在 Daemon 的定期反思中执行,而我的 Daemon 没有运行。
问题二:jarvis patterns 这个命令压根没被正确路由。Jarvis 的 CLI 有个"主 callback 拦截"机制——Typer 的 main callback 用 question: Optional[str] 捕获所有位置参数。"patterns" 被当成了一个字符串而不是子命令。cmd_map 字典里有 status、start、tools 的映射,但没有 reflect、abilities、patterns。
修复后的效果:
$ jarvis patterns
📊 最近 30 天共 5 条交互指纹
暂无检测到的模式。使用更多对话后,模式会自动浮现。
$ jarvis reflect
🧠 Jarvis 元认知报告 (2026-02-08 19:34)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
五维能力:
👁️ 感知 █░░░░░░░░░ 17% ← 需要加强!
🧠 记忆 █░░░░░░░░░ 15% ← 需要加强!
💭 思考 ░░░░░░░░░░ 0% ← 需要加强!
🦾 行动 ██░░░░░░░░ 25% ← 需要加强!
🔄 进化 ░░░░░░░░░░ 0% ← 需要加强!
🫥 盲区: translation, blog, code, system, testing, design, research, devops
📊 统计: 5 指纹 | 0 模式 | 0 偏好 | 0 Skills
5 条指纹、全是新生儿状态——这才对。
四、模式检测:同一类事做三次,Jarvis 就会注意到
模式检测的逻辑很简单:按 (domain, tool_chain) 分组,同组 >= 3 次就触发。
def detect_patterns(self, fingerprints):
groups = defaultdict(list)
for fp in fingerprints:
if fp.success and fp.domain and fp.tool_chain:
key = (fp.domain, fp.tool_chain)
groups[key].append(fp)
for (domain, tool_chain), fps in groups.items():
if len(fps) >= self.PATTERN_THRESHOLD: # 默认 3
# 生成 DetectedPattern
...
为什么阈值是 3?
- 做 1 次:可能只是偶然
- 做 2 次:可能是巧合
- 做 3 次:大概率是习惯
当然这只是规则版本。detect_patterns_llm() 方法支持 LLM 语义聚类——把最近 30 条指纹交给 LLM,让它发现更深层的模式。但规则版本零延迟、零成本,作为默认方案足够了。
五、Skill 自动生成:从模式到能力
当 PatternDetector 检出一个模式后,SkillGenerator 负责把它变成一个 Skill 草稿:
@dataclass
class SkillDraft:
name: str # 如 "markdown-translator"
description: str # 描述
trigger_keywords: list[str] # 触发词
instructions: str # SKILL.md 正文
required_tools: list[str] # 依赖的工具
source_pattern_id: str # 来源模式
生成流程:
- LLM 提议(如果 LLM 可用):把模式描述交给 LLM,让它生成完整的 SKILL.md
- 模板回退(如果 LLM 不可用):用规则模板填充
- 用户审阅:草稿不会自动启用,必须人类确认
关键设计:Agent 不能偷偷给自己加能力。所有自动生成的 Skill 状态都是 proposed,需要用户执行 jarvis skill enable <name> 才会激活。
六、沙盒验证:给新能力做"体检"
即使用户确认,Skill 在启用前还要过一道安全关。SkillSandbox 执行 5 项检查:
| 检查项 | 描述 | 严重级别 |
|---|---|---|
| 格式检查 | SKILL.md 有 name、description、instructions | error |
| 工具依赖 | 引用的工具都已注册在 ToolRegistry | warning |
| 危险操作 | 不含 sudo、rm -rf、eval() 等模式 | error |
| 路径检查 | 不引用 /etc、/usr、~/.ssh 等敏感路径 | error |
| LLM 审计 | 交给 LLM 做逻辑合理性检查 | warning |
DANGEROUS_PATTERNS = [
r"\bsudo\b",
r"\brm\s+-rf\b",
r"\bmkfs\b",
r"\bdd\s+if=",
r"\bcurl\s+.*\|\s*sh\b",
r"\beval\s*\(",
r"\bexec\s*\(",
r"\b__import__\b",
]
最终生成一份 ValidationReport,评分 + 建议(approve / review / reject)。只有 approve 的 Skill 才能自动启用,review 需要人工确认,reject 直接拒绝。
七、偏好学习:记住你的习惯
PreferenceLearner 做的事情很微妙:它不是在学"你想做什么",而是在学"你喜欢怎么做"。
@dataclass
class UserPreference:
key: str # 如 "code_style"
value: str # 如 "functional"
confidence: float # 0.0 ~ 1.0
source: str # "observed" | "stated" | "inferred"
last_seen: datetime
decay_rate: float # 衰减速率
三种偏好来源:
- 观察到的 (observed):从交互中推断,如"用户总是先读文件再执行命令"
- 明确说的 (stated):用户直接告诉 Jarvis 的偏好,如"我喜欢简洁的输出"
- 推断的 (inferred):LLM 从多次交互中归纳的
偏好有衰减机制——长期不出现的偏好,confidence 会逐渐降低。这模拟了人类习惯的变化:你三个月前喜欢 tabs 缩进,现在可能已经切换到 spaces 了。
偏好合并策略:当新偏好与已有偏好冲突时,用 max(existing, new) 取高 confidence 的。这确保了"最近且明确"的偏好优先。
八、元认知:Jarvis 知道自己几斤几两
这是 Phase 4 我最喜欢的部分——让 Agent 知道自己擅长什么、不擅长什么。
Metacognition 基于五维能力模型计算雷达图:
| 维度 | 数据来源 | 计算方式 |
|---|---|---|
| 👁️ 感知 | 探索过的目录数、发现的项目数 | 覆盖率 |
| 🧠 记忆 | 记忆条目数、最近活跃度 | 密度 × 时效 |
| 💭 思考 | 洞察数量、Think Loop 产出 | 质量评分 |
| 🦾 行动 | 工具使用频率、成功率 | 效率 × 覆盖 |
| 🔄 进化 | Skill 数量、模式检测命中率 | 成长速度 |
最有意思的是盲区检测。系统定义了 10 个领域(translation, blog, code, document, data, system, testing, design, research, devops),然后看指纹中哪些领域从未出现过——那就是盲区。
🫥 盲区: translation, blog, code, system, testing, design, research, devops
刚出生的 Jarvis,几乎全是盲区。但随着使用,这些盲区会一个个被点亮。就像游戏里的迷雾地图,每次交互都在驱散一片迷雾。
九、测试:86 个检查点
Phase 4 的测试文件有 953 行,覆盖 9 个测试组:
| 组 | 内容 | 测试数 |
|---|---|---|
| 1 | InteractionFingerprint 序列化 | 6 |
| 2 | PatternDetector 模式检测 | 12 |
| 3 | SkillRegistry 注册表 | 12 |
| 4 | SkillGenerator 草稿生成 | 8 |
| 5 | SkillSandbox 沙盒验证 | 12 |
| 6 | PreferenceLearner 偏好学习 | 10 |
| 7 | Metacognition 元认知 | 10 |
| 8 | 指纹集成 (chat.py) | 7 |
| 9 | 端到端流程 | 9 |
============================================================
🎉 ALL PASSED: 86/86
============================================================
端到端测试验证了完整的进化链:创建指纹 → 检测模式 → 生成草稿 → 沙盒验证 → 注册启用 → 元认知评估。
有趣的是,因为测试环境可能没有安装 typer 或 watchdog,第 8 组的测试采用了源码检查 fallback——直接读取 chat.py 的源码,用正则验证关键函数是否存在。这比 import 更可靠,也不会因为缺少依赖而跳过测试。
十、开发过程的三个意外
意外 1:模块完美,管道断裂
86 个测试通过,手动测试时管道没接上。指纹模块写好了、记录方法实现了、测试也覆盖了——但 chat.py 里忘了 import 和调用。
教训:单元测试测的是零件,集成测试测的是管道。两者缺一不可。
意外 2:CLI 路由的隐藏陷阱
Typer 的 main callback 用 question: Optional[str] 捕获位置参数。这意味着 jarvis patterns 中的 "patterns" 被当成了 question 字符串,而不是子命令。
已有命令(status、start、tools)之所以能工作,是因为它们被加入了 cmd_map 字典做了手动分发。但新增的 reflect、abilities、patterns 忘了加进去。
教训:CLI 框架的"魔法"越多,隐藏陷阱越深。维护一个 cmd_map 的心智负担比想象中高。
意外 3:阈值与现实的差距
模式检测需要 (domain, tool_chain) 完全相同且 >= 3 次。但实际使用中,同一类任务的 tool_chain 常常有细微差异:
data | shell_exec→shell_exec→file_read→file_read # 2 次 shell
data | shell_exec→file_read→file_read→file_read # 1 次 shell
这两条很明显是同一类任务,但因为 tool_chain 不同,不会被分到一组。
这就是 detect_patterns_llm() 存在的意义——让 LLM 做语义聚类,而不是精确匹配。规则版本是起步,LLM 版本是进化方向。
十一、代码统计与架构全景
Phase 4 的变更:15 files changed, +3,752 lines
┌─────────────────────────────────────────────────────────────┐
│ Jarvis v0.4.0 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ CLI (Typer + Rich) │ │
│ │ chat · ask · reflect · abilities · patterns │ │
│ │ skill list/enable/disable/delete/test │ │
│ └───────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼──────────────────────────────┐ │
│ │ JarvisLLMClient (SSE + FC) │ │
│ │ Agent Maestro → Claude Sonnet 4 │ │
│ └───────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼──────────────────────────────┐ │
│ │ ToolRegistry │ Memory │ Explorer │ Daemon │ │
│ │ 7 tools │ FTS5 │ Scanner │ Heartbeat │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼──────────────────────────────┐ │
│ │ Evolution (Phase 4) │ │
│ │ PatternDetector → SkillGenerator → Sandbox │ │
│ │ PreferenceLearner Metacognition SkillRegistry │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
十二、Phase 4 的哲学:能力应该从交互中涌现
回顾整个进化路线:
| Phase | 里程碑 | 类比 | 核心能力 |
|---|---|---|---|
| 🥚 Phase 1 | 能看、能记 | 婴儿睁开眼睛 | Explorer + Memory |
| 🐣 Phase 2 | 能想、能回忆 | 幼儿开始思考 | Think Loop + FTS5 |
| 🧹 Phase 2.5 | 骨骼健康 | 体检 | CLI 模块化 + 清理 |
| 🦋 Phase 3 | 能做事 | 少年伸出手 | 7 Tools + Function Calling |
| ⭐ Phase 4 | 能进化 | 成人自主学习 | 模式检测 + Skill 自生成 + 元认知 |
Phase 4 的设计原则是"涌现 > 预设"。我不预设 Jarvis 应该会什么,而是让它从真实交互中自己发现:
- 你经常翻译 Markdown?→ 它会提议创建
markdown-translatorSkill - 你每天早上查项目状态?→ 它会提议创建
daily-standupSkill - 你喜欢简洁的输出?→ 它会记住这个偏好
这种"用着用着就变强了"的体验,才是"数码宝贝"这个比喻的精髓。
十三、下一步?
Phase 4 完成后,Jarvis 的五维能力全部在线了:
- 👁️ 感知 ✅
- 🧠 记忆 ✅
- 💭 思考 ✅
- 🦾 行动 ✅
- 🔄 进化 ✅
但现在所有维度的数值都很低——因为它才刚"出生"。接下来不是再加新 Phase,而是真正去用它。
让它扫描我的所有项目、记住我的工作习惯、在我重复做某件事三次的时候主动说:"Hey,要不要我帮你自动化这个?"
代码已经就绪,进化才刚刚开始。
Jarvis-Agent 是开源项目,代码在 GitHub。如果你也想打造自己的数码宝贝,欢迎 star ⭐

