{"id":28185984,"url":"https://github.com/glidea/llm-structed","last_synced_at":"2025-07-23T17:35:46.789Z","repository":{"id":275962581,"uuid":"927753472","full_name":"glidea/llm-structed","owner":"glidea","description":"llm-structed is an LLM Client optimized for structured output scenarios","archived":false,"fork":false,"pushed_at":"2025-02-06T14:05:01.000Z","size":33,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-16T06:13:19.895Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/glidea.png","metadata":{"files":{"readme":"README-zh.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-05T13:44:01.000Z","updated_at":"2025-04-09T14:20:19.000Z","dependencies_parsed_at":"2025-05-16T06:23:27.209Z","dependency_job_id":null,"html_url":"https://github.com/glidea/llm-structed","commit_stats":null,"previous_names":["glidea/llm-structed"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/glidea/llm-structed","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glidea%2Fllm-structed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glidea%2Fllm-structed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glidea%2Fllm-structed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glidea%2Fllm-structed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/glidea","download_url":"https://codeload.github.com/glidea/llm-structed/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glidea%2Fllm-structed/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266720725,"owners_count":23974026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-16T06:13:19.611Z","updated_at":"2025-07-23T17:35:46.765Z","avatar_url":"https://github.com/glidea.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 背景\n在 chat 场景中，通常模型不需要返回结构化的数据。但在 LLM 应用开发里，模型通常被视为提供某种原子能力的 API Service ，此时我们希望直接得到一个 JSON ，通常的解法有：\n\n## 1. 直接在 Prompt 里强调输出格式\n* 优：简单，对模型 API 没有任何额外要求\n* 缺：格式不稳定，特别是对于能力较差的模型\n\n## 2. 使用 response_format: { type: \"json_object\" } + Prompt 说明具体字段\n* 优：总是确保返回合法 JSON\n* 缺：字段不稳定，特别是对于能力较差的模型\n\n## 3. 使用 response_format: { type: \"json_schema\", json_schema: {\"strict\": true, \"schema\": ...} }\n* 优：确保返回合法 JSON ，且字段稳定\n* 缺：仅部分模型支持\n\n## SDK\n* 在 OpenAI 提供的 [SDK]( https://platform.openai.com/docs/guides/structured-outputs?example=structured-data#how-to-use) 中直接支持 Class 作为 Response Format ，但仅支持 Python\n* 在 [go-openai]( https://github.com/sashabaranov/go-openai) 中，使用方式过于通用繁琐\n* llm-structed 专门针对结构化场景优化，对方案 3 和方案 2 提供原生支持\n\n# llm-structed\n\nllm-structed 是一个针对结构化输出场景优化的 LLM Client：\n* 自动将 Go 结构体定义转换为 Response JSON Schema\n* 自动把 LLM 的输出转换为 Go 结构体\n* 基于 struct tags 的友好声明式配置\n* 轻量\n* 基于 [Json Schema or Json Object](https://platform.openai.com/docs/guides/structured-outputs#supported-schemas)\n* 只支持 OpenAI 兼容的 LLM，主流提供商基本都有对应的兼容接口，比如 [Gemini](https://ai.google.dev/gemini-api/docs/openai)\n\n## 安装\n\n```bash\ngo get github.com/glidea/llm-structed\n```\n\n## 快速开始\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/glidea/llm-structed\"\n)\n\ntype Summary struct {\n\tTitle    string   `json:\"title\" desc:\"The title of the summary\"`\n\tContent  string   `json:\"content\" desc:\"A concise summary of the article content\"`\n\tKeywords []string `json:\"keywords\" desc:\"Key topics mentioned in the article\"`\n\tScore    int      `json:\"score\" desc:\"The quality score of the article (1-10)\"`\n\tCategory string   `json:\"category\" desc:\"The category of the article\" enum:\"Technology,Science,Business,Health,Education,Other\"`\n}\n\nfunc main() {\n\t// New client (In minimal configuration, you only need to set the APIKey)\n\tcli, _ := llmstructed.New(llmstructed.Config{\n\t\tBaseURL:                   \"https://openrouter.ai/api/v1\",\n\t\tAPIKey:                    \"sk-...\",\n\t\tModel:                     \"google/gemini-flash-1.5\",\n\t\tTemperature:               0.3,\n\t\tStructuredOutputSupported: true,\n\t\tRetry:                     1,\n\t\tDebug:                     true,\n\t\t// See source code comments of llmstructed.Config for these config detail\n\t})\n\tctx := context.Background()\n\n\t// Structured Outputed\n\tvar summary Summary\n\t_ = cli.Do(ctx, []string{`Please generate a summary of this article: Artificial Intelligence (AI) is transforming the way we live and work. It refers to\n\tcomputer systems that can perform tasks that normally require human intelligence. These\n\ttasks include visual perception, speech recognition, decision-making, and language\n\ttranslation. Machine learning, a subset of AI, enables systems to learn and improve\n\tfrom experience without being explicitly programmed. Deep learning, particularly,\n\thas revolutionized AI by using neural networks to process complex patterns in data.`,\n\t}, \u0026summary)\n\tfmt.Printf(\"Go Struct: %v\\n\\n\", summary)\n\n\t// Simple method for single value\n\tstr, _ := cli.String(ctx, []string{\"Hello, who are you?\"})\n\tfmt.Printf(\"String: %s\\n\\n\", str)\n\tlanguages, _ := cli.StringSlice(ctx, []string{\"List some popular programming languages.\"})\n\tfmt.Printf(\"String Slice: %v\\n\\n\", languages)\n\tcount, _ := cli.Int(ctx, []string{`How many words are in this sentence: \"Hello world, this is a test.\"`})\n\tfmt.Printf(\"Integer: %d\\n\\n\", count)\n\tyes, _ := cli.Bool(ctx, []string{\"Are you happy?\"})\n\tfmt.Printf(\"Boolean: %v\\n\\n\", yes)\n\ttrues, _ := cli.BoolSlice(ctx, []string{\"Are these statements true? [\\\"The sky is blue\\\", \\\"Fish can fly\\\", \\\"Water is wet\\\"]\"})\n\tfmt.Printf(\"Boolean Slice: %v\\n\\n\", trues)\n\tpi, _ := cli.Float(ctx, []string{\"What is the value of pi (to two decimal places)?\"})\n\tfmt.Printf(\"Float: %.2f\\n\\n\", pi)\n}\n```\n\n## 与 [go-openapi](https://github.com/sashabaranov/go-openai) 的区别\n\n* 更轻量\n* 专注与结构化场景，不支持额外功能\n* 调用方式简单直观。作为对比以下是 go-openapi 的结构化输出示例\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/sashabaranov/go-openai\"\n\t\"github.com/sashabaranov/go-openai/jsonschema\"\n)\n\nfunc main() {\n\tclient := openai.NewClient(\"your token\")\n\tctx := context.Background()\n\n\ttype Result struct {\n\t\tSteps []struct {\n\t\t\tExplanation string `json:\"explanation\"`\n\t\t\tOutput      string `json:\"output\"`\n\t\t} `json:\"steps\"`\n\t\tFinalAnswer string `json:\"final_answer\"`\n\t}\n\tvar result Result\n\tschema, err := jsonschema.GenerateSchemaForType(result)\n\tif err != nil {\n\t\tlog.Fatalf(\"GenerateSchemaForType error: %v\", err)\n\t}\n\tresp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{\n\t\tModel: openai.GPT4oMini,\n\t\tMessages: []openai.ChatCompletionMessage{\n\t\t\t{\n\t\t\t\tRole:    openai.ChatMessageRoleSystem,\n\t\t\t\tContent: \"You are a helpful math tutor. Guide the user through the solution step by step.\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tRole:    openai.ChatMessageRoleUser,\n\t\t\t\tContent: \"how can I solve 8x + 7 = -23\",\n\t\t\t},\n\t\t},\n\t\tResponseFormat: \u0026openai.ChatCompletionResponseFormat{\n\t\t\tType: openai.ChatCompletionResponseFormatTypeJSONSchema,\n\t\t\tJSONSchema: \u0026openai.ChatCompletionResponseFormatJSONSchema{\n\t\t\t\tName:   \"math_reasoning\",\n\t\t\t\tSchema: schema,\n\t\t\t\tStrict: true,\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"CreateChatCompletion error: %v\", err)\n\t}\n\terr = schema.Unmarshal(resp.Choices[0].Message.Content, \u0026result)\n\tif err != nil {\n\t\tlog.Fatalf(\"Unmarshal schema error: %v\", err)\n\t}\n\tfmt.Println(result)\n}\n```\n\n## 最佳实践\n\n* 使用 `desc` 标签来描述字段含义\n* 使用 `enum` 标签来描述字段可选值\n\n这些标签会自动注入到生成的 JSON Schema 中，以丰富上下文\n\n## 许可证\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglidea%2Fllm-structed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglidea%2Fllm-structed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglidea%2Fllm-structed/lists"}