WeKnora 问答实现

提问时,WeKnora 提供两种运行时:快速问答与智能推理。快速问答是固定顺序的事件插件链;智能推理是 ReAct 循环,由模型通过 Function Calling 选用工具。产品上没有单独的「计划模式」「循环模式」------计划、循环次数是智能推理里的工具或上限。两条路径共用同一套 SSE 事件。

快速问答:各步骤做什么

无知识库且未开网页搜索时,流水线很短:可选加载历史 → 流式生成。否则动态拼成完整 RAG 链:

go 复制代码
// internal/application/service/session_knowledge_qa.go
pipeline = types.NewPipelineBuilder().
    AddIf(hasHistory, types.LOAD_HISTORY).
    Add(types.QUERY_UNDERSTAND).
    Add(types.CHUNK_SEARCH_PARALLEL).
    Add(types.CHUNK_RERANK).
    AddIf(req.WebSearchEnabled, types.WEB_FETCH).
    Add(types.CHUNK_MERGE).
    Add(types.FILTER_TOP_K).
    AddIf(chatManage.DataAnalysisEnabled, types.DATA_ANALYSIS).
    Add(types.INTO_CHAT_MESSAGE).
    Add(types.CHAT_COMPLETION_STREAM).
    Build()

各阶段职责:

阶段 做什么
LOAD_HISTORY 按轮数从消息表取历史问答,拼进后续上下文。
QUERY_UNDERSTAND 一次模型调用做查询改写与意图分类(寒暄、闲聊、要检索、追问等)。意图若不需要检索,后续检索插件仍会触发,但内部直接 next(),不打向量库。有图时可用视觉模型一并出图描述。
CHUNK_SEARCH_PARALLEL 并行跑知识库混合检索(稠密 + 稀疏 + RRF,见混合检索)与可选网页搜索;图谱开启时还可并行实体检索。召回不足且打开查询扩展时,再并发变体查询补一轮。
CHUNK_RERANK 用重排模型对候选段落打分、按阈值排序并按 RerankTopK 截断;未配置重排模型则跳过。
WEB_FETCH(可选) 对网页搜索命中的 URL 拉正文,丰富引用材料。
CHUNK_MERGE 去重、历史引用弱化、子块抬父块、重叠区间合并、FAQ 答案填充等,整理成一份待进提示词的结果集。
FILTER_TOP_K 再截一刀,控制最终进模型的条数。
DATA_ANALYSIS(可选) 表格类分析开启时,走数据分析相关步骤。
INTO_CHAT_MESSAGE 按上下文模板把检索结果与用户问题拼成模型输入;引用别名在此前后处理(见回答溯源)。拼好的内容可写入 RenderedContent 供下轮历史使用。
CHAT_COMPLETION_STREAM 流式调用对话模型,经事件总线推到 SSE。

快速问答侧的「插件」是按事件类型注册的流水线插件 :每个插件声明自己关心的 EventType,EventManager 按流水线顺序触发;插件内决定干活还是跳过。阶段固定、行为可关,排障时能对上「卡在改写还是重排」。

智能推理:循环机制与 Function Calling

智能推理不是「只调一次模型、在一次响应里塞满所有工具」。它分两层,容易混在一起:

层次 是什么 次数感
外层循环(ReAct) 每轮 一次 LLM 调用;有工具则执行并写回 messages,再开下一轮 由 MaxIterations 封顶(引擎常量默认约 20;智能体配置可改,校验还有更小的兜底与硬上限)
单轮 Function Calling 这一次 LLM 响应里,模型可返回 0~多个 tool_calls(可并行执行) 跟「20」无关;20 不是「Function Calling 默认能调 20 次」

也就是说:多轮 = 多次 LLM 调用 ;Function Calling = 某一轮响应里选用工具的协议 。轮与轮之间靠累加的 messages 协作(上一轮的工具结果成为下一轮输入),没有单独的「计划模式」产品开关------计划类能力是可选工具(如 todo_write),循环次数是配置上限。

每轮内部大致是 Think → Analyze → Act → Observe:

  1. Think:系统提示、历史、当前用户轮 + 已注册工具的 Function Definition → 对话模型(开启 function calling)。
  2. Analyze :若自然停止(stop / end_turn 等)且 无 tool_calls,本轮文本即最终答案,结束外层循环。没有名为 final_answer 的工具。
  3. Act :若有 tool_calls,按名从注册表执行(串行或并行由配置决定),写成 tool 消息。
  4. Observe :结果追加进 messages,进入 下一轮 LLM ;同时可做上下文压缩(见上下文管理)。
go 复制代码
// internal/agent/engine.go(分析是否答完)
verdict := e.analyzeResponse(ctx, response, step, ...)
if verdict.isDone { /* FinalAnswer = 本轮文本;跳出循环 */ }
// 否则 executeToolCalls → 写回 messages → 继续下一轮 LLM

流式开场白 也是单轮内的事,与 MaxIterations 无关:模型可能先流式吐出「我去查一下」,再发出 tool_call。前端一开始可能把它当答案展示;一旦确认本轮有工具调用,这段字改判为开场白(preamble),挪进步骤树,不写入最终消息。真正答案仍是某轮「无工具、自然停止」时的纯文本。

可插拔工具各自做什么

内置工具实现同一接口,按名注册;UI / 预设里的清单与 AvailableToolDefinitions 对齐。常见几类:

工具 作用
knowledge_search 语义检索(内部仍走混合检索 + 重排)
grep_chunks 关键词快速定位块
list_knowledge_chunks 按文档列出分块,便于通读
query_knowledge_graph 查知识图谱实体关系
get_document_info 文档元数据
web_search / web_fetch 网页搜索与抓取正文(开网页搜索时才挂上)
data_schema / data_analysis / database_query 表结构、分析与库查询
wiki_* 读/搜/写 Wiki 页、源文档、问题标记等(有 Wiki 库时才保留)
thinking / todo_write 逐步思考、列计划;预设里常默认关掉,避免简单问答浪费 token
read_skill / execute_skill_script 渐进读取技能说明、在沙箱跑技能脚本
MCP 工具 远端 MCP 服务上的工具,包装后同名注册进同一注册表(名形如 mcp_{服务}_{工具})

危险 MCP 可走审批门;需 OAuth 的服务可在会话中授权后再续跑。

插拔怎么设计

核心是三层:统一接口 → 注册表 → 按配置装配。

1. 工具接口(所有内置 / MCP / 技能执行器都实现):

go 复制代码
// internal/types/agent.go
type Tool interface {
    Name() string
    Description() string
    Parameters() json.RawMessage // JSON Schema,给 Function Calling
    Execute(ctx context.Context, args json.RawMessage) (*ToolResult, error)
}

2. 注册表 ToolRegistry:RegisterTool(同名先到先得,防劫持)、GetFunctionDefinitions(按名排序,便于提示缓存)、ExecuteTool(校验参数后再 Execute)。引擎只依赖注册表,不依赖某个具体工具类型。

3. 装配策略(创建引擎时):

  • 以智能体配置里的 AllowedTools 白名单为准;空则用默认列表。不会静默塞进用户没选的工具。
  • 再按能力裁剪:本轮没有知识库范围时去掉 KB/Wiki/数据分析类;没有 Wiki 库时去掉 wiki_*;开启网页搜索时才追加 web_search / web_fetch。
  • MCP 按 MCPSelectionMode(all / selected / none)拉服务,每个远端工具包装成实现同一 Tool 接口的适配器,再 RegisterTool。
  • 技能开启时注册 read_skill 等;脚本执行受沙箱模式约束。

因此「插拔」不是改引擎主循环,而是:实现 Tool → 出现在允许列表且通过能力过滤 → 注册进表 → 模型在 Function Calling 里看见并选用 。快速问答的流水线插件是另一套插拔(按 EventType),面向固定阶段;智能推理的工具插拔面向开放动作集。两者都把「扩展点」收成接口 + 注册,而不是在核心路径里写死 if tool == xxx。

提升了什么

固定链保证日常问答路径可预期、可按阶段观测;工具注册表让多步任务、MCP、技能在同一循环里扩展,而不复制第二套 Agent 运行时。前端仍只消费一套流式事件。

好处与代价

好处

  • 快速问答阶段清晰,意图不需检索时可跳过重活。
  • 工具白名单 + 能力过滤,减少模型调用必然失败的工具。
  • MCP / 技能与内置工具同一接口,扩展面统一。

代价

  • 快速问答默认不会因首轮检索差而自主多跳(除非查询扩展等补丁)。
  • 智能推理轮次多、上下文与费用更高;过渡句与最终答案的流式区分增加复杂度。
  • 工具过多或计划/思考类默认打开时,简单问答也容易绕远路。

基于 WeKnora v0.7.0

上一篇:WeKnora 混合检索 | 下一篇:WeKnora 上下文管理

相关推荐
小卿噢1 小时前
94% 的集群在空等——数据倾斜实测
后端
五百万啊啊啊啊1 小时前
RSA+AES 混合加密的三个坑
后端
糖果豆豆1 小时前
监控全绿,线上还是抖了一下——加埋点只能解决三种"看不见"里的一种
后端·架构
琳达爱分享1 小时前
五个线程同时去换 token,四个被踢下线:刷新接口其实不是幂等的
后端·算法·架构
数据库小学妹1 小时前
MySQL表碎片与 OPTIMIZE TABLE:空间回收的代价与边界
数据库·后端·mysql
故图1 小时前
全字段 INSERT IGNORE:大约 70 行零日期,把证照列表加载打崩
后端
Json____1 小时前
用 FastAPI + Vue3 打造一套健身私教课预约管理系统
后端·fastapi·it学习·wwwoop.com
aibo8603082 小时前
基于 Spring Boot + Vue3 的博物馆藏品管理系统设计与实现
java·spring boot·后端
苍何2 小时前
Codex+Obsidian,多平台同步,一键分发,太香了!
后端