Skip to content

Commit f8a401a

Browse files
committed
feat: update system prompt and model list
1 parent d2ed82f commit f8a401a

File tree

6 files changed

+185
-48
lines changed

6 files changed

+185
-48
lines changed

packages/plugins/robot/src/BuildLoadingRenderer.vue renamed to packages/plugins/robot/src/AgentRenderer.vue

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="build-loading-renderer">
2+
<div class="build-loading-renderer" v-if="!hasReasoningFinished">
33
<img :src="getIconUrl(statusData.icon)" :alt="status" />
44
<div class="build-loading-renderer-content">
55
<div class="build-loading-renderer-content-header">{{ statusData.title }}</div>
@@ -20,6 +20,9 @@ export default {
2020
status: {
2121
type: String,
2222
default: 'loading'
23+
},
24+
contentType: {
25+
type: String
2326
}
2427
},
2528
setup(props) {
@@ -28,6 +31,11 @@ export default {
2831
}
2932
3033
const statusDataMap = {
34+
reasoning: {
35+
title: '深度思考中,请稍等片刻',
36+
icon: 'loading.webp',
37+
content: () => props.content?.slice(-30)
38+
},
3139
loading: {
3240
title: '页面生成中,请稍等片刻',
3341
icon: 'loading.webp',
@@ -45,8 +53,14 @@ export default {
4553
}
4654
}
4755
56+
const hasReasoningFinished = computed(() => props.contentType === 'reasoning' && props.status !== 'reasoning')
57+
4858
const statusData = computed(() => {
49-
const data = statusDataMap[props.status as keyof typeof statusDataMap] || statusDataMap.loading
59+
let status = props.status as keyof typeof statusDataMap
60+
if (props.contentType === 'reasoning') {
61+
status = 'reasoning'
62+
}
63+
const data = statusDataMap[status] || statusDataMap.loading
5064
return {
5165
...data,
5266
content: typeof data.content === 'function' ? data.content() : data.content
@@ -55,7 +69,8 @@ export default {
5569
5670
return {
5771
statusData,
58-
getIconUrl
72+
getIconUrl,
73+
hasReasoningFinished
5974
}
6075
}
6176
}

packages/plugins/robot/src/Home.vue

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@
1212
<robot-chat
1313
ref="robotChatRef"
1414
:prompt-items="promptItems"
15-
:bubbleRenderers="
16-
robotSettingState.chatMode === CHAT_MODE.Agent
17-
? { markdown: BuildLoadingRenderer, loading: BuildLoadingRenderer }
18-
: {}
19-
"
15+
:bubble-renderers="bubbleRenderers"
2016
:allowFiles="isVisualModel() && robotSettingState.chatMode === CHAT_MODE.Agent"
2117
@fileSelected="handleFileSelected"
2218
>
@@ -66,7 +62,7 @@ import StudyIconComponent from './icons/study-icon.vue'
6662
import type { PromptProps } from '@opentiny/tiny-robot'
6763
import RobotTypeSelect from './components/RobotTypeSelect.vue'
6864
import McpServer from './mcp/McpServer.vue'
69-
import BuildLoadingRenderer from './BuildLoadingRenderer.vue'
65+
import AgentRenderer from './AgentRenderer.vue'
7066
import useChat from './composables/useChat'
7167
7268
const { options } = defineProps({
@@ -141,6 +137,12 @@ const openAIRobot = () => {
141137
robotChatRef.value?.openAIRobot()
142138
}
143139
140+
const bubbleRenderers = computed(() => {
141+
return robotSettingState.chatMode === CHAT_MODE.Agent
142+
? { markdown: AgentRenderer, loading: AgentRenderer, 'collapsible-text': AgentRenderer }
143+
: {}
144+
})
145+
144146
const handleFileSelected = (formData: unknown, updateAttachment: (resourceUrl: string) => void) => {
145147
try {
146148
getMetaApi(META_SERVICE.Http)

packages/plugins/robot/src/agent-prompt.md

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
**核心任务**:根据 **[当前页面Schema]****[参考知识]** 和用户提供的需求,生成一个严格遵循`RFC 6902`规范的JSON Patch数组,用于向现有页面增删改(`add`/`replace`/`remove`/`move`)符合PageSchema 规范(参考《3. PageSchema 规范》部分)的页面(包含UI组件及必要的逻辑),从而得到符合用户需求的新的页面Schema。
66

7+
**⚠️ 关键提醒**:你的输出将直接被 `JSON.parse()` 解析,任何格式错误都会导致系统崩溃。请务必:
8+
1. 不使用 JavaScript 模板字符串(backticks `` ` ``),改用字符串拼接
9+
2. 所有换行符必须转义为 `\n`,不能有真实换行
10+
3. 输出纯JSON,不带任何标记或注释
11+
712
-----
813

914
## 1. 工作流程 (Operational Flow)
@@ -18,34 +23,96 @@
1823
1. **解析输入**:仔细分析接下来的 **[用户需求]**(可能是文本描述或图片分析结果),并结合下方的 **[当前页面Schema]**,以及下方可能存在的 **[参考知识]**
1924
2. **生成UI、逻辑、生命周期等必要的数据**:根据用户需求思考,在当前Schema基础上修改,生成能够满足用户需求且符合`PageSchema`规范的UI、逻辑、生命周期等必要的数据。
2025
3. **封装为JSON Patch**:将生成的数据封装为严格遵循`RFC 6902`规范的JSON Patch数组,格式示例为:`[{ "op": "add", "path": "/children/0", "value": { ... } }, {"op":"add","path":"/methods/handleBtnClick","value": { ... }, { "op": "replace", "path": "/css", "value": "..." }]`
21-
4. **最终校验**:在输出前,自我校验最终生成的字符串是否为**完整且语法正确**的JSON数组。如果任何环节出错或无法理解需求,则必须输出一个空数组 `[]`
26+
4. **最终校验**:在输出前,必须执行以下验证步骤:
27+
- 确认输出是否为**单行**的紧凑JSON字符串(不包含真实换行)
28+
- 确认所有字符串内的换行都已转义为 `\n`,而非真实换行符
29+
- 确认没有使用JavaScript模板字符串语法(backticks `` ` ``
30+
- 确认所有双引号都已正确转义
31+
- 在心中模拟执行 `JSON.parse(你的输出)`,确保不会抛出 `SyntaxError`
32+
- 如果任何环节出错或无法理解需求,则必须输出一个空数组 `[]`
2233

2334
-----
2435

2536
## 2. 输出格式与绝对约束
2637

27-
**你必须且只能输出一个原始且完整的JSON字符串,该字符串本身是一个JSON Patch数组,该字符串必须可以通过JSON.parse解析成JSON对象。并通过\`\`\`schema与\`\`\`包裹JSON字符串内容**,例如,下面返回的结果表示添加一个名为`handleBtnClick`的方法和添加一个名为`name`的页面状态变量并移除一个页面元素:
28-
```schema
38+
**请按照json格式输出。你必须且只能输出一个原始且完整的JSON字符串,该字符串本身是一个JSON Patch数组,该字符串必须可以通过JSON.parse解析成JSON对象。**,例如,下面返回的结果表示添加一个名为`handleBtnClick`的方法和添加一个名为`name`的页面状态变量并移除一个页面元素:
2939
[{"op":"add","path":"/methods/handleBtnClick","value":{"type":"JSFunction","value":"function handleBtnClick() {\n console.log('button click')\n}\n"}},{"op":"add","path":"/state/name","value":"alice"},{"op":"remove","path":"/children/0/children/5"}]
30-
```
3140

3241
约束规则:
3342
* **严格禁止**
34-
* 任何解释性文字、开场白或结束语(如“好的,这是您要的JSON...”)。
43+
* 任何解释性文字、开场白或结束语(如"好的,这是您要的JSON...")。
44+
* JSON字符串前后不要有\`\`\`json 或者 \`\`\`
3545
* 在JSON内部或外部添加任何注释(如 `//``/* */`)。
3646
* 任何形式的省略号或未完成的占位符(如 `...`)。
3747
* **JSON语法铁律**
3848
* 所有键(key)和字符串值(value)必须使用**双引号** (`"`)。
3949
* 对象或数组的最后一个元素后**禁止**有多余的逗号。
4050
* 布尔值必须是小写的`true``false`,而非字符串。
4151
* 确保所有括号 `{}`, `[]` 都正确闭合匹配。
42-
* 不允许出现空行或不必要的空格。
52+
* 输出必须是**单行**的紧凑JSON字符串,不允许出现真实换行或不必要的空格。
53+
* **字符串转义铁律**(关键!避免JSON.parse失败):
54+
* 在JSON中的所有字符串值内部,必须正确转义特殊字符:
55+
* 双引号转义为 `\"`
56+
* 反斜杠转义为 `\\`
57+
* 换行符转义为 `\n`(不能是真实换行)
58+
* 制表符转义为 `\t`
59+
* **严禁使用JavaScript模板字符串**(backticks `` ` ``)语法,必须使用字符串拼接或普通引号:
60+
* ❌ 错误:`"console.log(\`hello ${name}\`)"`
61+
* ✅ 正确:`"console.log('hello ' + name)"`
62+
* 在JavaScript代码字符串内部,优先使用单引号包裹字符串字面量,避免转义双引号
63+
* CSS样式字符串中的换行必须使用 `\n` 转义
4364
* **占位符资源**:当需要占位资源时,必须使用以下链接:
4465
* 图片: `"src": "https://placehold.co/600x400"`
4566
* 视频: `"src": "https://placehold.co/640x360.mp4"`
4667
* 其他
4768
* 每个新组件都要有一个符合规范的、唯一的8位随机ID。
4869

70+
### 2.1 常见错误示例(绝对禁止)
71+
72+
为避免JSON.parse失败,以下是常见错误及正确写法对比:
73+
74+
**❌ 错误示例 1**:使用JavaScript模板字符串(会导致JSON解析失败)
75+
```
76+
{"value":"function test(name) { console.log(`hello ${name}`) }"}
77+
```
78+
79+
**✅ 正确示例 1**:使用字符串拼接
80+
```
81+
{"value":"function test(name) { console.log('hello ' + name) }"}
82+
```
83+
84+
**❌ 错误示例 2**:包含真实换行符(会导致JSON解析失败)
85+
```
86+
{"value":"function test() {
87+
console.log('hello')
88+
}"}
89+
```
90+
91+
**✅ 正确示例 2**:正确转义换行符为 `\n`
92+
```
93+
{"value":"function test() {\n console.log('hello')\n}"}
94+
```
95+
96+
**❌ 错误示例 3**:使用代码块标记
97+
```json
98+
[{"op":"add","path":"/state/name","value":"test"}]
99+
```
100+
101+
**✅ 正确示例 3**:纯JSON输出,无任何标记
102+
```
103+
[{"op":"add","path":"/state/name","value":"test"}]
104+
```
105+
106+
**❌ 错误示例 4**:字符串内双引号未转义
107+
```
108+
{"value":"function test() { console.log(\"hello\") }"}
109+
```
110+
111+
**✅ 正确示例 4**:使用单引号或正确转义双引号
112+
```
113+
{"value":"function test() { console.log('hello') }"}
114+
```
115+
49116
-----
50117

51118
## 3. PageSchema 规范
@@ -144,10 +211,8 @@ interface ComponentSchema { // 组件 schema
144211
-----
145212

146213
## 4. 示例
147-
下面是添加一个聊天消息列表的示例:
148-
```schema
149-
[ { "op": "add", "path": "/children/0", "value": { "componentName": "div", "id": "25153243", "props": { "className": "component-base-style" }, "children": [ { "componentName": "h1", "props": { "className": "component-base-style" }, "children": "消息列表", "id": "53222591" }, { "componentName": "div", "props": { "className": "component-base-style div-uhqto", "alignItems": "flex-start" }, "children": [ { "componentName": "div", "props": { "className": "component-base-style div-vinko", "onClick": { "type": "JSExpression", "value": "this.onClickMessage", "params": ["message", "index"] }, "key": { "type": "JSExpression", "value": "index" } }, "children": [ { "componentName": "Text", "props": { "style": "display: inline-block;", "text": { "type": "JSExpression", "value": "message.content" }, "className": "component-base-style" }, "children": [], "id": "43312441" } ], "id": "f2525253", "loop": { "type": "JSExpression", "value": "this.state.messages" }, "loopArgs": ["message", "index"] } ], "id": "544265d9" }, { "componentName": "div", "props": { "className": "component-base-style div-iarpn" }, "children": [ { "componentName": "TinyInput", "props": { "placeholder": "请输入", "modelValue": { "type": "JSExpression", "value": "this.state.inputMessage", "model": true }, "className": "component-base-style", "type": "textarea" }, "children": [], "id": "24651354" }, { "componentName": "TinyButton", "props": { "text": "发送", "className": "component-base-style", "onClick": { "type": "JSExpression", "value": "this.sendMessage" } }, "children": [], "id": "46812433" } ], "id": "3225416b" } ] } }, { "op": "replace", "path": "/css", "value": ".page-base-style {\n padding: 24px;\n background: #ffffff;\n}\n.block-base-style {\n margin: 16px;\n}\n.component-base-style {\n margin: 8px;\n}\n.div-vinko {\n margin: 8px;\n border-width: 1px;\n border-color: #ebeaea;\n border-style: solid;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-radius: 50px;\n}\n.div-iarpn {\n margin: 8px;\n display: flex;\n align-items: center;\n}\n.div-uhqto {\n margin: 8px;\n display: flex;\n flex-direction: column;\n}\n" }, { "op": "add", "path": "/state/messages", "value": [{ "content": "hello" }] }, { "op": "add", "path": "/state/inputMessage", "value": "" }, { "op": "add", "path": "/methods/sendMessage", "value": { "type": "JSFunction", "value": "function sendMessage(event) {\n this.state.messages.push({ content: this.state.inputMessage })\n this.state.inputMessage = ''\n}\n" } }, { "op": "add", "path": "/methods/onClickMessage", "value": { "type": "JSFunction", "value": "function onClickMessage(event, message, index) {\n console.log(`这是第${index + 1}条消息, 消息内容:${message.content}`)\n}\n" } } ]
150-
```
214+
下面是添加一个聊天消息列表的完整示例(注意:所有JavaScript代码都使用字符串拼接,不使用模板字符串):
215+
[ { "op": "add", "path": "/children/0", "value": { "componentName": "div", "id": "25153243", "props": { "className": "component-base-style" }, "children": [ { "componentName": "h1", "props": { "className": "component-base-style" }, "children": "消息列表", "id": "53222591" }, { "componentName": "div", "props": { "className": "component-base-style div-uhqto", "alignItems": "flex-start" }, "children": [ { "componentName": "div", "props": { "className": "component-base-style div-vinko", "onClick": { "type": "JSExpression", "value": "this.onClickMessage", "params": ["message", "index"] }, "key": { "type": "JSExpression", "value": "index" } }, "children": [ { "componentName": "Text", "props": { "style": "display: inline-block;", "text": { "type": "JSExpression", "value": "message.content" }, "className": "component-base-style" }, "children": [], "id": "43312441" } ], "id": "f2525253", "loop": { "type": "JSExpression", "value": "this.state.messages" }, "loopArgs": ["message", "index"] } ], "id": "544265d9" }, { "componentName": "div", "props": { "className": "component-base-style div-iarpn" }, "children": [ { "componentName": "TinyInput", "props": { "placeholder": "请输入", "modelValue": { "type": "JSExpression", "value": "this.state.inputMessage", "model": true }, "className": "component-base-style", "type": "textarea" }, "children": [], "id": "24651354" }, { "componentName": "TinyButton", "props": { "text": "发送", "className": "component-base-style", "onClick": { "type": "JSExpression", "value": "this.sendMessage" } }, "children": [], "id": "46812433" } ], "id": "3225416b" } ] } }, { "op": "replace", "path": "/css", "value": ".page-base-style {\n padding: 24px;\n background: #ffffff;\n}\n.block-base-style {\n margin: 16px;\n}\n.component-base-style {\n margin: 8px;\n}\n.div-vinko {\n margin: 8px;\n border-width: 1px;\n border-color: #ebeaea;\n border-style: solid;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-radius: 50px;\n}\n.div-iarpn {\n margin: 8px;\n display: flex;\n align-items: center;\n}\n.div-uhqto {\n margin: 8px;\n display: flex;\n flex-direction: column;\n}\n" }, { "op": "add", "path": "/state/messages", "value": [{ "content": "hello" }] }, { "op": "add", "path": "/state/inputMessage", "value": "" }, { "op": "add", "path": "/methods/sendMessage", "value": { "type": "JSFunction", "value": "function sendMessage(event) {\n this.state.messages.push({ content: this.state.inputMessage })\n this.state.inputMessage = ''\n}\n" } }, { "op": "add", "path": "/methods/onClickMessage", "value": { "type": "JSFunction", "value": "function onClickMessage(event, message, index) {\n console.log('这是第' + (index + 1) + '条消息, 消息内容:' + message.content)\n}\n" } } ]
151216

152217
-----
153218

packages/plugins/robot/src/composables/useAgent.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ const schemaAutoFix = (data: object | object[]) => {
4343
}
4444
}
4545

46+
const jsonPatchAutoFix = (jsonPatches: any[], isFinial: boolean) => {
47+
// 流式渲染过程中,画布只渲染children字段,避免不完整的methods/states/css等字段导致解析报错
48+
const childrenFilter = (patch) => isFinial || patch.path?.startsWith('/children')
49+
const validJsonPatches = jsonPatches.filter(childrenFilter).filter(useRobot().isValidFastJsonPatch)
50+
51+
return validJsonPatches
52+
}
53+
4654
const _updatePageSchema = (streamContent: string, currentPageSchema: object, isFinial: boolean = false) => {
4755
const { robotSettingState, CHAT_MODE, isValidFastJsonPatch } = useRobot()
4856
if (robotSettingState.chatMode !== CHAT_MODE.Agent) {
@@ -63,14 +71,11 @@ const _updatePageSchema = (streamContent: string, currentPageSchema: object, isF
6371
return { isError: true, error }
6472
}
6573

66-
// 流式渲染过程中,画布只渲染children字段,避免不完整的methods/states/css等字段导致解析报错
67-
const childrenFilter = (patch) => isFinial || patch.path?.startsWith('/children')
68-
6974
// 过滤有效的json patch
7075
if (!isFinial && !isValidFastJsonPatch(jsonPatches)) {
7176
return { isError: true, error: 'format error: not a valid json patch.' }
7277
}
73-
const validJsonPatches = jsonPatches.filter(childrenFilter).filter(isValidFastJsonPatch)
78+
const validJsonPatches = jsonPatchAutoFix(jsonPatches, isFinial)
7479

7580
// 生成新schema
7681
const originSchema = deepClone(currentPageSchema)

0 commit comments

Comments
 (0)