Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fa7cd07
feat: 统一进化蓝图落地——EventSink 解耦 / 上下文基质 / M1 注册表 / 孵化台深化
renmengkai1993 Jul 1, 2026
d7b4d4b
feat(prototype): 原型设计升级为按需求(孵化台草稿)的实用功能 + 支持改现有页面
renmengkai1993 Jul 1, 2026
b6d196d
feat(prototype): 原型提示词强制绑定需求孵化台需求 + 输入框/命名微调
renmengkai1993 Jul 1, 2026
7a7ea28
feat(chat): 会议室对话右键软删除
renmengkai1993 Jul 1, 2026
5eecb38
feat(chat): 会议室消息气泡右键软删除(改为消息级)
renmengkai1993 Jul 1, 2026
efa91fe
refactor(prototype): 精简原型设计提示词为「选需求→生成」
renmengkai1993 Jul 1, 2026
4451d73
feat(context): 全量上下文基质·万物可引 — pull-provider 重构 + 内联引用 + 删取景框页
renmengkai1993 Jul 1, 2026
5ec2e79
chore: 更新代码图守护进程的pid和启动时间
renmengkai1993 Jul 1, 2026
c08b908
style(incubator): 需求孵化台头栏统一为 audit-top 标准结构
renmengkai1993 Jul 1, 2026
4e66178
fix(context): 选引用上下文来源不再连带关闭引用面板
renmengkai1993 Jul 1, 2026
d6d0886
style(incubator): 头栏项目选择器改为紧凑版
renmengkai1993 Jul 1, 2026
3b9625e
fix(context): 引用面板选中某来源后其余来源选项不再消失
renmengkai1993 Jul 1, 2026
0faa741
feat(context): 引用面板条目两行元信息 + 可读预览,解决无法区分
renmengkai1993 Jul 1, 2026
110839a
feat(conversation): 会议室归档时进行 Innate 学习
renmengkai1993 Jul 1, 2026
323a6c1
feat(concurrency): 按核预算 + 分相位租约治理并发编码 CPU 占满
renmengkai1993 Jul 3, 2026
589dde1
feat(conversation): 会议室上下文面板工作区/只读文件检索快速查找
renmengkai1993 Jul 3, 2026
1e5fe2a
fix(trace): 精简环节产出筛选栏 + schema 体检显示修复
renmengkai1993 Jul 3, 2026
d03ae6e
feat(context): 上下文基质搜索优先取用——万物可引与操作清爽兼得
renmengkai1993 Jul 4, 2026
6d41b13
chore: prepare open-source release automation
renmengkai1993 Aug 9, 2026
8588ffd
ci: update core GitHub actions
renmengkai1993 Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
530 changes: 530 additions & 0 deletions .autoforge/docs/孵化台深化/原型图.html

Large diffs are not rendered by default.

Binary file added .autoforge/docs/孵化台深化/原型图.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
230 changes: 230 additions & 0 deletions .autoforge/docs/孵化台深化/设计方案.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# 上下文基质 · 搜索优先取用 — 细节实施方案

> 目标:保住「万物可引」的可及性承诺,同时把操作面收窄——**可及层 100% 不动,
> 把"枚举"降级为"搜索的兜底视图"**。人侧 Picker 翻转为搜索优先;机器侧补全
> pull 工具(带 query 的召回 + 按 id 取全文);注入侧收敛默认预算。
>
> 分支:`feat/context-search-first`。**零 DB 迁移**。

---

## 0. 现状盘点(以代码为准,2026-07)

| 层 | 现状 | 结论 |
|---|---|---|
| 可及层 | `core/context_providers.rs` 声明式 SOURCES(约 26 kind)+ NEVER_CONTEXT 黑名单 | ✅ 不动 |
| 索引 | `context_index`(0081)仅元数据**缓存**;`context::list` 活查 provider 为真源、缓存叠加兜底 | ⚠️ 决定了搜索必须下推 provider,不能只搜缓存 |
| Picker | `ContextPicker.tsx` 一次拉 200 条平铺 + 来源下拉 + 前端子串过滤 | ❌ 本方案主改造对象 |
| Agent pull | `tools/context_recall.rs` 的 `recall_context`(kinds+limit,snippet 400 字);**无 query、无取全文工具** | ⚠️ 增量补全 |
| 注入层 | `lens.rs` 六台子;meeting 预设 `include=[] budget=0`;IPC `assemble` `unwrap_or(0)` | ⚠️ 默认口子收敛(M0) |

