司南 — 记忆路由与传播控制层
基于 Honcho 的集成方案
命名: 司南(战国指南针)—— “司”掌管控,“南”定方向。路由指向该去的 Agent,ACL 把守关口,传播传递方向。
日期: 2026-06-01
来源: git@github.com:plastic-labs/honcho.git(commit b006fa6)
状态: 已拉取源码审查,等待后续集成实现
一、项目概览
Honcho(Plastic Labs)是 AI Agent 的记忆平台,Apache 2.0/AGPL-3.0 开源。共 119 个 Python 文件,~36,445 行。
目录结构
src/
├── main.py # FastAPI 入口
├── config.py (1339行) # 全量配置(70+ 参数)
├── models.py (579行) # SQLAlchemy 模型
├── db.py (79行) # 数据库会话
├── security.py # API Key 鉴权(简单的 bearer token)
├── deriver/ (2558行) # 消息→观察提取 pipeline
├── dreamer/ (2853行) # 跨 session 模式发现(核心亮点)
├── dialectic/ (921行) # 按需推理检索
├── crud/ (5711行) # 数据库访问层
├── llm/ (4430行) # 多 LLM 后端抽象
├── vector_store/(1029行) # LanceDB / TurboPuffer
├── routers/ (2284行) # FastAPI 路由
├── schemas/ (1257行) # Pydantic 模型
├── cache/ # 缓存层
├── reconciler/ # 向量同步
├── telemetry/ # 遥测(Sentry + Prometheus)
└── webhooks/ # Webhook 推送
配置体系(config.py)
配置参数极其完善,约 70+ 可配置项:
settings.DREAM.ENABLED # 总开关
settings.DREAM.DOCUMENT_THRESHOLD # 触发梦的显式观察数量阈值(默认 20)
settings.DREAM.MIN_HOURS_BETWEEN_DREAMS # 最短间隔(默认 1h)
settings.DREAM.IDLE_TIMEOUT_MINUTES # 延迟触发时间(默认 0 = 立即)
settings.DREAM.ENABLED_TYPES # 启用的梦类型
settings.DREAM.SURPRISAL.ENABLED # 是否启用奇异值采样
settings.DREAM.DEDUCTION_MODEL_CONFIG/INDUCTION_MODEL_CONFIG # 专用模型
settings.DREAM.HISTORY_TOKEN_LIMIT # 上下文历史 token 限制
启示: Honcho 的配置体系本身就是一份”需要哪些参数”的 checklist。
二、Dreaming 模块(2853 行)— 最核心的模块
架构
DreamScheduler(单例)
└── 事件驱动:新消息达到阈值 → schedule_delay(MIN_IDLE) → 执行
└── 防重复:同一个 (workspace, observer, observed) 不会同时有两个梦
└── 在内存队列中,非持久化(重启丢失)
Dream Orchestrator
├── 0. Surprisal Sampling(可选)
│ └── 几何奇异值 → 找"最意外"的观察 → 作为 hints 传给 specialists
│
├── 1. Deduction Specialist(演绎)
│ ├── 自治 Agent:最多 12 步工具循环
│ ├── 工具:search_memory, search_messages, get_recent_observations
│ ├── create_observations_deductive (带 source_ids + premises)
│ ├── delete_observations_deductive(清理过时)
│ └── update_peer_card(更新持久身份信息)
│
└── 2. Induction Specialist(归纳)
├── 自治 Agent:最多 10 步工具循环
├── create_observations_inductive(带 source_ids + pattern_type + confidence)
└── 置信度:2源=low, 3-4=medium, 5+=high
关键设计决策
Specialists 是完全自治的 Agent,不是 prompt:
- 不做”分析以下文本”这种一次性任务
- 自己做 discovery(search_memory)→ 决定下一步搜什么 → 创建/删除观察
- 允许走偏——prompt 强调”follow the evidence, pursue what’s interesting”
Surprisal 作为”软引导”机制:
- 几何奇异值采样 = 找当前观察集合中”最出乎模型预期”的那些
- 高奇异值的观察作为 hints 传给 specialists
- Specialists 可以选择跟或不跟——保持自主性
两阶段顺序:Deduction 先跑,Induction 后跑
- Deduction 创建的演绎观察可以在同一轮被 Induction 消费
- Induction 永远不会写 peer card(归 Deduction 管),职责清晰
调度逻辑
# dream_scheduler.py 核心逻辑
check_and_schedule_dream(collection):
if explicit_docs >= THRESHOLD:
if hours_since_last_dream >= MIN_HOURS:
if no pending dream in queue:
schedule_delayed_dream(IDLE_TIMEOUT)- 触发条件:新显式观察达到
DOCUMENT_THRESHOLD(默认 20) - 延迟:
IDLE_TIMEOUT_MINUTES(默认 0 = 立即) - 冷却:
MIN_HOURS_BETWEEN_DREAMS(默认 1h) - 防并发:schedule 和 queue 双重检查
观察层级
explicit → 原始提取(Deriver 产出)
deductive → 逻辑推论(Deduction Specialist 产出)
inductive → 模式归纳(Induction Specialist 产出)
关键:Dreaming 只消费 explicit 级别做触发条件,避免自循环。
实现参考价值
如果需要自研 Simplified Dreaming,2853 行中最有价值的部分:
- Specialist 的工具定义(agent_tools.py 中的工具函数)
- Surprisal sampling 算法(surprisal.py)
- Agent 循环 + 异常处理模式(orchestrator.py 的 try/finally 确保遥测不丢)
但如果选择集成 Honcho 而非自研,Dreaming 可以直接开箱即用。
三、Deriver 模块(2558 行)
架构
新消息(REST API)
→ enqueue.py(新消息队列)
→ QueueManager(后台队列管理)
→ consumer.py(消费线程)
→ 调 LLM:从消息提取观察
→ 写入 Document 表(level=explicit)
→ check_and_schedule_dream(触发梦)
三种队列
deriver_queue → 观察提取(每条消息必走)
dream_queue → 梦的执行(异步,有延迟)
reconciliation_queue → 向量同步(维护向量索引一致性)
Deriver 的消息处理
每条消息过 LLM,系统 prompt 要求提取:
- 关于 user 的显式事实
- 偏好/倾向
- 承诺/计划
- identity markers
输出格式是结构化的 JSON observation 列表,写入 Document 表(level=explicit)。
实现参考
Deriver 的 prompt 设计(src/deriver/prompts.py)和队列管理(src/deriver/queue_manager.py)是两份高价值参考。
但集成场景下 Deriver 也可选——如果我们不需要每条消息都过 LLM,可以只走批量/异步提取,路由层的实体提取 + FTS5 才是实时路径。
四、Dialectic 模块(921 行)
比想象中轻。核心是 dialectic/core.py 的实现:
get_context()
→ 根据 tokens 限制,拉取最近消息 + 摘要
→ 如果 hits 不足,用 semantic search 补充
→ 可选包含 peer card
→ 输出 OpenAI/Anthropic 格式的 messages
chat() → 5 级深度的推理链
levels: minimal → low → medium → high → max
每级增加搜索轮次和推理深度
Dialectic 是最容易直接集成的部分——get_context() 作为路由层的快速检索后端,chat() 作为深水区推理。
921 行的工作量如果自研大概 2-3 天可以复刻。
五、存储模型(models.py 579 行)
核心表
workspace — 顶层隔离域(一个 app = 一个 workspace)
│
├── peer — 实体(用户/Agent,各有一个 knowledge graph)
│
├── session — 会话分组(对应 channel/对话)
│
├── message — 原子消息(每个 turn 的记录)
│
├── document — 观察(核心记忆单元)
│ ├── level: explicit | deductive | inductive
│ ├── content: 观察内容(纯文本)
│ ├── source_ids: 来源 document 的 ID 列表(用于归因)
│ ├── observer: 谁做的观察
│ └── observed: 关于谁的观察
│
├── collection — Document 的聚合单位
│ └── internal_metadata: JSON(存储 dream 状态等)
│
├── peer_card — Peer 的持久身份信息
│ └── 格式:IDENTITY:/ATTRIBUTE:/RELATIONSHIP:/INSTRUCTION:
│
└── representation — 向量嵌入缓存对我们 ACL 设计的启示
Honcho 的 document/collection/peer_card 模型可以直接承载 ACL 元数据。ACL 信息可以存在 collection 的 internal_metadata 中,或扩展 document 增加 visibility 字段:
// 扩展 document,增加 ACL 字段
{
"content": "用户的 API key 在配置文件里",
"level": "explicit",
"visibility": "private", // 新增
"allowed_peers": ["user-A"], // 新增
"owner_peer": "user-A" // 新增
}Honcho 的 internal_metadata 是 JSON 字段,不需要 schema 变更就可以存这些信息。
六、集成方案(不是重造)
架构分层
┌──────────────────────────────────────────────┐
│ 你的路由传播层 │
│ │
│ 实体提取(NER/FTS5) → 路由决策 → 懒加载调度 │
│ ACL 校验(visibility check) │
│ 传播总线(事件驱动,跨 Peer 推送) │
│ │
│ 新增: │
│ - FTS5 倒排索引(实体 → 上下文块) │
│ - ACL 元数据存储 → 每次检索前插入权限过滤 │
│ - 改动写入路径:新消息 → Honcho SDK → 更新 FTS5 │
└──────────────────┬───────────────────────────┘
│ Honcho SDK (Python)
▼
┌──────────────────────────────────────────────┐
│ Honcho v3 存储+推理 │
│ │
│ POST /workspaces/{ws}/peers/{ob}/documents │
│ POST /workspaces/{ws}/peers/{ob}/messages │
│ GET /workspaces/{ws}/peers/{ob}/context │
│ │
│ 异步后台: │
│ - Deriver: 消息 → explicit observations │
│ - Dreaming: explicit → deductive → inductive │
│ - Dreaming: peer card 更新 │
└──────────────────────────────────────────────┘
API 集成级
不需要改 Honcho 一行源码,通过 REST API 或 SDK 对接:
from honcho import Honcho
client = Honcho(base_url="http://localhost:8010")
# 1. 消息进入
session = client.get_or_create_session(workspace, observer, observed)
session.add_message(role="user", content="...")
# 2. 路由层实体更新
entities = extract_entities(content) # 你的 NER
fts5_index.update(entities, session_id=session.id)
# 3. 路由 + ACL 校验
for entity in entities_mentioned_in_new_message:
if fts5_index.match(entity):
docs = fts5_index.get_context_blocks(entity)
allowed_docs = acl.filter(docs, target_peer=target) # 你的 ACL
inject_to_context(allowed_docs)我们需要写的代码
| 组件 | 估算行数 | 依赖 Honcho? |
|---|---|---|
| 实体提取 + FTS5 索引 | ~500 | 否(完全独立) |
| 路由决策引擎 | ~300 | 否 |
| 懒加载调度器 | ~200 | 否 |
| ACL 模型 + 校验 | ~500 | 否(元数据存 Honcho) |
| 传播总线 | ~400 | 是(需要 Honcho 的 peer 模型) |
| Honcho SDK 封装 | ~200 | 是 |
| 总计 | ~2100 行 |
两个改动路径
此方案需要在 Honcho 的写入路径上插一个代理——新消息写入时,路由层同步更新 FTS5 索引:
写入路径:
新消息 → Honcho API
→ Deriver 处理(异步)
→ 到达你的路由层(同异步均可)
→ 更新 FTS5 倒排索引
→ 写入 ACL 元数据(存 collection.metadata)
读取路径:
新问题 → 实体提取
→ 查 FTS5(路由决策:需要加载哪些上下文块?)
→ ACL 过滤(目标 peer 有权限读吗?)
→ 调用 Honcho get_context() 获取
→ 注入当前 prompt
七、自研 vs 集成对照
| 维度 | 自研(最初的偏误方向) | 集成 Honcho |
|---|---|---|
| Dreaming | 自己造 Agent 循环 | 直接用 — 代码已验证 86.9% LoCoMo |
| Deriver | 自己写队列 + LLM pipeline | 直接用 — 但可以按需开关 |
| Dialectic | 约 2-3 天 | 直接用 — get_context() + chat() |
| 存储层 | 自己设计模型 | 复用 — 但需要加 ACL 字段 |
| 我们的核心代码 | 全栈 | ~2100 行(路由+ACL+传播) |
| 外部依赖风险 | 无 | Honcho 断崖变更(v3 刚改完) |
| 控制力 | 完全 | 受 Honcho 设计约束 |
| 部署复杂度 | 低 | 高(Honcho 需要 PG + Redis + 向量) |
八、关键发现汇总
- Dreaming 是最不可替代的模块——自治 Agent 循环 + Surprisal 采样,纯 prompt 工程无法复现质量。且代码已完全开源,直接用。
- ACL 不需要 Honcho 改代码——利用
collection.internal_metadata的 JSON 字段,扩展visibility+allowed_peers即可。Document 也可以在创建时加自定义字段。 - Deriver 按需开关——如果追求低延迟,实时路径走路由层的 FTS5(不调 LLM),异步批量走 Deriver(产生 explicit observations 给 Dreaming 消费)。
- 写入路径代理是关键设计点——路由层不能”嵌入”Honcho,需要在 Honcho SDK 外面包一层,确保写入顺序:先写 Honcho → 更新 FTS5 → 写入 ACL 元数据。
- 代码规模参考——如果未来决定自研 Simplified Honcho,Dreaming(2853 行)+ Deriver(2558 行)是两座大山。集成方案只需要我们自己写约 2100 行新代码。