95 lines
3.3 KiB
Go
95 lines
3.3 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"strings"
|
||
)
|
||
|
||
func buildPrompt(summaries []RegionSummary) (string, error) {
|
||
regionsJSON, err := json.MarshalIndent(summaries, "", " ")
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return `你是微信 RPA 多模态任务规划器。
|
||
|
||
请根据图片和标注区域完成:
|
||
1. 识别当前聊天记录。
|
||
2. 识别联系人或会话对象,如果无法识别则填 unknown。
|
||
3. 判断是否需要回复。
|
||
4. 如果需要回复,生成 reply_text。
|
||
5. 如果聊天上下文不足以判断是否回复,可以规划滚动聊天区域查看更多记录。
|
||
6. 生成 dry-run 动作计划。
|
||
|
||
限制:
|
||
- 只能返回合法 JSON,不要 Markdown,不要解释。
|
||
- 不要编造坐标。
|
||
- 点击坐标只能使用我提供的区域中心点。
|
||
- 如果区域 type 是 custom,请重点参考 description 判断该区域的用途和可执行动作。
|
||
- description 是用户标注时填写的用途说明,优先级高于模型自行猜测。
|
||
- 微信发送消息使用键入回车键,不要规划点击发送按钮。
|
||
- 如果当前聊天内容不足以判断是否回复,可以返回 scroll 动作。
|
||
- scroll 只能作用于 chat_content 区域。
|
||
- delta_y < 0 表示向上滚动查看更多历史消息;delta_y > 0 表示向下滚动回到最新消息。
|
||
- 每次最多返回一个 scroll 动作,abs(delta_y) 不要超过 5。
|
||
- 如果滚动后需要重新识别,请追加 observe_again 动作。
|
||
- 不要在同一个任务里同时包含 scroll 和 type_text/press_enter;上下文不足时先滚动观察,不要直接回复。
|
||
- action.type 只能是 click、type_text、press_enter、scroll、observe_again、wait、noop。
|
||
- 当前是 dry_run,只生成计划,不要假设动作已执行。
|
||
- 如果无需回复,task_type 使用 wechat_observe 或 noop,并给出 observations。
|
||
|
||
标注区域如下:
|
||
` + string(regionsJSON) + `
|
||
|
||
请严格返回如下 JSON 结构:
|
||
{
|
||
"task_id": "",
|
||
"task_name": "识别微信聊天并生成回复",
|
||
"task_type": "wechat_reply",
|
||
"task_status": "planned",
|
||
"confidence": 0.0,
|
||
"llm_summary": {
|
||
"contact_name": "unknown",
|
||
"chat_summary": "",
|
||
"latest_user_message": "",
|
||
"reply_text": ""
|
||
},
|
||
"actions": [
|
||
{"action_id":"action_001","type":"click","name":"点击输入框","status":"pending","target_region":"input_box"},
|
||
{"action_id":"action_002","type":"type_text","name":"输入回复内容","status":"pending","text":""},
|
||
{"action_id":"action_003","type":"press_enter","name":"按回车发送","status":"pending"}
|
||
]
|
||
}`, nil
|
||
}
|
||
|
||
func extractJSON(text string) (string, error) {
|
||
trimmed := strings.TrimSpace(text)
|
||
trimmed = strings.TrimPrefix(trimmed, "```json")
|
||
trimmed = strings.TrimPrefix(trimmed, "```")
|
||
trimmed = strings.TrimSuffix(trimmed, "```")
|
||
trimmed = strings.TrimSpace(trimmed)
|
||
|
||
start := strings.Index(trimmed, "{")
|
||
end := strings.LastIndex(trimmed, "}")
|
||
if start < 0 || end < start {
|
||
return "", errors.New("no JSON object found in LLM response")
|
||
}
|
||
|
||
return trimmed[start : end+1], nil
|
||
}
|
||
|
||
func parseTaskFromLLM(raw string) (Task, string, error) {
|
||
jsonText, err := extractJSON(raw)
|
||
if err != nil {
|
||
return Task{}, "", err
|
||
}
|
||
|
||
var task Task
|
||
if err := json.Unmarshal([]byte(jsonText), &task); err != nil {
|
||
return Task{}, jsonText, err
|
||
}
|
||
|
||
return task, jsonText, nil
|
||
}
|