MINARA

学习系统

Agent 如何在重复任务中持续改进

Minara Agent 内置了一个学习循环,可记录成功的工具调用序列,并在后续轮次中以建议形式呈现。与模型微调不同,整个机制均基于 SQLite 行操作,无需训练任务、模型更新或离线流水线。本页说明各组成部分及其与技能系统的协作方式。

"学习"的含义。 在 Minara 中,"学习"不涉及微调或权重更新,模型本身保持不变。每轮开始前,Agent 会从 SQLite 中检索内容:一组已验证的 {tool_name, args} 序列、自由文本指导说明,以及结构化方法论。当前轮次与历史成功轮次相似时,历史序列会作为建议提供。设计上以可审计性换取复杂度:所有"已学习"的行为均以数据库行的形式存储,可读取、编辑或删除,运营者可完整检查。

实际效果请参阅功能 → 自我提升介绍了面向用户的界面,包括如何引导 Agent 保存一条经验,以及这些经验在后续会话中的呈现方式。

关于并行的决策反思循环(对特定技能调用是否正确进行两阶段 LLM 分类,按角色隔离),请参阅 角色记忆。该系统与本系统并行运行,回答的是另一个问题:不是"我如何成功",而是"这个具体决策是否正确,原因是什么"。

学到什么

三类产物存储于 learnings 表及 apps/agent/src/learning/ 下的关联表中:

  1. 工具序列。 Agent 成功完成任务时所执行的 {tool_name, args} 有序列表,在 Agent 显式调用 skill_learn 时于轮次末尾记录。
  2. 指导说明。 简短的自由文本片段,例如"获取 Polymarket 价格时,请对具体市场 URL 使用 web_extract,API 限速为每分钟 10 次"。与工具序列一同存储。
  3. 方法论。 带有成功标准的结构化多步计划,由 learning/structured-methodology.ts 存储,适用于纯工具序列表达力不足的深度研究工作流。

反馈循环

learning-system diagram

review-engine

learning/review-engine.ts 是一个轻量级 LLM 处理过程,在每轮结束时运行(通过安装在 app.ts 中的 review-engine-hook.ts)。其步骤如下:

  1. 检查本轮工具调用序列。
  2. 过滤掉调用次数少于 N 次或明显失败的轮次。
  3. 以快速档模型调用结构化提示词,询问"任务是否完成?新颖性如何?可复用性如何?"
  4. 输出包含 {score, summary, suggested_trigger, suggested_tool_sequence}ReviewResult

若评分超过阈值,结果将传递给技能管理器。

skill-manager

learning/skill-manager.ts 负责管理 learnings 表。收到合格评审结果后,写入以下数据:

{
  id: uuid,
  name: "hyperliquid_open_long_with_tp_sl",
  trigger: "open long on hyperliquid with tp/sl",
  tool_sequence: [...],
  guidance: "always set TP before SL; Hyperliquid's 'reduce_only' flag...",
  created_at,
  success_count: 1,
  failure_count: 0,
  last_used_at: null,
}

同时执行去重:若触发词相近的条目已存在(通过 learning/similarity.ts 计算余弦相似度,通过 learning/tfidf.ts 计算 TF-IDF),则更新现有行的计数器,而不新建重复条目。

evaluation-loop

learning/evaluation-loop.ts 在每轮开始时运行,步骤如下:

  1. 基于用户消息和路由上下文构建 TF-IDF 查询。
  2. 对所有学习条目评分。
  3. 返回前 K 个匹配项(默认 3 个)。
  4. 传给提示词构建器,作为 <learnings> 块追加到系统提示词中,包含触发词、工具序列摘要和指导说明。

LLM 可自由选择采纳或忽略该建议;两种选择均会更新学习条目的计数器。采纳并成功完成的轮次会使 success_count 递增;被忽略的学习条目则会缓慢衰减。

方法论:结构化计划

深度研究轮次会生成另一类产物:方法论。工具序列是扁平列表,而方法论是带有成功标准的阶段树:

