Skip to content

Feat: 关于【内置分块策略】、【“API 进程内直接执行对话流”重构为“异步任务执行 + SSE 增量拉取”】、【Skills 功能】这三个功能交流 #526

@1165506270

Description

@1165506270

我目前基于v0.5 做了【内置分块策略】、【“API 进程内直接执行对话流”重构为“异步任务执行 + SSE 增量拉取”】、【Skills 管理】几个功能。 @xerrors 麻烦看下这些功能描述,提供一些建议,是否采纳。
这几个功能代码 我已经提交到我 fork 的仓库了
https://github.com/1165506270/Yuxi-Know/tree/main

1) 内置分块策略(Built-in Chunking Presets)

为什么做
当前文档类型差异大(通用文档、问答文档、书籍章节、法规条款),单一切分方式会导致召回稳定性不足。
平台将分块能力产品化为“预设策略”,让用户在不理解底层算法细节的情况下,按文档类型选择合适切分方式并提升检索效果。

交互过程

用户在创建/编辑知识库时选择分块策略:general、qa、book、laws。
文件入库时默认继承知识库策略,也支持本次入库覆盖策略。
每个文件会记录本次实际使用的策略与参数快照(便于追溯与复现)。
切换知识库策略后,仅影响后续新入库文件,不自动重跑历史文件。

实现思路

参考 RAGFlow
在分块链路中引入统一调度器(ragflow-like dispatcher),按 chunk_preset_id 分发到对应解析流程。
策略映射对齐 RAGFlow:general -> naive,qa/book/laws 使用各自专用解析逻辑。
参数按固定优先级合并:请求参数 > 文件已有处理参数 > 知识库参数 > 预设默认参数。
不新增表:
知识库维度使用 knowledge_bases.additional_params 保存默认策略;
文件维度使用 knowledge_files.processing_params 保存实际执行快照(chunk_preset_id、chunk_parser_config、chunk_engine_version)。

2) 断线续流输出(Streaming Resume,Run + SSE)

为什么做

  • 原先“HTTP 直连流”模式下,前端离开对话页面任务就会中断, 长任务场景下体验不友好。
  • 目标是实现“前端可断开重连,但后台任务继续执行”,并支持从断点补流。

重构改动(核心架构变化)

  • 将“API 进程内直接执行对话流”重构为“异步任务执行 + SSE 增量拉取”。
  • 新增 worker 容器,专门负责执行 run(LangGraph / LLM / tools)。
  • api 容器不再承担长时执行,改为:
    1. 创建 run
    2. 提供 run 状态查询
    3. 提供 SSE 增量事件流
    4. 接收 cancel 请求
  • 运行基础设施职责划分:
    • Redis:任务队列、取消信号、短期事件流(Redis Stream)
    • Postgres:agent_runs 状态机、messages/tool_calls 最终落库、checkpointer 持久化

交互过程

  1. 客户端调用 POST /api/chat/agent/{agent_id}/runs 创建任务,拿到 run_id
  2. 客户端调用 GET /api/chat/runs/{run_id}/events?after_seq=... 建立 SSE。
  3. worker 从队列消费 run_id,执行任务并持续写入增量事件。
  4. 前端实时消费事件并记录最新 seq
  5. 若连接中断,前端自动重连并带上 after_seq=last_seq
  6. 服务端按游标补发缺失事件,再继续实时推送。
  7. run 终态后发送 close,前端收尾并刷新历史消息/状态。

分块与续流策略(当前实现)

  • worker 对增量文本做分块写入,flush 条件:
    • 100ms,或
    • 累计 512 字符
  • 分块事件以 loading 形式输出(payload.items)。
  • SSE 端按 after_seq 从 Redis Stream 增量读取。
  • 前端做 seq 单调门禁:seq <= runLastSeq 直接丢弃,避免重复渲染。
  • 空闲连接发送 heartbeat;终态时补发剩余事件后发送 close

用户侧效果

  • 页面刷新/离开后,不需要重新提问。
  • 回来后可从中断点继续显示输出。
  • 任务本身不会因为前端离开而被动取消(仅显式 cancel 才中断)。

3)Skills 管理

