feat(dispatcher): Eino 组件化补完 —— 检索 Retriever + 提示词 ChatTemplate
补完终态架构剩余两层(A 收尾): - 检索:新增 ragRetriever(eino_components.go)实现 components/retriever.Retriever, 包 mcp-go kb_search(NATS)、命中转 *schema.Document;retrieve() 改经组件, retriever 节点与报告分章检索统一走它。 - 提示词:buildMessages 改用 prompt.FromMessages + MessagesPlaceholder (FString 仅解析模板串、值原样注入 → JSON 花括号安全),产出消息序列与旧手拼等价。 - 工具:mcpTool 即 InvokableTool(Phase B),用于模型自主调用;静态 tool 节点 确定性裸调用 by design。 测试:buildMessages 形状(含花括号)+ ragRetriever 文档解析单测;make test-go 全绿。 性能:compose 每任务编译实测 ~13µs(基准),相对 LLM 秒级可忽略 → 编译图缓存 判为 premature optimization 暂不做;并行效率已由 Phase C DAG 调度拿到。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package eino
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudwego/eino/components/retriever"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
|
||||
"github.com/sundynix/sundynix-shared/contract"
|
||||
)
|
||||
|
||||
const defaultRetrieveTopK = 4
|
||||
|
||||
// ragRetriever 把 mcp-go 的混合检索(NATS kb_search)包成 Eino components/retriever.Retriever。
|
||||
// kb 在构造时绑定(owner 作用域库名);查询经 NATS 调 mcp-go,命中转 *schema.Document。
|
||||
type ragRetriever struct {
|
||||
caller ToolCaller
|
||||
kb string
|
||||
topK int
|
||||
}
|
||||
|
||||
// newRetriever 构造一个绑定到指定库的 Eino Retriever 组件。
|
||||
func (o *Orchestrator) newRetriever(kb string) retriever.Retriever {
|
||||
return &ragRetriever{caller: o.tools, kb: kb, topK: defaultRetrieveTopK}
|
||||
}
|
||||
|
||||
// Retrieve 实现 retriever.Retriever:经 NATS kb_search 检索,返回命中文档(不可用时降级空)。
|
||||
func (r *ragRetriever) Retrieve(ctx context.Context, query string, _ ...retriever.Option) ([]*schema.Document, error) {
|
||||
if r.caller == nil || r.kb == "" {
|
||||
return nil, nil
|
||||
}
|
||||
topK := r.topK
|
||||
if topK <= 0 {
|
||||
topK = defaultRetrieveTopK
|
||||
}
|
||||
cctx, cancel := context.WithTimeout(ctx, toolCallTimeout)
|
||||
defer cancel()
|
||||
res, err := r.caller.CallTool(cctx, contract.ToolSubjectGo("kb_search"), &contract.ToolCall{
|
||||
Tool: "kb_search", Args: map[string]any{"kb": r.kb, "q": query, "topK": topK},
|
||||
})
|
||||
if err != nil || res == nil || !res.OK || res.Content == "" || res.Content == "[]" {
|
||||
return nil, nil
|
||||
}
|
||||
var hits []struct {
|
||||
Text string `json:"text"`
|
||||
Score float64 `json:"score"`
|
||||
}
|
||||
if json.Unmarshal([]byte(res.Content), &hits) != nil {
|
||||
// 非结构化命中:整体作为单篇文档返回(与旧行为对齐)。
|
||||
return []*schema.Document{{Content: res.Content}}, nil
|
||||
}
|
||||
docs := make([]*schema.Document, 0, len(hits))
|
||||
for _, h := range hits {
|
||||
docs = append(docs, &schema.Document{
|
||||
Content: strings.TrimSpace(h.Text),
|
||||
MetaData: map[string]any{"score": h.Score},
|
||||
})
|
||||
}
|
||||
return docs, nil
|
||||
}
|
||||
Reference in New Issue
Block a user