Skip to content

Latest commit

 

History

History
659 lines (474 loc) · 18.4 KB

File metadata and controls

659 lines (474 loc) · 18.4 KB

English

AIL — AI 驱动的工作流语言

用自然语言描述业务逻辑,让 AI 帮你执行。

文档版本:v1.0


什么是 AIL?

传统代码只能处理确定性逻辑:if score > 5,结果是可预期的。但在 AI 工作流里,很多判断本质上是语义性的——"这个回答够好了吗?""任务完成了吗?"——这些判断需要 AI 来做,不是程序。

AIL 就是为此设计的。它的规则只有一条:

有 @  →  交给 AI 执行(语义判断,结果由 AI 决定)
没有 @  →  程序直接执行(确定性逻辑,和 Python 完全一致)

五分钟上手

下面这个例子让 AI 把一个模糊的任务描述反复打磨,直到没有问题为止:

def upgrade_mission(mission: str) -> str:
    with context(system="你是一个任务规划专家") as ctx:

        prompt analyze = """
            分析任务「{mission}」,列出你不清楚或需要优化的问题
        """

        prompt solve = """
            针对上述问题,给出解答和优化方案
        """

        output = @ask(analyze)                # AI 执行任务,返回 str

        loop max=10 until @judge("以下内容不再有待解决的问题:{output}"):
            output = @ask(solve)              # AI 判断是否继续

        return @ask("总结以上讨论,输出最终的任务提示词")

读一遍就能理解:

  1. 在一段对话里(with context),让 AI 分析任务、找问题
  2. 反复让 AI 解决问题,直到 AI 判断没问题了(loop until @judge
  3. 总结输出

核心构件

@ — AI 操作

所有 AI 操作都以 @ 开头。常用的:

操作 用途 返回值
@ask(prompt) 让 AI 执行任务 str
@judge("条件") 让 AI 做是/否判断 bool
@pick("说明", options=[...]) 让 AI 从选项中选一个 选项类型
@plan("目标") 让 AI 把目标拆成步骤 list[str]
@extract(text, type=T) 从文本里提取结构化数据 T
@eval(content, "标准") 让 AI 打分 float (0~1)
@validate(content, "条件") 断言,不满足就报错 无/异常
@act("说明") AI 自主选择并调用工具 str
@ask_user("提示") 向用户提问,等待输入 str
@confirm("描述") 请求用户确认,拒绝时终止 无/异常
@show("内容") 展示内容给用户(不阻塞)

prompt — 提示词模板

prompt 定义给 AI 用的提示词,{变量} 在调用时自动从当前作用域取值:

# 全局定义,跨函数复用
prompt common_summary = """
    请将以下内容总结为要点列表:{content}
"""

# 局部定义,就近使用(推荐)
def process(query: str) -> str:
    prompt do_analyze = """
        深入分析以下问题,给出结构化答案。
        问题:{query}
    """
    return @ask(do_analyze)   # query 自动从作用域取值

简短的提示词可以直接内联,不必定义 prompt

result = @ask("一句话总结:{text}")

变量插值规则:

变量类型 渲染结果
str / int / float / bool 直接转字符串
list[str] 编号列表:1. a\n2. b
list[type] 每项展开字段的编号列表
type 对象 字段逐行展开
dict key: value 逐行展开

context — 多轮对话

with context() as ctx: 创建一个对话区块,块内所有 @ 操作共享同一段对话历史——AI 能看到前面每一步的内容:

with context() as ctx:
    step1 = @ask(prompt_a)
    step2 = @ask(prompt_b)   # AI 能看到 step1 的内容
    step3 = @ask(prompt_c)   # AI 能看到前两步

创建时可以指定模型和角色:

with context(model="claude-opus") as ctx:               # 指定模型
with context(system="你是一名医疗顾问") as ctx:          # 设置角色
with context(model="claude-opus", system="...") as ctx:  # 同时设置

上下文对象的操作:

ctx.remember("用户偏好简洁回答")   # 注入背景信息(不计入对话轮次)
ctx.reset()                        # 清空对话历史
snapshot = ctx.save()              # 保存当前状态
ctx.restore(snapshot)              # 恢复到某个状态

不需要多轮对话时,直接用 @ask 即可,每次独立调用:

summary = @ask("一句话总结:{text}")   # 无上下文,独立调用

AI 操作详解

@ask — 执行任务

最基础的 AI 操作:

result = @ask(my_prompt)                       # 引用 prompt 模板(推荐)
result = @ask("一句话总结:{text}")            # 短提示词内联
result = @ask(my_prompt, model="claude-opus")  # 临时切换模型

@judge — 是/否判断

直接用在条件和循环里:

if @judge("output 中还有未解决的问题"):
    output = @ask(solve_issues)

ready = @judge("文档是否足够回答「{query}」:{docs}")

@plan — 拆解目标

让 AI 把一个大目标分解成步骤列表,再逐步执行:

steps = @plan("完成目标:{goal},可用工具:{tools}")

# 步骤间传递中间结果(常用模式)
result = ""
for step in steps:
    result = @ask("执行「{step}」,已有结论:{result},参考:{docs}")

@extract — 提取结构化数据

从 AI 的文本输出里提取你需要的结构:

# 提取为自定义类型
info = @extract(analysis, type=QueryInfo)

# 提取为基本类型
keywords = @extract(text, "提取关键词列表", type=list[str])
score    = @extract(text, "提取评分",       type=float)
flag     = @extract(text, "是否需要多步推理", type=bool)

# 语义排序(返回按相关性重排后的列表)
ranked = @extract(docs, "按与问题「{query}」的相关性排序", type=sorted[Document])
top5   = ranked[:5]

@eval — 打分

让 AI 对内容质量评分,返回 0~1 的浮点数:

# 单维度
score = @eval(answer, "回答与问题的相关性")

# 多维度(用 type= 指定字段,返回对象)
quality = @eval(answer, type={"relevance": float, "completeness": float})
if quality.relevance < 0.7:
    answer = @ask("改进回答,当前相关性评分:{quality.relevance}")

@validate — 断言

断言内容满足条件,不满足时抛出错误(配合 retry 使用可以自动重试):

# 单独使用:不满足就终止
@validate(result, "内容必须包含具体数字和来源")

# 配合 retry:不满足就重试整个块
retry max=3:
    result = @ask(generate)
    @validate(result, "内容必须超过 200 字")

控制流

AI 条件判断

@judge 直接用在 if 里,可与普通 Python 条件混用:

if @judge("output 中还有未解决的问题"):
    output = @ask(solve_issues)
elif len(results) == 0:         # 普通条件,同 Python
    output = @ask(try_another_way)
else:
    pass

AI 语义循环

每轮结束后 AI 重新判断是否退出。max= 是安全上限,建议始终设置

loop max=10 until @judge("任务是否已完成:{output}"):
    output = @ask(continue_task)

普通循环(同 Python)

for step in steps:
    result = @ask("执行步骤:{step}")

while not ready:
    output = @ask(continue_task)
    ready = @judge("是否已准备好:{output}")

并行执行

几个互不依赖的任务同时跑,节省时间:

# 工具并行
vec_docs, kw_docs = parallel:
    vec_docs = vector_search(vec_query, top_k=10)
    kw_docs  = keyword_search(kw_query)

# AI 任务并行
r1, r2, r3 = parallel:
    r1 = @ask("从技术角度分析:{content}")
    r2 = @ask("从商业角度分析:{content}")
    r3 = @ask("从用户角度分析:{content}")

summary = @ask("综合三个角度,给出最终结论:{r1} {r2} {r3}")

注意:parallel 块内可以读取外部变量,但不能修改外部变量。


可靠性

AI 调用可能失败,或返回不满足要求的结果。AIL 提供三种应对机制:

重试

@validate 不通过时,自动重试整个块:

retry max=3:
    report = @ask(generate_report)
    @validate(report, "报告必须包含结论、数据和来源")

超时

timeout 30s:
    result = @ask(complex_analysis)
# 超过 30 秒抛出 TimeoutError,支持 s / m / h

降级

任何异常(包括超时、ValidationError)都触发 fallback

try:
    result = @ask(deep_analysis, model="claude-opus")
fallback:
    result = @ask(basic_analysis)

组合使用

try:
    retry max=3:
        timeout 20s:
            result = @ask(generate_report)
        @validate(result, "报告必须完整")
fallback:
    result = @ask(basic_report)

工具、Skill 和插件

AIL 支持三种外部能力,语义不同,各司其职:

类型 说明 适用场景
use tool 确定性函数,无 AI,无状态 搜索、计算、文件读写
use skill 子 Agent,内部可有完整 AI 工作流 复杂子任务、可复用的 Agent
use plugin 带持久状态的外部服务 数据库、日历、邮件服务

tool — 工具函数

use tool vector_search(query: str, top_k: int) -> list[Document]
use tool send_email(to: str, subject: str, body: str) -> bool

# 声明后像普通函数一样调用
docs = vector_search("深度学习", top_k=10)
ok   = send_email("user@example.com", "报告", report)

skill — 子 Agent

内部可以有完整的 AI 工作流,对外像普通函数一样调用:

use skill summarizer(text: str) -> str
use skill rag_agent(query: str) -> (str, list[str])

summary = summarizer(text=article)
answer, citations = rag_agent(query=user_query)

plugin — 带状态的服务

有持久状态,通过 .方法名() 调用:

use plugin calendar
use plugin database as db

events = calendar.get_events(date="2026-04-10")
calendar.create_event(title="周会", time="14:00")

users = db.query("SELECT * FROM users WHERE active = 1")
db.save(new_record)

让 AI 自主选择工具

注册工具集后,由 AI 决定调用哪个:

use tools [search, calculator, read_file]
result = @act("根据用户问题选择合适的工具并执行:{query}")

记忆系统

memory 是内置对象,让 Agent 能跨会话记住信息。三个操作:

# 存储(key 可选,有 key 才能精确取回;tags 用于分类)
memory.save("用户喜欢简洁的列表式回答", key="user_preference", tags=["preference"])
memory.save(summary, key="last_summary", tags=["history"])
memory.save(user_profile, tags=["user"])   # 没有 key,只能用 search 找到

# 精确取回(按 key)
preference = memory.get("user_preference")

# 语义搜索(AI 驱动,返回最相关的 N 条)
related = memory.search("和 RAG 相关的历史讨论", top_k=5)

在提示词里使用记忆:

preference = memory.get("user_preference")
related    = memory.search("与当前话题相关的历史", top_k=3)

prompt personalized_answer = """
    用户偏好:{preference}
    相关历史:{related}
    基于以下内容回答问题:{query}
"""

result = @ask(personalized_answer)

人机交互

Agent 需要暂停等待用户时:

# 向用户提问(阻塞,等待输入)
extra = @ask_user("请补充更多背景信息")

# 请求用户确认(阻塞,拒绝时终止执行)
@confirm("即将向 {recipient} 发送报告,是否继续?")

# 展示中间结果给用户(非阻塞,不等待)
@show("已找到 {len(docs)} 篇文档,正在生成回答...")

数据结构

type 定义自定义结构,字段支持默认值和行内注释:

type Document:
    id:      str             # 文档唯一标识
    title:   str             # 文档标题
    content: str             # 正文内容
    score:   float = 0.0     # 相关性评分,0~1

type Point:
    x: float
    y: float

访问方式与 Python 完全一致:

doc   = docs[0]
title = doc.title
top5  = docs[:5]

函数定义

与 Python 语法完全一致,支持类型注解、默认参数、多返回值:

# 单返回值
def summarize(text: str, max_words: int = 200) -> str:
    prompt do_summarize = """
        将以下文本总结为不超过 {max_words} 字:{text}
    """
    return @ask(do_summarize)

# 多返回值
def analyze(query: str) -> (str, list[str]):
    with context() as ctx:
        answer   = @ask("回答问题:{query}")
        keywords = @extract(answer, "提取关键词", type=list[str])
    return answer, keywords

# 调用
summary = summarize("很长的一段文本...", max_words=100)
answer, keywords = analyze("什么是向量数据库?")

完整示例:RAG Agent

下面是一个完整的 RAG(检索增强生成)Agent,展示 AIL 大多数特性的综合运用。

use tool vector_search(query: str, top_k: int) -> list[Document]
use tool keyword_search(query: str)            -> list[Document]
use tool rerank(query: str, docs: list[Document]) -> list[Document]

type Document:
    id:      str           # 文档唯一标识
    title:   str           # 文档标题
    content: str           # 正文内容
    score:   float = 0.0   # 相关性评分,0~1

type QueryInfo:
    intent:     str        # 用户的核心意图
    keywords:   list[str]  # 关键词列表
    multi_step: bool       # 是否需要多步推理

def rag_answer(user_query: str, max_rounds=3) -> (str, list[str]):
    with context() as ctx:

        # 第一步:理解用户的问题
        prompt analyze_query = """
            分析用户问题:{user_query}
            请输出:1.核心意图 2.关键词列表 3.是否需要多步推理
        """
        analysis = @ask(analyze_query)
        info = @extract(analysis, type=QueryInfo)

        # 第二步:并行检索(向量 + 关键词同时进行)
        intent   = info.intent
        keywords = info.keywords

        vec_query = @ask("将意图「{intent}」改写为适合向量检索的语句")
        kw_query  = @ask("从关键词「{keywords}」中提取最重要的 3 个")

        vec_docs, kw_docs = parallel:
            vec_docs = vector_search(vec_query, top_k=10)
            kw_docs  = keyword_search(kw_query)

        top_docs = rerank(user_query, vec_docs + kw_docs)[:5]

        # 第三步:文档不够时,继续补充检索
        prompt find_gap = """
            当前文档还缺少哪些信息才能回答「{user_query}」
        """
        loop max=max_rounds until @judge("以下文档能否充分回答「{user_query}」:{top_docs}"):
            gap      = @ask(find_gap)
            new_docs = vector_search(@ask("根据缺口生成补充检索语句:{gap}"), top_k=5)
            top_docs = rerank(user_query, top_docs + new_docs)[:5]

        # 第四步:生成答案(支持单步和多步推理)
        if info.multi_step:
            steps = @plan("针对「{user_query}」制定推理步骤,参考:{top_docs}")
            result = ""
            for step in steps:
                result = @ask("执行「{step}」,已有结论:{result},参考:{top_docs}")
            final_context = result
        else:
            final_context = @ask("整理文档中与「{user_query}」相关的核心内容:{top_docs}")

        # 第五步:生成最终答案,校验质量
        prompt generate_answer = """
            基于以下内容回答用户问题。
            问题:{user_query}
            参考内容:{final_context}
            要求:有理有据,引用来源,不要编造
        """
        retry max=3:
            answer = @ask(generate_answer)
            @validate(answer, "回答必须基于参考内容,不得出现参考内容中没有的事实")

        quality = @eval(answer, type={"relevance": float, "completeness": float})
        if quality.relevance < 0.7 or quality.completeness < 0.7:
            answer = @ask("改进以上回答,使其更相关、更完整,当前评分:{quality}")

        # 第六步:提取引用来源,保存到记忆
        citations = @extract(answer, "提取引用来源,与以下文档对照", type=list[str], context=top_docs)
        memory.save(answer, key=f"rag:{user_query[:20]}", tags=["history"])

    return answer, citations

速查表

AI 操作

操作 返回 说明
@ask(prompt) str 让 AI 执行任务
@judge("条件") bool 让 AI 做是/否判断
@pick("说明", options=[]) 选项类型 让 AI 从选项中选择
@plan("目标") list[str] 让 AI 拆解目标为步骤
@extract(x, ..., type=T) T 提取结构化数据;sorted[T] 语义排序
@eval(x, "标准") float 让 AI 打分(0~1);type={} 多维度
@validate(x, "条件") 无/异常 断言,不满足则终止或重试
@act("说明") str AI 自主选择工具执行
@ask_user("提示") str 向用户提问(阻塞)
@confirm("描述") 无/异常 请求用户确认(阻塞)
@show("内容") 展示内容给用户(非阻塞)

控制流

语句 说明
if @judge(...): AI 条件分支,可与普通条件混用
loop max=N until @judge(...): AI 语义循环,每轮重新判断
while / for 普通循环,同 Python
parallel: 并行执行独立任务
with context(...) as ctx: 创建共享对话上下文

可靠性

语句 说明
retry max=N: 整块重试,直到 @validate 通过
timeout Ns: 超时(s/m/h)
try: / fallback: 出错降级,任何异常触发 fallback

声明与扩展

语句 说明
prompt name = """...""" 定义提示词模板(全局或局部)
type Name: 定义数据结构
use tool func(...) -> T 声明确定性工具函数
use skill func(...) -> T 声明子 Agent skill
use plugin name 声明带状态的外部插件
use tools [...] 注册工具集供 @act 使用
def func(...) -> T: 定义函数

记忆系统

操作 说明
memory.save(内容, key="", tags=[]) 存储记忆,key 可选,有 key 才能精确取回
memory.get("key") 精确读取记忆
memory.search("描述", top_k=N) 语义搜索记忆