GoForum › 🌐 V2EX
从“意图”到“执行”: AI 原生编程语言 Wenli (文理) 的设计哲学与路线图
uorz ·
2026-03-20 21:42 ·
0 次点赞 · 2 条回复
书接上文,我们探讨了 Wenli (文理) 诞生的初衷。今天,随着 spec.py 规范的明确以及 test_parser.py 验证逻辑的跑通,我想更深入地聊聊它的核心设计哲学,以及我们正在构筑的 AI 原生编程新范式。完整的语言规范可以在这里找到,现在该规范使用简体中文描述,相信只要符合 EBNF 范式,可以非常方便地为 Wenli 创建各种形式的方言版本并使用我们正在开发的工具链。
简要背景回顾
Wenli 不仅仅是另一种脚本语言,它是一场实验。我们的目标是打破自然语言的“模糊性”与机器指令的“确定性”之间的屏障。通过 Lark 解析器,Wenli 已经能够将类自然语言的语法结构精准转化为抽象数据树( ADT ),为后续的 AI 代理( Agent )调用打下了坚实的语法基础。
代码示例与对比
为了直观理解 Wenli 的表达力,我们来看一个典型的“技能调用”场景。
Wenli 源码
---
# 核心翻译流水线
## 输入:原始链接、目标语言(字符串)
## 参数:
- 最大重试次数:3
- 严格模式:真
- 默认模型:“gpt-4o”(模型类型)
## 输出:
- 最终结果:Markdown 文本
- 总耗时:数值
#### 这是单行注释,不应该被解析进 AST
使用网页加载器处理原始链接,保存为网页源码( HTML 文本)、加载状态。
- 超时时间:30
- 代理:“http://localhost:8080”
如果使用状态检查器处理加载状态以得到加载成功为假,则:
报错“网页加载失败,无法继续”。
否则:
“提取主要正文,翻译为{目标语言}”,保存为提取正文。
> 网页源码
- 温度:0.7
结束判断加载成功。
---
# 批量数据与循环测试
## 输入:文件列表
## 输出:结果报告
使用初使化工具处理文件列表,保存为队列。
对于队列中的每一个文件:
如果“检查文件类型”以得到是否为图片为真,则:
“描述这张图片:{文件}”,保存为图片描述。
- 视觉模式:真
否则:
使用文本读取器处理文件,保存为文本内容。
结束判断是否为图片。
结束处理文件。
---
# 代码生成器与多重赋值测试
## 输入:用户需求
## 输出:
- 代码行数:数值
` ``python
def setup_env():
print("Environment ready for AST testing!")
` ``
“生成相应的测试代码:{用户需求}”,保存为测试代码(代码文本)。使用保存器处理测试代码、用户需求,保存为保存状态。
“{测试代码} 保存到 临时文件夹 并运行”,保存为临时文件路径。
- 工具:写入文件、用户确认
`wc -l {临时文件路径}`,保存为代码行数。
> 临时文件路径
等效的 Python AST (简化表达)
在 Wenli 的解析器中,上述代码会被转化为如下嵌套的 ADT 结构:
[Routine(name='核心翻译流水线',
inputs=[VarDecl(name='原始链接', type_annot=None),
VarDecl(name='目标语言', type_annot='字符串')],
params=[ParamDefault(name='最大重试次数', default_val='3'),
ParamDefault(name='严格模式', default_val='真'),
ParamDefault(name='默认模型',
default_val=TypeCast(value='gpt-4o',
type_name='模型类型'))],
outputs=[VarDecl(name='最终结果', type_annot='Markdown 文本'),
VarDecl(name='总耗时', type_annot='数值')],
body=[StrictCall(tool='网页加载器',
args=['原始链接'],
targets=[VarDecl(name='网页源码', type_annot='HTML 文本'),
VarDecl(name='加载状态', type_annot=None)],
kwargs={'代理': 'http://localhost:8080', '超时时间': '30'},
contexts=[]),
IfStmt(condition=CondEval(tool='状态检查器',
arg='加载状态',
target='加载成功'),
expected_bool=False,
true_branch=[ExceptionStmt(message='网页加载失败,无法继续')],
false_branch=[AgentCall(prompt='提取主要正文,翻译为{目标语言}',
targets=[VarDecl(name='提取正文',
type_annot=None)],
kwargs={'温度': 0.7},
contexts=['网页源码'])])]),
Routine(name='批量数据与循环测试',
inputs=[VarDecl(name='文件列表', type_annot=None)],
params=[],
outputs=[VarDecl(name='结果报告', type_annot=None)],
body=[StrictCall(tool='初使化工具',
args=['文件列表'],
targets=[VarDecl(name='队列', type_annot=None)],
kwargs={},
contexts=[]),
ForeachStmt(collection='队列',
item='文件',
body=[IfStmt(condition=CondEval(tool='检查文件类型',
arg=None,
target='是否为图片'),
expected_bool=True,
true_branch=[AgentCall(prompt='描述这张图片:{文件}',
targets=[VarDecl(name='图片描述',
type_annot=None)],
kwargs={'视觉模式': '真'},
contexts=[])],
false_branch=[StrictCall(tool='文本读取器',
args=['文件'],
targets=[VarDecl(name='文本内容',
type_annot=None)],
kwargs={},
contexts=[])])])]),
Routine(name='代码生成器与多重赋值测试',
inputs=[VarDecl(name='用户需求', type_annot=None)],
params=[],
outputs=[VarDecl(name='代码行数', type_annot='数值')],
body=[CodeChunk(code_type='python',
code_content='def setup_env():\n'
' print("Environment ready for AST '
'testing!")'),
AgentCall(prompt='生成相应的测试代码:{用户需求}',
targets=[VarDecl(name='测试代码', type_annot='代码文本')],
kwargs={},
contexts=[]),
StrictCall(tool='保存器',
args=['测试代码', '用户需求'],
targets=[VarDecl(name='保存状态', type_annot=None)],
kwargs={},
contexts=[]),
AgentCall(prompt='{测试代码} 保存到 临时文件夹 并运行',
targets=[VarDecl(name='临时文件路径', type_annot=None)],
kwargs={'工具': ['写入文件', '用户确认']},
contexts=[]),
ShellCall(code='wc -l {临时文件路径}',
targets=[VarDecl(name='代码行数', type_annot=None)],
kwargs={},
contexts=['临时文件路径'])])]
等效的 Python 代码
def 核心翻译流水线(原始链接, 目标语言: 字符串, 最大重试次数=3, 严格模式=真, 默认模型=模型类型(gpt-4o)) -> tuple[Markdown 文本, 数值]:
网页源码: HTML 文本, 加载状态 = 网页加载器(原始链接, kwargs={'超时时间': '30', '代理': 'http://localhost:8080'})
if (加载成功 := 状态检查器(加载状态)) == False:
raise RuntimeError(网页加载失败,无法继续)
else:
提取正文 = llm_agent('提取主要正文,翻译为{目标语言}', args=(目标语言,), kwargs={'温度': 0.7}, history=['网页源码'])
return 最终结果, 总耗时
----------------------------------------
def 批量数据与循环测试(文件列表) -> tuple[Any]:
队列 = 初使化工具(文件列表)
for 文件 in 队列:
if (是否为图片 := llm_agent('检查文件类型')) == True:
图片描述 = llm_agent('描述这张图片:{文件}', args=(文件,), kwargs={'视觉模式': '真'})
else:
文本内容 = 文本读取器(文件)
return 结果报告
----------------------------------------
def 代码生成器与多重赋值测试(用户需求) -> tuple[数值]:
def setup_env():
print("Environment ready for AST testing!")
测试代码: 代码文本 = llm_agent('生成相应的测试代码:{用户需求}', args=(用户需求,))
保存状态 = 保存器(测试代码, 用户需求)
临时文件路径 = llm_agent('{测试代码} 保存到 临时文件夹 并运行', args=(测试代码,), tools=['写入文件', '用户确认'])
代码行数 = run_shell(wc -l {临时文件路径}, history=['临时文件路径'])
return 代码行数
----------------------------------------
设计哲学:在“文”与“理”之间寻求平衡
Wenli 的核心命名便揭示了它的灵魂:“文”代表语言的表达能力与灵活性,“理”代表严谨的架构逻辑与执行约束。
A. 语言能力与严格架构的博弈
我们不希望 Wenli 变成一个漫无边际的 Prompt 集合,也不希望它像 C++ 那样充满内存管理的琐碎细节。
- 文( Language ): 赋予开发者直接描述“技能( Skill )”和“意图( Intent )”的能力。
- 理( Architecture ): 强制要求明确的上下文( Context )管理和工具注入规范。这确保了无论 AI 如何生成代码,其逻辑边界始终在架构控制之内。
B. 原生支持的核心特性
Wenli 在语法层面原生集成了现代 AI 开发的必备要素:
- 原生技能( Skills )与代码块: 技能是第一类公民,支持逻辑块与原子工具的无缝组合。
- 智能代理调用( Agent Calls ): 语法上区分“确定性调用”与“启发式 Agent 决策调用”。
- 有意义的自动类型转换: 考虑到 LLM 输出的多样性,Wenli 支持基于语义的类型转换(例如:将字符串 “Yes” 智能转换为布尔值
True)。 - 上下文与变量注入: 运行时环境(如用户偏好、历史记录)可以作为
CONTEXT被自动注入,无需在函数间层层传递。
征途前瞻:Wenli 的下一步
目前我们已经完成了语法的解析与校验,接下来的开发计划将围绕如何让 Wenli “跑起来”且“好用”展开:
- ADT 虚拟执行机 (VM): 开发专门用于解释执行 ADT 树的虚拟机,处理异步 IO 挂起与状态回溯,确保 Wenli 指令的非阻塞运行。
- 标准库与工具自动发现: 构建底层标准库,实现一套插件机制,让 Wenli 能自动识别环境中的 OpenAPI 接口或本地 Python 函数并转化为原生工具。
- 思想编译器 (Idea-to-Wenli Compiler): 利用 LLM 将人类模糊的构思直接编译为符合规范的 Wenli 代码,实现“愿景即代码”。
- 官方工具链支持 (LSP): 开发语言服务器协议实现,为 Neovim/VSCode 提供语法高亮、自动补全及静态类型检查,提升编写效率。
- 云端热修复机制 (Cloud LLM Hot-fix): 当代码运行报错时,系统自动捕获轨迹并请求云端 LLM 生成补丁,实现运行时的逻辑自我修正。
- 脚本蒸馏 (Static Script Distillation): 提供将动态的 Wenli 逻辑“蒸馏”为高性能静态脚本(如纯 Python 或 Rust )的功能,用于生产环境的大规模部署。
Wenli 不仅仅是在写程序,它是在为 AI 编织思考的纹理。 我们相信,未来的编程不再是人去适应机器,而是人与机器共同拥有一种“文理兼修”的语言。欢迎对此感兴趣的 Geek 们一起交流,共同见证 Wenli 的进化。欢迎大家一起讨论!
2 条回复
添加回复
你还需要 登录
后发表回复
AI 代理( Agent )能稳定调用的前提是模型训练数据里必须有大量用这个语言编写的源码