{
  id, name,
  phases: [
    {name: "Gather", criteria: [...], tools_used: [...]},
    {name: "Synthesize", criteria: [...], depends_on: ["Gather"]},
    {name: "Verify", criteria: [...], depends_on: ["Synthesize"]},
  ],
  asset_class: "crypto_alt",
  ...
}

存储层为 learning/methodology-store.ts,深度研究技能从中读取数据以初始化多阶段研究计划。对于"收集哪些中间证据"比"调用哪些工具"更重要的任务,方法论是更精细的学习产物。

与向量记忆的区别

简单的向量记忆只存储事实并按需召回。学习系统存储的是过程:"如何完成此类任务",并将其作为可执行建议提供。二者的区别如下:

  • 向量记忆回答"我了解 BTC 的哪些信息?"
  • 学习系统回答"我通常如何处理在 Hyperliquid 带 TP/SL 做多 BTC 的请求?"

两者互补,Agent 同时使用。记忆查询在技能层通过 memory_search 发起;学习查询在 Agent 循环中于首次 LLM 调用之前发起,作为提示词组装的一部分。

安全属性

学习条目是建议,绝非强制指令。具体而言:

  1. 学习条目无法绕过权限等级钩子。 若建议的 tool_sequence 包含四级工具,在轮次来源不允许的情况下仍会被阻止。
  2. 学习条目无法绕过 L3 风险门控。 若建议的序列需要激活带 requires_user_confirmation 的技能,正常确认流程照常执行。
  3. 学习条目不能存储密钥。 工具序列中记录的 args 经过与审计日志相同的脱敏处理。
  4. 失败的轮次不会成为学习条目。 评审引擎在技能管理器接触之前已将其过滤。

检查与管理

# 按成功率排序的顶级学习条目
sqlite3 $dataDir/minara.db \
  "SELECT name, success_count, failure_count
     FROM learnings
    ORDER BY success_count - failure_count DESC LIMIT 20;"

# 最近使用的条目
sqlite3 $dataDir/minara.db \
  "SELECT name, last_used_at FROM learnings
    WHERE last_used_at IS NOT NULL
    ORDER BY last_used_at DESC LIMIT 10;"

# 删除有问题的学习条目
sqlite3 $dataDir/minara.db "DELETE FROM learnings WHERE id = '...'"

不提供"降级"操作。若某条学习条目存在误导,直接删除即可;如果该条目确实有用,Agent 会重新推导出来。

配置

相关环境变量(参见 env-vars):

  • MINARA_LEARNING_ENABLED 为总开关(默认 true)。
  • MINARA_LEARNING_MIN_CALLS 为触发评审所需的最低工具调用次数(默认 3)。
  • MINARA_LEARNING_SCORE_THRESHOLD 为写入学习条目所需的评审评分(0–10,默认 7)。
  • MINARA_LEARNING_TOP_K 为每轮提供的学习建议数量(默认 3)。

MINARA_LEARNING_ENABLED=false 可完全禁用该循环:不写入、不建议、不评审。Agent 仍正常运行,只是不会随时间加速。

预算追踪

学习系统发起的每次 LLM 调用均通过 learning/budget-tracker.ts 执行,该模块按类别时间窗口强制设置硬性上限。此设计源于一项评审警告:两阶段评判加上事后探测,若出现 bug 或对抗性提示词,可能导致 LLM 费用暴增 10 倍。硬性预算是熔断器。

四个类别,各自拥有独立的每日和每月上限:

类别用途
learning评审引擎、方法论提取、角色反思、技能学习
agent主 Agent 循环轮次本身
workflow工作流与 Autopilot 轮次
experiment离线实验、回测、A/B 测试,生产环境不使用此类别

状态持久化至 llm_usage SQLite 表:

CREATE TABLE llm_usage (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  category TEXT NOT NULL,
  task TEXT NOT NULL,
  model TEXT NOT NULL,
  input_tokens INTEGER NOT NULL,
  output_tokens INTEGER NOT NULL,
  cost_usd REAL NOT NULL,
  date TEXT NOT NULL,
  ts TEXT NOT NULL
);

