From 2d06d34e5b90b7e69e0607c339e8f570af17a523 Mon Sep 17 00:00:00 2001 From: "qichi.liang" Date: Wed, 20 May 2026 18:17:31 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20AI=E9=A2=84=E6=B5=8B=E6=97=A0=E5=8F=8D?= =?UTF-8?q?=E9=A6=88=20+=20=E5=A2=9E=E5=8A=A0=E6=8E=A8=E7=90=86=E5=BB=BA?= =?UTF-8?q?=E8=AE=AE=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useAIPredict 增加 markdown 代码块剥离逻辑 - Prompt 模板要求返回纯 JSON - 可视化编辑器显示预测错误信息 - 展示 AI 推理建议(建议如何优化配方) --- .../ses_1bcf4061fffeZ83r0Ra7NUB40Y.json | 10 ++++++++++ backend/src/services/ai/templates/index.ts | 2 +- frontend/src/components/FormulaVisualEditor.tsx | 10 +++++++++- frontend/src/hooks/useAIPredict.ts | 3 ++- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 .sisyphus/run-continuation/ses_1bcf4061fffeZ83r0Ra7NUB40Y.json diff --git a/.sisyphus/run-continuation/ses_1bcf4061fffeZ83r0Ra7NUB40Y.json b/.sisyphus/run-continuation/ses_1bcf4061fffeZ83r0Ra7NUB40Y.json new file mode 100644 index 0000000..8dc4e4e --- /dev/null +++ b/.sisyphus/run-continuation/ses_1bcf4061fffeZ83r0Ra7NUB40Y.json @@ -0,0 +1,10 @@ +{ + "sessionID": "ses_1bcf4061fffeZ83r0Ra7NUB40Y", + "updatedAt": "2026-05-20T01:45:53.650Z", + "sources": { + "background-task": { + "state": "idle", + "updatedAt": "2026-05-20T01:45:53.650Z" + } + } +} \ No newline at end of file diff --git a/backend/src/services/ai/templates/index.ts b/backend/src/services/ai/templates/index.ts index 990d4b1..cb1717a 100644 --- a/backend/src/services/ai/templates/index.ts +++ b/backend/src/services/ai/templates/index.ts @@ -3,7 +3,7 @@ import type { ChatMessage } from '../providers/types.js' export function predictMetricsPrompt(ingredients: Array<{ name: string; percentage: number; category: string }>): ChatMessage[] { const ingList = ingredients.map(i => `- ${i.name} (${i.category}): ${i.percentage}%`).join('\n') return [ - { role: 'system', content: '你是一名资深化妆品配方工程师。根据成分列表预测配方的肤感指数、稳定性评分和成本估算。返回 JSON 格式:{"sensoryIndex":{"spreadability":0-100,"absorption":0-100,"stickiness":0-100,"overall":0-100},"stabilityScore":0-100,"costEstimate":元/kg,"confidence":0-1,"reasoning":"简短理由"}' }, + { role: 'system', content: '你是一名资深化妆品配方工程师。根据成分列表预测配方的肤感指数、稳定性评分和成本估算。直接返回纯 JSON,不要用 ``` 包裹:{"sensoryIndex":{"spreadability":0-100,"absorption":0-100,"stickiness":0-100,"overall":0-100},"stabilityScore":0-100,"costEstimate":元/kg,"confidence":0-1,"reasoning":"简短理由"}' }, { role: 'user', content: `请分析以下配方的指标:\n${ingList}` }, ] } diff --git a/frontend/src/components/FormulaVisualEditor.tsx b/frontend/src/components/FormulaVisualEditor.tsx index fe1b736..430b83c 100644 --- a/frontend/src/components/FormulaVisualEditor.tsx +++ b/frontend/src/components/FormulaVisualEditor.tsx @@ -121,7 +121,7 @@ export default function FormulaVisualEditor({ phases: initialPhases, onSave }: P } finally { setSaving(false) } } - const { prediction, loading: predicting, predict } = useAIPredict() + const { prediction, loading: predicting, error: predictError, predict } = useAIPredict() const handlePredict = () => { predict(allIngredients.map(i => ({ @@ -169,6 +169,7 @@ export default function FormulaVisualEditor({ phases: initialPhases, onSave }: P {predicting ? : } {predicting ? '预测中...' : 'AI 预测指标'} + {predictError && {predictError}} {hasPrediction && ( @@ -188,6 +189,13 @@ export default function FormulaVisualEditor({ phases: initialPhases, onSave }: P )} + {hasPrediction && prediction!.reasoning && ( +
+

💡 AI 建议

+

{prediction!.reasoning}

+
+ )} + {hasPrediction && (
diff --git a/frontend/src/hooks/useAIPredict.ts b/frontend/src/hooks/useAIPredict.ts index 6390f8b..754a5b4 100644 --- a/frontend/src/hooks/useAIPredict.ts +++ b/frontend/src/hooks/useAIPredict.ts @@ -56,7 +56,8 @@ export function useAIPredict() { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)) as { type: string; content: string } if (data.type === 'result') { - const parsed = JSON.parse(data.content) as PredictionResult + const clean = data.content.replace(/```(?:json)?\s*/g, '').replace(/```/g, '').trim() + const parsed = JSON.parse(clean) as PredictionResult setPrediction(parsed) } else if (data.type === 'error') { setError('AI 预测失败')