feat: 1.增加 eino tool 相关配置,货易通商品上传参数配置化 2. eino tool 注册方法调整
This commit is contained in:
parent
d0ba329024
commit
d8df571cce
|
|
@ -74,11 +74,19 @@ tools:
|
|||
zltxOrderAfterSaleResellerBatch:
|
||||
enabled: true
|
||||
base_url: "https://gateway.dev.cdlsxd.cn/zltx_api/admin/afterSales/reseller_pre_ai"
|
||||
|
||||
# eino tool 配置
|
||||
eino_tools:
|
||||
# 货易通商品上传
|
||||
hytProductUpload:
|
||||
enabled: true
|
||||
base_url: "https://gateway.dev.cdlsxd.cn/zltx_api/admin/oursProduct"
|
||||
|
||||
|
||||
base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/supplier/batch/add/complete"
|
||||
add_url: "https://gateway.dev.cdlsxd.cn/sw//#/goods/goodsManage"
|
||||
# 货易通供应商查询
|
||||
hytSupplierSearch:
|
||||
base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/supplier/list"
|
||||
# 货易通仓库查询
|
||||
hytWarehouseSearch:
|
||||
base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/warehouse/list"
|
||||
|
||||
default_prompt:
|
||||
img_recognize:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type Config struct {
|
|||
Coze CozeConfig `mapstructure:"coze"`
|
||||
Sys SysConfig `mapstructure:"sys"`
|
||||
Tools ToolsConfig `mapstructure:"tools"`
|
||||
EinoTools EinoToolsConfig `mapstructure:"eino_tools"`
|
||||
Logging LoggingConfig `mapstructure:"logging"`
|
||||
Redis Redis `mapstructure:"redis"`
|
||||
DB DB `mapstructure:"db"`
|
||||
|
|
@ -141,8 +142,6 @@ type ToolsConfig struct {
|
|||
CozeExpress ToolConfig `mapstructure:"cozeExpress"`
|
||||
// Coze 公司查询工具
|
||||
CozeCompany ToolConfig `mapstructure:"cozeCompany"`
|
||||
// 货易通商品上传
|
||||
HytProductUpload ToolConfig `mapstructure:"hytProductUpload"`
|
||||
}
|
||||
|
||||
// ToolConfig 单个工具配置
|
||||
|
|
@ -155,6 +154,16 @@ type ToolConfig struct {
|
|||
AddURL string `mapstructure:"add_url"`
|
||||
}
|
||||
|
||||
// EinoToolsConfig eino tool 配置
|
||||
type EinoToolsConfig struct {
|
||||
// 货易通商品上传
|
||||
HytProductUpload ToolConfig `mapstructure:"hytProductUpload"`
|
||||
// 货易通供应商查询
|
||||
HytSupplierSearch ToolConfig `mapstructure:"hytSupplierSearch"`
|
||||
// 货易通仓库查询
|
||||
HytWarehouseSearch ToolConfig `mapstructure:"hytWarehouseSearch"`
|
||||
}
|
||||
|
||||
// LoggingConfig 日志配置
|
||||
type LoggingConfig struct {
|
||||
Level string `mapstructure:"level"`
|
||||
|
|
|
|||
|
|
@ -32,11 +32,6 @@ const (
|
|||
"货品图片": ["string"], // 商品多图,取1-2个即可
|
||||
"电商销售价格": "string", // 商品电商销售价格 decimal(10,2)
|
||||
"销售价": "string", // 商品销售价格 decimal(10,2)
|
||||
"供应商报价": "string", // 商品供应商报价 decimal(10,2)
|
||||
"税率": "string", // 商品税率 x%
|
||||
"默认供应商": "string", // 供应商名称
|
||||
"默认存放仓库": "string", // 仓库名称
|
||||
"利润": "string", // 商品利润 decimal(10,2)
|
||||
"备注": "string", // 备注
|
||||
"长": "string", // 商品长度,decimal(10,2)+单位
|
||||
"宽": "string", // 商品宽度,decimal(10,2)+单位
|
||||
|
|
@ -44,135 +39,14 @@ const (
|
|||
"重量": "string", // 商品重量(kg)
|
||||
"SPU名称": "string", // 商品SPU名称
|
||||
"SPU编码": "string" // 编码串,jd_{timestamp}_rand(1000-999)
|
||||
"供应商报价": "string", // 商品供应商报价 decimal(10,2)
|
||||
"税率": "string", // 商品税率 x%
|
||||
"利润": "string", // 商品利润 decimal(10,2)
|
||||
"默认供应商": "string", // 供应商名称
|
||||
"默认存放仓库": "string", // 仓库名称
|
||||
}`
|
||||
)
|
||||
|
||||
// 商品属性模板
|
||||
const (
|
||||
HYTProductPropertyTemplate = `{
|
||||
"important_data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"supplier_name": {
|
||||
"type": "string",
|
||||
"description": "供应商名称"
|
||||
},
|
||||
"warehouse_name": {
|
||||
"type": "string",
|
||||
"description": "仓库名称"
|
||||
},
|
||||
"profit": {
|
||||
"type": "float64",
|
||||
"description": "利润 decimal(10,2)"
|
||||
},
|
||||
"tax_rate": {
|
||||
"type": "integer",
|
||||
"description": "税率 (x)%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"goods_info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "商品名称"
|
||||
},
|
||||
"brand": {
|
||||
"type": "string",
|
||||
"description": "品牌"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "分类"
|
||||
},
|
||||
"price": {
|
||||
"type": "float64",
|
||||
"description": "市场价 decimal(10,2)"
|
||||
},
|
||||
"sales_price": {
|
||||
"type": "float64",
|
||||
"description": "建议销售价 decimal(10,2)"
|
||||
},
|
||||
"discount": {
|
||||
"type": "integer",
|
||||
"description": "折扣百分比 公式:(市场价-建议销售价)/市场价*100"
|
||||
},
|
||||
"goods_attributes": {
|
||||
"type": "string",
|
||||
"description": "商品属性"
|
||||
},
|
||||
"goods_bar_code": {
|
||||
"type": "string",
|
||||
"description": "商品条码"
|
||||
},
|
||||
"goods_illustration": {
|
||||
"type": "string",
|
||||
"description": "商品插图"
|
||||
},
|
||||
"goods_num": {
|
||||
"type": "string",
|
||||
"description": "商品编号"
|
||||
},
|
||||
"introduction": {
|
||||
"type": "string",
|
||||
"description": "商品介绍"
|
||||
},
|
||||
"spu_name": {
|
||||
"type": "string",
|
||||
"description": "SPU名称"
|
||||
},
|
||||
"spu_num": {
|
||||
"type": "string",
|
||||
"description": "SPU编号"
|
||||
},
|
||||
"stock": {
|
||||
"type": "integer",
|
||||
"description": "库存"
|
||||
},
|
||||
"tax_rate": {
|
||||
"type": "integer",
|
||||
"description": "税率"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"description": "单位"
|
||||
},
|
||||
"weight": {
|
||||
"type": "string",
|
||||
"description": "重量"
|
||||
}
|
||||
}
|
||||
},
|
||||
"goods_media_list": {
|
||||
"type": "array",
|
||||
"description": "商品媒体文件列表",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "媒体文件URL"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer",
|
||||
"description": "媒体类型(1:图片, 2:视频)"
|
||||
},
|
||||
"sort": {
|
||||
"type": "integer",
|
||||
"description": "排序序号"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
)
|
||||
|
||||
// 外部平台地址
|
||||
const (
|
||||
HYTProductListPageURL = "https://gateway.dev.cdlsxd.cn/sw//#/goods/goodsManage"
|
||||
)
|
||||
|
||||
// 缓存key
|
||||
const (
|
||||
CapabilityProductIngestCacheKey = "ai_scheduler:capability:product_ingest:%s"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,17 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
func Call(ctx context.Context, cfg config.ToolConfig, toolReq *ProductUploadRequest) (toolResp *ProductUploadResponse, err error) {
|
||||
type Client struct {
|
||||
cfg config.ToolConfig
|
||||
}
|
||||
|
||||
func New(cfg config.ToolConfig) *Client {
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Call(ctx context.Context, toolReq *ProductUploadRequest) (toolResp *ProductUploadResponse, err error) {
|
||||
// 商品有且只能有一个
|
||||
if len(toolReq.GoodsList) != 1 {
|
||||
err = errors.New("商品只能有一个")
|
||||
|
|
@ -20,7 +30,7 @@ func Call(ctx context.Context, cfg config.ToolConfig, toolReq *ProductUploadRequ
|
|||
|
||||
req := l_request.Request{
|
||||
Method: "Post",
|
||||
Url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/supplier/batch/add/complete",
|
||||
Url: c.cfg.BaseURL,
|
||||
Json: apiReq,
|
||||
}
|
||||
res, err := req.Send()
|
||||
|
|
@ -51,7 +61,7 @@ func Call(ctx context.Context, cfg config.ToolConfig, toolReq *ProductUploadRequ
|
|||
}
|
||||
|
||||
toolResp = &ProductUploadResponse{
|
||||
PreviewUrl: "https://gateway.dev.cdlsxd.cn/sw//#/goods/goodsManage",
|
||||
PreviewUrl: c.cfg.AddURL,
|
||||
SpuNum: toolReq.GoodsList[0].GoodsInfo.SpuNum,
|
||||
Id: resMap.Data.Ids[0],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ func Test_Call(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
toolResp, err := Call(context.Background(), config.ToolConfig{}, req)
|
||||
client := New(config.ToolConfig{})
|
||||
toolResp, err := client.Call(context.Background(), req)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Call() error = %v", err)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,27 @@
|
|||
package supplier_search
|
||||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/pkg/l_request"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Call(ctx context.Context, name string) (int, error) {
|
||||
type Client struct {
|
||||
cfg config.ToolConfig
|
||||
}
|
||||
|
||||
func New(cfg config.ToolConfig) *Client {
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Call(ctx context.Context, name string) (int, error) {
|
||||
if name == "" {
|
||||
return 0, errors.New("supplier name is empty")
|
||||
// 如果没有供应商名,返回0,不报错,由上层业务决定是否允许
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
reqBody := SearchRequest{
|
||||
|
|
@ -27,7 +38,7 @@ func Call(ctx context.Context, name string) (int, error) {
|
|||
|
||||
req := l_request.Request{
|
||||
Method: "Post",
|
||||
Url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/supplier/list",
|
||||
Url: c.cfg.BaseURL,
|
||||
Json: apiReq,
|
||||
Headers: map[string]string{
|
||||
"User-Agent": "Apifox/1.0.0 (https://apifox.com)",
|
||||
|
|
|
|||
|
|
@ -1,13 +1,24 @@
|
|||
package warehouse_search
|
||||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/pkg/l_request"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Call(ctx context.Context, name string) (int, error) {
|
||||
type Client struct {
|
||||
cfg config.ToolConfig
|
||||
}
|
||||
|
||||
func New(cfg config.ToolConfig) *Client {
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Call(ctx context.Context, name string) (int, error) {
|
||||
if name == "" {
|
||||
// 如果没有仓库名,返回0,不报错,由上层业务决定是否允许
|
||||
return 0, nil
|
||||
|
|
@ -22,7 +33,7 @@ func Call(ctx context.Context, name string) (int, error) {
|
|||
|
||||
req := l_request.Request{
|
||||
Method: "Get",
|
||||
Url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/warehouse/list",
|
||||
Url: c.cfg.BaseURL,
|
||||
Params: params,
|
||||
Headers: map[string]string{
|
||||
"User-Agent": "Apifox/1.0.0 (https://apifox.com)",
|
||||
|
|
|
|||
|
|
@ -1,16 +1,29 @@
|
|||
package tools
|
||||
|
||||
type Tool interface{
|
||||
Name() string
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/domain/tools/hyt/product_upload"
|
||||
"ai_scheduler/internal/domain/tools/hyt/supplier_search"
|
||||
"ai_scheduler/internal/domain/tools/hyt/warehouse_search"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
Hyt *HytTools
|
||||
// Zltx *ZltxTools
|
||||
}
|
||||
|
||||
var registry = map[string]Tool{}
|
||||
|
||||
func Register(t Tool){
|
||||
registry[t.Name()] = t
|
||||
type HytTools struct {
|
||||
ProductUpload *product_upload.Client
|
||||
SupplierSearch *supplier_search.Client
|
||||
WarehouseSearch *warehouse_search.Client
|
||||
}
|
||||
|
||||
func Get(name string) Tool{
|
||||
return registry[name]
|
||||
func NewManager(cfg *config.Config) *Manager {
|
||||
return &Manager{
|
||||
Hyt: &HytTools{
|
||||
ProductUpload: product_upload.New(cfg.EinoTools.HytProductUpload),
|
||||
SupplierSearch: supplier_search.New(cfg.EinoTools.HytSupplierSearch),
|
||||
WarehouseSearch: warehouse_search.New(cfg.EinoTools.HytWarehouseSearch),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,36 +2,33 @@ package hyt
|
|||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/data/constants"
|
||||
toolManager "ai_scheduler/internal/domain/tools"
|
||||
toolPu "ai_scheduler/internal/domain/tools/hyt/product_upload"
|
||||
toolSs "ai_scheduler/internal/domain/tools/hyt/supplier_search"
|
||||
toolWs "ai_scheduler/internal/domain/tools/hyt/warehouse_search"
|
||||
"ai_scheduler/internal/domain/workflow/runtime"
|
||||
"ai_scheduler/internal/entitys"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
eino_ollama "github.com/cloudwego/eino-ext/components/model/ollama"
|
||||
"github.com/cloudwego/eino/components/prompt"
|
||||
"github.com/cloudwego/eino/compose"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
)
|
||||
|
||||
const WorkflowID = "hyt.productUpload"
|
||||
|
||||
func init() {
|
||||
runtime.Register(WorkflowID, func(d *runtime.Deps) (runtime.Workflow, error) {
|
||||
return &productUpload{cfg: d.Conf}, nil
|
||||
return &productUpload{cfg: d.Conf, toolManager: d.ToolManager}, nil
|
||||
})
|
||||
}
|
||||
|
||||
type productUpload struct {
|
||||
cfg *config.Config
|
||||
data *ProductUploadWorkflowInput
|
||||
cfg *config.Config
|
||||
toolManager *toolManager.Manager
|
||||
data *ProductUploadWorkflowInput
|
||||
}
|
||||
|
||||
type ProductUploadWorkflowInput struct {
|
||||
|
|
@ -41,8 +38,8 @@ type ProductUploadWorkflowInput struct {
|
|||
func (o *productUpload) ID() string { return WorkflowID }
|
||||
|
||||
func (o *productUpload) Invoke(ctx context.Context, rec *entitys.Recognize) (map[string]any, error) {
|
||||
// 构建工作流 (使用 V2 Graph 版本)
|
||||
runnable, err := o.buildWorkflowV2(ctx)
|
||||
// 构建工作流
|
||||
runnable, err := o.buildWorkflow(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -103,65 +100,11 @@ type ProductUploadContext struct {
|
|||
UploadResp *toolPu.ProductUploadResponse
|
||||
}
|
||||
|
||||
// buildWorkflow 构建基于 Graph 的并行工作流
|
||||
func (o *productUpload) buildWorkflow(ctx context.Context) (compose.Runnable[*ProductUploadWorkflowInput, map[string]any], error) {
|
||||
// 定义工作流
|
||||
c := compose.NewChain[*ProductUploadWorkflowInput, map[string]any]()
|
||||
|
||||
// AI映射工具所需参数'
|
||||
paramMappingModel, err := eino_ollama.NewChatModel(ctx, &eino_ollama.ChatModelConfig{
|
||||
BaseURL: o.cfg.Ollama.BaseURL,
|
||||
Timeout: o.cfg.Ollama.Timeout,
|
||||
Model: o.cfg.Ollama.Model,
|
||||
Thinking: &eino_ollama.ThinkValue{Value: true},
|
||||
Options: &eino_ollama.Options{Temperature: 0.5},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1. 构建参数LLM数映射提示词
|
||||
c.AppendChatTemplate(prompt.FromMessages(
|
||||
schema.FString,
|
||||
schema.SystemMessage("你是一个专业的商品参数解析器,你需要根据用户输入的商品描述,解析出商品的目标参数。"),
|
||||
schema.SystemMessage("目标参数:"+constants.HYTProductPropertyTemplate),
|
||||
schema.UserMessage("用户输入:{{.Text}}"),
|
||||
))
|
||||
// 2. 调用LLM
|
||||
c.AppendChatModel(paramMappingModel)
|
||||
|
||||
// 3.工具参数整理
|
||||
c.AppendLambda(compose.InvokableLambda(func(_ context.Context, in *schema.Message) (*toolPu.ProductUploadRequest, error) {
|
||||
toolReq := &toolPu.ProductUploadRequest{}
|
||||
if err := json.Unmarshal([]byte(in.Content), toolReq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toolReq, nil
|
||||
}))
|
||||
|
||||
// 4.工具调用
|
||||
c.AppendLambda(compose.InvokableLambda(func(_ context.Context, in *toolPu.ProductUploadRequest) (*toolPu.ProductUploadResponse, error) {
|
||||
toolRes, err := toolPu.Call(ctx, o.cfg.Tools.HytProductUpload, in)
|
||||
return toolRes, err
|
||||
}))
|
||||
|
||||
// 5.结果数据映射
|
||||
c.AppendLambda(compose.InvokableLambda(func(_ context.Context, in *toolPu.ProductUploadResponse) (map[string]any, error) {
|
||||
return map[string]any{
|
||||
"预览URL(货易通商品列表)": in.PreviewUrl,
|
||||
"SPU编码": in.SpuNum,
|
||||
"商品ID": in.Id,
|
||||
}, nil
|
||||
}))
|
||||
|
||||
// 6.编译工作流
|
||||
return c.Compile(ctx)
|
||||
}
|
||||
|
||||
// buildWorkflowV2 构建基于 Graph 的并行工作流
|
||||
func (o *productUpload) buildWorkflowV2(ctx context.Context) (compose.Runnable[*ProductUploadWorkflowInput, map[string]any], error) {
|
||||
g := compose.NewGraph[*ProductUploadWorkflowInput, map[string]any]()
|
||||
|
||||
// 1. DataMapping 节点: 解析 JSON -> 填充基础 Request -> 提取供应商/仓库名
|
||||
// 1. DataMapping 节点: 解析 JSON -> 填充基础 Request
|
||||
g.AddLambdaNode("data_mapping", compose.InvokableLambda(func(ctx context.Context, in *ProductUploadWorkflowInput) (*ProductUploadContext, error) {
|
||||
state := &ProductUploadContext{
|
||||
mu: &sync.Mutex{}, // 初始化锁
|
||||
|
|
@ -176,6 +119,21 @@ func (o *productUpload) buildWorkflowV2(ctx context.Context) (compose.Runnable[*
|
|||
if err := json.Unmarshal([]byte(in.Text), &ingestData); err != nil {
|
||||
return nil, fmt.Errorf("解析商品数据失败: %w", err)
|
||||
}
|
||||
|
||||
// 必填校验
|
||||
if ingestData.SupplierName == "" {
|
||||
return nil, errors.New("供应商名称不能为空")
|
||||
}
|
||||
if ingestData.WarehouseName == "" {
|
||||
return nil, errors.New("仓库名称不能为空")
|
||||
}
|
||||
if ingestData.Profit == "" {
|
||||
return nil, errors.New("利润不能为空")
|
||||
}
|
||||
if ingestData.TaxRate == "" {
|
||||
return nil, errors.New("税率不能为空")
|
||||
}
|
||||
|
||||
state.IngestData = &ingestData
|
||||
state.SupplierName = ingestData.SupplierName
|
||||
state.WarehouseName = ingestData.WarehouseName
|
||||
|
|
@ -240,32 +198,38 @@ func (o *productUpload) buildWorkflowV2(ctx context.Context) (compose.Runnable[*
|
|||
|
||||
// 2. 获取供应商ID 节点
|
||||
g.AddLambdaNode("get_supplier_id", compose.InvokableLambda(func(ctx context.Context, state *ProductUploadContext) (*ProductUploadContext, error) {
|
||||
if state.SupplierName != "" {
|
||||
supplierId, err := toolSs.Call(ctx, state.SupplierName)
|
||||
if err != nil {
|
||||
// 记录日志,但不阻断流程,可能允许 ID 为 0
|
||||
fmt.Printf("warning: failed to get supplier id for %s: %v\n", state.SupplierName, err)
|
||||
} else {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
state.UploadReq.SupplierId = supplierId
|
||||
}
|
||||
if state.SupplierName == "" {
|
||||
return state, errors.New("供应商名称不能为空")
|
||||
}
|
||||
|
||||
supplierId, err := o.toolManager.Hyt.SupplierSearch.Call(ctx, state.SupplierName)
|
||||
if err != nil {
|
||||
// 记录日志,但不阻断流程,可能允许 ID 为 0
|
||||
fmt.Printf("warning: failed to get supplier id for %s: %v\n", state.SupplierName, err)
|
||||
} else {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
state.UploadReq.SupplierId = supplierId
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}))
|
||||
|
||||
// 3. 获取仓库ID 节点
|
||||
g.AddLambdaNode("get_warehouse_id", compose.InvokableLambda(func(ctx context.Context, state *ProductUploadContext) (*ProductUploadContext, error) {
|
||||
if state.WarehouseName != "" {
|
||||
warehouseId, err := toolWs.Call(ctx, state.WarehouseName)
|
||||
if err != nil {
|
||||
fmt.Printf("warning: failed to get warehouse id for %s: %v\n", state.WarehouseName, err)
|
||||
} else {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
state.UploadReq.WarehouseId = warehouseId
|
||||
}
|
||||
if state.WarehouseName == "" {
|
||||
return state, errors.New("仓库名称不能为空")
|
||||
}
|
||||
|
||||
warehouseId, err := o.toolManager.Hyt.WarehouseSearch.Call(ctx, state.WarehouseName)
|
||||
if err != nil {
|
||||
fmt.Printf("warning: failed to get warehouse id for %s: %v\n", state.WarehouseName, err)
|
||||
} else {
|
||||
state.mu.Lock()
|
||||
defer state.mu.Unlock()
|
||||
state.UploadReq.WarehouseId = warehouseId
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}))
|
||||
|
||||
|
|
@ -280,7 +244,7 @@ func (o *productUpload) buildWorkflowV2(ctx context.Context) (compose.Runnable[*
|
|||
|
||||
// 5. 上传节点
|
||||
g.AddLambdaNode("upload_product", compose.InvokableLambda(func(ctx context.Context, state *ProductUploadContext) (*ProductUploadContext, error) {
|
||||
toolRes, err := toolPu.Call(ctx, o.cfg.Tools.HytProductUpload, state.UploadReq)
|
||||
toolRes, err := o.toolManager.Hyt.ProductUpload.Call(ctx, state.UploadReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"ai_scheduler/internal/domain/workflow/runtime"
|
||||
"ai_scheduler/internal/pkg/utils_ollama"
|
||||
|
||||
toolManager "ai_scheduler/internal/domain/tools"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
|
|
@ -13,7 +15,7 @@ var ProviderSetWorkflow = wire.NewSet(NewRegistry)
|
|||
// NewRegistry 注入共享依赖并注册默认 Registry,确保自注册工作流可被发现
|
||||
func NewRegistry(conf *config.Config, llm *utils_ollama.Client) *runtime.Registry {
|
||||
// 步骤1:设置运行时依赖(配置与LLM客户端),供工作流工厂在首次实例化时使用;必须在任何调用 Invoke 之前完成,否则会触发 "deps not set"
|
||||
runtime.SetDeps(&runtime.Deps{Conf: conf, LLM: llm})
|
||||
runtime.SetDeps(&runtime.Deps{Conf: conf, LLM: llm, ToolManager: toolManager.NewManager(conf)})
|
||||
// 步骤2:创建新的工作流注册表;注册表负责按工作流ID惰性实例化并缓存单例实例,保障并发访问下的安全
|
||||
r := runtime.NewRegistry()
|
||||
// 步骤3:将该注册表设置为全局默认,便于通过 runtime.Default() 获取;自注册的工作流可通过默认注册表被发现并调用
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ package workflow
|
|||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
toolManager "ai_scheduler/internal/domain/tools"
|
||||
"ai_scheduler/internal/pkg/utils_ollama"
|
||||
)
|
||||
|
||||
// 仅声明依赖结构,避免在 workflow 包内实现注册中心逻辑导致循环依赖
|
||||
type Deps struct {
|
||||
Conf *config.Config
|
||||
LLM *utils_ollama.Client
|
||||
Conf *config.Config
|
||||
LLM *utils_ollama.Client
|
||||
ToolManager *toolManager.Manager
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package runtime
|
|||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
toolManager "ai_scheduler/internal/domain/tools"
|
||||
"ai_scheduler/internal/entitys"
|
||||
"ai_scheduler/internal/pkg/utils_ollama"
|
||||
"context"
|
||||
|
|
@ -16,8 +17,9 @@ type Workflow interface {
|
|||
}
|
||||
|
||||
type Deps struct {
|
||||
Conf *config.Config
|
||||
LLM *utils_ollama.Client
|
||||
Conf *config.Config
|
||||
LLM *utils_ollama.Client
|
||||
ToolManager *toolManager.Manager
|
||||
}
|
||||
|
||||
type Factory func(deps *Deps) (Workflow, error)
|
||||
|
|
|
|||
Loading…
Reference in New Issue