跳转到主要内容
以 sub-linear 时间构建 sub-Linear 应用。
在本工作坊中,你将从零构建 Sublinear:一个项目与议题(issue)跟踪器,单个 Agent 可通过 HeavenBase MCP 工具创建记录、更新工作、回答状态问题,并对议题做语义搜索。 整个应用只有一个 Python 文件:定义数据模型、删除并重建演示工作区以保证每次运行可重复、种子化允许的标签,然后让 Agent 通过 MCP 操作该工作区。
运行工作坊前请设置 OPENROUTER_API_KEY。默认情况下,Sublinear 通过 OpenRouter 同时使用聊天模型(deepseek/deepseek-v4-flash)与嵌入模型(openai/text-embedding-3-small),可能产生少量费用(< $0.01)。

1. 初始化

从 import、常量与后端配置开始。为简化起见,我们仅使用 SQLite + 内存后端,你也可以按需换成其他后端或继续扩展。 main 后端存储实体并支持 SQL 查询;vec 后端存储议题嵌入并支持向量搜索。
接下来,我们先定义一些辅助函数,包括标签规范化、优先级排序、文本嵌入,以及议题文本构造。 工作坊使用 embed preset,默认解析为 OpenRouter 上 OpenAI 兼容的 openai/text-embedding-3-small 路由。你可以改用 embed-local,在 Ollama / LM Studio / vLLM 上本地运行小型嵌入模型。

2. 实体定义

从零构建应用时,先用类似 Pydantic 的声明式方式把数据模型想清楚。 在 Sublinear 的设计中,我们有项目(Project)、里程碑(Milestone)和议题(Issue)。项目用于组织工作并设定目标;里程碑是项目检查点,可用于进度跟踪;议题是具体工作项,可分配、打标签、附加 tags,并嵌入以支持语义搜索。我们还有 Label 实体存储允许的标签词汇,以及 View 实体存储已保存的过滤与展示偏好。 Label 实体:
使用 .compute 标注由任意 Python 函数计算得到的字段。该函数以初始化输入作为关键字参数,返回将写入字段的转换结果。
Milestone 实体:
Project 实体:
这里出现新概念 strategy,用于决定字段在后端上的存储方式。即使同一类型存储在同一后端,不同 strategy 也会形成不同的物理布局。例如 hb.SideTable 会把数组存成独立表,用外键关联主表,每个元素一行。
View 实体:
作为对比,display 也是数组字段,但未指定 strategy,因此使用数组默认的 hb.InlineColumn strategy,即在主表中以内联列存储(具体为 TEXTJSON/JSONBARRAY,取决于后端)。
Issue 实体(最复杂的一个):
这里还有新概念 query_compute,用于转换查询时的参数。例如写查询 emb.near("Hello") 时,参数 "Hello" 会先经 embed_text 处理成向量,再与最近的嵌入匹配。

3. 工作区构建

创建工作区、注册实体,并种子化标签词汇。Agent 查询这些行,并在项目与议题上存储标签 object_id。标签与 tag 数组路由到 SQLite 侧表,因此 labels.array_contains("debugging") 等分析过滤无需单独搜索后端即可工作。
搭建步骤会调用 sublinear_workspace(reset=True),每次运行前会删除演示工作区。跟做工作坊时请保留该重置;需要 Sublinear 数据持久化时再移除。
接下来,为工作区种子化一些标签词汇。

4. Sublinear Agent

工作区构建完成后,可以直接通过 MCP 暴露给 Agent。 任何 Harness(Claude Code、Codex、Copilot 等)都可用来创建 Agent;这里我们用 HeavenBase 简单的 LLMSession。向会话添加 MCP 用 session.add_mcp(...);同时可用简洁的系统提示词引导 Agent。
此时 sublinear 已经是一个 Agent 函数,可以回答问题并对 Sublinear 工作区执行命令。 每个会话中,Agent 只面对一个 MCP 表面:HeavenBase 工作区 toolkit。它可写入记录、查询结构化行,并运行语义 near 搜索,直到会话返回内容或达到 max_tool_turns。以下是 Agent 可用接口列表(与 HeavenBase MCP 页面相同): 其中 upsert 最适合创建与整行替换。对 Agent 编辑,set 更方便:Agent 查询现有行后只补丁修改过的字段。 语义搜索的关键是:模型发送的是文本,不是向量。Issue.emb 上的 query_compute(embed_text) 在 HeavenBase 将 near 操作路由到 vec 之前,会把查询字符串转为向量;随后 HeavenBase 从 SQLite 水合匹配的议题行。 语义搜索查询示例:

5. 动手试试

在 docs 仓库根目录运行 python workshops/sublinear/sublinear_app.py 试用 Sublinear Agent。 以下脚本向 Sublinear 发送五条用户请求;每条请求使用新会话,但所有会话共享同一持久 HeavenBase 工作区。请求包括:初始化项目、添加议题、认领议题、统计 debugging 议题,以及对 “launch readiness, debugging, and final polish” 做语义搜索。

5.1. 预期行为

五次调用刻意使用独立的 LLMSession 实例,表明 Agent 不依赖聊天记忆;每轮通过 MCP 查询同一 HeavenBase 工作区来恢复上下文。 运行过程中,Agent 应:
  1. upsert 创建 HB-GUI 项目,省略 object_id,由 HeavenBase 对项目 name 做哈希。
  2. 查询项目与标签词汇,再创建 S1S7 议题,含截止日期、标签、tags 与计算得到的嵌入。
  3. set 补丁 S1,仅改 assigneeestimate,计算字段保持一致。
  4. 根据已存储行回答 debugging 数量问题,通常通过过滤 Issue.labels 中的 debugging 标签 ID。JSON 规范可用 array_contains;HeavenBase 也会将数组字段上的 contains 规范为同一操作。
  5. Issue.emb 做文本 near 查询回答语义搜索问题,返回最接近的相关议题及分数。
成功运行会创建 HB-GUI 项目、添加七条带推断标签 ID 的议题、用 set 将 S1 分配给 Bob、回答 debugging 工作包含 S3 与 S6,并对向量字段使用 near.query 文本,对 launch/debugging/polish 相关议题(如 S6、S7、S2)排序。

5.2. 示例回复

模型措辞可能不同,但成功运行应达到以下具体结果:
重点不在于回复的逐字措辞。该应用展示单个 Agent 可操作结构化记录、计算字段、将向量路由到向量后端,并查询同一工作区以得到分析答案。

进一步探索

相关资源:
  • HeavenBase MCP - 通过 MCP 暴露工作区
  • 首个 LLM - 配置 Sublinear 使用的 chat preset
  • 首个 MCP - 持久化并复用已注册的工具集 (Toolkit)
  • 实体 - 类语法、默认值、compute Hook 与逻辑类型
  • 路由 - 字段级后端放置
  • 查询 - JSON near 查询与过滤