在智能体应用、大模型工具链快速普及的背景下,很多团队的瓶颈已经从“模型效果不够好”转移到“AI 应用工程化落地难”。这里面的关键短板之一,就是编程语言和工程框架对 AI 工作负载的支持程度还不够“原生”。紫金会议工业论坛上,“AI 原生语言设计:仓颉的 AI 亲和化探索”这一议题,正好切中了这个方向。

本文不打算做会议内容的逐段转述,而是以这个议题为引子,梳理清楚几个问题:什么是 AI 原生语言?为什么传统语言写 AI 应用会感觉别扭?仓颉语言在 AI 亲和化上做了哪些探索?以及作为普通开发者,我们该如何借助这类能力提升 AI 应用的工程质量。文章会包含可参考的示意代码、工程闭环设计和踩坑思路,适合正在用大模型 API 做应用开发,又想了解“语言层能帮我们做什么”的读者。

1. 背景与核心概念

1.1 什么是 AI 原生语言

AI 原生语言这个概念,现在还没有一个绝对统一的学术定义。但从工程实践来看,它通常指:一门编程语言在语法、类型系统、并发模型、标准库和工具链层面,不是“事后补丁式”地支持 AI 开发,而是从设计之初就把大模型调用、张量计算、工具调用、流式输出、结构化输出、评测与可观测性等场景作为一等公民。

传统通用语言当然也能做 AI 应用开发。你完全可以用 Java 写一个 HTTP 接口,再调用 OpenAI、文心、通义等模型接口。但你会发现,大量代码都花在了“翻译”和“适配”上:把用户输入拼成 Prompt、把模型返回的 JSON 解析成对象、把流式响应拼装成 SSE 协议、再处理超时和重试。语言本身没有为这些高频操作提供太多帮助。

AI 原生语言的思路则相反。它希望把“模型调用”这种操作抽象成语言内的一个自然表达,让开发者写的是业务逻辑,而不是胶水代码。

1.2 为什么需要“AI 亲和化”

“AI 亲和化”是比“AI 原生”更温和、更务实的说法。它的意思是:不一定要从零发明一套颠覆性语言,而是在已有语言基础上,针对 AI 开发场景做语法、类型、并发、工具链上的系统性优化,让开发者写 AI 应用时更少踩坑、更多复用、更容易验证。

用仓颉语言作为讨论对象很有意思。仓颉本身是一门面向全场景、强调高性能和多范式融合的编程语言。它不是一个只服务于 AI 的“玩具语言”,而是一个通用系统编程语言。在这个前提下探索 AI 亲和化,就比“专门造一门 AI 语言”更贴近工业级落地。

社区里对“开源仓颉2.0”“仓颉skill”等话题的讨论也很活跃。虽然很多能力还在快速演进中,但至少说明一个趋势:语言的 AI 亲和化,已经是语言设计层面需要正面回应的问题了。

1.3 AI 亲和化不等于自动生成代码

需要先澄清一个误区:AI 亲和化并不是指“语言能用中文写代码”或者“IDE 能自动补全代码”。所谓亲和,强调的是“AI 应用形态”与“语言能力”的匹配。

举个例子:如果你要做一个智能客服 Agent,这个 Agent 需要判断用户意图、调用查询函数、再生成回复。用传统方式,这个流程要拆成多个 if-else 分支、状态机、上下文对象,加上工具调用的数据结构定义。用 AI 亲和的语言设计,我们更希望:

  • 类型系统能约束模型输出,而不是让 JSON Schema 和代码类型脱节;
  • 工具调用能通过函数签名自动生成,而不是手写一堆参数说明;
  • 流式输出能像普通迭代器一样使用,而不是手动处理缓冲区。

这些才是 AI 亲和化探索的核心问题。

2. 传统语言开发 AI 应用时的几道坎

在理解仓颉的探索之前,先看看用 Java、Python、Go 等主流语言写 AI 应用时,普遍会遇到哪些不顺手的地方。

2.1 类型与契约的脱节

大模型接口返回的数据,本质上是非结构化文本或 JSON。模型返回内容是否符合预期,运行时才知道。

常见做法是:定义一个 DTO 类,然后用 Jackson、Gson 或 Pydantic 做反序列化。但这里存在两个问题:

  • 模型的输出格式如果和代码定义不一致,运行期才会抛异常;
  • Prompt 中的格式说明和代码里的类型定义是两份“契约”,很容易不同步。

