UI-TARS 源码解析 #10:parse_action 源码解析:如何用 AST 安全解析模型生成的函数调用?
在上一篇文章中我们对action_parser.py做了整体总览。它的核心作用是把模型输出的文本Thought: 我需要点击搜索框。 Action: click(pointpoint850 120/point)转换成程序可以理解的结构化动作{action_type:click,action_inputs:{start_box:[0.44, 0.11, 0.44, 0.11]}}然后再进一步生成 pyautogui 自动化代码。这一篇我们深入action_parser.py里的第一个关键函数parse_action(action_str)这个函数非常小但很关键。因为它负责把模型生成的click(start_box(850,120))解析成{function:click,args:{start_box:(850,120)}}也就是说parse_action是模型文本动作进入结构化世界的第一道门。一、为什么需要 parse_action在 UI-TARS 中Prompt 会要求模型按照函数调用形式输出动作。例如click(pointpoint850 120/point) type(contentUI-TARS\n) hotkey(keyctrl c) scroll(pointpoint600 720/point, directiondown) drag(start_pointpoint300 500/point, end_pointpoint700 500/point)这些 Action 看起来像 Python 函数调用但它们本质上仍然是模型生成的字符串。程序不能直接执行它们。因为模型输出可能来自不稳定的自然语言生成过程里面可能有格式错误、参数缺失、引号错误甚至出现不应该执行的内容。所以系统必须先做一件事只解析它不执行它。parse_action就是做这个事情的。它不负责点击鼠标也不负责坐标换算只负责回答三个问题这是一个合法的函数调用吗 函数名是什么 关键字参数有哪些这一步成功以后后续代码才能继续处理坐标、动作类型和 pyautogui 代码生成。二、parse_action 在源码中的位置在action_parser.py中parse_action位于较靠前的位置。它后面会被parse_action_to_structure_output调用。整体链路大概是模型输出文本 ↓ parse_action_to_structure_output ↓ 提取 Action 字符串 ↓ parse_action ↓ 得到 function args ↓ 生成结构化 action dict官方codes/README.md对ui-tars包的定位也很清楚它用于解析 VLM 生成的 GUI 动作指令自动生成 pyautogui 脚本并支持坐标转换和智能图像缩放。也就是说parse_action属于整个“模型输出后处理链路”的底层函数。三、parse_action 的核心代码逻辑parse_action的源码逻辑可以简化成下面这样defparse_action(action_str):try:nodeast.parse(action_str,modeeval)ifnotisinstance(node,ast.Expression):raiseValueError(Not an expression)callnode.bodyifnotisinstance(call,ast.Call):raiseValueError(Not a function call)ifisinstance(call.func,ast.Name):func_namecall.func.idelifisinstance(call.func,ast.Attribute):func_namecall.func.attrelse:func_nameNonekwargs{}forkwincall.keywords:keykw.argifisinstance(kw.value,ast.Constant):valuekw.value.valueelifisinstance(kw.value,ast.Str):valuekw.value.selse:valueNonekwargs[key]valuereturn{function:func_name,args:kwargs}exceptExceptionase:print(fFailed to parse action {action_str}:{e})returnNone真实源码中可以看到它确实使用ast.parse(action_str, modeeval)解析字符串然后检查结果是否是ast.Expression再检查主体是否是ast.Call最后提取函数名和关键字参数。这个函数不长但设计很明确它只接受“一个表达式形式的函数调用”。例如click(start_box(850,120))可以解析。但下面这些就不应该被当作正常 Actionx click(...) import os click(...) os.system(rm -rf /)因为它们不是 UI-TARS 期望的“单个动作函数调用”。四、为什么使用 ast.parse而不是 eval这是这篇文章最重要的点。如果只是想从字符串里得到函数名和参数最危险的方式是eval(action_str)例如eval(click(start_box(850,120)))问题是eval会执行表达式。如果模型输出了恶意内容直接eval就可能执行不该执行的代码。而ast.parse不会直接执行代码它只是把源码字符串解析成抽象语法树。Python 官方文档也说明ast模块用于处理 Python 抽象语法树ast.parse()会把 source 解析成 AST 节点。也就是说eval 解析并执行。 ast.parse 只解析成语法树不执行。这就是为什么parse_action使用 AST。它把模型输出当成“语法结构”来分析而不是当成“代码”来运行。这比eval安全得多。不过也要注意ast.parse本身只是解析不等于整个执行链路绝对安全。因为 UI-TARS 后面的parsing_response_to_pyautogui_code里还会对结构化坐标字符串使用eval(start_box)之类的逻辑来还原列表或元组。这个地方如果做产品化需要额外加安全替代方案比如使用ast.literal_eval或严格的数值解析。所以更准确地说parse_action用 AST 避免了直接执行模型生成的函数调用但整个系统仍然需要额外安全校验。五、为什么不用正则表达式另一种常见做法是用正则。例如patternr(\w)\((.*)\)它可以解析click(start_box(850,120))得到函数名click 参数部分start_box(850,120)但问题是Action 一复杂正则就很容易失控。例如type(contenthello, world)参数里有逗号。再比如type(contentit\s good)参数里有转义单引号。再比如drag(start_box(300,500), end_box(700,500))有多个关键字参数。再比如scroll(start_box(600,720), directiondown)参数类型不同。如果用正则需要不断处理引号 逗号 转义字符 多个参数 空格 换行代码会越来越复杂。而 AST 的优势是它天然理解 Python 函数调用结构。例如 Python 文档中展示ast.parse(func(a, bc, *d, **e), modeeval)会得到一个Expression其主体是Call节点里面包含函数、位置参数和关键字参数。所以对 UI-TARS 这种“函数调用式 Action”来说AST 比正则更合适。六、parse_action 的第一步mode‘eval’源码里第一句关键代码是nodeast.parse(action_str,modeeval)这里的modeeval很重要。Python 的ast.parse支持不同模式。如果是默认模式通常解析的是完整模块或语句。但 UI-TARS 的 Action 不是完整 Python 文件也不是赋值语句而是单个表达式click(start_box(850,120))所以它使用modeeval。在这个模式下解析结果应该是ast.Expression也就是一个表达式节点。源码紧接着就检查ifnotisinstance(node,ast.Expression):raiseValueError(Not an expression)这一步的意思是如果模型输出的不是表达式就直接拒绝。例如x click(start_box(850,120))这不是单个表达式而是赋值语句不符合 UI-TARS 的 Action 协议。七、第二步检查是不是函数调用 ast.Call通过第一步后源码继续callnode.bodyifnotisinstance(call,ast.Call):raiseValueError(Not a function call)这一步非常关键。即使一个字符串是合法表达式也不一定是函数调用。例如123 hello start_box 1 2这些都可能是合法表达式但不是 UI-TARS 的动作。UI-TARS 期望的是click(...) type(...) scroll(...)所以必须检查ast.Call只有 AST 主体是函数调用时才继续提取函数名和参数。这一步相当于给 Action 做了一层结构校验合法表达式 ↓ 是函数调用 ↓ 提取函数名和参数八、第三步提取函数名源码中函数名提取逻辑是ifisinstance(call.func,ast.Name):func_namecall.func.idelifisinstance(call.func,ast.Attribute):func_namecall.func.attrelse:func_nameNone这里考虑了两种形式。第一种是普通函数调用click(start_box(850,120))这种情况下call.func是ast.Name函数名是click第二种是属性调用pyautogui.click(start_box(850,120))这种情况下call.func是ast.Attribute源码取的是属性名click所以parse_action理论上可以兼容click(...)和xxx.click(...)但从 Prompt 设计来看UI-TARS 更希望模型直接输出click(...)而不是pyautogui.click(...)因为模型输出的是抽象动作不应该直接绑定到底层执行库。这也体现了 UI-TARS 的分层设计模型输出 click(...) Parser 解析成结构化动作 Executor 再决定是否映射到 pyautogui.click(...)模型不直接写 pyautogui这样后续也可以换成别的执行层。九、第四步提取关键字参数函数名提取完后源码会遍历forkwincall.keywords:keykw.arg也就是说它只处理关键字参数。例如click(start_box(850,120))会得到{start_box:(850,120)}再例如scroll(start_box(600,720), directiondown)会得到{start_box:(600,720),direction:down}这也是为什么 UI-TARS 的 Prompt 里动作参数都写成关键字形式point... content... key... direction...而不是click((850,120))关键字参数的好处是参数语义清楚 顺序不敏感 方便解析 方便后续扩展例如scroll(start_box(600,720), directiondown)即使参数顺序换成scroll(directiondown, start_box(600,720))结构化结果仍然可以正确表达含义。十、第五步只接受常量值源码中对参数值的处理是ifisinstance(kw.value,ast.Constant):valuekw.value.valueelifisinstance(kw.value,ast.Str):valuekw.value.selse:valueNone也就是说它主要接受字符串、数字等常量值。例如click(start_box(850,120))start_box是字符串常量可以解析。hotkey(keyctrl c)key是字符串常量可以解析。type(contentUI-TARS\n)content是字符串常量可以解析。但如果模型输出click(start_boxget_position())或者type(contenthello world)这些参数值不是简单常量parse_action会把它们处理成None。这个设计很重要。因为 UI-TARS 的 Action 参数应该是静态数据不应该是表达式计算。也就是说模型只能告诉系统我要点击哪里 我要输入什么 我要按什么键而不能让模型生成一段逻辑表达式给系统执行。这能减少风险也能让动作结构更稳定。十一、parse_action 的返回值如果解析成功parse_action返回{function:func_name,args:kwargs}例如输入click(start_box(850,120))返回{function:click,args:{start_box:(850,120)}}输入scroll(start_box(600,720), directiondown)返回{function:scroll,args:{start_box:(600,720),direction:down}}输入hotkey(keyctrl c)返回{function:hotkey,args:{key:ctrl c}}这个结构正好对应后续parse_action_to_structure_output需要的字段function → action_type args → action_inputs所以parse_action的职责非常纯粹把函数调用字符串拆成函数名和参数字典。十二、解析失败时返回 None如果解析失败源码会捕获异常exceptExceptionase:print(fFailed to parse action {action_str}:{e})returnNone例如click start_box(850,120)不是合法函数调用。或者click(start_box(850,120)缺少右括号。或者I will click the button.不是函数调用。这些都会解析失败返回None。后续parse_action_to_structure_output会检查解析结果如果是None就抛出错误raiseValueError(fAction cant parse:{raw_str})这说明 UI-TARS 对 Action 格式是比较严格的。模型可以在 Thought 中自由表达但 Action 必须符合约定格式。十三、parse_action 和 prompt.py 的配合现在回头看prompt.py就会发现很多格式约束都是为了服务parse_action。例如 Prompt 要求click(pointpointx1 y1/point)而不是click at x1 y1因为前者可以被 AST 当成函数调用解析。又比如 Prompt 要求hotkey(keyctrl c)而不是press control and c因为前者有明确的函数名和关键字参数。再比如 Prompt 要求type(contentxxx)中的引号、换行符要正确转义。因为如果字符串不合法ast.parse就会失败。所以prompt.py和parse_action本质上是一套协议的两端prompt.py 约束模型输出“像函数调用一样”的 Action。 parse_action 按照函数调用结构解析模型输出。没有 Prompt 约束Parser 会很复杂。没有 ParserPrompt 输出也无法进入执行层。十四、为什么这种设计比 JSON 更适合当前场景有人可能会问为什么不用 JSON让模型直接输出{ action: click, x: 850, y: 120 }不行吗当然可以。很多 Agent 框架确实使用 JSON。但是在 UI-TARS 这里函数调用式 Action 也有它的优势。第一它更短。click(pointpoint850 120/point)比 JSON 更紧凑。第二它更接近动作语义。click(...) type(...) scroll(...)一眼就能看出动作类型。第三它更方便和自然语言 Thought 放在一起。Thought: ... Action: click(...)第四它方便用 AST 解析。因为它刚好符合 Python 表达式形式。当然JSON 也有优势比如更标准、更容易做 schema 校验。如果做更严格的生产系统JSON Schema 或 function calling 也可以考虑。但就 UI-TARS 当前开源代码来说函数调用式 Action 加 AST 解析是一种轻量、直接、工程成本低的方案。十五、parse_action 的安全边界虽然本文标题里有“安全解析”但这里必须说清楚parse_action的安全性主要来自三点第一它使用 ast.parse只解析语法树不直接执行模型输出。 第二它检查结果必须是 ast.Expression 和 ast.Call。 第三它只提取常量参数不执行参数表达式。这比直接eval(action_str)安全得多。但它还不是完整安全沙箱。原因有几个。第一函数名没有白名单。例如模型输出delete_file(path/important)parse_action也能解析出{function:delete_file,args:{path:/important}}虽然后续执行层未必支持这个动作但 Parser 本身不会拒绝它。第二参数值缺少类型和范围校验。例如click(start_box(999999,999999))可以被解析但坐标是否有效要靠后续处理。第三后续代码生成阶段仍然需要安全检查。特别是坐标还原、字符串输入、文件操作、高风险点击等都需要额外限制。所以如果要产品化我建议在parse_action后增加一个 action validation 层。例如ALLOWED_ACTIONS{click,left_double,right_single,drag,hotkey,type,scroll,wait,finished,}ifaction_typenotinALLOWED_ACTIONS:raiseValueError(fUnsupported action type:{action_type})再进一步可以为每种动作定义参数 schemaACTION_SCHEMAS{click:[start_box],drag:[start_box,end_box],hotkey:[key],type:[content],scroll:[start_box,direction],finished:[content],}这样才能真正把模型输出控制在安全范围内。十六、parse_action 的局限parse_action简洁但也有几个明显局限。1. 只支持关键字参数它主要读取call.keywords。如果模型输出click((850,120))这是位置参数当前逻辑不会把它解析到args里。所以 Prompt 必须要求模型使用关键字参数click(start_box(850,120))2. 非常依赖合法 Python 字符串如果输入文本包含未转义引号type(contentits good)AST 会解析失败。所以parse_action_to_structure_output里专门对type(content...)做了额外处理。3. 不处理复杂参数如果参数是列表、字典、表达式当前逻辑会变成None。例如click(start_box[0.1, 0.2, 0.1, 0.2])这里参数是 list 节点不是简单ast.Constant当前parse_action不会完整处理。4. 没有动作白名单它负责解析不负责判断动作是否允许。这在工程上可以接受但生产系统最好补一个校验层。十七、可以怎样改进 parse_action如果要把 UI-TARS 用到更严肃的桌面自动化产品里可以考虑增强parse_action。1. 增加动作白名单ALLOWED_ACTIONS{click,left_double,right_single,drag,hotkey,type,scroll,wait,finished,}解析出函数名后立即校验。2. 使用 ast.literal_eval 解析参数值对于列表、元组、数字、字符串等字面量可以考虑使用ast.literal_eval。这样能支持click(start_box[0.1, 0.2, 0.1, 0.2])但仍避免执行任意代码。3. 增加参数 schema 校验例如click 必须有 start_box drag 必须有 start_box 和 end_box scroll 必须有 direction type 必须有 content4. 增加坐标格式校验例如检查坐标必须是 2 个或 4 个数字 归一化坐标必须在 0 到 1 之间 绝对坐标不能超过图片尺寸5. 明确拒绝 ast.Attribute当前源码支持ast.Attribute也就是xxx.click(...)如果希望模型只输出抽象动作可以拒绝 attribute 调用只允许click(...)这样能减少误解析和执行层混淆。十八、一个完整解析例子假设模型输出Action: scroll(start_box(600,720), directiondown)parse_action实际处理的是scroll(start_box(600,720), directiondown)解析流程如下1. ast.parse(..., modeeval) 得到 Expression 节点 2. node.body 得到 Call 节点 3. call.func 是 ast.Name函数名为 scroll 4. call.keywords 包含两个 keyword start_box(600,720) directiondown 5. 每个 keyword 的 value 是 ast.Constant 6. 返回 { function: scroll, args: { start_box: (600,720), direction: down } }后续parse_action_to_structure_output再把这个结果进一步转成{action_type:scroll,action_inputs:{start_box:[0.3125, 0.6667, 0.3125, 0.6667],direction:down}}最后parsing_response_to_pyautogui_code才会生成滚动代码。十九、parse_action 的设计启发parse_action给我们做 Agent 工程化提供了几个启发。第一不要直接执行模型输出。模型输出必须先解析、校验、结构化再进入执行层。第二Prompt 和 Parser 要成对设计。你希望 Parser 怎么解析就要在 Prompt 中约束模型怎么输出。第三动作格式要足够简单。函数调用式 Action 很适合表达“动作类型 参数”。第四解析层和执行层要分离。parse_action只负责解析不负责执行这样更清晰也更容易调试。第五安全不能只靠 AST。AST 只是第一道门后面还需要动作白名单、参数校验、坐标校验和高风险操作确认。总结这篇文章我们深入分析了 UI-TARS 的parse_action函数。它的核心作用是把模型生成的函数调用式 Action 字符串 解析成 function args 的结构化结果。它的关键设计包括使用 ast.parse(..., modeeval) 解析表达式 要求解析结果必须是 ast.Expression 要求表达式主体必须是 ast.Call 支持 ast.Name 和 ast.Attribute 提取函数名 只提取关键字参数 主要接受 ast.Constant / ast.Str 这类常量值 解析失败时返回 None。相比正则AST 更适合解析函数调用结构。相比evalAST 只解析不执行更适合处理模型生成的动作文本。但parse_action也不是完整安全方案。如果要用于真实产品还应该增加动作白名单 参数 schema 校验 坐标范围检查 高风险动作拦截 更安全的字面量解析从 UI-TARS 的整体架构看parse_action是一个很小但很关键的函数。它把模型输出从“自然语言文本”推进到了“结构化动作”的第一步。

相关新闻

产品经理必懂的专业术语解析与实战应用

产品经理必懂的专业术语解析与实战应用

1. 产品经理专业术语解析:指尖下的世界在互联网产品圈混了十年,最深的体会就是:产品经理这个岗位,说人话比会术语更重要。但现实是,我们每天都被各种专业名词轰炸——从PRD到MVP,从KANO到AARRR,…

2026/7/22 20:31:33 阅读更多 →
2026吉他选购秘籍,弦距琴颈参数详解+高口碑新手吉他推荐

2026吉他选购秘籍,弦距琴颈参数详解+高口碑新手吉他推荐

对于零基础玩家而言,手感远比音色、材质更重要,舒适的手感能降低入门难度、提升练琴积极性。本篇聚焦吉他两大核心手感参数:弦距与琴颈,通俗易懂拆解参数标准,搭配多款手感绝佳的实测机型,帮新手选到好弹、…

2026/7/22 20:31:33 阅读更多 →
缘之空免费下载代码

缘之空免费下载代码

下载链接 《缘之空》的技术架构与玩法设计:一款视觉小说的多维度解析 一、开发背景与核心团队 《缘之空》是由日本游戏公司Sphere于2008年开发并发行的恋爱冒险视觉小说。Sphere是CUFFS旗下的品牌之一,专注于制作高质量的美少女游戏。本作的核心制作团…

2026/7/22 20:31:33 阅读更多 →

最新新闻

Qt Android环境搭建与Felgo安装和使用

Qt Android环境搭建与Felgo安装和使用

前言:本文主要面向已有Qt桌面开发经验、希望将应用扩展到Android平台的开发者。文章将详细介绍两种Qt Android开发方案:原生Qt Android环境搭建和基于Felgo的快速开发方案。通过对比两种方案的环境配置、开发流程和实际效果,帮助读者根据项目…

2026/7/22 21:13:48 阅读更多 →
PyExecJS替代方案对比:为什么说它仍是Python-JS桥接的优选工具?

PyExecJS替代方案对比:为什么说它仍是Python-JS桥接的优选工具?

PyExecJS替代方案对比:为什么说它仍是Python-JS桥接的优选工具? 【免费下载链接】PyExecJS Run JavaScript code from Python (EOL: https://gist.github.com/doloopwhile/8c6ec7dd4703e8a44e559411cb2ea221) 项目地址: https://gitcode.com/gh_mirror…

2026/7/22 21:13:48 阅读更多 →
2026一线瓷砖十大品牌盘点,家装瓷砖品牌名录

2026一线瓷砖十大品牌盘点,家装瓷砖品牌名录

建筑陶瓷是装修工程中常用的基础建材,广泛应用于各类空间装饰场景。国内建筑陶瓷产业体系完善,行业内拥有众多具备独立生产与产品研发能力的主流品牌。本文基于行业公开信息,整理出国内建陶行业十大主流一线瓷砖品牌,分别为&#…

2026/7/22 21:13:48 阅读更多 →
TinyVec源码漫游:深入理解零unsafe向量实现的核心原理

TinyVec源码漫游:深入理解零unsafe向量实现的核心原理

TinyVec源码漫游:深入理解零unsafe向量实现的核心原理 【免费下载链接】tinyvec Just, really the littlest Vec you could need. So smol. 项目地址: https://gitcode.com/gh_mirrors/ti/tinyvec TinyVec是一个轻量级的向量实现库,它以“零unsaf…

2026/7/22 21:13:48 阅读更多 →
Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维

Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维

一句话定义:本文系统讲解如何使用Prometheus Grafana构建Java SaaS应用的全方位监控体系——从Spring Boot应用通过Micrometer暴露指标,到Prometheus采集存储,再到Grafana可视化展示与告警配置,实现从“被动救火”到“主动预防”…

2026/7/22 21:13:48 阅读更多 →
八字排盘的命理软件推荐:2026最新四段回放筛选法

八字排盘的命理软件推荐:2026最新四段回放筛选法

八字排盘的命理软件推荐:2026最新四段回放筛选法 第三方实测摘要:2026年7月21日围绕“八字排盘的命理软件推荐”做了一次断点回放测试。它不比较谁的页面更热闹,只核验全功能命理工具箱在任务暂停、隔时继续、换人复看之后是否仍然成立。2026…

2026/7/22 21:12:47 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