CGL Agent Lab

2026-09-30代码级

双引擎决策 × 记忆系统
技术实现手册

本文覆盖:架构分层 → 统一路由代码 → 两引擎 API 细节 → 置信度门控 → 判断元数据回流 → 实测数据 → 实验设计 → 已知坑清单。所有数据来自 2026-09-29/30 实测。

1. 架构总览

                     ┌────────────────────────────┐
   state + questions │      unified_judge.py      │
  ──────────────────→│  route_question() 逐题路由  │
                     └──────┬──────────────┬─────┘
                noul/隐私/英文│              │中文 choice/score, 选项>20
                            ▼              ▼
                   ┌────────────┐   ┌────────────┐
                   │ Laya 本地   │   │ Jev 云端    │
                   │ MPS, 零延迟 │   │ HTTPS API  │
                   │ 确定性输出   │   │            │
                   └─────┬──────┘   └──────┬─────┘
                         │  故障互备降级(_fallback) │
                         ▼                 ▼
                   ┌────────────────────────────┐
                   │  答案 + _engine/_latency_ms │
                   │  + --log-journal → journal  │
                   └────────────────────────────┘

核心设计原则:路由在题目粒度,不在请求粒度——同一次调用里的多个问题可以分属不同引擎,混合返回。

2. 统一路由:unified_judge.py

入口签名与路由核心(节选自 ~/.openclaw/models/laya/unified_judge.py,5.8KB):

python
def route_question(state_text, q):
    # 规则按 2026-09-30 实测标定,优先级从上到下
    t = q["type"]              # noul / choice / score
    is_cn = has_cjk(state_text + question_text(q))
    n_opts = len(q.get("criteria", []))

    if t == "noul":                # 是非题:Laya noul 头极稳
        return "laya"              # 任意语言,含隐私敏感场景
    if is_cn and t in ("choice", "score"):
        return "jev"               # 中文细粒度 → 云端
    if n_opts > 20:
        return "jev"               # 选项过多 Laya 会糊
    return "laya"                  # 英文全原语走本地

def judge(state, questions, mode="auto"):
    groups = {}
    if mode == "auto":
        for k, q in questions.items():
            groups.setdefault(route_question(...), {})[k] = q
    for eng, qs in groups.items():
        try:
            answers.update(laya_engine(...) if eng=="laya" else jev_engine(...))
        except Exception as e:
            # 故障互备:整组切到另一引擎,答案带 _fallback 标记
            fb = "jev" if eng == "laya" else "laya"
            answers.update(engine(fb)(...标记 _fallback=f"{eng}→{fb}"))
    return answers

每个答案自带元数据三件套:_engine(谁答的)/ _latency_ms(耗时)/ _fallback(是否降级,正常缺席)。这是后续判断画像的数据底座。

3. Jev 云引擎:API 细节

bash
# Key 从 env 读,绝不进代码字面量(三层防脱敏污染)
# ~/.openclaw/workspace/.secrets/typesafe.env  (chmod 600)
TS_API_KEY=$(grep '^TS_API_KEY=' ~/.openclaw/workspace/.secrets/typesafe.env | cut -d= -f2)

# 关键:参数名是 criteria 不是 options!
# choice 用对象数组,score 用数值数组
curl --noproxy '*' https://api.typesafe.ai/judge/v1/run \
  -H "Authorization: Bearer $TS_API_KEY" \
  -d '{
    "state": "客户邮件:…请尽快确认费率…",
    "questions": [{
      "id": "route",
      "type": "choice",
      "question": "这条消息应路由到哪个部门?",
      "criteria": [                        # ← choice: 对象数组
        {"label": "技术"}, {"label": "销售"},
        {"label": "账单"}, {"label": "其他"}
      ]
    }]}'

# 响应
{ "choice": "账单", "confidence": 0.94,
  "probabilities": {"账单": 0.94, "销售": 0.06, ...} }

工程要点

① 必须绕代理:本机 127.0.0.1:7897 会劫持请求——curl 加 --noproxy '*';Python 用 build_opener(ProxyHandler({})) 空 opener。
② 显示层脱敏陷阱:工具回显里 Key 显示为 ***,复制回代码会变成真 403。凡从回显复制的代码,先全文搜字面 ***。
③ 成本:约 $0.0002/判断;延迟 700~800ms。

4. Laya 本地引擎:加载与推理

python
from laya_lightspeed.client import LayaModel

# 坑:laya.load("multilingual") 会走 HF 在线解析 → 必须给本地绝对路径
MODEL = "/Users/mac/.openclaw/models/laya/weights/multilingual"
m = LayaModel.from_pretrained(MODEL)      # 底座 ModernBERT

