内容
<div align="center">
# 枕叶-RS
**智能体的阅读皮层 — 在实时网络上保持礼貌的眼睛,以及对所读内容的记忆。**
*搜索 · 阅读 · 交互 · 记忆。纯 Rust,一个二进制文件,一个 SQLite 文件,无浏览器引擎。*
[](https://rustup.rs)
[](#-工具表面-9)
[](docs/development.md)
[](#-快速开始)
[](LICENSE)
</div>
---
给大型语言模型一个原始的 `curl` 工具,它就变成了一个不好的网络公民:它会发出可疑的请求,遭到限速或屏蔽,并将 400 KB 的导航浏览器内容倒入其自身的上下文窗口。枕叶(Occipital)是中间层 — 一个工程化的提取管道,首先要成为一个**好公民**,一个返回干净的 Markdown 而不是 HTML 的提取器,以及一个**衰减**的知识缓存,因为网络内容会腐烂。
它支持 **MCP**、**REST** 和 **CLI**,在任何智能体栈中独立运行,并且只占用一个 8 MB 的二进制文件,没有 Chromium,没有无头浏览器,也没有 JavaScript 引擎。
```console
$ occipital dom https://html.duckduckgo.com/html/
https://html.duckduckgo.com/html/ 鸭鸭鹅 HTML:无 JavaScript 的私人搜索
snapshot: held
forms:
#1 POST https://html.duckduckgo.com/html/ — 文本 "q"
$ occipital submit https://html.duckduckgo.com/html/ --form 1 --field "q=礼貌的网络爬虫"
[form#1 POST https://html.duckduckgo.com/html/ — q=礼貌的网络爬虫]
[状态 200]
## [Rust 网络爬虫:完整初学者指南](https://www.scrapingbee.com/blog/web-scraping-rust/)
Rust **网络爬虫** 解释给初学者:学习如何使用 **Rust** 爬取网站…
智能体看到这一点,人类也看到这一点 — 它们看到的是同一个视图。
## ✨ 内部
<table>
<tr>
<td width="50%" valign="top">
### 🕊 构造礼貌
每个可注册域的令牌桶,具有附加抖动的令牌桶,一个全局并发上限,`Retry-After` 感知的退避,`robots.txt` 带有 **`Crawl-delay`** 尊重,以及一个诚实、可识别的用户代理。良好的公民身份 — 永远不会伪装、轮换或逃避。
</td>
<td width="50%" valign="top">
### 📖 阅读模式,而不是 HTML
每个页面都以干净的 Markdown 返回,带有解析的链接列表,因此智能体推理信号而不是浏览器内容 — 并且可以跟随的 UI 可以渲染相同的内容给人类。
</td>
</tr>
<tr>
<td valign="top">
### 🧠 遗忘的缓存
获取的页面存储、嵌入并随着年龄和不使用而丧失意义。搜索首先命中缓存;只有未命中或过时条目才会进行实时请求(条件 GET,因此刷新几乎是免费的)。GC 会修剪没有人阅读的内容 — 默认情况下网络内容会腐烂。
</td>
<td valign="top">
### 🖱 手,而不是眼睛
页面带有**元素注册表**:具有稳定序数的链接和表单。`web_click` 遵循 `link:3`;`web_submit` 填充 `form:1` 并发送它。GET 请求使用缓存;POST 请求是故意的,仅在需要时重试,不会缓存。
</td>
</tr>
<tr>
<td valign="top">
### 🪄 SPA 抢救,无需 JS 引擎
当页面在客户端渲染时,枕叶(Occipital)挖掘静态 HTML *确实* 携带的内容 — `__NEXT_DATA__`,状态 blob,`ld+json`,`noscript`,元数据 — 并标记`salvaged`。当甚至那也失败时,它会说 **`js_required`** 而不是返回空白页面。永远不会执行脚本。
</td>
<td valign="top">
### 📊 诚实的轨迹
每个尝试都落在有界的请求日志中:方法,URL,状态,**礼貌等待**,持续时间和拒绝。枕叶(Occipital)*是* 网络栈,因此可观察性是一个功能,而不是附加功能。`occipital log` / `GET /log`。
</td>
</tr>
<tr>
<td valign="top">
### 🔎 可插拔搜索
默认是一个无密钥、礼貌的爬取(DuckDuckGo HTML / SearXNG),因此小型节点开箱即用。密钥提供者(Brave · Tavily · Bing)是可选的,具有 `0600` 密钥存储和密钥丢失时的优雅回退。
</td>
<td valign="top">
### 🗝 会话和身份 — 可选
一个持久的 cookie jar(默认关闭),因此多步骤流程和操作员提供的登录在重启后存活,加上每个域的标头和显式代理。**一个 jar,一个身份**;诚实的 UA 不能被覆盖,跨站点 `Set-Cookie` 被拒绝。
</td>
</tr>
</table>
## 🚀 快速开始
```bash
git clone https://github.com/buckster123/Occipital-RS
cd Occipital-RS
cargo build --release # 所有四个 crate;无需模型下载,无 ONNX
```
将 MCP 服务器连接到任何客户端(Claude Code 示例):
```json
{
"mcpServers": {
"occipital": {
"command": "/path/to/target/release/occipital-mcp",
"env": { "OCCIPITAL_DB": "/path/to/occipital.db" }
}
}
}
```
然后要求你的智能体搜索或阅读某些内容。除此之外不需要任何其他操作 — 无需 API 密钥,无需模型,无需浏览器。
### 两个层级,一个代码库
| | 嵌入式 | 召回 | 构建 |
|---|---|---|---|
| **Nano** (默认)| 关闭 — 甚至不编译 ONNX 运行时 | FTS5 关键词 | `cargo build --release` |
| **Micro+** | `BAAI/bge-small-en-v1.5` | 余弦语义 + FTS5 | `cargo build --release --features embeddings` 并设置 `OCCIPITAL_EMBED_MODEL` |
默认构建故意很小 — 一个 **8.3 MB** 的剥离 `occipital-mcp`,无需模型下载 — 因此 Raspberry Pi 类节点运行完整的 Web 界面。嵌入式是一个可选的**构建功能**,而不仅仅是一个运行时标志。
## 🧰 工具表面(9)
| 工具 | 功能 |
|------|--------------|
| `web_search` | 查询 → 排名结果,缓存优先;仅未命中访问实时提供者 |
| `web_fetch` | URL → 阅读模式 Markdown + 链接 + 表单,条件刷新 |
| `web_dom` | URL → 元素注册表:链接和表单,具有稳定序数 |
| `web_click` | 按序点击 — `link:N` 跟随它,`form:N` 提交它 |
| `web_submit` | 填充并提交表单按序(GET 缓存 · POST 有意) |
| `web_recall` | 对**已读**页面进行语义/关键词搜索 — 无实时请求 |
| `web_save` | 固定页面使其不被衰减和 GC 删除 |
| `web_forget` | 从缓存中删除页面 |
| `web_distill` | LLM-精选缓存页面成摘要 · 要点 · 实体 · 标签 |
每个 `web_search` / `web_fetch` 结果都是一个扁平的、`kind`-鉴别的 JSON 对象,作为渲染负载 — UI 可以显示智能体正在读取的内容,而不需要第二个通道。纯 MCP 客户端直接忽略它。
## 🕊 礼貌合同
这是区分器,因此在代码中强制执行,而不是在文本中承诺:
- **速率**:默认每个可注册域 0.5 req/s,仅附加抖动(间距是下限,绝不是平均值),全局 4 个飞行中的获取。
- **robots.txt**:按来源识别,缓存 TTL,`Crawl-delay` **提升** 该域的间隔(夹紧,夹紧已记录)。拒绝是干净的,已记录的“被 robots 阻止” — 而不是静默跳过。
- **身份**:一个诚实,可联系的用户代理。绝不随机化,绝不被域头规则覆盖。
- **写入**:POST 仅在显式 `web_submit` / `web_click` 下发生 — 绝不是从提取,刷新或重试逻辑 — 并且**绝不会自动重放**。
- **缓存是最大的赢家**:重新询问的查询产生零个实时请求。
默认设置保持保守,尽管每个旋钮都是可调的。完整的合同:
[docs/politeness.md](docs/politeness.md)。
## 🧭 读取过程
```mermaid
flowchart LR
A([web_search / web_fetch]) --> C{缓存命中<br/>且新鲜?}
C -- 是 --> S([提供缓存 · 0 请求])
C -- 过时 --> V[条件 GET<br/>If-None-Match]
V -- 304 --> S
C -- 未命中 --> P[礼貌门<br/>robots · 速率 · 并发]
V -- 200 --> E
P --> E[提取<br/>Markdown · 链接 · 表单]
E --> Z[(SQLite<br/>缓存 · 嵌入 · 轨迹)]
E --> R([阅读模式结果])
```
四个 crate,一个库:
```
crates/
occipital/ # 库 — 获取 · 礼貌 · 提取 · 抢救 · 提供者 · 缓存 · 衰减 · 精炼 · 会话
occipital-mcp/ # MCP 通过 stdio — 智能体面向的插入件(9 个工具)
occipital-api/ # axum REST — 管理 + 查询
occipital-cli/ # clap CLI — 操作,密钥,cookie,查询
```
更深入: [docs/architecture.md](docs/architecture.md) · 浏览设计:
[docs/agent-browsing.md](docs/agent-browsing.md)。
## 🖥 CLI 和 REST
```bash
occipital search "礼貌的爬虫" # 缓存优先搜索
occipital fetch <url> [--fresh] # 阅读模式 Markdown
occipital dom <url> # 元素注册表(链接 + 表单)
occipital click <url> link:2 # 按序跟随链接
occipital submit <url> --form 1 --field q=rust
occipital recall "我读到了关于 X 的什么"
occipital distill [url] [--limit N] # 精炼缓存页面
occipital log [--limit N] # 请求轨迹
occipital cookies list|clear [domain] # 会话 jar (可选)
occipital keys set|list|rm <provider> # 0600 密钥存储
occipital gc | status
```
REST(`occipital-api`,默认环回):
`/health` `/stats` `/search` `/fetch` `/dom` `/click` `/submit` `/recall` `/distill` `/save`
`/forget` `/log` `/gc` `/keys`.
## ⚙️ 配置
所有内容都由环境驱动,具有礼貌的默认设置;以下内容均非必需。
| 变量 | 默认值 | 目的 |
|-----|---------|---------|
| `OCCIPITAL_DB` | `<data_dir>/occipital/occipital.db` | SQLite 路径 |
| `OCCIPITAL_EMBED_MODEL` | `BAAI/bge-small-en-v1.5` | `""` → 仅 FTS5(Nano) |
| `OCCIPITAL_SEARCH_PROVIDER` | `duckduckgo` | `duckduckgo` \| `searxng` \| `brave` \| `tavily` \| `bing` |
| `OCCIPITAL_RATE_PER_DOMAIN` | `0.5` | 每个可注册域的请求/秒 |
| `OCCIPITAL_MAX_CONCURRENCY` | `4` | 全局飞行中的获取 |
| `OCCIPITAL_RESPECT_ROBOTS` | `1` | 尊重 robots.txt(CLI:`--obey-robots`) |
| `OCCIPITAL_FRESH_TTL_SECS` | `86400` | 缓存新鲜度窗口 |
| `OCCIPITAL_COOKIES` | `0` | 可选会话 jar(一个 jar,一个身份) |
| `OCCIPITAL_CURATE_BACKEND` | `auto` | 蒸馏 LLM:Ollama → Anthropic → `off` |
| `OCCIPITAL_AUTO_DISTILL` | `off` | 背景精炼:`off` \| `local` \| `on` |
完整表格(衰减,GC,快照,代理,标头,robots TTL,请求日志,精炼)位于
[CLAUDE.md](CLAUDE.md#environment-variables) 和 [docs/politeness.md](docs/politeness.md)。
## 🧩 与 ApexOS-RS 一起使用
枕叶(Occipital)是**独立优先**的 — 上面所有内容都适用于任何 MCP/REST/CLI 堆栈,而无需 ApexOS 依赖。[ApexOS-RS](https://github.com/buckster123/ApexOS-RS) 是其最丰富的消费者,并展示了主机可以在其基础上构建的内容:
- **自动配置**:ApexOS 的安装程序克隆,构建并注册 `occipital-mcp` 作为 agentd 插件,从节点的层级中选择 Nano 或嵌入式构建。
- **跟随窗口**:桌面版渲染 `kind`-鉴别的工具负载作为阅读视图 — 人类可以浏览智能体推理的相同 Markdown 内容,具有 **LIVE/CACHED** 徽章和会话的 breadcrumb 轨迹。
- **"前往" 导航**:点击链接(或输入 URL)会向智能体发出提示,以便人类可以在不中断回合的情况下重定向读取。
Web **工具** 是枕叶(Occipital)的;**窗口** 是 ApexOS 的。这种分离是故意的 — 请参见 [docs/follow-along.md](docs/follow-along.md) 以获取主机实现的合约,该合约不需要新的事件类型和此处没有更改。
同一个家族: **[CerebroCortex-RS](https://github.com/buckster123/CerebroCortex-RS)** 是内存皮层 — 使用 FSRS/ACT-R 衰减的情景记忆。枕叶(Occipital)记住**外部世界**(公共,获取,基于陈旧度衰减); Cerebro 记住智能体的**自身体验**(私有,赚取)。不同的生命周期 → 分别的数据库,相同的嵌入式模型和存储成语,因此一个节点使用单个模型加载运行两个。
## 📚 文档
| 文档 | 适用于 |
|-----|-----|
| [docs/architecture.md](docs/architecture.md) | crate 图,缓存/衰减模型,存储模式 |
| [docs/politeness.md](docs/politeness.md) | 完整的刮擦礼仪合约 |
| [docs/agent-browsing.md](docs/agent-browsing.md) | 浏览设计 — 页面模型,动词,抢救,会话和 JS 门 |
| [docs/follow-along.md](docs/follow-along.md) | 智能体↔UI 负载合约 |
| [docs/build-roadmap.md](docs/build-roadmap.md) | 分阶段构建历史记录和门控(0-16,完整) |
| [docs/development.md](docs/development.md) | 构建,测试,层级,约定 |
## 🙅 它故意不做的事情
没有 JavaScript 引擎,没有无头浏览器,没有 CDP 服务器。没有 UA 轮换,头部伪造,cookie 养殖,代理轮换或 CAPTCHA 解决。没有付费墙或登录墙绕过。真正需要 JS 的页面会诚实地标记为已禁用渲染,并且文档留有侧车门 — 而不是硬塞到核心。如果站点说否,我们会尊重它。
## 📄 许可证
Apache-2.0 — 见 [LICENSE](LICENSE)。
---
<div align="center">
<sub>礼貌与质量是同一轴:被阻塞的智能体是更差的智能体。</sub>
</div>