每次 LLM 调用时,追踪器会:

  1. 将预估费用与该类别当前的每日和每月累计值相加。
  2. 若预估总量超过硬性上限,在调用发出前抛出 BudgetExceededError
  3. 若预估总量超过软阈值(低于硬性上限),记录 warn 级别的结构化日志,但允许调用继续。
  4. 调用完成后,将实际 token 数量和费用写回 llm_usage

预算在重启后保持有效,因为状态存储于 SQLite,不依赖内存计数器。

不启动 Agent 也可检查消费情况:

sqlite3 $dataDir/minara.db \
  "SELECT category, SUM(cost_usd) FROM llm_usage
    WHERE date = date('now') GROUP BY 1;"

REPL 的 /budget 命令提供相同的交互式视图。

方法论存储

learning/methodology-store.ts 是第二阶段的核心:Agent 学习哪类分析方法能为哪类资产产生有价值的信号,并在未来类似分析中调取。

每条方法论存储以下字段:

字段含义
idUUID
asset_class已知资产类别之一(major_cryptolayer_1defi_blue_chipmeme_coinstock 等)
methodology方法的自由文本描述
evidence支撑证据文本
confidenceWilson 下界置信度分数,范围 [0, 1]
times_used成功应用次数
times_correct通过结果验证的应用次数
quarantine值为 1 直至该方法通过足够多次成功使用且未触发异常检测为止
dedup_key结构化字段的哈希值,用于 O(1) 语义去重
structured_json归一化的 StructuredMethodology(见下文)

隔离与注入防御

新方法论初始处于 隔离 状态,confidence: 0.1,在通过足够多次成功使用前不会注入提示词。每次写入时,异常检测会通过 scanMethodologyForInjection 扫描方法论文本,在提示词注入模式落库前将其拦截。

置信度提升

每次应用某方法论并验证结果后:

  • 成功:递增 times_correcttimes_used,以二项分布的 Wilson 下界重新计算 confidence(对小样本有惩罚)。
  • 失败:仅递增 times_used,重新计算置信度。失败频繁的方法论,置信度会降至注入阈值以下。
  • 毕业confidence >= INJECTION_THRESHOLDtimes_used >= MIN_USES 时,将 quarantine 置为 0,该方法论即可用于提示词注入。

Wilson 下界优于直接计算 times_correct / times_used,因为它不会让 1 次成功的幸运结果盖过 30 次中成功 15 次的稳定表现。

机构模式:反思阶梯

机构模式是写入方法论存储最频繁的来源。每次运行都会召集多个 LLM 角色(分析师、多空辩论、风险委员会、投资组合经理)并记录它们的决策。这些决策进入一个延迟的反思循环,将每次判断与实际结果对照评分,并把经验晋级回上文所述的存储。

为何决策角色采用强制结构化工具调用

每个产出决策的角色发出的工具调用都必须匹配一个 Zod schema(AnalystReportSchemaTraderProposalSchemaPortfolioDecisionSchema 等)。与调用并存的自由文本会被丢弃。两点原因,都与反思循环有关:

  • 确定性。 反思评分需要跨运行比较相同字段。对自由文本的补救式解析会随模型升级而漂移;固定 schema 则不会。
  • 可比性。 今天置信度 0.71 的 Buy,只有在 schema 恒定时,才能与上周置信度 0.62 的 Buy 直接比较。

INSTITUTION_LEARNING_ENABLED=1 时,捕获钩子在每次运行后写入 institution_runs(运行元数据)和 institution_role_outputs(每个角色的结构化输出)。反思阶梯稍后在各窗口到期时写入 institution_reflections

阶梯

learning/institution/reflect.ts 中的运行器按固定计划重新审视每次运行:

窗口触发问题
1d运行后 24 小时触发条件是否成立?
7d运行后 7 天基准情形是否兑现?
30d运行后 30 天时间范围估计是否正确?
90d / 180d / 365d更长论点是否持久?
lazy下次查询该 ticker 时复用为该运行的 Phase 0 回溯

