wechat_ai/agent/planner.go

95 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}