Metadata-Version: 2.4
Name: firefly-judgment
Version: 0.1.5
Summary: 萤 (Firefly): 可嵌入 Agent Harness 的自成长毫秒级判断层
Author: Firefly
License-Expression: MIT
Keywords: llm,agent,decision-engine,tool-routing,safety-gate,online-learning,sidecar
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=utf-8
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Provides-Extra: fast
Requires-Dist: numpy>=1.24; extra == "fast"
Dynamic: license-file

# 萤（Firefly）—— 毫秒级判断层

萤是一个给 LLM Agent 用的**判断层**：把 Agent 里大量"重复、高频、可学习"的决策（选哪个工具、要不要拦截、用哪个模型、这步谁来判）从昂贵的 LLM 推理下沉为毫秒级的小模型判断；萤不确定时自动回退给 LLM/用户，LLM 的判断结果自动回流为训练数据——**越用越准，越用越省**。

```
LLM Agent 决策点
   │ 低置信度 / 复杂语义 → System 2：LLM / 用户（结果回流训练）
   ▼
萤（System 1）：毫秒级 · 可学习 · 可解释 · 可审计 · 可回滚
```

## 能力总览

| 阶段 | 能力 |
|---|---|
| P0 判断内核 | 工具路由萤、安全守门萤（硬规则不可学习覆盖）、在线自学习、置信度校准、反事实解释、审计哈希链、HTTP 边车 |
| P1 进化引擎 | 新工具冷启动（注册即学会）、回放防遗忘、影子评估 → A/B 灰度 → 全量发布 → 一键回滚、行为漂移自愈、淘汰遗忘、重启自动恢复、历史数据批量导入 |
| P2 萤群 | 校验萤、循环萤（防死循环硬规则）、调度萤（模型/推理力度）、记忆萤、元决策萤（决定"这步谁来做"）、**从日志自动发现决策点** |

零第三方依赖，纯 Python 标准库，`pip install` 即用。

## 安装

```bash
# 方式一：从 PyPI 安装（推荐，进程内调用，零网络开销）
pip install firefly-judgment
# 之后其他项目里直接 from firefly import ...

# 本地开发安装（在本仓库根目录执行）：pip install -e .

# 方式二：HTTP 边车（跨语言/跨进程共享同一个萤）
python -m firefly.server.http          # 默认 0.0.0.0:8000
# 任何语言通过 /v1/decide、/v1/feedback、/v1/tools、/v1/metrics 接入
```

## 快速开始（3 分钟）

```python
from firefly.bootstrap import build_bus
from firefly.sdk import FireflyClient
from firefly.protocol import ToolSpec

# llm_decider：萤置信度不足时的 System 2（签名 (request, response) -> 选项id）
def my_llm(request, response):
    return call_your_llm(request.state.task)   # 返回它认为对的选项 id

bus = build_bus(llm_decider=my_llm)
firefly = FireflyClient.in_process(bus)

# 注册你的工具（动态选项空间：注册即可被路由，无需重启）
firefly.register_tool(ToolSpec(
    id="search", name="search",
    description="search the web for external information retrieval",
    tags=["retrieval"], cost=0.001, latency_ms=500, risk="low",
    success_rate=0.92,
))
firefly.register_tool(ToolSpec(
    id="read_file", name="read_file",
    description="read a local file from disk",
    tags=["file"], cost=0.0, latency_ms=2, risk="low",
    success_rate=0.98,
))

# 决策 → 执行 → 反馈（闭环的关键：把结果喂回来）
decision = firefly.decide(
    node="tool_router",
    state={"task": "查一下今天的科技新闻"},          # 中英文都能理解
    options=[t.to_option() for t in firefly.list_tools()],
)
if not decision.fallback_used:
    result = run_tool(decision.choice)               # 你的工具执行逻辑
    firefly.feedback(decision.trace_id,
                     "success" if result.ok else "failure")
# 若 fallback_used=True：decision.choice 是 LLM 给的答案，
# 照常执行；feedback 时该答案会作为教师信号自动训练萤
```

注意前几次决策置信度不足属正常——**萤核是随机初始化的在线学习器，需要预热**。喂 10~20 次反馈后即可形成稳定判断力；有历史数据可用批量导入（见下），几秒完成预热。

## 怎么结合到 LLM 项目

