Golang 中强制 LLM 返回 JSON 的无感解法

92 天前
 IndexOutOfBounds

https://github.com/glidea/llm-structed

背景

在 chat 场景中,通常模型不需要返回结构化的数据。但在 LLM 应用开发里,模型通常被视为提供某种原子能力的 API Service ,此时我们希望直接得到一个 JSON ,通常的解法有:

1. 直接在 Prompt 里强调输出格式

2. 使用 response_format: { type: "json_object" } + Prompt 说明具体字段

3. 使用 response_format: { type: "json_schema", json_schema: {"strict": true, "schema": ...} }

SDK

例子

package main

import (
	"context"
	"fmt"

	"github.com/glidea/llm-structed"
)

type Summary struct {
	Title    string   `json:"title" desc:"The title of the summary"`
	Content  string   `json:"content" desc:"A concise summary of the article content"`
	Keywords []string `json:"keywords" desc:"Key topics mentioned in the article"`
	Score    int      `json:"score" desc:"The quality score of the article (1-10)"`
	Category string   `json:"category" desc:"The category of the article" enum:"Technology,Science,Business,Health,Education,Other"`
}

func main() {
	// New client (In minimal configuration, you only need to set the APIKey)
	cli, _ := llmstructed.New(llmstructed.Config{
		BaseURL:                   "https://openrouter.ai/api/v1",
		APIKey:                    "sk-...",
		Model:                     "google/gemini-flash-1.5",
		Temperature:               0.3,
		StructuredOutputSupported: true, // 使用方案 3
		Retry:                     1,
		Debug:                     true,
		// See source code comments of llmstructed.Config for these config detail
	})
	ctx := context.Background()

	// Structured Outputed
	var summary Summary
	_ = 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
	computer systems that can perform tasks that normally require human intelligence. These
	tasks include visual perception, speech recognition, decision-making, and language
	translation. Machine learning, a subset of AI, enables systems to learn and improve
	from experience without being explicitly programmed. Deep learning, particularly,
	has revolutionized AI by using neural networks to process complex patterns in data.`,
	}, &summary)
	fmt.Printf("Go Struct: %v\n\n", summary)

	// Simple method for single value
	str, _ := cli.String(ctx, []string{"Hello, who are you?"})
	fmt.Printf("String: %s\n\n", str)
	languages, _ := cli.StringSlice(ctx, []string{"List some popular programming languages."})
	fmt.Printf("String Slice: %v\n\n", languages)
	count, _ := cli.Int(ctx, []string{`How many words are in this sentence: "Hello world, this is a test."`})
	fmt.Printf("Integer: %d\n\n", count)
	yes, _ := cli.Bool(ctx, []string{"Are you happy?"})
	fmt.Printf("Boolean: %v\n\n", yes)
	trues, _ := cli.BoolSlice(ctx, []string{"Are these statements true? [\"The sky is blue\", \"Fish can fly\", \"Water is wet\"]"})
	fmt.Printf("Boolean Slice: %v\n\n", trues)
	pi, _ := cli.Float(ctx, []string{"What is the value of pi (to two decimal places)?"})
	fmt.Printf("Float: %.2f\n\n", pi)
}
2616 次点击
所在节点    分享创造
14 条回复
abc634
92 天前
我的一个小经验:
让 api 返回 markdown 或者 html 或者 xml ,都比 json 好。
然后再解析 xml 就简单了。
IndexOutOfBounds
92 天前
@abc634 我理解场景有些区别,markdown 是半结构化的,主要用于直接展示,比如直接让 AI 用 markdown 写篇文章,这是很好的选择

json 是开发内部使用的,比如你需要提供接口给前端做二次展示

另外通过 json schema 可以做到很强的约束,比如文中给文章分类的例子,通过 enum:"Technology,Science,Business,Health,Education,Other" 强限制分类范围
IndexOutOfBounds
92 天前
otakustay
92 天前
用 function calling 呢?你需要的模型里有不支持 function 的吗
还有个办法是 Assistant Prefill ,不过我估计也不稳
otakustay
92 天前
@abc634 XML 层次深了也不行,我还特地为这个调过专门的方案
reeco
92 天前
还是 1 的方案吧,2 ,3 的方案你换个非 openai 的模型照样不能用
ychost
92 天前
如果输出的 JSON 没有嵌套,就没必要让它输出 JSON 了,直接输出 kv 对,然后代码解析这样容错性更好
IndexOutOfBounds
92 天前
@otakustay 暂时不支持 function call ,不过确实有这个想法,自动注入结构体 Method
IndexOutOfBounds
92 天前
@reeco 非 openai ,比如 gemini ,deepseek 也支持 json 输出的,算是一个通用的规范了

https://openrouter.ai/models?fmt=cards&order=newest&supported_parameters=structured_outputs
otakustay
92 天前
要不这样,用写代码的思路,把你要的 schema 变成 TS interface ,然后下面写个 function 定义接收这个类型的参数,再一段注释说明你的需求,最后是 functionName({起头,让模型给你补……
74123gzy
92 天前
@otakustay #10 那不还是 1. 直接在 Prompt 里强调输出格式
lovestudykid
91 天前
还有个办法是给 json 开个头
neptuno
91 天前
试了很多,function calling 是最优解
abc634
88 天前
@otakustay 谢谢,
我之前还没有碰到 这种情况,我碰到的是上下文太长超出窗口,
(多页内容)
然后我就把前一页的 xml 内容和新的页面 丢给 gpt 让他自己补全,效果还可以。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/1109217

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX