import { tool } from 'ai'; import { z } from 'zod'; import { PAGE_OPERATE_ACTIONS, PAGE_OPERATE_TARGET_MAX_LENGTH, PAGE_OPERATE_VALUE_MAX_LENGTH, PAGE_OPERATE_TOOL, normalizePageOperateEffect, } from './PageOperateContract'; /** * operatePage 工具的服务端执行体:刻意什么都不做,只把入参规整成效果指令回去。 * * 页面上有哪些输入框、哪些按钮,只有浏览器里当前那张页面知道;服务端猜不出来, * 也不该猜。命中判定与真正的填写/点击都在浏览器侧(operateInDocument)。 * 因此执行结果不会回流给模型——模型只知道"指令已下发",不知道有没有点中, * 失败必须由宿主在页面上出声(mountPageOperateEffect 的 onFailure)。 */ export function createPageOperateTools() { return { [PAGE_OPERATE_TOOL]: tool({ description: [ '在用户当前正在看的页面上执行一步操作:往输入框/下拉框里填值,或点击按钮。', '用户说「把 X 填成 Y」用 action=fill;说「点一下 X」「提交」「查询」用 action=click。', '一次只做一步;要先填再点就分多次调用,按顺序来。', '本工具只操作当前页面,不做页面跳转。', ].join(''), inputSchema: z.object({ action: z.enum(PAGE_OPERATE_ACTIONS) .describe('fill=往输入项里填值;click=点击按钮'), target: z.string().min(1).max(PAGE_OPERATE_TARGET_MAX_LENGTH) .describe('控件名:fill 填输入项的标签文字,click 填按钮上的文字;用页面上或用户原话里的名字'), value: z.string().max(PAGE_OPERATE_VALUE_MAX_LENGTH).optional() .describe('action=fill 时要填入的内容;click 不需要'), }), execute: (input: unknown) => normalizePageOperateEffect( (input ?? {}) as { action?: unknown; target?: unknown; value?: unknown }, ), }), }; }