一旦 Prompt 改了,要求模型返回新字段,代码 DTO 也要跟着改。如果忘记同步,线上就会出现大量反序列化失败。

2.2 异步与流式处理的复杂度

现在主流大模型接口都支持流式输出(SSE)。这在体验上很好,字符一个个蹦出来,用户不用干等。但对服务端开发来说,流式处理意味着:

  • 需要处理 SSE 协议解析;
  • 需要把多个事件块拼成完整内容;
  • 需要处理连接中断、超时、取消;
  • 需要把流式内容转发给客户端时保持低延迟。

这些逻辑跟业务没有直接关系,但占了很大代码量。语言如果没有良好的协程或异步支持,写起来非常容易出并发问题。

2.3 工具调用和结构化输出的“手工劳动”

Agent 应用经常需要模型调用外部函数,例如查询天气、查数据库、调订单服务。技术上需要在请求里给出工具定义(一般是 JSON Schema),然后根据模型返回的工具调用参数,路由到对应函数。

这个过程里,下面几步都非常繁琐:

  • 手写工具函数的 JSON Schema;
  • 把模型返回的参数 JSON 反序列化成函数真实参数;
  • 校验参数类型、必填项、枚举值;
  • 把函数执行结果拼回 Prompt,再让模型生成最终回复。

每一步都有出错风险,而且错误往往要等到线上运行才能暴露。

2.4 上下文和会话状态的混乱

聊天类应用需要保存上下文。实现时,要么把消息列表存在 Session 里,要么用 Redis 缓存,要么直接传给模型。无论哪种方式,开发者都要自己管理消息数组的拼接、截断、压缩。

当应用升级到多 Agent 协作时,上下文管理会变得更复杂:每个 Agent 看到什么消息、哪些消息可以传播、哪些工具结果要带回主对话。没有语言层或框架层抽象,这些代码会迅速腐化。

2.5 评测与可观测性缺失

传统业务代码,写完可以跑单测断言结果。但 AI 应用的输出没有标准答案,不能简单断言“相等”。需要一套评测集、相似度判断、人工评估机制。

同时,大模型调用涉及 Token 消耗、延迟、模型版本、Prompt 版本等多维信息,传统日志系统很难把一次智能体决策的完整链路还原出来。

3. 仓颉 AI 亲和化设计的关键切入点

结合工业论坛议题和 AI 工程实践,仓颉语言的 AI 亲和化探索,大概率会围绕下面几个方向展开。需要说明的是,这部分更多是结合 AI 原生语言应有能力做的梳理,不一定是仓颉当前已发布功能的完整说明。

3.1 原生张量与数值计算支持

AI 应用绕不开向量、矩阵、Embedding 等数值计算。传统做法是在语言里引入 NumPy、TensorFlow、PyTorch 等外部库。AI 亲和化的语言设计,会把多维数组、张量运算、自动求梯度等能力下沉到语言运行时或标准库中。

这样做的好处是:

  • 类型系统可以感知张量形状,编译期发现维度不匹配;
  • 数值计算不再依赖重量级运行时,可以做到更轻量;
  • 跨设备调度(CPU、GPU、NPU)能成为语言能力的一部分。

比如写一个向量点积,传统语言可能要 import 库并考虑数据对齐;AI 亲和语言可以像写普通算术表达式一样,同时对边界做静态检查。

3.2 类型安全的模型上下文

这是很关键的差异化设计。语言如果能把“模型调用”看成一等公民,就可以引入类似下面的抽象:

  • Prompt 模板不再只是字符串拼接,而是带类型的模板;
  • 上下文消息有专门的类型,区分 user、assistant、tool、system;
  • 模型返回结果绑定到声明式类型上,编译期能发现字段不匹配。

这意味着,当你声明一个 FormatResult 类型并要求模型按此输出时,语言工具链会生成对应的 JSON Schema,并且在反序列化时做严格校验。Prompt 和代码不再脱节。

3.3 声明式的工具调用

AI 亲和化最值得期待的一部分,是工具调用的“函数式声明”。

设想一下,如果语言运行库能识别哪些函数可以被模型调用,并自动生成工具描述,那么开发者就只需要写一个普通函数:

func getWeather(city: String): WeatherInfo {
    // 业务实现
}

框架自动把函数签名、参数注释、返回类型转换成 JSON Schema 发送给模型。模型返回“调用 getWeather,参数 city=北京”后,语言运行时自动完成解析和调用。整个链路对开发者是透明的。

这种设计极大减少了胶水代码,也让函数复用变得简单:你的业务函数不用知道模型的格式要求,它只是普通的、可以被 AI 调用的能力单元。