一个典型 Agent 的每一步都在问 LLM。下面是萤接管各决策点的接入姿势——每个决策点都是独立节点，按需取用：

### 1. 工具路由（最常用）：agent 选工具前问萤

```python
decision = firefly.decide(
    node="tool_router",
    state={"task": user_input},
    options=[t.to_option() for t in my_tools],
)
tool = decision.choice           # 毫秒级；不确定时自动回退 LLM（fallback_used=True）
```

### 2. 安全守门：执行工具前最后一道闸（硬规则不可被学习绕过）

```python
gate = firefly.raw_decide({
    "node": "safety_gate",
    "options": [{"id": tool_call_name, "features": {"description": tool_args_text}}],
    "state": {"task": user_input, "risk": "high"},
})
if gate.choice == "block":  return refuse()
if gate.choice == "ask":    return ask_human()
# gate.certificate 非空 = 硬规则裁决（如拦截 rm -rf /），带审计证书
```

### 3. 调度萤：给任务选模型档位（省钱的关键）

```python
r = firefly.decide(node="scheduler",
                   state={"task": user_input},
                   fallback="none")   # 档位选择禁用外部回退
model = MY_MODELS[r.choice]           # 默认三档 fast / balanced / deep
# 也可用 raw_decide 传入你自己的候选模型列表 options=[{"id": "gpt-4o", ...}, ...]
```

### 4. 元决策萤：这一步到底让谁来做

```python
r = firefly.decide(node="meta_controller",
                   state={"task": user_input, "risk": "high"},
                   fallback="none")   # 元层自身不再回退，避免循环
# r.choice ∈ firefly / llm / user / explore
# 硬规则：未知未知 → 强制 user；高风险 → 禁止 explore（不可被学习绕过）
```

### 5. 校验萤 / 循环萤 / 记忆萤

```python
# 校验工具产出（置信度不足时其本身就输出 uncertain，无需外部回退）
v = firefly.raw_decide({"node": "verifier",
    "state": {"task": task, "extra": {"output": tool_output}},
    "constraints": {"fallback": {"mode": "none"}}})
# v.choice ∈ pass / fail / uncertain

# 控制重试循环（attempt 达上限时硬规则强制 stop，防死循环）
loop = firefly.raw_decide({"node": "loop_controller",
    "state": {"task": task, "extra": {
        "attempt": 3, "last_error": "connection timeout", "max_attempts": 5}},
    "constraints": {"fallback": {"mode": "none"}}})
# loop.choice ∈ continue / retry / switch / stop

# 整理长期记忆（对每条记忆 保留/压缩/丢弃）
m = firefly.raw_decide({"node": "memory_curator",
    "state": {"task": "整理记忆", "extra": {"memory_items": memories}},
    "constraints": {"fallback": {"mode": "none"}}})
```

> P2 五节点（校验/循环/调度/记忆/元决策）是固定小动作空间的元判断，建议统一 `fallback none`：它们的输出空间内已含"不确定/询问"等保守选项，被外部 LLM 回退改写反而会破坏语义。工具路由（`tool_router`）则相反——保留回退，让 LLM 兜底正是设计意图。

### 6. 进化引擎：新工具注册即学会，历史数据秒级预热

```python
from firefly.evolution import EvolutionEngine

engine = EvolutionEngine(
    bus,
    model_dir=".firefly_models",   # 版本/回放缓冲落盘，重启自动恢复
)
engine.manage_defaults()           # 纳管全部节点（影子/A/B/灰度/回滚）

# 批量导入历史决策记录：直接训练线上核，几秒完成预热
engine.import_history([
    {"task": "查一下今天的科技新闻", "target_id": "search"},
    {"task": "读取本地配置文件",     "target_id": "read_file"},
    # ... 几百条历史 (任务, 正确工具) 对
])

# 之后：新工具注册 → 后台自动 合成数据→训练→影子→灰度→全量，全程不中断
bus.register_tool(new_tool_spec)
engine.start(interval=30)          # 后台 tick；也可手动 engine.tick()
engine.promote("tool_router")      # 影子达标后推进：灰度 10%→50%→全量
engine.rollback("tool_router")     # 出问题一键回滚 stable 基线
```

### 7. 从 LLM 调用日志自动发现决策点（把重复的 LLM 判断固化成萤）

