From bf8b220a99b30cbcd1d556921081b221752db319 Mon Sep 17 00:00:00 2001 From: Cold <16419454+cold502@user.noreply.gitee.com> Date: Mon, 12 Jan 2026 17:36:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=96=B0=E7=9A=84mongo?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=A0=BC=E5=BC=8F,=E5=B9=B6=E4=B8=94?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dclient=E7=9A=84apitoken=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ragflow/chat.go | 12 ++++++--- ragflow/client.go | 62 ++++++++++++++++++++++++++++++++++++++-------- ragflow/dataset.go | 30 ++++++++++++++++++++-- ragflow/session.go | 5 ++++ 4 files changed, 94 insertions(+), 15 deletions(-) diff --git a/ragflow/chat.go b/ragflow/chat.go index 31e1b4b..bdce50b 100644 --- a/ragflow/chat.go +++ b/ragflow/chat.go @@ -34,9 +34,10 @@ type CreateChatRes struct { // UpdateChatReq 更新对话配置请求 type UpdateChatReq struct { - Name string `json:"name,omitempty"` // 对话配置名称 - DatasetIds []string `json:"dataset_ids,omitempty"` // 关联的知识库ID列表 - Prompt *PromptConfig `json:"prompt,omitempty"` // 提示词配置 + Name string `json:"name,omitempty"` // 对话配置名称 + Description string `json:"description,omitempty"` // 对话描述 + DatasetIds []string `json:"dataset_ids,omitempty"` // 关联的知识库ID列表(RAGFlow API使用下划线格式) + Prompt *PromptConfig `json:"prompt,omitempty"` // 提示词配置 } // 聊天助手管理 @@ -122,6 +123,11 @@ func (c *Client) CreateChat(ctx context.Context, req *CreateChatReq) (*Chat, err if res.Code != 0 { return nil, gerror.Newf("create chat failed: %s", res.Msg) } + // 检查响应数据是否为空:防止RAGFlow API返回 {"code":0, "data":null} + // 如果不检查直接返回,调用方会收到 (nil, nil),导致空指针异常 + if res.Data == nil { + return nil, gerror.Newf("create chat returned null data: %s", res.Msg) + } return res.Data, nil } diff --git a/ragflow/client.go b/ragflow/client.go index d5958bf..24a426a 100644 --- a/ragflow/client.go +++ b/ragflow/client.go @@ -7,9 +7,10 @@ import ( "sync" "sync/atomic" - commonHttp "gitee.com/red-future---jilin-g/common/http" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/gclient" ) var ( @@ -116,7 +117,23 @@ func (r *CommonResponse) IsSuccess() bool { return r.Code == 0 } -// request 发送 HTTP 请求(使用统一的common/http包,支持负载均衡) +// request 发送 HTTP 请求 +// +// 为什么不使用 common/http 包: +// 1. common/http/http.go:61 会用内部请求的Authorization覆盖RAGFlow API key: +// Httpclient.SetHeader("Authorization", g.RequestFromCtx(ctx).GetHeader("Authorization")) +// 这会导致RAGFlow API认证失败,因为内部token不是RAGFlow的API key +// +// 2. common/http/http.go:69-74 强制解析为内部API响应格式(ghttp.DefaultHandlerResponse): +// resultStrut := &ghttp.DefaultHandlerResponse{} +// if err = gconv.Struct(result, &resultStrut); err != nil { +// err = errors.New(resultStrut.Message) +// } else if resultStrut.Code == 200 || resultStrut.Code == 0 { +// gconv.Struct(resultStrut.Data, target) +// } +// RAGFlow API返回格式与内部API不同,会导致解析失败 +// +// 因此直接使用 g.Client() 调用第三方API,避免上述问题 func (c *Client) request(ctx context.Context, method, path string, body interface{}, result interface{}) (err error) { endpoint := c.getNextEndpoint() if endpoint == "" { @@ -124,26 +141,51 @@ func (c *Client) request(ctx context.Context, method, path string, body interfac } fullURL := endpoint + path - headers := map[string]string{ - "Authorization": "Bearer " + c.APIKey, - "Content-Type": "application/json", + // 添加详细日志:请求信息 + g.Log().Infof(ctx, "RAGFlow请求: %s %s", method, fullURL) + if body != nil { + bodyJSON := g.NewVar(body).String() + g.Log().Infof(ctx, "RAGFlow请求体: %s", bodyJSON) } + // 创建新的HTTP客户端实例(避免共享状态) + client := g.Client() + client.SetHeader("Authorization", "Bearer "+c.APIKey) + client.SetHeader("Content-Type", "application/json") + + var response *gclient.Response switch method { case "GET": - err = commonHttp.Get(ctx, fullURL, headers, result, body) + response, err = client.Get(ctx, fullURL, body) case "POST": - err = commonHttp.Post(ctx, fullURL, headers, result, body) + response, err = client.Post(ctx, fullURL, body) case "PUT": - err = commonHttp.Put(ctx, fullURL, headers, result, body) + response, err = client.Put(ctx, fullURL, body) case "DELETE": - err = commonHttp.Delete(ctx, fullURL, headers, result, body) + response, err = client.Delete(ctx, fullURL, body) default: return gerror.Newf("unsupported method: %s", method) } if err != nil { - return gerror.Newf("RAGFlow API request failed: %v", err) + g.Log().Errorf(ctx, "RAGFlow HTTP请求失败: %v", err) + return gerror.Wrapf(err, "HTTP request to RAGFlow failed") + } + + if response == nil { + return gerror.New("HTTP response is nil") + } + + defer response.Close() + + // 读取响应体 + respBytes := response.ReadAll() + g.Log().Infof(ctx, "RAGFlow响应: %s", string(respBytes)) + + // 解析JSON到result + if err = gjson.DecodeTo(respBytes, result); err != nil { + g.Log().Errorf(ctx, "RAGFlow响应解析失败: %v, 原始响应: %s", err, string(respBytes)) + return gerror.Wrapf(err, "failed to decode RAGFlow response") } return diff --git a/ragflow/dataset.go b/ragflow/dataset.go index 431b332..b74970b 100644 --- a/ragflow/dataset.go +++ b/ragflow/dataset.go @@ -4,6 +4,7 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" ) // 数据集管理 @@ -82,17 +83,38 @@ type DeleteDatasetsReq struct { // CreateDataset 创建数据集 func (c *Client) CreateDataset(ctx context.Context, req *CreateDatasetReq) (*Dataset, error) { + g.Log().Infof(ctx, "CreateDataset请求: name=%s, description=%s, embedding_model=%s", req.Name, req.Description, req.EmbeddingModel) + var res struct { Code int `json:"code"` Data *Dataset `json:"data"` Msg string `json:"message"` } if err := c.request(ctx, "POST", "/api/v1/datasets", req, &res); err != nil { + g.Log().Errorf(ctx, "CreateDataset请求失败: %v", err) return nil, err } - if res.Code != 0 { - return nil, gerror.Newf("create dataset failed: %s", res.Msg) + + g.Log().Infof(ctx, "CreateDataset响应: code=%d, msg=%s, data_is_nil=%v", res.Code, res.Msg, res.Data == nil) + + // code=101表示dataset名称已存在(正常业务场景,不是错误) + // 调用方应该通过ListDatasets查找已有dataset并复用 + if res.Code == 101 { + return nil, gerror.Newf("Dataset名称已存在: %s", res.Msg) } + + // 其他非0的code表示真正的错误 + if res.Code != 0 { + return nil, gerror.Newf("创建知识库失败(code=%d): %s", res.Code, res.Msg) + } + + // code=0但data=null,表示创建异常(可能是RAGFlow配置问题,如embedding模型不可用、权限不足等) + // 这不是正常状态,应该返回错误而不是(nil, nil) + if res.Data == nil { + return nil, gerror.Newf("创建知识库返回空数据(code=0,data=null),可能是RAGFlow配置问题: %s", res.Msg) + } + + g.Log().Infof(ctx, "CreateDataset成功: id=%s, name=%s", res.Data.Id, res.Data.Name) return res.Data, nil } @@ -134,6 +156,10 @@ func (c *Client) ListDatasets(ctx context.Context, req *ListDatasetsReq) (*ListD if err := c.request(ctx, "GET", path, nil, &res); err != nil { return nil, err } + + // 添加调试日志 + g.Log().Infof(ctx, "ListDatasets原始响应: code=%d, total=%d, data_len=%d", res.Code, res.Total, len(res.Data)) + if res.Code != 0 { return nil, gerror.Newf("list datasets failed: code=%d", res.Code) } diff --git a/ragflow/session.go b/ragflow/session.go index d534b04..7659eb2 100644 --- a/ragflow/session.go +++ b/ragflow/session.go @@ -90,6 +90,11 @@ func (c *Client) CreateSession(ctx context.Context, chatId string, req *CreateSe if res.Code != 0 { return nil, gerror.Newf("create session failed: %s", res.Msg) } + // 检查响应数据是否为空:防止RAGFlow API返回 {"code":0, "data":null} + // 如果不检查直接返回,调用方会收到 (nil, nil),导致空指针异常 + if res.Data == nil { + return nil, gerror.Newf("create session returned null data: %s", res.Msg) + } return res.Data, nil }