### 关键设计决策

1. **搜索用 LIKE 下推 provider,不建 FTS5。**
理由:① 活查是真源,context_index 缓存不全,FTS 建在缓存上会漏;② FTS5 默认
unicode61 分词对中文无效(整句一个 token),trigram tokenizer 又要求较新 SQLite 编译
选项;③ LIKE `%q%` 是字节级子串,中文天然可用;④ 零迁移、零同步维护。
性能护栏:每来源 LIMIT + 前端 200ms debounce;桌面单用户量级下 LIKE 扫题名列毫秒级。
正文全文搜索列为 non-goal(后续可选 FTS5 trigram 增强)。
2. **呈现分组放前端常量,注入排序留后端 kind_rank,各管各的。**
分组(产物/过程/配置/外部)是纯 UI 概念;后端 `kind_rank` 是注入优先级。两者语义不同,
强行一处真源反而耦合。两处各加注释互指。
3. **Agent 侧基于现有 `recall_context` 增量**(加 query 参数 + 新增 `read_context` 姊妹工具),
不新建工具框架。

---

## M0 · 注入侧默认收敛(独立小刀,可先落)

**改动**

1. `core/context.rs` 加常量:
```rust
/// 装配默认预算(调用方未显式给预算时的兜底;显式传 0 仍表示不限)。
pub const DEFAULT_BUDGET_BYTES: i64 = 49_152; // 48 KB
```
2. `commands/context.rs::assemble_context`:`budget_bytes.unwrap_or(0)` →
`budget_bytes.unwrap_or(context::DEFAULT_BUDGET_BYTES)`。
语义:`None`=默认 48K;`Some(0)`=显式不限(保留逃生口)。
3. `core/lens.rs` meeting 预设:`budget 0` → `DEFAULT_BUDGET_BYTES`。
`include=[]`(全部来源)**保留**——会议室的跨角色哲学不变,预算兜底即可;
漏注入的内容 Agent 经 M4 的 pull 工具够得着。

**验证**:`lens.rs` 现有测试若断言 meeting budget==0 需同步;`cargo test` 全绿。

---

## M1 · 后端搜索:query 下推 provider

### 1.1 `core/context_providers.rs`

- `SourceProvider` trait 的 `enumerate` 加参数(内部 trait,直接改签名):
```rust
async fn enumerate(&self, db:&Db, project_id:&str, repo:Option<&str>,
limit:i64, query: Option<&str>) -> Result<Vec<ContextItem>>;
```
- **TableSource 实现**:SQL 追加 `AND (<title表达式>) LIKE ? ESCAPE '\'`
(title 是声明里的 SQL 表达式,LIKE 直接作用其上)。新增纯函数:
```rust
/// LIKE 模式转义:% _ \ 前加 \,再包 %…%。
fn like_pattern(q:&str) -> String
```
- **FileSource 实现**:枚举 rels 后按相对路径小写 `contains` 过滤(文件来源量小,内存过滤足够)。
- `enumerate_all` 透传 `query`。

### 1.2 `core/context.rs`

- `list()` 加 `query: Option<&str>` 参数(调用点:`assemble`、`commands/context.rs`、
`tools/context_recall.rs`、`core/rpc.rs`——全部先传 `None` 保持零回归)。
- 缓存叠加层 `list_cached` 同步加 `AND (title LIKE ? ESCAPE '\' OR labels LIKE ? ESCAPE '\')`。
- **排序分叉**:
- `query == None`:维持现状(created_at 倒序)——不动现有装配行为。
- `query == Some`:改 `(kind_rank(kind), created_at DESC)`——过程类(rank 4)天然沉底,
实现"kind 先验降权",不引入打分器。
- `per_source` 摊平逻辑不变(搜索态它就是防单来源垄断的配额)。

