From a46848b1c953c14ca4036c126d643be238b39ec5 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 7 May 2026 09:26:09 -0600 Subject: [PATCH] feat: add guidance LLM helpers and system prompt --- prompt/llm.py | 39 +++++++++++++++++++++++++++++++++++++++ prompt/tests.py | 25 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/prompt/llm.py b/prompt/llm.py index 20d2c5e..5f215ae 100644 --- a/prompt/llm.py +++ b/prompt/llm.py @@ -31,6 +31,22 @@ document.getElementById('btn').onclick = function() { }; ```""" +GUIDANCE_SYSTEM_PROMPT = """你是一个提示词写作教练,帮助学生写出清晰、具体的网页需求描述。 + +判断标准——满足以下条件视为"够好": +- 有明确主题(例如:登录页、计时器、商品卡片列表) +- 至少包含以下一项具体描述:颜色/布局/风格、交互行为(按钮点击、动画等)、页面内容(文字、数量、图标) + +判断为"不够好"的典型情况: +- 目标太泛,如"做一个好看的页面" +- 只有主题,完全没有任何视觉、交互或内容描述 + +规则: +1. 如果提示词不够好,用 1-2 个启发性问题引导学生补充细节,不要直接给出答案 +2. 如果提示词已经够好,以 [READY] 开头回复,简短夸奖学生并说明可以生成了 +3. 用中文回复,语气鼓励,简洁明了 +4. 不要生成任何代码""" + DEFAULT_MODEL = "deepseek-chat" # Models served by the ARK (Volcengine) endpoint @@ -106,3 +122,26 @@ def extract_code(text: str) -> dict: result["js"] = script_match.group(1).strip() return result + + +def parse_guidance_response(full_response: str) -> tuple[str, bool]: + if full_response.startswith("[READY]"): + return full_response[len("[READY]"):].lstrip("\n"), True + return full_response, False + + +async def stream_guidance(history: list[dict]): + """Stream guidance coaching response. Yields content chunks.""" + messages = [{"role": "system", "content": GUIDANCE_SYSTEM_PROMPT}] + messages.extend(history) + client, model = _get_client("") + async with client as c: + stream = await c.chat.completions.create( + model=model, + messages=messages, + stream=True, + ) + async for chunk in stream: + delta = chunk.choices[0].delta + if delta.content: + yield delta.content diff --git a/prompt/tests.py b/prompt/tests.py index c99caff..fd7786d 100644 --- a/prompt/tests.py +++ b/prompt/tests.py @@ -295,3 +295,28 @@ class PromptHistoryTest(TestCase): self.assertIsNone(data[0]["code_html"]) self.assertIsNone(data[0]["code_css"]) self.assertIsNone(data[0]["code_js"]) + + +from prompt.llm import parse_guidance_response + + +class ParseGuidanceResponseTest(TestCase): + def test_ready_prefix_with_newline_stripped(self): + content, is_ready = parse_guidance_response("[READY]\n很好,可以生成了!") + self.assertEqual(content, "很好,可以生成了!") + self.assertTrue(is_ready) + + def test_ready_prefix_without_newline_stripped(self): + content, is_ready = parse_guidance_response("[READY]很好,可以生成了!") + self.assertEqual(content, "很好,可以生成了!") + self.assertTrue(is_ready) + + def test_no_ready_prefix_unchanged(self): + content, is_ready = parse_guidance_response("你的提示词不够具体,请问...") + self.assertEqual(content, "你的提示词不够具体,请问...") + self.assertFalse(is_ready) + + def test_empty_string(self): + content, is_ready = parse_guidance_response("") + self.assertEqual(content, "") + self.assertFalse(is_ready)