Skills 管理模块用于集中维护可供 Agent 只读引用的技能包。
本期采用“文件系统存内容,数据库存索引”模式:

  1. 技能目录存储在 /app/saves/skills(本地 save_dir/skills)。
  2. 技能元数据(slug/name/description/dir_path)存储在 skills 表。
  3. Agent 配置通过 context.skills 选择技能,运行时挂载到 /skills 且只读。

权限与入口

  1. 系统设置中新增 Skills 管理 页签(仅 superadmin 可见)。
  2. admin 仅可调用列表接口(用于 Agent 配置选择 skills)。
  3. user 无 skills 管理权限。

导入规范(ZIP)

  1. 单包单技能,且必须包含一个 SKILL.md
  2. SKILL.md 必须包含 frontmatter,且 namedescription 必填。
  3. name 需满足 slug 规则:小写字母/数字/短横线。
  4. 导入时执行路径安全校验,拒绝绝对路径与 .. 路径穿越。
  5. slug 冲突时自动追加 -v2/-v3...,并自动改写 SKILL.mdname 为最终 slug。
  6. 导入采用临时目录 + 原子替换,避免半成品落盘。

在线管理能力

  1. Skills 列表:来自数据库,避免全量目录扫描。
  2. 目录树:按原生目录结构展示。
  3. 文件级 CRUD:支持新建文件/目录、编辑文本文件、删除文件/目录。
  4. 文件编辑仅允许文本类型(如 md/py/js/ts/json/yaml/toml/txt 等)。
  5. SKILL.md 保存后会重新解析,并同步更新数据库中的 name/description
  6. 支持导出单个 skill 为 ZIP。
  7. 删除 skill 时会同时删除目录与数据库记录(硬删除)。

Agent 运行时行为

  1. context.skills 用于配置技能 slug 列表。
  2. 运行时按会话构建 SkillResolver 快照(同一会话首次构建,后续复用)。
  3. 运行时仅暴露快照中的可见 skills 到 /skills/<slug>/...
  4. /skills 路径只读,不允许写入、编辑、上传。
  5. 同会话内若 context.skills 变化会触发快照重建。
  6. 后台修改 skills 内容后,已有会话不会自动刷新,需新会话或调整 context.skills 才生效。

依赖类型说明

每个 skill 支持三类依赖,均在 Skills 管理页维护:

  1. tool_dependencies:该 skill 需要的内置工具名列表。
  2. mcp_dependencies:该 skill 需要的 MCP 服务器名列表。
  3. skill_dependencies:该 skill 依赖的其他 skill slug 列表。

约束与语义:

  1. 依赖在保存时做合法性校验,不允许引用不存在的工具/MCP/skill。
  2. skill_dependencies 不允许包含自身。
  3. skill_dependencies 按递归闭包生效,自动去重、去环、保序。

渐进式加载流程

系统不会在会话开始时一次性加载全部依赖,而是按阶段渐进加载:

阶段 1:会话启动前(构建 skill 可见集)

  1. 读取 context.skills 作为用户显式选择的 skills(selected)。
  2. SkillResolver 递归展开 skill_dependencies,得到 visible_skills(selected + 依赖闭包)。
  3. 把快照写入 runtime.context.skill_session_snapshot
  4. 基于 visible_skills 构建 skills prompt 段,并在 abefore_agent 预拼接到 system_prompt
  5. /skills 只挂载 visible_skills,所以被依赖 skill 从会话首轮起即可被读取。

结论:skill_dependencies 是“会话启动即生效”的。

阶段 2:技能激活时(按需激活)

  1. Agent 通过 read_file 读取 /skills/<slug>/SKILL.md 时,视为激活该 skill。
  2. 仅当 <slug>skill_session_snapshot.visible_skills 内,激活才被接受。
  3. 激活结果写入 activated_skills(去重保序)。

结论:只有“真正被读取并使用”的 skill 才会进入后续依赖注入计算。

阶段 3:后续模型轮次(注入工具与 MCP 依赖)

  1. awrap_model_call 中,基于 activated_skills 计算依赖闭包。
  2. 聚合闭包内 skill 的 tool_dependenciesmcp_dependencies
  3. 仅把这些依赖工具/MCP 合并进本轮可用工具集。
    tool_dependenciesmcp_dependencies 是“激活后按需加载”的,不会在会话首轮全量注入。

Metadata

Metadata

Assignees

No one assigned

    Labels

    featNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions