ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从意图到契约:一种描述AI系统的程序语言构想

从意图到契约:一种描述AI系统的程序语言构想

最近在帮团队梳理一套RAG Agent的调用链时,我盯着满屏的Python代码突然问身边的同事:如果让一个不懂代码的产品经理来描述这个系统的行为,他会怎么讲?大概率是“用户问一个问题,我们从知识库里找到相关片段,让模型生成回答,再附上引用出处”。这种自然语言描述和真实代码之间的鸿沟,已经大到让人有点不安了。当AI系统越做越复杂,我们是不是需要一种专门用来“描述AI程序”的语言?这不是拍脑袋想出来的概念,而是我在同时接触Python Agent框架、Rust Agent生态和提示词工程之后,越来越强烈的直觉——我们缺的不是更强的模型,而是一层能把“意图”和“实现”重新分开的描述语言。这篇文章就把我脑内的“AI语言”构想完整写出来:它不打算替代Python或Rust,而是想成为人和AI系统之间的一张可执行的蓝图。

1. AI时代编程范式正在发生什么变化:从“实现逻辑”到“描述意图”

老一代程序员写代码,本质是在给机器写剧本:先做这个,再做那个,如果条件成立就跳到另一条路。这种“实现逻辑”的范式统治了计算机行业七十年,编译器、操作系统、数据库、Web服务,统统建立在这套“告诉机器每一步怎么做”的根基上。但到了大模型时代,事情开始变得微妙。当我们调用一个聊天模型时,我们不再逐行指示模型如何处理token,而是用自然语言描述“你是一个客服助手,请根据以下文档回答用户问题”。模型自己完成剩余的大量推理工作——理解语义、组织知识、生成语气合适的回复。代码写的东西,悄悄从“如何做”变成了“做什么”。

这个转变不是渐进式的,它正在动摇编程范式的底层假设。传统编程里,我们有一整套精确的工具来表达逻辑:类型系统、条件分支、循环、函数调用、异常处理。但AI系统里最核心的东西——意图、上下文、置信度、不确定性、安全边界——这些概念在传统语言里几乎找不到直接对应的语法结构。你可以用Python写一个Agent循环,用Rust写一个推理管线,但你会发现代码里大部分时间在处理“胶水逻辑”:把prompt拼好、处理重试、解析JSON输出、检查是否符合格式。真正属于AI的那部分,反而被埋在了一层又一层工具代码下面。

我印象最深的一个项目是给某业务线做一个多Agent协作系统。系统里有意图识别Agent、知识检索Agent、写作Agent、质检Agent。跑起来之后,最困难的不是让模型输出正确答案,而是让这几个Agent之间的协作顺序、容错策略、上下文传递变得可理解、可测试、可解释。每次改需求,产品经理说“某个问题没有引用来源,需要回退”,我就得在agent框架配置里翻半天。那一刻我意识到,问题不在于哪个模型不够强,而在于我们描述这整套系统的方式太原始了。

于是我开始琢磨一种“程序描述语言”(Program Description Language,简称PDL)式的构想。它不描述算法本身,而是描述AI系统应该具备的行为、约束、资源和验收标准。传统的编译器吃掉源代码,生成可执行机器码;这种AI语言则把规格描述翻译成一组可执行的Agent流程、提示词策略和校验规则。

2. 现有工具描述AI系统时的四个断层

在提出任何新东西之前,先把旧东西的问题看清楚。我梳理了当前描述AI系统时最常见的四个断层,每个都对应一类让工程师头疼的真实场景。

断层一:自然语言提示词与代码之间存在“翻译损耗”。

现在的AI Agent配置,很大一部分是以自然语言提示词存在的。提示词写在配置文件里,写在代码字符串里,甚至写在产品经理给开发者的文档里。它的优点是灵活,缺点是没法测试、没法版本对比、没法约束。你改了一个词,可能让整个Agent的好评率跌掉五个点,但代码层面完全看不出原因。提示词本身不是程序,它缺乏结构、缺乏变量、缺乏类型,本质上是一种“一次性口头订单”。真正严肃的AI工程,不能把核心逻辑寄托在一段没有约束的散文上。

断层二:控制流与数据流纠缠不清。

一个像样的Agent绝不只是“输入一句,输出一句”。它需要循环:检索不到结果时怎么办?生成内容被安全策略拦截了怎么办?多Agent协作中某个环节超时了怎么办?这些控制逻辑如果用Python写,需要手动管理状态机;如果用Rust写,要考虑生命周期和错误处理。但站在用户视角,这些决策本质上都属于“任务描述”的一部分——你希望系统在什么条件下重试、在什么条件下放弃、在什么条件下切换策略。现在的做法是让工程师把这类逻辑散落在框架回调函数里,既难改也难读。

断层三:非确定性在类型系统里没有位置。

传统语言的类型系统擅长表达“这是一个整数”“这是一个字符串”,但AI系统里到处是概率、置信度、采样温度、随机种子。同一个prompt,不同温度下输出完全不同;同一个检索器,不同top-k下召回结果差别很大。这些“不确定性的量”目前只能靠参数配置来体现,而无法成为类型系统的一部分。结果就是:错误处理很笨拙,测试很难稳定复现,跨团队协作时大家对“这个接口会返回什么样的结果”心里完全没底。

断层四:资源边界和权限边界表达模糊。

大模型时代的安全问题,很大程度上不是模型“坏”,而是边界没有描述清楚。一个Agent能用哪些知识库?能调哪些工具?输出要不要过滤个人信息?哪些数据不允许进入上下文窗口?这些问题在现有语言里没有专门的表达位置。你可以写一堆if判断,可以在框架配置里加白名单,但本质上这些约束游离在系统描述之外,导致审计、合规、安全评审都非常困难。尤其是涉及到具体行业落地时,“AI安全工程师”和“AI应用与安全工程师”这类角色最常抱怨的就是:你没法从代码里快速回答“这个系统到底有哪些边界”。

这四个断层叠加起来,就能解释当下AI工程的荒诞感:我们一边用最精密的工程手段训练模型,一边用最原始的手段去编排模型。AI语言构想的核心动机,就是给上述断层提供一套统一的描述框架。

3. “AI语言”的设计骨架:以意图为核心的分层描述模型

那么,这个“AI语言”到底长什么样?我先说设计原则,再给语法草图。设计原则可以用一句话概括:把任务规格写得像一份合同,而不是像一段代码。

所谓“合同”,就是写明双方责任、资源范围、验收条件、违约处理。具体到语言设计上,我设想了五个核心概念:

**task(任务)**是顶层单位,描述一个完整的AI行为,类似传统语言里的函数或类。每个task必须有一个name、一个version,以及一段自然语言级别的目的描述——这段描述不是给机器执行的prompt,而是给人看的规格说明,同时也是机器的意图锚点。

**resource(资源)**是把外部依赖统一表达出来。模型、知识库、工具API、数据库、缓存,全都属于资源。每个资源有类型、有访问权限、有加载策略。这样做的价值在于,AI系统的依赖不再散落在prompt和代码里,而是一进制可审计的清单。

**contract(契约)**是AI语言里最关键的语法概念。它定义输入输出接口,以及验收条件。比如“输入是1到2000字符的用户消息”“输出必须包含引用来源”“不能输出指定名单上的实体”。任何AI task执行完,运行时都会检查契约是否满足,不满足就触发修复或降级。这等于把传统编程里的“类型检查”升级成了“语义检查”。

**flow(流程)**是唯一保留显式控制流的地方。为什么保留?因为不是所有AI系统都能完全自由地编排步骤。有些场景必须固定顺序:先身份验证,再查知识库,最后生成回复。在这个层面允许人也允许机器介入,但flow应当是可选而非必须的。AI语言默认拥抱“让运行时决定最佳执行路径”,只有需要强约束时,才显式声明flow。

**guard(守卫)**负责安全与合规边界。它独立于任务流程存在,是全局的、不可被业务逻辑绕过的保护层。守卫可以声明“禁止PII进入模型上下文”“所有模型调用必须记录日志”“输出必须通过内容安全过滤”。理论上讲,guard是AI语言对“AI安全工程师”这个角色最大的贡献——安全策略终于能被单独写、单独审、单独测试了。

这五个概念构成了一个分层模型:最外层是业务方看得懂的task描述,中间层是工程师调整的flow和contract,最底层是编译器解释器负责解析的resource与guard。上层告诉系统“我们要什么”,下层决定系统“怎么能安全地做到”。这个分层模型也是我坚持称为“程序描述语言”而不是“编程语言”的原因——它更像IEEE里的规格说明语言,只是规格的对象变成了AI系统。

4. 一段虚构的AI语言代码:语法草图与运行语义

纸上谈兵没有说服力,我直接给一段虚构的语法草图。下面这段代码描述的是一个“客服工单智能分诊与回复”的例子,你可以把它理解成传统编程里的伪代码,但我尽量把语法设计得接近真实语言:

task "客服工单智能分诊与回复" { version: "0.1" purpose: "根据帮助文档,对用户问题进行意图分诊,并生成带引用的回复" resource knowledge { source: vector-db("help-docs") sync: daily permission: read-only } resource model { provider: openai-compatible("gpt-4o") options: { temperature: 0.2, max_tokens: 512 } } contract { input user_message: text(1..2000) output reply: text expected: { contains_citation = true, no_unsafe_content = true, reply_length <= 500 } } guard { block: [pii, profanity, unsafe] log: all_model_calls, all_retrievals allowed_tools: [knowledge.search] } flow { intent = classify(model, user_message); docs = retrieve(knowledge, [user_message, intent]); draft = generate(model, context=[docs, user_message], system="客服助手"); reply = validate(draft, contract); return repair(reply); } }

我来逐个解释这段代码的“运行语义”。这个语言不是直接跑给模型看的,而是先经过一个编译器或解释器,生成一份“执行计划”。执行计划里包含三样东西:每个步骤具体的prompt模板、步骤之间的数据流、以及需要检查的规则。

第一步intent = classify(model, user_message),运行时会对model资源发起一次专用分类任务。值得注意的细节是,classify是语言内置的语义操作,不是模型输出一个JSON再自己解析。编译器知道如何把这个操作转化为一次带结构化输出的模型调用,并在输出不合规时自动重试。

第二步docs = retrieve(knowledge, [user_message, intent])表示一次受限的检索操作。knowledge资源在guard里被声明为read-only,同时guard的allowed_tools限制了运行时的工具调用范围。如果在flow里试图调用一个不在允许列表里的工具,编译器会直接报错。这相当于把权限校验提到了编译期,而不是运行期。

第三步生成后,validate(draft, contract)是一个值得一提的设计。传统语言里,函数返回什么就返回什么;AI语言里,一个task的输出必须满足contract里声明的语义条件。运行时会把draft送进一个轻量级验证器,检查它是否真的包含引用、是否触发全球内容安全规则、长度是否超限。如果验证失败,flow里的repair(reply)会触发一次“修复”操作——把错误信息拼进新prompt,让模型自己修改输出。这个机制我称它为“语义级异常处理”,它对应了真实AI系统里最常用的纠错回环,只是把它显式化了。

我还想在语法上强调两点。第一,purpose字段是给人类读的,也是给编译器的“意图提示”用的。编译器在生成prompt时,会把purpose并入系统提示词;在生成执行计划时,purpose可以作为模型选择、参数调整的参考。第二,guard是强制性的吗?我的构想里,至少在正式环境是强制性的。如果一个task没有声明guard,编译器可以拒绝编译或强制加载默认守卫。安全不能靠自觉,这点尤其适用于To B和To G场景。

这种语言和现有DSL最大的区别在于:它不只是配置,而是有“语义检查”能力的描述。传统的DSL(比如yaml配置)只能定义“有什么”,AI语言还要定义“必须达到什么”,并且运行时真的去验证。

5. 为什么Rust可能成为AI语言的最佳底座

聊到实现层面,很多人会首先想到Python——毕竟大模型生态几乎都在Python里。但我的判断是:如果AI语言要真正走向工程化,Rust反而是更合适的底座。这个判断和近期社区里“基于Rust语言AI Agent”的热度上升是吻合的,而我认为这种热度背后有结构性的原因。

Rust的核心优势有三个。第一是内存安全与并发安全。AI Agent系统本质上是一堆并发任务的编排:检索线程、模型调用线程、日志线程、安全过滤线程同时运转。Rust的所有权模型可以强制避免数据竞争,这在Python里要靠经验或事件循环来规避,在Rust里则被编译器盯死。第二是无GC的确定性。AI语言需要精确控制延迟和资源占用,尤其是在边缘设备或实时推理场景下,垃圾回收带来的不确定停顿可能会让整个agent行为偏离预期。第三是可靠的类型系统与模式匹配。AI语言里需要频繁处理“成功、失败、重试、降级、违禁”这类带tag的枚举状态,Rust的enum和Result、Option类型几乎是天生契合的。

我构想的实现路径是这样的:AI语言不是另一个从零写的脚本解释器,而是一个“规格描述编译器”,前端负责解析AI语言语法,后端生成Rust代码或直接生成AST执行图。生成的代码落在某个Rust Agent框架上——比如现成的agent runtime,由这个runtime负责并发调度、重试、资源管理。Rust编译器本身就成了AI语言的第一道防线:AI语言里声明式和约束式的语义,可以被翻译成强类型的Rust结构,很多contract不满足的情况能在编译期就暴露出来。

举个例子,AI语言里的resource声明翻译成Rust后,会变成带权限标记的结构体:struct Resource { pool: ResourcePool, permission: Permission }。ai语言的guard翻译成Rust宏,进一步展开为对模型调用函数的包装,强制每次调用前经过安全过滤器。这种“规格层—执行层—系统层”的依赖关系,让AI系统每一层都有自己的职责,而不是像现在这样所有逻辑挤在Python里。

当然,这不是说Python毫无优势。做原型、做交互实验、做快速验证,Python依然最快。但AI语言处理的是“正式的系统描述”,应该更接近基础设施,而Rust在这方面的严谨性是加分项。我甚至设想过一个很具体的产品形态:用AI语言写一个“Agent规格文件”,然后通过命令行工具ai run spec.ai编译并执行,通过ai audit spec.ai输出完整的资源清单和守卫清单。这让“AI训练师”“AI安全工程师”这类角色第一次有了一个可以开会评审的“代码级产物”。

6. 从构想到落地:场景、路径与必须直面的边界

聊完了设计,最后讲怎么落地,以及我会保持什么样的怀疑心态。先说场景,我长期看好三个方向。

第一个方向是企业级AI系统的治理与审计。现在的企业汇入AI安全工程师、AI应用与安全工程师后,最头疼的是没法向监管和内部风控演示“这个系统到底怎么运作的”。AI语言天然适合生成审计报告:每个任务有多少资源、多少guard、多少次模型调用,全在一份etxt文件里。安全人员可以像review代码一样review规格,而不是去看一堆散落的prompt。

第二个方向是教育与科研。人工智能方向的课程教材、导论、期末大作业,很容易陷入“调API写小demo”的低水平重复。如果有一门AI语言作为教学载体,学生学的是“如何描述一个AI任务”、“如何定义安全边界”,比盲目堆功能更能培养工程素养。科研论文里用AI语言描述实验设置,也能减少“复现不了”的尴尬——模型版本、检索器参数、交互策略全部以规格形式公开。

第三个方向是多Agent系统的编排。单个Agent用Python写没问题,但几十个Agent、上千个协作任务时,Orchestration层面需要的就是一份可合并、可继承、可版本对比的规格文档。AI语言里task与task之间可以复用契约、组合守卫,这比在代码里互相调函数要清晰得多。

落地路径我建议分四步走,步子太大会摔。第一步,先用Rust内部实现一个Embedded DSL,把task、contract、guard四个核心概念做成宏和结构体,团队内部小范围试用。第二步,根据真实项目反馈把DSL“提纯”——凡是反复手工修改的部分,就值得变成语法糖或独立语法。第三步,实现独立的解析器和编译器前端,输出中间表示,接入Rust agent runtime。第四步,等社区有一定共识,再考虑做IDE插件、LSP服务、以及规格交换标准。

但必须说,这个构想还有三个我现在无法回避的边界。

第一是自然语言与精确约束的矛盾。AI语言想在purpose字段使用自然语言,又想在其他地方保持强约束。如果自然语言理解错了任务,后面所有契约守卫都是空转。所以“意图正确性”会是一个永远需要人来兜底的环节,AI语言不能彻底解决“需求理解错”的问题。

第二是评判标准的不可穷尽性。contract里的expected条件再丰富,也覆盖不了真实世界里千奇百怪的bad case。语言设计者不能因为有了语义验证就放松对测试集、评估集的建设。契约只是“最低要求”,不是“高质量保证”。

第三是标准化阻力。AI语言如果要成为通用标准,必须跟模型厂商、框架社区、安全监管机构都形成共识。这不是技术问题,而是生态博弈。也许最终它不会成为一个独立的编程语言,而是以一种“配置家族”或“交换格式”的形式存在,那也算实现了我一半的目标。

我不会神话这个构想。它在现在的形态下,更像是一个思考框架:当你设计AI系统时,试着先写一份“规格合同”——写明资源、契约、守卫,然后才让工程师去实现。就算AI语言永远不落地,这套思维方式也足以让AI工程项目少踩很多坑。

就我个人在真实Agent项目里的体感来说,当一个系统只有两三个Agent时,用现有语言硬写完全足够;但一旦任务规模超过某个临界点,缺的就不只是更强的执行层,而是一层能让所有人对齐的“描述层”。AI语言能不能成为那层描述,我还没十足把握,但我确信方向没错。至少,从下一次动手写Agent前先写一份“可执行的规格”开始,变化的苗头就已经出现了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进