每个标准窗口将该次运行与实际价格走势(price-source.ts)对照评分,并为运行角色输出中引用的每条方法论,将结果反馈给 methodologyStore.recordOutcome()。方法论在其 Wilson 置信边界(>= 0.55 才显现)处晋级,与上文置信度提升路径所用门控相同,因此少数几次运行无法晋级一条不稳定的规则。lazy 与手动反思是快照,不参与该循环。

结构化方法论去重

自由文本难以去重。"Buy BTC on RSI dip"与"Enter long when RSI oversold"表达同一思路,却几乎没有共同 token。更糟的是,Jaccard 相似度可能将"Buy BTC at support"与"Sell BTC at support"合并(相同 token,相反操作)。

learning/structured-methodology.ts 的解决方案是要求评判 LLM 输出有限词表的归一化字段:

字段允许值
directionbullish / bearish / neutral
primary_signalmomentum / mean_reversion / technical / fundamental / on_chain / sentiment / macro / event
timeframeintraday / short / medium / long
indicators已知指标数组(rsimacdfunding_rate 等)

去重使用结构化字段的哈希值direction + primary_signal + timeframe + 排序后的 indicators + asset_class)。哈希相同的两条方法论视为重复,存储层递增现有行的计数器而非插入新行。

自由文本描述仍会保留,供人工阅读和提示词注入使用;结构化字段仅作为去重键。

相似度:Jaccard(旧版)与 TF-IDF

引入结构化去重之前,回退方案是文本相似度。目前存在两种实现:

  • Jaccard 4-gramlearning/similarity.ts)是 v1 旧版实现,计算成本低、与语言无关,但在改写时易出错,且对语义取反("Buy BTC / Sell BTC"陷阱)的判断有误。
  • TF-IDF 余弦相似度learning/tfidf.ts)是推荐的替代方案,基于词级别、具备停用词感知能力,同样与语言无关,处理改写时表现更好。findMostSimilarTfidf 是方法论存储采用的默认路径。

仅当 TF-IDF 失败时(极少发生,如语料库为空或分词异常),存储层才会回退到 Jaccard。两者均只在结构化去重哈希未命中时才被调用,因此调用频率远低于 v1 时期。

编写新的学习产物时,请直接使用 findMostSimilarTfidf,不要另行实现第三种相似度函数。

审计子系统

学习闭环会写很多逐行 forensic 数据(methodology_lifecycle_eventsmethodology_casesmethodology_cron_runs),但这些表只能一次回答一个问题。审计子系统(learning/methodology-audit.ts)是聚合视图,读取这些 forensic 行,在六个维度上计算 0-100 复合健康分,每次 pass 落一行到 methodology_audit_reports,附结构化 findings 和运维侧 advisory action。

子系统永不变更学习状态。它的唯一写盘点是审计报告行,以及学习 cron 自己写的心跳行(见下文隔离不变式)。

六个评分维度

每个维度都是 learning/methodology-audit-scoring.ts 里的纯函数。函数返回 { score: number | null, findings, advisory_actions }null 分数表示"样本不足以诚实评分",会从复合分里 drop 掉并把权重重分配给其他维度。

维度读什么测什么
synthesis_qualityreflection_adjusted.reason_text parse惩罚 flag-side 与 recovery-side 判决之间的反复振荡,单向 flag 或单向 recovery 的稳定走势满分。低样本窗口下也会浮现 market_stress_freezesynthesis_auto_demote 事件。
graduation_fp_rategraduated 后续 30 天内的 demoted/requantized_by_judge在 FP 率上取 Wilson 下界。毕业后还没走完观察窗口又未反转的不计入分子分母,所以一批新毕业不会把分数拉高。
attribution_integrity窗口内 methodology_cases.outcome_state(0.7 × 解析率 + 0.3 × (1 − 积压占比)) × 100。无 closed case 且无 14 天以上 pending 积压时返回 null(健康的全新安装,没东西可评)。不做 attribution_model drift 检测,因为记录值是有意保留的快照。
coverage_healthmethodologies 中 graduated 且 times_used ≥ 10按 CLAUDE.md §13 资产类标准对五大顶层组(crypto / stock / index / commodity / forex)做覆盖度评估。五组各持有 ≥ 3 条活跃方法论得满分,低于三组阈值后线性扣分。
quarantine_churnmethodology_lifecycle_events 的状态变更类 kind排除 reflection_adjusted(在 6 小时 synthesis 节奏下每天可合理触发 4 次)。高 churn = 同一条方法论在窗口内 ≥ 3 次状态变更。
cron_healthmethodology_cron_runs 心跳 + pending 积压滞后时间相对 2× 预期间隔加积压惩罚。滞后 > 7 天分数归零(loop 看起来已死)。心跳表空 + pending 积压非零也归零(loop 明显不在干活)。

