作為開發人員與法學碩士合作時,您通常希望接收結構化格式的資料。雖然這在早期的 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「函數呼叫」最明顯的用例。
為 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中文網其他相關文章!