### 1.3 测试(`context_providers.rs` / `context.rs` 内联 tests)

- `like_pattern` 转义:含 `%`/`_`/`\` 的 query 不被当通配符。
- 中文子串命中 title。
- 搜索态排序:同时命中 spec 与 llm_trace 时 spec 在前。
- `query=None` 行为与改造前完全一致(现有测试即回归网,全跑)。

---

## M2 · IPC 面(向后兼容,不新增命令)

- `commands/context.rs::list_context_items` 加 `query: Option<String>` 参数,透传 `list`。
Tauri 可选参数向后兼容,旧前端调用不受影响。
- `core/rpc.rs` 的 `ctx.list` 注册表命令同步透传(args 里可选 `query` 字段)。
- `src/services/index.ts`:`listContextItems(projectId, kinds?, limit?, query?)` 加尾参。

---

## M3 · Picker 翻转:枚举 → 搜索优先(前端)

只改 `src/components/ContextPicker.tsx`(必要的少量 CSS 类若缺,按规范先加
`src/index.css` 再登记 DESIGN.md;优先复用 `mention-pop/mention-row/chip` 现有类)。

### 3.1 分组常量(呈现层,注释指向后端 kind_rank)

```ts
/** 呈现分组(纯 UI;注入优先级见后端 core/context.rs::kind_rank,两者独立)。 */
const KIND_GROUPS: Record<string, string[]> = {
产物: [file_pinned, file_priority, project_spec, workspace_spec, workspace_doc,
workspace_deliverable, material, issue, incubator_draft, delivery_artifact,
prototype_prompt, project_meta, attachment],
过程: [chat_message, agent_output, code_agent_log, llm_trace, cr_review,
test_session, scan_finding, deployment, worktree_session, security_audit],
配置: [cfg_agent, cfg_code_agent, cfg_mcp],
外部: [mcp_result, web_result],
};
```
未知 kind 兜底进「过程」组,保证新来源不消失。

### 3.2 两态交互

**默认态(输入框为空)——候选面板,不再是全量列表:**
- 一次 `listContextItems(projectId, undefined, 120)`(后端 per_source 摊平已限每来源条数),
前端按 KIND_GROUPS 分组:
- 「产物」组默认展开,组内按 kind 小节、每 kind 显示最近 **3** 条;
- 「过程」「配置」「外部」默认**折叠**,组头显示计数徽章(`chip`),点击展开显示组内
最近 10 条;
- 组头样式:mono kicker(大写+大字距),复用现有 kicker 惯例,不新造。
- 面板底部固定一行浅色提示:`输入关键词可搜索全部 N 类来源`(text-faint)——把"万物可引"
的承诺显式告知,消除"是不是被裁掉了"的疑虑。

**搜索态(输入 ≥1 字符,debounce 200ms):**
- 调 `listContextItems(projectId, kind ? [kind] : undefined, 100, query)`——召回面向
**全部来源**(后端 LIKE 下推);
- 结果仍按组分节但**全部展开**;删除现有前端子串过滤逻辑(被后端搜索取代);
- 保留现有来源下拉(`Select`)作为搜索态的 kind 收窄器,行为不变。

### 3.3 消费点回归

四个消费点(`Conversations.tsx` 会议室@ / `BlueprintStudio.tsx` / `IntakePanel.tsx` /
`Audit.tsx`)只经 props 用 Picker,接口不变(内部翻转),逐一手测:
默认态渲染、组折叠展开、搜索命中中文、`restricted`(sourceKinds 限定)模式下分组正确降级
——restricted 时只显示交集内的组。

---

## M4 · Agent pull 补全(机器侧兑现"万物可用")

改 `src-tauri/src/agents/tools/context_recall.rs`(同文件加姊妹工具)。

### 4.1 `recall_context` 加 `query`

- spec 的 properties 加:
`"query": { "type":"string", "description":"标题关键词(可选);留空=按时间取最近" }`
- `call` 透传 `context::list(.., query)`;描述文案补一句"可用 query 关键词检索全部历史"。

### 4.2 新工具 `read_context`(按 id 取全文)

`recall_context` 只给 400 字 snippet,注释里"可进一步取用"目前无实现——这是 pull 闭环
缺的最后一块:

```rust
// spec: read_context
// args: { "id": "<kind>:<source_id>", "max_chars": int(默认4000, clamp 500..8000) }
async fn call(...) {
let item = context::get(&self.db, &id).await?.ok_or(...)?;
// 【安全·必须】项目归属校验:get() 对缓存 miss 会从 id 反构最小条目,
// 不带 project 归属 → 不校验就是跨项目读洞。
// item.project_id 非空必须 == self.project_id;为空一律拒绝。
...
let body = context::fetch_content(&self.db, &item, max_chars).await?;
// 【安全·必须】trust == external_untrusted 的条目过 has_obvious_injection,
// 命中则替换为拒绝提示(对齐 push 流程的既有约定)。
Ok(body)
}
```

- 注册:`mod.rs` 的 builtin 工厂列表在 `RecallContextFactory` 旁加 `ReadContextFactory`
(同样要求 `ctx.project_id` 存在才 build)。
- **预算护栏**:不新造机制——tool-loop 现有 `MAX_TOOL_ITERS(10)` +
`truncate_tool_output(12K)` 已封顶单任务 pull 总量;本工具单次 ≤8K 即可。

### 4.3 测试

- recall:query 命中/不命中;query=None 行为与现状一致。
- read_context:①取到全文;②**跨项目 id 拒绝**(建两项目数据,A 的工具读 B 的 id);
③external_untrusted + 注入样式正文被拦;④max_chars clamp。

---

## M5 · 收尾

- `cargo test`(基线 293)+ `tsc --noEmit` 全绿。
- `npm run tauri:dev` 手测四个 Picker 消费点 + 会议室里让 Agent 实际调
`recall_context(query=..)` → `read_context(id)` 链路。
- DESIGN.md:若为组折叠新增了 CSS 类,同步登记;未新增则免。

## 交互迁移说明(实现时的体验红线)

人侧动作(打开 Picker、@ 提及、来源下拉、选中引用)**全部保留、语义不变**;
变的只是"弹层打开那一刻看到什么":

| | 现在 | 方案后 |
|---|---|---|
| 弹层刚打开 | 200 条 26 类混排平铺,滚动找 | 四组候选面板:产物组展开(每 kind 最近 3 条),过程/配置/外部折叠成计数组头 |
| 想找某个东西 | 滚 + 前端子串过滤(**只在已拉的 200 条里**,没进来的搜不到) | 打字 → 后端全量召回(可及范围反而变大) |
| 来源下拉 | 有 | 保留,行为不变 |

新习惯只有一个:**空弹层是"入口"不是"清单",精确找东西靠打字**。为此有一条
体验红线——默认态面板底部**必须**有一行 text-faint 提示
`输入关键词可搜索全部 N 类来源`,把"万物可引"的承诺显式告知,
消除"来源是不是被裁掉了"的疑虑。实现/评审时此行不可省。

机器侧:Agent 从"只能按时间召回 + 400 字摘要"升级为"关键词检索 + 按 id 取全文",
对操作者透明——体感是 Agent 答问时能自己捞历史,而不是干等人手动 @ 喂料。

## Non-goals(本期明确不做)

- 正文全文检索(FTS5 trigram)——LIKE 搜 title+labels 先跑,验证不够再加。
- 相关性打分/向量检索——kind_rank 降权 + 关键词已覆盖主要噪音。
- lens 个人预设持久化 UI、per-kind 注入配额、跨来源内容去重(P2,另立方案)。
- context_index 的投影补齐后台任务(缓存覆盖率问题,与搜索正交)。

## 落地顺序与依赖

```
M0(独立,随时可落)
M1 → M2 → M3(人侧链)
M1 → M4(机器侧链,与 M2/M3 并行)
M5 收尾
```
每阶段独立可验证、可单独成 CR;M1 是唯一公共依赖。
Loading
Loading