fix2
This commit is contained in:
parent
cdda235597
commit
a6c429a66d
|
|
@ -75,6 +75,14 @@ func BuildMissionResearchMapUserPrompt(in CopyResearchMapInput) string {
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MissionResearchMapJSONOnlyRetryPrompt() string {
|
||||||
|
return strings.TrimSpace(`上次回覆沒有包含可解析的 JSON。請**只**輸出一個 JSON 物件:
|
||||||
|
- 直接以 { 開頭、以 } 結尾,不要任何思考過程、說明文字或 markdown 圍欄
|
||||||
|
- 需含 audienceSummary、contentGoal、questions、pillars、exclusions、suggestedTags、benchmarkNotes 欄位
|
||||||
|
- suggestedTags 至少 4 項,每項含 tag/reason/searchIntent/searchType
|
||||||
|
- 不要使用 ... 省略,也不要加註解或尾端逗號`)
|
||||||
|
}
|
||||||
|
|
||||||
func ParseMissionResearchMapOutput(raw string) (MissionResearchMap, error) {
|
func ParseMissionResearchMapOutput(raw string) (MissionResearchMap, error) {
|
||||||
payload, err := extractCopyMapJSON(raw)
|
payload, err := extractCopyMapJSON(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -78,37 +78,59 @@ func runAnalyzeCopyMission(ctx context.Context, step StepContext, deps AnalyzeCo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := deps.AI.GenerateText(ctx, domai.GenerateRequest{
|
systemPrompt := libviral.BuildMissionResearchMapSystemPrompt()
|
||||||
|
userPrompt := libviral.BuildMissionResearchMapUserPrompt(libviral.CopyResearchMapInput{
|
||||||
|
Label: mission.Label,
|
||||||
|
SeedQuery: mission.SeedQuery,
|
||||||
|
Brief: mission.Brief,
|
||||||
|
Persona: persona.Persona,
|
||||||
|
StyleBenchmark: persona.StyleBenchmark,
|
||||||
|
PersonaAudienceSummary: persona.CopyResearchMap.AudienceSummary,
|
||||||
|
PersonaContentGoal: persona.CopyResearchMap.ContentGoal,
|
||||||
|
PersonaQuestions: append([]string(nil), persona.CopyResearchMap.Questions...),
|
||||||
|
PersonaPillars: append([]string(nil), persona.CopyResearchMap.Pillars...),
|
||||||
|
})
|
||||||
|
// ResearchGenerateRequest raises max_tokens so the full research map JSON is
|
||||||
|
// not truncated mid-object (truncation is the main cause of parse failures).
|
||||||
|
result, err := deps.AI.GenerateText(ctx, placement.ResearchGenerateRequest(domai.GenerateRequest{
|
||||||
Provider: providerID,
|
Provider: providerID,
|
||||||
Model: credential.Model,
|
Model: credential.Model,
|
||||||
Credential: domai.Credential{
|
Credential: domai.Credential{
|
||||||
APIKey: credential.APIKey,
|
APIKey: credential.APIKey,
|
||||||
},
|
},
|
||||||
System: libviral.BuildMissionResearchMapSystemPrompt(),
|
System: systemPrompt,
|
||||||
Messages: []domai.Message{
|
Messages: []domai.Message{
|
||||||
{
|
{Role: "user", Content: userPrompt},
|
||||||
Role: "user",
|
|
||||||
Content: libviral.BuildMissionResearchMapUserPrompt(libviral.CopyResearchMapInput{
|
|
||||||
Label: mission.Label,
|
|
||||||
SeedQuery: mission.SeedQuery,
|
|
||||||
Brief: mission.Brief,
|
|
||||||
Persona: persona.Persona,
|
|
||||||
StyleBenchmark: persona.StyleBenchmark,
|
|
||||||
PersonaAudienceSummary: persona.CopyResearchMap.AudienceSummary,
|
|
||||||
PersonaContentGoal: persona.CopyResearchMap.ContentGoal,
|
|
||||||
PersonaQuestions: append([]string(nil), persona.CopyResearchMap.Questions...),
|
|
||||||
PersonaPillars: append([]string(nil), persona.CopyResearchMap.Pillars...),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed, err := libviral.ParseMissionResearchMapOutput(result.Text)
|
parsed, err := libviral.ParseMissionResearchMapOutput(result.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app.For(code.AI).SvcThirdParty("拷貝任務研究地圖 LLM 回傳無法解析:" + err.Error())
|
// Model sometimes replies with reasoning/prose and no JSON object.
|
||||||
|
// Retry once, explicitly demanding a raw JSON object.
|
||||||
|
jsonOnlyResult, retryErr := deps.AI.GenerateText(ctx, placement.ResearchGenerateRequest(domai.GenerateRequest{
|
||||||
|
Provider: providerID,
|
||||||
|
Model: credential.Model,
|
||||||
|
Credential: domai.Credential{
|
||||||
|
APIKey: credential.APIKey,
|
||||||
|
},
|
||||||
|
System: systemPrompt,
|
||||||
|
Messages: []domai.Message{
|
||||||
|
{Role: "user", Content: userPrompt},
|
||||||
|
{Role: "assistant", Content: result.Text},
|
||||||
|
{Role: "user", Content: libviral.MissionResearchMapJSONOnlyRetryPrompt()},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
if retryErr != nil {
|
||||||
|
return app.For(code.AI).SvcThirdParty("拷貝任務研究地圖 LLM 回傳無法解析:" + err.Error())
|
||||||
|
}
|
||||||
|
parsed, err = libviral.ParseMissionResearchMapOutput(jsonOnlyResult.Text)
|
||||||
|
if err != nil {
|
||||||
|
return app.For(code.AI).SvcThirdParty("拷貝任務研究地圖 LLM 回傳無法解析:" + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entityTags := make([]missionentity.SuggestedTag, 0, len(parsed.SuggestedTags))
|
entityTags := make([]missionentity.SuggestedTag, 0, len(parsed.SuggestedTags))
|
||||||
|
|
|
||||||
|
|
@ -135,11 +135,11 @@ func runExpandCopyMissionGraph(ctx context.Context, step StepContext, deps Expan
|
||||||
return app.For(code.AI).SysInternal("knowledge graph llm prompt load failed")
|
return app.For(code.AI).SysInternal("knowledge graph llm prompt load failed")
|
||||||
}
|
}
|
||||||
kgVars := map[string]string{
|
kgVars := map[string]string{
|
||||||
"seed": seed,
|
"seed": seed,
|
||||||
"product_brief_line": libkg.OptionalPromptLine("任務補充", mission.Brief),
|
"product_brief_line": libkg.OptionalPromptLine("任務補充", mission.Brief),
|
||||||
"target_audience_line": libkg.OptionalPromptLine("目標受眾", mission.ResearchMap.AudienceSummary),
|
"target_audience_line": libkg.OptionalPromptLine("目標受眾", mission.ResearchMap.AudienceSummary),
|
||||||
"persona_line": libkg.OptionalPromptLine("人設", persona.Persona),
|
"persona_line": libkg.OptionalPromptLine("人設", persona.Persona),
|
||||||
"research_pillars_line": libkg.BulletPromptLine("內容支柱", mission.ResearchMap.Pillars),
|
"research_pillars_line": libkg.BulletPromptLine("內容支柱", mission.ResearchMap.Pillars),
|
||||||
"research_questions_line": libkg.BulletPromptLine("受眾提問", mission.ResearchMap.Questions),
|
"research_questions_line": libkg.BulletPromptLine("受眾提問", mission.ResearchMap.Questions),
|
||||||
}
|
}
|
||||||
userPrompt, err = libprompt.KnowledgeGraphLLMUser(kgVars)
|
userPrompt, err = libprompt.KnowledgeGraphLLMUser(kgVars)
|
||||||
|
|
@ -195,12 +195,37 @@ func runExpandCopyMissionGraph(ctx context.Context, step StepContext, deps Expan
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
graph, err := libkg.ParseSynthOutput(result.Text, libkg.SynthInput{
|
synthIn := libkg.SynthInput{
|
||||||
Seed: seed,
|
Seed: seed,
|
||||||
TargetAudience: mission.ResearchMap.AudienceSummary,
|
TargetAudience: mission.ResearchMap.AudienceSummary,
|
||||||
}, braveSources)
|
}
|
||||||
|
graph, err := libkg.ParseSynthOutput(result.Text, synthIn, braveSources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app.For(code.AI).SvcThirdParty("延伸知識產生失敗,請重試:" + err.Error())
|
// Model occasionally replies with reasoning/prose and no JSON object.
|
||||||
|
// Retry once, explicitly demanding a raw JSON object.
|
||||||
|
jsonOnlyReq := placement.ResearchGenerateRequest(domai.GenerateRequest{
|
||||||
|
Provider: providerID,
|
||||||
|
Model: credential.Model,
|
||||||
|
Credential: domai.Credential{
|
||||||
|
APIKey: credential.APIKey,
|
||||||
|
},
|
||||||
|
System: systemPrompt,
|
||||||
|
Messages: []domai.Message{
|
||||||
|
{Role: "user", Content: userPrompt},
|
||||||
|
{Role: "assistant", Content: result.Text},
|
||||||
|
{Role: "user", Content: libkg.KnowledgeGraphJSONOnlyRetryPrompt()},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
retryResult, retryErr := deps.AI.GenerateText(ctx, jsonOnlyReq)
|
||||||
|
if retryErr != nil {
|
||||||
|
return app.For(code.AI).SvcThirdParty("延伸知識產生失敗,請重試:" + err.Error())
|
||||||
|
}
|
||||||
|
retryGraph, retryParseErr := libkg.ParseSynthOutput(retryResult.Text, synthIn, braveSources)
|
||||||
|
if retryParseErr != nil {
|
||||||
|
return app.For(code.AI).SvcThirdParty("延伸知識產生失敗,請重試:" + retryParseErr.Error())
|
||||||
|
}
|
||||||
|
graph = retryGraph
|
||||||
|
result = retryResult
|
||||||
}
|
}
|
||||||
if libkg.GraphTooThin(graph) {
|
if libkg.GraphTooThin(graph) {
|
||||||
retryReq := placement.ResearchGenerateRequest(domai.GenerateRequest{
|
retryReq := placement.ResearchGenerateRequest(domai.GenerateRequest{
|
||||||
|
|
@ -217,10 +242,7 @@ func runExpandCopyMissionGraph(ctx context.Context, step StepContext, deps Expan
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if retryResult, retryErr := deps.AI.GenerateText(ctx, retryReq); retryErr == nil {
|
if retryResult, retryErr := deps.AI.GenerateText(ctx, retryReq); retryErr == nil {
|
||||||
if retryGraph, parseErr := libkg.ParseSynthOutput(retryResult.Text, libkg.SynthInput{
|
if retryGraph, parseErr := libkg.ParseSynthOutput(retryResult.Text, synthIn, braveSources); parseErr == nil && !libkg.GraphTooThin(retryGraph) {
|
||||||
Seed: seed,
|
|
||||||
TargetAudience: mission.ResearchMap.AudienceSummary,
|
|
||||||
}, braveSources); parseErr == nil && !libkg.GraphTooThin(retryGraph) {
|
|
||||||
graph = retryGraph
|
graph = retryGraph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue