ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

qwen-code Web Shell 会话内容搜索:基于 Daemon 转录扫描的会话检索设计解析

qwen-code Web Shell 会话内容搜索:基于 Daemon 转录扫描的会话检索设计解析 qwen-code Web Shell 会话内容搜索基于 Daemon 转录扫描的会话检索设计解析【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读当 web-shell 侧边栏的 Search sessions 只按标题、会话 ID 和 Git 上下文过滤时用户回忆起来的往往是对话正文里的某个关键词——而这些关键词通常不会出现在自动生成的标题中。本文基于 qwen-code 仓库中的设计文档 docs/design/2026-08-31-web-shell-session-content-search.md完整讲解会话内容搜索Session Content Search的端到端方案新增的 daemon HTTP 端点、磁盘转录流式扫描的匹配算法、前端防抖 Hook 与结果合并策略并给出对应的源码实现与测试证据。读完后你将掌握该功能从请求路由、匹配语义到 UI 渲染的完整链路以及它如何与既有的本地标题过滤形成快路径 慢路径的分层检索架构。背景为什么标题搜索不够用问题的本质web-shell 侧边栏的 Search sessions 输入框最初只对已加载进客户端的DaemonSessionSummary对象做过滤可匹配字段仅有会话标签label会话 IDGit 上下文但会话标题往往是通用模板或自动生成的如修复登录页样式处理 feedback用户真正记住的是对话里说过的具体词句。于是出现典型的检索失败场景用户记得自己在某次对话中提过qdrant 索引但标题过滤结果为空。设计文档 2026-08-31-web-shell-session-content-search.md 将这一痛点定义为搜索只覆盖元数据、不覆盖正文。方案取舍为什么不用现成的q参数一个直觉方案是在既有 sessions 列表路由上追加q参数但设计文档明确否决了它理由是该路由自身带有两类复杂语义游标分页cursor pagination搜索结果需要排序与分页与列表游标混在一起会互相干扰live / persisted 合并语义列表路由负责合并内存中的 live 会话与磁盘持久化会话搜索逻辑不应被卷入这套状态机。因此设计上采用独立、纯增量additive-only的新端点与列表路由彻底解耦。这一取舍思路在 packages/cli/src/serve/routes/session.ts 的实现中得到印证新路由与列表路由是两个独立 handler互不共享分页逻辑。Daemon APIGET /workspace/:id/sessions/search端点签名与注册新端点同时注册了两个拼写形态单数/复数对应仓库中工作区路由的两种历史命名GET /workspace/:id/sessions/search?qtextmaxResults1-50 GET /workspaces/:workspace/sessions/search?qtextmaxResults1-50源码中由searchWorkspaceSessionsHandler工厂统一生成两种 handler 并注册app.get(/workspace/:id/sessions/search, searchWorkspaceSessionsHandler(id)); app.get(/workspaces/:workspace/sessions/search, searchWorkspaceSessionsHandler(workspace));参见 packages/cli/src/serve/routes/session.ts。工作区作用域语义端点沿用 sessions 列表路由的工作区级运行时解析规则packages/cli/src/serve/routes/session.ts仅解析已解析的运行时resolved-runtime绝不回退到主运行时只读的次要工作区只能检索自己的持久化存储不能触碰主运行时的会话。从源码结构看resolveRuntimeForCatalogRoute将运行时解析与 catalog 路由解耦保证搜索端点与列表端点共享同一套运行时归属判定避免跨工作区串数据。参数校验参数规则失败响应q非空、trim 后 ≤ 200 字符400code: invalid_search_querymaxResults整数1–50400code: invalid_search_max_results常量定义在路由源码中const SESSION_SEARCH_QUERY_MAX_LENGTH 200; const SESSION_SEARCH_MAX_RESULTS_LIMIT 50;校验实现细节packages/cli/src/serve/routes/session.tsq先做trim()再校验长度——空白填充不计入长度上限与服务端匹配器的空白归一化保持一致maxResults用正则/^\d$/拒绝非纯数字字符串再经Number.parseIntNumber.isSafeInteger校验防止溢出与小数。请求生命周期与取消搜索端点把请求的 abort 信号透传给扫描过程packages/cli/src/serve/routes/session.ts监听req.once(aborted)与res.once(close)任一触发即controller.abort()扫描中途 abort 直接静默返回不产生错误响应因为客户端已经离开若 daemon 处于 draining 状态返回503code: daemon_draining其他异常统一返回500code: session_search_failed并写入 stderr 日志。这意味着用户在输入框继续打字时上一个 in-flight 请求会被立刻取消避免旧结果覆盖新结果、也避免浪费磁盘 IO。响应契约{ results: [ { session: DaemonSessionSummary, snippet: string } ] }结果按最近修改时间倒序排列最新的会话排最前session是完整sidecar 增强后的 summary因此幽灵命中尚未加载进客户端 catalog 的会话也能直接渲染snippet是单行、约 120 字符的匹配摘录。服务端响应的组装实现在 packages/cli/src/serve/server/session-list.ts 的searchWorkspaceSessionsForResponse它先用SessionService.searchSessionContent拿到命中再用getSessionListItem重建 summary并应用applyOrganizationpin / group / color与 worktree/PR sidecar 增强——保证搜索结果行与 catalog 行的组织状态完全一致不破坏下游的 pin/group 不变量。匹配引擎SessionService.searchSessionContent核心算法匹配核心在 packages/core/src/services/sessionService.ts 的searchSessionContent()采用无全文本索引的流式扫描策略读目录 → 过滤出 *.jsonl 会话文件 → 按 mtime 倒序 → 截取最近 maxFiles 个 → 逐文件流式读行 → 首个命中即停 → 收集 hit → 达到 maxResults 停止关键参数与默认值参数默认值含义maxFiles200最多扫描的会话文件数v1 无索引的硬性扫描边界maxResults20最多返回的命中数signal无AbortSignal中途取消扫描实现要点均有对应源码/测试佐证项目隔离每个文件的第一条记录会经过sessionBelongsToCurrentProject校验——chats 目录可能存放跨项目共享的会话不能把别的项目的会话算进来packages/core/src/services/sessionService.ts容错解析每行经jsonl.parseLineTolerant解析即使出现}{粘连的损坏行#3606 形态也能解出两条记录不会静默丢弃取消与让步stat 目录阶段每SESSION_LIST_CANCEL_YIELD_INTERVAL个文件throwIfAborted()并setImmediate让步一次逐行阶段每 1024 行检查一次 abort——大目录、大文件下 UI 依然响应目录缺失ENOENT直接返回空数组不抛错旧工作区没有 chats 目录时搜索自然为空。匹配文本的提取规则extractRecordSearchTextpackages/core/src/services/sessionService.ts决定哪些文本参与匹配规则与用户可见的展示投影保持一致仅匹配user与assistant两类记录subtype记录斜杠命令、遥测等一律跳过user 记录优先取prompt 的displayTextpayload存在时否则回退到 text parts去掉尾部 hook-submit-context partassistant 记录匹配其message.parts文本。这样保证搜得到的就是用户屏幕上看到过的内容。大小写、空白与 Unicode 细节查询与正文在匹配前做完全一致的归一化packages/core/src/services/sessionService.tstrim() 空白折叠/\s/g → toLowerCase()normalizeSigma希腊语词尾 sigma 的折叠对齐保证σ/ς等价匹配用indexOfCaseInsensitive大小写不敏感且不按代码点切分规避toLowerCase改变字符串长度导致的边界错位。测试 packages/core/src/services/sessionService.search.test.ts 覆盖了这些边界matches user message text case-insensitively大小写不敏感normalizes whitespace runs in the query空白折叠对齐matches Greek text ending in sigma for every sigma query formsigma 折叠matches supplementary-plane case pairs in both directions增补平面字符never splits a surrogate pair at a snippet boundarysnippet 边界不拆代理对。Snippet 生成buildSearchSnippet负责把命中文本压缩为单行约 120 字符的摘录折叠所有空白为单个空格多行消息变成一行以命中位置为中心截取窗口两端用省略号表示截断窗口边界保证不会拆散 surrogate pair也不会以孤立的前导代理结尾对应两条专项测试。从源码结构看snippet 与查询共用同一套空白折叠逻辑因此a b匹配a b、查询a b也能匹配两者所见即所得。前端useSessionContentSearchHook 与结果合并Hook 的行为契约前端 Hook 实现在 packages/web-shell/client/components/sidebar/useSessionContentSearch.ts行为可总结为行为细节防抖300 msDEBOUNCE_MS最短查询少于 2 个字符不发起请求MIN_QUERY_LENGTH超长查询截断到 200 字符与服务端q上限一致并小心地不把代理对切半请求取消每次 effect 清理时controller.abort()被取代的请求直接丢弃错误降级任何失败含 daemon 太老返回 404都回退为本地过滤UI 不报错值得注意的工程细节渲染期重置查询/工作区/客户端变化时在 render 阶段而非 passive effect就把 hits 重置为空——否则新 key 会短暂配对旧 key 的 settled hits造成闪现失效键invalidationKey由reload token 会话成员 key组成。catalog 成员变化会话被删除/归档会立刻清空已 settle 的 hits防止已删除的会话以幽灵行继续渲染。调用方在 WebShellSidebar.tsx 中用排序后的 sessionId 列表拼成成员 keypoll 驱动的 catalog 更新只有在 id 集合真正变化时才触发失效不会每个 tick 都重查。结果合并策略WebShellSidebar主工作区与WorkspaceSection次要工作区都遵循同一套合并规则本地匹配优先标题 / ID / Git 匹配的会话先按 catalog 顺序排在最前快路径内容命中随后服务端返回的 content hits 按 recency 顺序追加去重保活若某个命中已在加载的 catalog 中则保留其 catalog 条目live 状态、组织信息不丢失否则用搜索结果返回的完整 summary 渲染幽灵会话也能显示渲染命中会话在标签下方多渲染一行 muted 截断文本即 snippet。端到端测试佐证多工作区集成测试 packages/cli/src/serve/multi-workspace-sessions.test.ts 覆盖了完整链路GET /workspace/secondary-id/sessions/search?qqdrant正常返回命中空查询q%20%20、超长q201 字符均返回invalid_search_query非法maxResults返回invalid_search_max_results复数拼写/workspaces/:workspace/sessions/search同样注册生效其他工作区 selector 的转义encodeURIComponent路径也有断言。非目标v1 边界设计文档明确列出 v1 不做的事理解这些边界有助于判断功能适用范围无全文本索引 / 无持久化搜索缓存——扫描是每次请求时的有界流式 IOmaxFiles200/maxResults20保证防抖搜索的响应性不搜索归档会话——只覆盖 active 会话归档检索留作后续SessionOverviewPanel面板内的搜索不在本设计范围**CLI picker 的会话搜索#6824**不在本设计范围但文档明确说明该 daemon 端点被设计为可复用的未来 CLI / VS Code 对话搜索可直接调用它不做匹配词高亮——snippet 只是纯文本截断。从源码看整体架构整个功能的调用链可以归纳为WebShellSidebar / WorkspaceSection └─ useSessionContentSearch(client, workspaceCwd, query, invalidationKey) └─ client.searchWorkspaceSessions(...) [SDK 侧 HTTP 调用] └─ GET /workspace/:id/sessions/search └─ searchWorkspaceSessionsHandler (routes/session.ts) ├─ 参数校验 AbortController 生命周期 └─ searchWorkspaceSessionsForResponse (server/session-list.ts) ├─ SessionService.searchSessionContent (sessionService.ts) │ └─ searchSessionFileForQuery: 流式逐行 extractRecordSearchText └─ summary 重建 organization / sidecar 增强分层设计带来的直接收益快路径客户端本地标题/ID/Git 过滤零网络开销慢路径daemon 磁盘扫描由 300ms 防抖、2 字符阈值、请求取消和有界扫描共同约束容错旧版 daemon 不支持该路由时 404 → Hook 静默降级为本地过滤功能自动软降级。这套本地快路径 服务端慢路径 优雅降级的模式也可以作为其他需要全文检索但暂时不愿引入索引的 UI 场景的参考范式。相关文档与源码索引设计文档docs/design/2026-08-31-web-shell-session-content-search.md路由与校验packages/cli/src/serve/routes/session.ts服务端响应组装packages/cli/src/serve/server/session-list.ts匹配引擎packages/core/src/services/sessionService.ts核心单测packages/core/src/services/sessionService.search.test.ts前端 Hookpackages/web-shell/client/components/sidebar/useSessionContentSearch.ts侧边栏接入packages/web-shell/client/components/sidebar/WebShellSidebar.tsx端到端测试packages/cli/src/serve/multi-workspace-sessions.test.ts【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表