3.4 流式输出与协程深度结合

仓颉语言本身在多范式和高性能方面下了不少功夫。AI 亲和化设计会把流式输出和协程结合,让开发者像“遍历集合”一样处理模型输出。

例如:

for await (chunk in model.stream(prompt)) {
    handle(chunk)
}

模型输出被抽象为异步迭代器。开发者不需要关心网络缓冲、断点重连、取消信号,语言框架负责把这些底层能力封装好。

3.5 多智能体编排的语言级支持

单次模型调用只是 AI 应用的基础。真正的工业应用往往是多个智能体协作:一个 Agent 负责拆解问题,另一个负责检索,第三个负责生成回答。

语言级支持多智能体编排,会让代码更接近“业务流程描述”:

  • 智能体是独立执行单位,有输入、输出、状态;
  • 智能体之间通过消息传递协作;
  • 编排规则可以通过声明式语法表达,而不是嵌套回调。

当然,这个方向目前还在探索中。多 Agent 的调试、可观测、安全控制,比单 Agent 复杂得多。

3.6 编译期检查带来的可靠性

传统语言里,AI 调用的错误只能靠运行时 try-catch。AI 亲和化语言如果能把部分校验提前到编译期,价值会非常大。

例如:

  • Prompt 模板的变量是否都填充了;
  • 工具函数的参数类型是否与模型声明一致;
  • 流式处理的取消分支是否被正确传播;
  • 上下文消息类型是否被非法混用。

这些原本依赖开发纪律的问题,如果能在编译期拦截,能减少大量线上事故。

4. AI 亲和化开发流程的示意示例

这一节我们用“仓颉风格的示意代码”来表达 AI 亲和化的设计思想。再次强调:下面代码是为了展示语言设计思路,并不代表仓颉当前已发布的完整 API。实际开发时请以官方文档为准。

4.1 创建项目结构

这里先约定一个简化后的项目目录:

ai-demo/
├── src/
│   ├── main.cj
│   ├── weather.cj
│   └── model.cj
├── config/
│   └── model.toml
└── tests/
    └── evaluate.cj
  • main.cj 是入口;
  • weather.cj 定义天气查询工具函数;
  • model.cj 封装模型调用逻辑;
  • config/model.toml 配置模型名称、温度、超时;
  • tests/evaluate.cj 存放简单的评测用例。

4.2 定义一个带类型的模型调用任务

以“从用户输入中提取结构化信息”为例。传统方式需要手写 Prompt 加 JSON Schema。AI 亲和化设计希望代码像下面这样直接表达:

// 示意代码,不代表仓颉当前已发布的真实 API
public struct OrderExtract {
    var orderNo: String
    var customerName: String
    var amount: Decimal
}

public func extractOrder(text: String): OrderExtract {
    let result: OrderExtract = model.complete[OrderExtract](
        prompt: "从下面文本中提取订单信息:{input}",
        input: text
    )
    return result
}

关键点在于: model.complete[OrderExtract] 是一种“泛型 + 类型参数”的写法。语言工具链会根据 OrderExtract 的字段结构自动生成 JSON Schema,并在模型返回后自动做类型校验和转换。

如果模型返回的 JSON 缺少 orderNo 字段,或 amount 不是数字,这里可以在进入业务逻辑前就抛出明确的类型异常,而不是等到业务用到该字段时才发现。

4.3 声明一个可被模型调用的工具函数

假设我们的智能体需要查询天气。传统做法是手写工具描述 JSON,AI 亲和化设计可以让普通函数自动变成模型可调用的工具:

// 示意代码,不代表仓颉当前已发布的真实 API
@ModelTool(description: "查询指定城市的实时天气")
public func getWeather(city: String, unit: String = "celsius"): WeatherInfo {
    // 实际业务:调用后端服务或第三方 API
    return WeatherService.query(city: city, unit: unit)
}

这里使用了 @ModelTool 标注。框架扫描到这个标注后,会做两件事:

  1. 根据函数签名和注释生成 JSON Schema;
  2. 在运行时解析模型返回的“工具调用参数”,并自动绑定到该函数。

开发者不需要再手写参数说明,也不需要手工 map 参数名。函数本身保持普通、可测试、可复用。

4.4 处理流式输出

对话场景通常需要边生成边返回。AI 亲和化语言可以把模型流式输出抽象成异步迭代器:

// 示意代码,不代表仓颉当前已发布的真实 API
public func chatStream(messages: Array<Message>) {
    for await chunk in model.stream(messages: messages) {
        // 将增量内容推送给前端
        sendToClient(chunk.text)
    }
}

model.stream 返回一个异步可迭代对象。语言运行时负责:

  • 建立 SSE 连接;
  • 持续接收数据块;
  • 处理超时和网络中断;
  • 支持调用方取消。

4.5 运行与验证:建议的验证步骤

如果使用的是一个真实支持以上语法的语言工具链,验证流程大致如下:

  1. 编写编译测试,确认类型错误能否在编译期被拦截;
  2. 编写单元测试,使用 mock 模型返回数据验证 OrderExtract 的解析逻辑;
  3. 本地运行一个 Agent 交互脚本,确认工具调用链路正常;
  4. 用评测集跑一轮批量测试,记录准确率和 Token 消耗。

虽然当前仓颉的具体实现可能还没有开放这些 API,但上面流程适合作为你评估任何“AI 友好框架”时的通用验证清单。

5. 从语言到工程:AI 应用的完整闭环

语言设计解决的是“写起来顺不顺”的问题,工程落地还需要一整套配套机制。这里把 AI 应用从开发到交付的关键环节拆开看。

5.1 数据准备与结果缓存

AI 应用不是只有模型调用。很多场景下,请求数据要经过清洗、去重、向量化,再交给模型。为了降低成本和延迟,缓存策略非常重要。

建议的使用顺序:

  1. 先查本地缓存(例如 Redis);
  2. 缓存未命中,再判断是否需要检索 RAG 知识库;
  3. 组合上下文后调用模型;
  4. 返回值同时写入缓存,设置合理的过期时间。

缓存键不能只基于用户输入原文,还应该包含模型版本、Prompt 版本。否则模型升级后,可能命中旧缓存,导致行为不一致。

5.2 评测集与回归

AI 应用最怕“模型一升级,业务全崩了”。这需要建立评测集:

  • 每类典型输入至少准备 20~50 条样例;
  • 标注预期结果或可接受的结果范围;
  • 对输出做多维评估:准确率、相关性、格式合规、是否触发安全策略;
  • 每次修改 Prompt、升级模型、调整参数时,都跑一次回归。

语言层如果能提供“结构化输出类型”,评测集就可以自动校验输出是否符合类型要求,节省大量精力。

5.3 可观测性与链路追踪

传统链路追踪解决“一次请求经过哪些服务”。AI 应用的追踪还需要额外记录:

  • Prompt 内容;
  • 模型返回内容;
  • Token 消耗;
  • 工具调用输入输出;
  • 延迟分布;
  • 模型版本、Prompt 版本;
  • 缓存命中情况。

建议把每次模型调用建模成一个 span,写入统一的追踪系统。问题排查时,按 requestId 把整条链路拉出来,就能看到是哪一步产生了错误或耗时异常。

5.4 安全与权限

调用大模型时,尤其要注意注入和越权:

  • 用户输入内容可能包含恶意指令,需要考虑提示注入防护;
  • 工具函数不要暴露高权限操作,比如删除用户数据、执行系统命令;
  • 涉及用户隐私的内容,脱敏后再传入模型;
  • 模型输出也不能直接渲染到页面,要做 XSS 过滤;
  • 工具调用必须有审计日志,记录“模型在什么上下文下调用了什么函数”。

这些约束如果能在语言层用类型系统表达,例如“只有标记为安全的函数才允许被模型调用”,工程约束会可靠得多。

5.5 部署与版本化

AI 应用部署时,模型 API 的版本、Prompt 的版本、代码版本必须一起管理。

推荐做法:

  • 把 Prompt 作为配置文件,而不是硬编码在代码里;
  • 使用配置中心管理模型名称、温度、超时参数;
  • 线上发布时同时记录模型版本和 Prompt 版本;
  • 准备灰度开关,方便在模型效果异常时快速回退。

6. 常见问题与排查思路

AI 应用开发中,很多问题表现相似,但根源不同。下面整理一张排查表:

问题现象 常见原因 解决思路
模型返回内容解析失败 模型输出格式与代码类型不一致 检查 Prompt 中的格式说明,确认代码结构体字段;引入更严格的输出约束
工具调用的参数缺失 模型没有按 JSON Schema 返回完整字段 检查工具描述是否清晰;增加必填字段校验;必要时用示例参数
流式输出到一半断开 网络超时或服务端主动断开 增加重试和断点续传机制;检查代理和超时配置
上下文越变越长,费用飙升 没有做上下文截断和压缩 设置最大轮数;对历史消息做摘要压缩;按 Token 数控制
缓存命中但是结果不准确 缓存键没有包含 Prompt 版本或模型版本 优化缓存键设计;重要结果不做长期缓存
Agent 调用链路过深,无法定位问题 缺少链路追踪 建立请求级追踪;把模型调用、工具调用都打点记录
模型升级后行为变化 模型版本漂移 使用版本固定的模型;先在评测集跑回归再切换线上
用户输入包含恶意指令 Prompt 注入 引入输入过滤;对系统提示做加固;危险操作禁止暴露为工具

排查时,建议先看链路追踪,确认问题出现在“输入处理、模型调用、工具调用、输出处理”哪一个环节,再针对性解决。不要一上来就改 Prompt。

7. 最佳实践与工程建议

7.1 让语言层帮你守住契约

如果你使用的语言或框架支持结构化输出、类型推断、编译期校验,一定要用起来。与其在运行时做防错,不如在编译期消灭错误。

具体操作:

  • 确认框架是否支持从类型定义自动生成 JSON Schema;
  • 确认返回结果能否绑定到强类型对象;
  • 确认工具函数是否能通过函数签名自动暴露给模型。

如果框架不支持,也要在代码里封装一个语义层,把模型交互收敛到少数几个文件里,避免散落各处。

7.2 工具函数要“小而纯”

可被模型调用的函数,尽量做到:

  • 输入输出简单清晰;
  • 没有隐藏副作用;
  • 不做高风险操作;
  • 参数有明确枚举或格式说明。

函数越简单,模型越容易正确调用。复杂逻辑应放在函数内部,而不是暴露给模型做判断。比如不要暴露 executeSql(sql: String) ,而要暴露 queryOrderByNo(orderNo: String)

7.3 把评测纳入 CI 流程

AI 应用的 CI 不应该只跑单元测试。更完善的做法是:在合并代码前,自动跑一组精简评测集,验证 Prompt 修改或模型切换不会导致核心场景回归。

评测集不用一开始做得很大,可以从高频场景开始,比如:

  • 客服意图识别;
  • 订单信息抽取;
  • 工具调用准确性;
  • 敏感内容拦截。

后续发现问题,再把失败用例加入评测集,持续迭代。

7.4 预留人工兜底

无论模型效果多好,生产环境都必须预留人工兜底入口。比如:

  • 客服 Agent 提供“转人工”开关;
  • 内容生成类应用提供审核机制;
  • 自动化工具调用前,关键操作需要用户确认。

AI 应用的价值是提效,不是替人做最终决策。特别是涉及扣款、发消息、删数据等操作,必须人工确认。

7.5 关注 Token 成本增长曲线

AI 应用上线后,Token 消耗会随着用户量增长快速上升。建议提前做好:

  • 请求级成本监控;
  • 按用户维度的额度限制;
  • 缓存策略优化;
  • 模型分级调用(简单问题用小模型,复杂问题用大模型)。

一个简单的分流规则就能节约不少成本:判断用户问题是否需要复杂推理,不需要时走轻量模型,需要时再升级到大模型。

8. 总结与学习路线

这篇内容从“AI 原生语言”和“AI 亲和化”的概念切入,梳理了传统语言开发 AI 应用的五个痛点,分析了仓颉作为一门高性能通用语言,在 AI 亲和化方向上可能的设计切入点:类型安全的模型上下文、声明式工具调用、流式输出、多智能体编排、编译期检查。同时给出了 AI 应用从工程关闭到部署交付的完整闭环思路。

如果你现在刚开始接触 AI 应用开发,可以按下面路线继续深入:

  1. 先掌握一门主流语言的异步编程和 HTTP 调用基础;
  2. 了解大模型 API 的基本用法,包括普通对话、流式输出、工具调用;
  3. 用一个小项目把“提取结构化信息 + 工具调用 + 流式输出”完整跑通;
  4. 学习 Agent 编排框架,理解多智能体协作的常见模式;
  5. 建立评测集和可观测性体系,逐步完善工程化能力;
  6. 关注仓颉等新兴语言在 AI 亲和化上的后续进展,保持对新语言特性的敏感度。

语言层的能力会在未来几年持续演进,但工程化的核心并不变:快一点、稳一点、看得见一点。把类型契约、评测、链路追踪、安全兜底这些基本功做扎实,再配合更亲和的开发语言,AI 应用才能真正从“能跑”走向“好用”。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