如果你的 LLM 项目里有一类判断反复出现（如意图分类、请求分派），把日志喂给挖掘器：

```python
from firefly.evolution import DecisionPointMiner, register_decision_point

logs = [{"point": "intent",           # 同一决策点打同一个标签
         "input": user_text,
         "output": llm_answer_label}  # 输出需为短标签（可枚举）
        for user_text, llm_answer_label in your_history]

for s in DecisionPointMiner().mine(logs):
    print(s.name, s.action_space, f"{s.frequency} 条日志，熵 {s.entropy:.2f}")
    # 人工审核通过后：
    register_decision_point(bus, s)             # 注册新萤节点
    engine.manage(s.name)
    engine.import_history([{"task": x.task, "target_id": x.target_id}
                           for x in s.samples])  # 灌入初始样本
```

## 反馈闭环：outcome 从哪来

`firefly.feedback(trace_id, outcome, corrected_choice=None, extra=None)`：

| outcome | 含义 | 学习效果 |
|---|---|---|
| `success` | 工具执行成功 / 用户采纳 | 强化本次选择 |
| `failure` / `timeout` / `user_rejected` | 执行失败 | 惩罚本次选择 |
| `user_corrected` | 用户/LLM 给出正确答案 | 以 1.2 倍权重学习 `corrected_choice` |

任何节点的决策都走同一个闭环；守门硬规则、循环上限、元决策安全规则带证书，反馈无法改变其结论。

## HTTP 边车（跨语言接入）

```bash
python -m firefly.server.http --host 0.0.0.0 --port 8000
```

```bash
curl -X POST localhost:8000/v1/decide -H "Content-Type: application/json" -d '{
  "node": "tool_router",
  "state": {"task": "查一下今天的科技新闻"},
  "options": [{"id": "search", "features": {"description": "search the web"}}]
}'
```

端点：`POST /v1/decide`、`POST /v1/feedback`、`POST /v1/tools`、`GET /v1/tools`、`GET /v1/metrics`、`GET /health`。

## API 速查

| 对象 | 关键方法 |
|---|---|
| `FireflyClient` | `in_process(bus)` / `http(url)` / `decide()` / `raw_decide()` / `feedback()` / `register_tool()` / `metrics()` |
| `build_bus()` | `llm_decider`、`user_decider`（回退执行者）、`swarm_nodes=True`（挂 P2 全部节点）、`gate_policy`（自定义守门规则） |
| `EvolutionEngine` | `manage_defaults()` / `manage(name)` / `start()` / `tick()` / `promote()` / `rollback()` / `import_history()` |
| `DecisionPointMiner` | `mine(logs) -> [DecisionPointSuggestion]` |
| `register_decision_point` | `(bus, suggestion)` 注册新节点 |

## 使用注意事项

1. **必须预热**：核随机初始化，冷启动决策接近随机。用 `import_history` 批量导入历史数据是最快的预热方式；没有历史数据就上线初期让 LLM 当老师（回退闭环会自动积累训练数据）。
2. **反馈必须接**：不接 `feedback` 萤就不会进化。工具执行结果、用户采纳/纠正都是现成的信号源。
3. **学习期隔离回退**：批量训练/演示时传 `constraints: {"fallback": {"mode": "none"}}`，避免低置信度期间决策被 explore 劫持干扰训练信号。
4. **语义边界**：萤的特征是哈希级语义（同义词汇能对上，深层推理不行）。需要真正理解的判断本来就该回退 LLM——这正是系统设计，调好 `fallback.threshold`（默认 0.6）即可。
5. **重启恢复**：给 `EvolutionEngine` 传 `model_dir`，版本历史、回放缓冲、active 萤核重启后自动恢复。
6. **进程常驻**：审计日志与缓存目前为内存态，适合常驻服务；HTTP 边车模式天然满足。

## 更多示例

- [examples/quickstart.py](examples/quickstart.py) — P0 全流程（路由/学习/守门/指标）
- [examples/evolution_demo.py](examples/evolution_demo.py) — P1 进化引擎全链路（导入历史→冷启动→影子→灰度→发布→漂移自愈→回滚）
- [examples/swarm_demo.py](examples/swarm_demo.py) — P2 萤群 + 决策点自动发现
- [tests/](tests/) — 94 个测试用例，是最好的用法文档