复合得分与档位

默认权重和档位:

composite = 0.22·synthesis_quality + 0.22·graduation_fp_rate + 0.22·attribution_integrity
          + 0.14·coverage_health   + 0.08·quarantine_churn   + 0.12·cron_health

档位:≥ 80 healthy · 60-79 watch · 40-59 degraded · < 40 alarm · disabled(off switch)

null 维度会被 drop,剩余权重重新归一化到和为 1。落库的报告记录 dimension_weights_used 字段,运维读 JSON 时能看到具体哪些维度参与了打分。

隔离不变式

审计读四张学习表(methodologiesmethodology_lifecycle_eventsmethodology_casesmethodology_case_hints),对它们零写入。三层保障叠加:

  1. 合作式空闲调度。审计 cron(learning/methodology-audit-cron.ts)和 AgentLoop.run() 共享一个 BusyTrackercore/busy-tracker.ts)。每个 tick 启动前检查 inFlight > 0(跳过)和 idle 时长(不足则延期)。Orchestrator 在每个 SQL 阶段之间用 yieldIfBusy 让步,turn 进来时立即暂停。连续 N 次延期后饥饿守卫强制执行,避免常忙的 agent 长期不被审计。
  2. 纯函数评分边界methodology-audit-scoring.ts 里的维度评分函数只接受 Methodology / MethodologyLifecycleEvent / MethodologyCase 类型的纯数组,结构上看不到 MemoryStore handle,无法误调到 .prepare(...).run(...)
  3. E2E 表哈希不变式tests/e2e/methodology-audit.test.ts 在每次审计 pass 前后对所有学习表做 SHA-256 哈希,要求字节级一致。这比行数比对更严,一个仅改 updated_at 的 UPDATE 行数对得上但哈希过不去。

审计对学习的唯一反向接触点是 methodology_cron_runs 心跳行,在每次学习 cron tick 末尾写入。in-process 调度器(learning/methodology-cron.ts)和 CLI cron 路径(gateway/learning-cli.tsrunFullCronCli)都要写这一行,保证文档化的 system-cron 部署下审计 cron_health 维度也能正常工作。

运维这个审计子系统

cron 是 opt-in。默认参数面向日级被动监控;当学习闭环跑了一两周积累足够数据后,把 METHODOLOGY_AUDIT_CRON_ENABLED=1 翻开。完整 env 参考见环境变量 → 方法论审计子系统

四个 CLI 命令覆盖典型运维流程:

minara learning audit run [--window-days N]              # 内联跑一次审计
minara learning audit show [--latest|--pass <id>]        # 查单条报告
minara learning audit trend [--days N]                   # 复合分历史 + sparkline
minara learning audit findings [--severity high|medium|low]  # 下钻 findings

完整 CLI 接口见 CLI 子命令 → audit

延期项:主动重评

早期方案曾设想第七个维度,随机让 agent 在历史"确定性错"的 trading case 上重新决策,看学习闭环现在会不会做出不同选择。这块工作被推迟。要做诚实的回放需要先冻结历史决策上下文(价格、新闻、sentiment、当时活跃的方法论、工具输出),让重新决策看到的信息和原决策一致。否则问 agent "现在 ETH 该买吗"测的是当前判断,不是学习是否纠正了过去的错误。两条前置依赖:(a) 在 case-recorder 写 hint / case 时附带的快照表,(b) 一个独立的 ProbeAgentLoop,不走 createApp(),所以重放和线上 agent 零共享状态(skill session、tool registry、hooks)。

本页目录