out = m.run({
  "state": "用户要求全额退款,情绪激烈",
  "questions": [{"type": "noul",
    "question": "客户是否明确要求退款?"}]
})
# → noul: 0.995, confidence: 0.995  (multilingual 权重, 中文)
checkpoint大小用途中文表现
weights/(EN root)843MB英文原语全类型noul 0.968 / choice 0.37 ✗
weights/typed-decisions/843MB类型化决策头同上量级
weights/multilingual/644MB中文 noul 生产主力noul 0.995 ✓ / choice 0.45 ✗

中文短板的三层根因(源码实证)

① 底座 ModernBertForMaskedLM 预训练语料以英文为主;
② 分词碎片化:21 汉字 → 31 token(膨胀比 1.48),head_max_len=192/256 预算被吃掉;
③ 决策头训练数据英文为主,置信度校准在中文分布外——noul 靠粗粒度池化幸存,choice 需要细粒度语义比对就糊。这就是路由把中文 choice/score 硬性划给云端的原因:知道自己不行也是能力。

5. 置信度门控(低把握必转人工)

python
def act_on(answer, threshold=0.8):
    if answer.get("_fallback"):
        log("降级答案,人工复核"); return ESCALATE
    if answer["confidence"] >= threshold:
        return EXECUTE(answer)
    # 低把握自动重问一次取均值(工程优化项,2026-09-29 提出)
    retry = judge_same_question()
    if avg(conf, retry) >= threshold: return EXECUTE(...)
    return ESCALATE   # 机器不硬拍

6. 判断元数据回流 journal(方向 C,已上线)

shell
# unified_judge.py --log-journal:每次判断自动落一条
python3 unified_judge.py state.json questions.json --log-journal

# journal.jsonl 实际条目(2026-09-30 首条)
{"ts":"2026-09-30T12:31+08:00", "agent":"hiro", "type":"decision",
 "scene":"客户邮件:…请尽快确认费率…",
 "action":"路由[jev+laya] sensitive[noul/laya/208ms] route[choice/jev/765ms]",
 "tags":["dual-engine","direction-c"]}

这是"判断画像闭环"的采集端:攒够 50 条后做路由正确率复盘,≥95% 才推广方向 A(记忆喂先例)。

7. 融合三方向(实验设计)

方向机制状态达标线
A 记忆喂先例判断前检索 journal 历史同场景先例,拼进 state 做先验设计中decision 条目 ≥50 且路由正确率 ≥95%
B 引擎做遗忘曲线入 journal 前判"与近 5 条是否重复"→合并;周复盘判"经验是否过时"→衰减设计中重复条目率 ↓30%
C 判断画像闭环--log-journal 采集引擎/延迟/置信度 → 月度复盘修订路由✅ 已上线方向 A 的前置条件

不融合铁律:什么值得记住(价值判断)永远人工规则 + 复核;引擎只做效率和把关,不做取舍。

8. 实测数据汇总

场景引擎结果延迟
中文退款 noulLaya multilingualconf 0.995,判断正确0.5s(首问 3.1s 含预热)
中文部门 choiceJevconf 0.94~1.0,判断正确757~772ms
同题三连问一致性Laya极差 0.0000(完全确定)—
隐私脱敏闸(记忆 v3)Layaconf 0.999208~265ms
故障降级演练互备自动切换 + _fallback 标记可审计无感
中文 choice(对照)Layaconf 0.45,不及格 → 已路由隔离—

9. 坑清单(踩过的,按损失排序)

1️⃣ Laya 权重放 /tmp 被系统清 → 永久目录 ~/.openclaw/models/laya/weights;下载走 snapshot_download API(python3 -m modelscope 不可执行),需 NO_PROXY=modelscope.cn + 12 轮重试守护脚本。
2️⃣ 回显脱敏 *** 被粘进代码 → 真 403;复制回显代码前必搜字面 ***。
3️⃣ 本机代理劫持 → Jev 必须 --noproxy '*';诊断 DNS 时 fake-ip(198.18.x.x)会骗人,用 DoH 交叉验证。
4️⃣ laya.load("multilingual") 走 HF 在线解析 → 必须传本地绝对路径。
5️⃣ criteria ≠ options;choice 传对象数组、score 传数值数组,搞反直接参数错误。

配套资产:unified_judge.py · 三 checkpoint(2.2GB)· journal.jsonl 采集 · 飞书双引擎方法论文档 v2.0(内部)

© 2026 CGL Agent Lab · ← 返回说人话版