首頁 > 後端開發 > Golang > 如何在 Go 中一致地從 Claude 檢索有效的 JSON

如何在 Go 中一致地從 Claude 檢索有效的 JSON

Patricia Arquette
發布: 2024-10-17 22:22:29
原創
304 人瀏覽過

How to Consistently Retrieve Valid JSON from Claude  in Go

作為開發人員與法學碩士合作時,您通常希望接收結構化格式的資料。雖然這在早期的 GPT-3 時代並不總是有效,但目前支援函數呼叫的模型在這方面做得更好。

在這篇文章中,我想分享一個名為 CallClaudeForceTool 的簡單函數,您可以使用它來呼叫 Claude 來接收結構化資料/JSON 輸出。就我而言,它是用 Golang 編寫的,並傳回我需要的任何類型的結構。

首先,您需要定義結構並將其以 JSON 模式的格式提供給 LLM。在我的範例中,我定義了一個結構類型,用作查詢某些資料的函數的輸入。

這是查詢函數的輸入的結構:

type QueryArgs struct {
    SearchQuery string `json:"searchQuery,omitempty"`
    TypeFilter  string `json:"typeFilter,omitempty"`
}
登入後複製

以下是 JSON Schema 定義。新增好的名稱和描述在這裡實際上很重要,因為 LLM 在產生 JSON 資料時將使用此資訊:

var QueryFnSchema = JSONSchema{
    Type: ai.DataTypeObject,
    Properties: map[string]ai.JSONSchema{
        "searchQuery": {
            Type:        ai.DataTypeString,
            Description: "The search text query to use. Any data that contains this text will be searched. If not provided, then no text comparison will be performed.",
        },
        "typeFilter": {
            Type:        ai.DataTypeString,
            Description: `The type to filter on. If not provided, data of any type will be searched. Can only be one of: "location", "event", "person", "organization".`,
            Enum:        []string{"location", "event", "person", "organization"},
        },
    },
}

type JSONSchema struct {
    Type        DataType              `json:"type,omitempty"`
    Description string                `json:"description,omitempty"`
    Enum        []string              `json:"enum,omitempty"`
    Properties  map[string]JSONSchema `json:"properties,omitempty"`
    Required    []string              `json:"required,omitempty"`
    Items       *JSONSchema           `json:"items,omitempty"`
}

func (d JSONSchema) MarshalJSON() ([]byte, error) {
    if d.Properties == nil {
        d.Properties = make(map[string]JSONSchema)
    }
    type Alias JSONSchema
    return json.Marshal(struct {
        Alias
    }{
        Alias: (Alias)(d),
    })
}

type DataType string

const (
    DataTypeObject  DataType = "object"
    DataTypeNumber  DataType = "number"
    DataTypeInteger DataType = "integer"
    DataTypeBoolean DataType = "boolean"
    DataTypeString  DataType = "string"
    DataTypeArray   DataType = "array"
    DataTypeNull    DataType = "null"
)
登入後複製

我正在使用一個簡單的 JSON Schema 結構。您可以為此使用依賴項,但此結構通常足以滿足 LLM 函數呼叫。它與 Anthropic 的 Claude 和 OpenAI 的 ChatGPT 配合得很好。

您可以根據 QueryArgs 的等效項或您需要並希望 LLM 回應的任何資料來定義自己的架構,而不是 QueryFnSchema。

現在是時候使用 CallClaudeForceTool 函數了(將在帖子末尾解釋):

func main() {
// ...
    queryArgs, claudeResponse := CallClaudeForceTool[QueryArgs]([]ClaudeLLMMessage{
            {Role: "user", Content: getQueryPrompt(inputMessage)},
        }, ToolDefinition{
            Name:        "search",
            Description: "Search for data in the database",
            InputSchema: queryFnSchema,
        }, ClaudeConfig{
            MaxTokens:   4000,
            Temperature: 0.8,
            System:      SYSTEM_PROMPT,
        })

    result := db.Query(queryArgs)
// ...
}
登入後複製

您可以根據自己的需求取代 getQueryPrompt(inputMessage) 、 SYSTEM_PROMPT 和任何配置。

queryArgs 變數現在包含您定義的格式的 LLM 產生的數據,在我的例子中是 QueryArgs 結構。此時,我使用它作為實際函數呼叫的參數來查詢我的資料庫。但是,您也可以根據某些輸入或提示產生任何結構化數據,然後在程式碼中使用它。

最後,這是 CallClaudeForceTool 函數和任何輔助型別。其目標是呼叫 Claude,強制其使用提供的單一工具,並結束而不用任何後續工具/函數輸出回應 Claude 或接收任何最終回應文字訊息。我們只希望 Claude 用結構化資料回覆一次。

func CallClaudeForceTool[T any](messages []ClaudeLLMMessage, tool ToolDefinition, config ClaudeConfig) (T, *ClaudeCreateMessageResponse, error) {
    payload := ClaudeCreateMessagePayload{
        Model:    "claude-3-5-sonnet-20240620",
        Messages: messages,
        Tools:    []ToolDefinition{tool},
        ToolChoice: ClaudeToolChoice{
            Type: ToolChoiceTypeTool,
            Name: tool.Name,
        },
        MaxTokens:   config.MaxTokens,
        Temperature: config.Temperature,
        System:      config.System,
    }

    payloadBytes, err := json.Marshal(payload)
    if err != nil {
        return *new(T), nil, fmt.Errorf("impossible to marshal payload: %w", err)
    }

    req, err := http.NewRequest(
        http.MethodPost,
        "https://api.anthropic.com/v1/messages",
        bytes.NewReader(payloadBytes),
    )
    if err != nil {
        return *new(T), nil, fmt.Errorf("impossible to create request: %w", err)
    }
    req.Header.Set("x-api-key", os.Getenv("ANTHROPIC_API_KEY"))
    req.Header.Set("content-type", "application/json")
    req.Header.Set("anthropic-version", "2023-06-01")

    // send the request
    httpClient := http.Client{Timeout: 60 * time.Second}
    res, err := httpClient.Do(req)
    if err != nil {
        return *new(T), nil, fmt.Errorf("impossible to send request: %w", err)
    }
    defer res.Body.Close()

    resBody, err := io.ReadAll(res.Body)
    if err != nil {
        return *new(T), nil, fmt.Errorf("impossible to read all body of response: %w", err)
    }

    // unmarshal the response
    var response ClaudeCreateMessageResponse
    err = json.Unmarshal(resBody, &response)
    if err != nil {
        return *new(T), nil, fmt.Errorf("impossible to unmarshal response: %w", err)
    }

    // Get the tool response
    var toolInput T
    for _, content := range response.Content {
        if content.Type != ClaudeResponseContentTypeToolUse || content.Name != tool.Name {
            continue
        }
        inputBytes, err := json.Marshal(content.Input)
        if err != nil {
            return *new(T), &response, fmt.Errorf("impossible to marshal tool response: %w", err)
        }
        err = json.Unmarshal(inputBytes, &toolInput)
        if err != nil {
            return *new(T), &response, fmt.Errorf("impossible to unmarshal tool response: %w", err)
        }
        return toolInput, &response, nil
    }

    return toolInput, &response, fmt.Errorf("impossible to find tool response")
}

// Auxiliary types

type ClaudeLLMMessage struct {
    Role    string `json:"role"`
    Content string `json:"content"`
}

type ClaudeConfig struct {
    MaxTokens   int
    Temperature float64
    System      string
}

type ClaudeCreateMessagePayload struct {
    Model       string             `json:"model"`
    MaxTokens   int                `json:"max_tokens"`
    Messages    []ClaudeLLMMessage `json:"messages"`
    Stream      bool               `json:"stream,omitempty"`
    System      string             `json:"system,omitempty"`
    Temperature float64            `json:"temperature,omitempty"`
    ToolChoice  ClaudeToolChoice   `json:"tool_choice,omitempty"`
    Tools       []ToolDefinition   `json:"tools,omitempty"`
}

type ClaudeCreateMessageResponse struct {
    Content []struct {
        Type  ClaudeResponseContentType `json:"type"`
        Text  string                    `json:"text,omitempty"`
        ID    string                    `json:"id,omitempty"`
        Name  string                    `json:"name,omitempty"`
        Input map[string]interface{}    `json:"input,omitempty"`
    } `json:"content"`
    Id           string `json:"id"`
    Model        string `json:"model"`
    Role         string `json:"role"`
    StopReason   string `json:"stop_reason"`
    StopSequence string `json:"stop_sequence"`
    Type         string `json:"type"`
    Usage        struct {
        InputTokens  int `json:"input_tokens"`
        OutputTokens int `json:"output_tokens"`
    } `json:"usage"`
}

type ClaudeToolChoice struct {
    Type ToolChoiceType `json:"type"`
    Name string         `json:"name,omitempty"`
}

type ToolChoiceType string

const (
    ToolChoiceTypeAuto ToolChoiceType = "auto"
    ToolChoiceTypeCode ToolChoiceType = "any"
    ToolChoiceTypeTool ToolChoiceType = "tool"
)

type ToolDefinition struct {
    Name        string     `json:"name"`
    Description string     `json:"description"`
    InputSchema JSONSchema `json:"input_schema"`
}
登入後複製

一些可以使用的範例:

  • 呼叫函數,如我的範例所示,這可能是LLM「函數呼叫」最明顯的用例。

    • 函數呼叫功能的完整流程將包括使用工具/函數的輸出實際回應 Claude,並且通常會有一個來回,直到 Claude 回應其最終訊息。
  • 為 API 請求產生結構化資料:您可以使用 LLM 根據使用者輸入為 API 請求產生 JSON 負載,確保資料符合所需的架構。

  • 資料驗證和轉換:LLM 可用於驗證輸入資料並將其轉換為結構化格式,然後可用於進一步處理或儲存在資料庫中。

  • 通常,每當您有一些文字並想要提取或將其轉換為特定結構,而不希望訓練自己的自訂分類器時。


就是這樣!請告訴我這是否有用,或分享您如何在申請中使用法學碩士。

Bis demnächst!

~馬丁


註:

  • 這最初發佈在我的部落格上:https://blog.embiem.me/unleash-your-home-hardware-processing-long-running-tasks-at-home

  • 任何代碼均在 MIT 許可證下按原樣提供。

  • 封面圖片是人工智慧產生的。

以上是如何在 Go 中一致地從 Claude 檢索有效的 JSON的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板