内容
# StructFocus
> **上下文管理(Context-as-a-Tool)** —— 把长上下文注意力管理做成一个 MCP Server,让任意 Agent 宿主(Claude Code / Cursor / Cline)三行接入。
>
> LLM 上下文注意力管理。≈ MemGPT/Letta 的上下文部分,减掉 agent 框架,加社区标准 benchmark。




长对话里,多数 agent 直接丢最早的消息(FIFO 截断)。StructFocus 换个思路:**不丢,只是不一直放在眼前** —— 概括 → 胶囊 → 指针 → 召回。
## 为什么
FIFO 截断永久丢失信息。在 160 轮对话中,纯 FIFO 只保留约 33% 的注入话题;StructFocus 通过将闲置上下文压缩成胶囊并按需召回,保留 **100%**。
长上下文召回率(注入的话题是否还留在上下文):
| 轮数 | FIFO | StructFocus |
|:---:|:---:|:---:|
| 20–80 | 100% | 100% |
| 160 | 33% | **100%** |
NIAH(针在草堆):见 `packages/context/bench/hardcore.ts` —— 20 格硬核网格 + 语义干扰项,可本地复现。
## 30 秒快速开始
任意支持 MCP 的客户端,在 `mcp.json` 里登记即可,无需改任何框架源码:
```json
{
"mcpServers": {
"structfocus": {
"command": "npx",
"args": ["-y", "@structfocus/mcp"],
"env": {
"STRUCT_LLM_API_KEY": "sk-xxx",
"STRUCT_LLM_BASE_URL": "https://api.deepseek.com/v1",
"STRUCT_LLM_MODEL": "deepseek-chat"
}
}
}
}
```
### 配置 LLM 压缩(可选)
引擎概括旧上下文时用 LLM 做语义摘要。**不配 Key 也能跑**(走确定性回退:头尾截取,免费但粗略)。
三条环境变量,适配任何 OpenAI 兼容 API:
| 环境变量 | 说明 | 默认值 |
|:---|:---|:---|
| `STRUCT_LLM_API_KEY` | API Key(必填) | — |
| `STRUCT_LLM_BASE_URL` | API 地址 | `https://api.openai.com/v1` |
| `STRUCT_LLM_MODEL` | 模型名 | `gpt-4o-mini` |
## 架构
四层热/冷流水线。活跃对话保持热度;闲置上下文逐步压缩并推送到磁盘,按需召回。
```
┌─────────────────────────────────────────┐
│ LLM 输入窗口 │
│ ┌───────────────────────────────────┐ │
用户/助手 ─────▶ │ │ L1 系统 · L2 任务/焦点 · L3 历史│ │
│ └───────────────────────────────────┘ │
└───────────────┬───────────────┬──────────┘
│ 逐出/压缩│ 召回
▼ ▲
┌────────────────────────────────────────┐ ┌─────────────────────────┐
│ L4 胶囊(语义摘要) │ │ 内容存储(BM25 磁盘) │
│ - 按语义分段 → 概括 → 胶囊│ │ - 逐出/截断文本 │
│ - 展开:context("<id>") 恢复│ │ - 按关键词/语义召回│
└────────────────────────────────────────┘ └─────────────────────────┘
│
▼
┌──────────────────────────┐
│ L5 指针(轻量级) │
│ - "[已打包至胶囊 x]" │
└──────────────────────────┘
```
## MCP 工具
| 工具 | 目的 |
|:---|:---|
| `context_inject` | 注入一条上下文(对话 / 工具输出 / 日志) |
| `context_recall` | 按自然语言语义召回历史上下文 |
| `context_status` | 查看引擎完整状态(token / 胶囊数 / 活跃条目 / 磁盘 / LLM 健康 / 策略) |
| `context_forget` | 忘记(卸载)指定上下文 |
| `context_focus` | 聚焦指定文件/目录到工作上下文 |
| `context_set_policy` | 热更新管理策略(如 `{ conservative: true }` 开启保守模式) |
| `context_stats` | 精简状态速览(更紧凑,适合每次调用后扫一眼) |
| `context_search` | 在历史原文(ContentStore)中按关键词全文检索 |
## 基准测试
| 基准 | 验证什么 | 需要 Key? | 报告 |
|:---|:---|:---:|:---|
| NIAH(hardcore 网格) | 长上下文针中针召回 | 可选 | `packages/context/bench/HARDCORE_V2_REPORT.md` |
| BM25 搜索精度 | BM25 vs 朴素 `includes`(P@5/R@5) | **否** | 本地生成 (pnpm bench:bm25) |
| LoCoMo 长对话 | 多轮召回 + 时序(Cat2)推理 | 是 | `packages/context/bench/LOCOMO_REPORT.md` |
| 多跳 QA(1.3) | 跨文档推理 | 是 | 待补 |
| DocQA 750K(1.4) | 长文档问答 | 是 | 待补 |
统一入口 `bench/run.mjs` 支持 `--suite <bm25|niah|multihop|docqa|all>`;无 Key 的 BM25 套件可本地直接跑:
```bash
pnpm bench:bm25
```
## 安装与构建
```bash
pnpm install
pnpm build # tsc -b(context → dist)
pnpm test # vitest run(context 173 + mcp 19 = 192 用例)
pnpm lint # eslint packages/context/src packages/mcp/src
```
本地起一个 MCP Server(stdio):
```bash
cd packages/mcp
node --experimental-strip-types ./src/index.ts
```
## 贡献
**我们欢迎 Issue 与 Pull Request。** StructFocus 是开放共建的项目——你的 Bug 报告、修复、基准数据、集成方案,都能让「替代 FIFO 截断」做得更好。
- 🐛 **发现 Bug 或 FIFO 反超的场景?** [开 issue](https://github.com/kongkongpq/struct-focus/issues)——这是最有价值的贡献。
- 🔧 **想贡献代码?** Fork 后从 `main` 切分支开 PR。参见 [CONTRIBUTING.md](./CONTRIBUTING.md) 的本地搭建、测试规范与 PR 清单。
- 💬 **有疑问?** 用 [GitHub Discussions](https://github.com/kongkongpq/struct-focus/discussions) 或普通 issue。
- 🔒 **安全问题?** 请按 [SECURITY.md](./SECURITY.md) 私下举报,不要开公开 issue。
推到 `main` 或开 PR 都会触发 CI(`typecheck → lint → test`),请保持通过。
## License
[Apache-2.0](./LICENSE)