LLM导购生成:如何让AI不说谎、不编造、不乱推荐

LLM导购生成:如何让AI不说谎、不编造、不乱推荐

受控生成:给AI戴上紧箍咒

AI"胡说八道"的一天

用户问:"有没有适合敏感肌的洗面奶?"

AI回答

"为您推荐以下商品:

  1. 资生堂温和氨基酸洗面奶

    • 采用日本进口氨基酸配方
    • 富含玻尿酸和神经酰胺
    • 原价¥299,现在限时¥199!
    • 库存充足,下单即发货
  2. 兰蔻敏感肌修护洁面乳

    • 添加独家专利成分X-PRO
    • 获得美国FDA认证
    • 特价¥399,买二送一!"

问题来了:

  1. ❌ 系统里根本没有资生堂这款商品(AI编造的)
  2. ❌ "神经酰胺"、"X-PRO"都是AI瞎编的成分
  3. ❌ 价格完全是臆造的,实际价格是¥259
  4. ❌ "买二送一"促销活动并不存在
  5. ❌ 库存其实只剩2件了

这就是典型的LLM幻觉(Hallucination)问题:AI会自信满满地编造事实。

在电商场景,这是致命的:

  • 推荐不存在的商品 → 用户投诉
  • 编造价格信息 → 法律风险
  • 虚假库存 → 客服爆炸

那么,如何让AI只基于真实数据回答,不编造、不胡说?

这就是"受控生成"(Controlled Generation)要解决的问题。

受控生成的核心思路

传统LLM生成(无约束)

ini 复制代码
prompt = f"用户查询:{query}"
response = llm.invoke(prompt)
# AI自由发挥,天马行空

问题:

  • ❌ AI会调用训练数据中的"常识"(但可能过时或错误)
  • ❌ AI会"脑补"细节(编造成分、功效)
  • ❌ AI会生成看起来合理但完全虚假的信息

受控生成(强约束)

ini 复制代码
prompt = f"""
用户查询:{query}

【你只能使用以下商品信息】:
- 商品A:标题、描述、价格、库存
- 商品B:标题、描述、价格、库存

【严格规则】:
1. 只能推荐给定的商品
2. 不得编造任何信息
3. 价格必须使用提供的数据
4. 库存状态必须准确
"""
response = llm.invoke(prompt)
# AI被"约束"在给定的信息范围内

关键区别:

维度 无约束生成 受控生成
信息来源 LLM训练数据 + 想象 提供的上下文数据
准确性 不可控 可验证
幻觉率 高(20-40%) 低(<5%)
法律风险
用户信任

五层防护机制

我们设计了五层防护,确保AI不会乱说话。

第一层:严格的System Prompt

这是AI的"行为准则":

ini 复制代码
system_prompt = """
你是一个专业的电商商品导购助手。

【严格遵守的约束】:
1. 只能推荐给定的商品,不得编造或提及未提供的商品
2. 不得编造商品功效、成分或特性
3. 价格、库存、促销信息必须严格使用提供的实时数据,不得猜测
4. 推荐理由必须可回溯到商品的具体字段(标题、描述、标签等)
5. 如果某商品缺货,必须明确告知用户

【回答格式】:
1. 简短理解用户需求
2. 推荐2-3款最合适的商品,每款商品包含:
   - 商品名称
   - 推荐理由(基于商品特性)
   - 价格和促销信息
   - 库存状态
3. 给出选择建议

保持专业、友好的语气,回答简洁明了。
"""

关键点:

  • ✅ 明确禁止编造
  • ✅ 强制使用提供的数据
  • ✅ 要求可追溯性
  • ✅ 规定具体格式

第二层:结构化上下文

不是简单地把商品信息扔给AI,而是精心格式化:

python 复制代码
def _format_product_info(
    self,
    product_context: List[ProductContext],
    real_time_data: Dict[str, ProductRealTimeData]
) -> str:
    """格式化商品信息"""
    info_list = []
    
    for ctx in product_context:
        sku_id = ctx.sku_id
        rt_data = real_time_data.get(sku_id)
        
        # 清晰标注每个字段
        info = f"【{ctx.title}】\n"
        info += f"SKU: {sku_id}\n"
        info += f"商品描述: {ctx.description}\n"
        info += f"产品亮点: {', '.join(ctx.highlights)}\n"
        
        if ctx.instructions:
            info += f"使用说明: {ctx.instructions}\n"
        
        # 实时数据(价格、库存)单独标注
        if rt_data:
            info += f"当前价格: ¥{rt_data.price}\n"
            info += f"库存状态: {'有货' if rt_data.stock > 0 else '缺货'}({rt_data.stock}件)\n"
            if rt_data.promotion:
                info += f"促销活动: {rt_data.promotion}\n"
        
        info_list.append(info)
    
    return "\n---\n".join(info_list)

示例输出:

makefile 复制代码
【美赞臣安儿宝A+婴幼儿配方奶粉3段】
SKU: SKU_1001
商品描述: 美国原装进口,添加DHA和ARA,支持宝宝智力发育
产品亮点: 进口奶源, DHA配方, 易消化
使用说明: 每次30ml温水加一勺奶粉,摇匀后喂养
当前价格: ¥259.0
库存状态: 有货(45件)
促销活动: 限时8折

---

【惠氏启赋蓝钻婴幼儿奶粉3段】
SKU: SKU_1002
商品描述: 爱尔兰原装进口,添加OPO结构脂,亲和人体更易吸收
产品亮点: 进口奶源, OPO结构脂, 促进吸收
当前价格: ¥368.0
库存状态: 有货(12件)

为什么要这样格式化?

  1. 字段明确标注:AI知道哪些是标题、哪些是价格
  2. 实时数据突出:价格和库存单独标注,避免AI猜测
  3. 结构清晰 :用分隔符---区分不同商品
  4. 信息完整:所有必要信息都提供,AI不需要"脑补"

第三层:结构化输出(Structured Output)

传统方式的问题:

ini 复制代码
# AI可能返回任意格式
response = """
我推荐美赞臣奶粉!它有DHA配方,价格才199元,超划算!
还有惠氏的也不错,促销价299。
"""
# 怎么提取SKU?怎么验证价格?

结构化输出方案:

python 复制代码
from pydantic import BaseModel, Field

class LLMGenerateResponse(BaseModel):
    """强制LLM返回特定结构"""
    response: str = Field(description="生成的导购回答")
    recommended_skus: List[str] = Field(description="推荐的SKU ID列表")

# LangChain 1.1.0新特性
structured_llm = llm.with_structured_output(LLMGenerateResponse)

# AI必须返回这个结构
result: LLMGenerateResponse = structured_llm.invoke(messages)

# 类型安全,直接访问
print(result.response)
print(result.recommended_skus)  # ['SKU_1001', 'SKU_1002']

优势:

  • ✅ 强制AI返回特定格式
  • ✅ 类型安全,避免解析错误
  • ✅ 便于后续验证(知道AI推荐了哪些SKU)

第四层:多维度校验

即使有了约束,AI仍可能"打擦边球"。所以需要机器校验:

python 复制代码
def _validate_response(
    self,
    response_text: str,
    recommended_skus: List[str],
    valid_skus: Set[str],
    real_time_data: Dict[str, ProductRealTimeData]
) -> None:
    """多维度校验生成内容"""
    
    # 1️⃣ 校验SKU合法性
    invalid_skus = set(recommended_skus) - valid_skus
    if invalid_skus:
        raise ValidationError(f"LLM推荐了未提供的商品: {invalid_skus}")
    
    # 2️⃣ 校验价格信息
    price_pattern = r'¥\s*([\d,]+(?:.\d+)?)'
    found_prices = re.findall(price_pattern, response_text)
    
    for price_str in found_prices:
        price = float(price_str.replace(',', ''))
        valid_prices = [rt.price for rt in real_time_data.values()]
        
        if price not in valid_prices:
            raise ValidationError(f"发现未授权的价格: ¥{price}")
    
    # 3️⃣ 校验库存状态
    if '有货' in response_text or '缺货' in response_text:
        for sku, rt_data in real_time_data.items():
            if sku in response_text:
                expected_status = '有货' if rt_data.stock > 0 else '缺货'
                # 验证库存描述是否匹配
                if expected_status == '缺货' and '有货' in response_text:
                    raise ValidationError(f"商品{sku}已缺货,但生成文本说有货")
    
    # 4️⃣ 可扩展:校验成分、功效等
    # ...

校验流程:

markdown 复制代码
AI生成回答
    ↓
提取推荐的SKU → 验证是否在给定范围
    ↓
提取价格 → 验证是否匹配实时数据
    ↓
提取库存描述 → 验证是否准确
    ↓
全部通过 → 返回给用户
    ↓
任何失败 → 触发Fallback

第五层:Fallback降级机制

如果AI生成失败或校验不通过,使用模板生成:

python 复制代码
def _generate_fallback_response(
    self,
    query: str,
    product_context: List[ProductContext],
    real_time_data: Dict[str, ProductRealTimeData]
) -> str:
    """基于模板的安全降级方案"""
    
    response = f"根据您查询"{query}",为您推荐以下商品:\n\n"
    
    for i, ctx in enumerate(product_context[:3], 1):
        rt_data = real_time_data.get(ctx.sku_id)
        
        response += f"{i}. {ctx.title}\n"
        response += f"   特点: {', '.join(ctx.highlights[:3])}\n"
        
        if rt_data:
            response += f"   价格: ¥{rt_data.price}"
            if rt_data.promotion:
                response += f" ({rt_data.promotion})"
            response += "\n"
            
            if rt_data.stock > 0:
                response += f"   库存充足\n"
            else:
                response += f"   暂时缺货\n"
        
        response += "\n"
    
    return response.strip()

Fallback的特点:

  • ✅ 完全基于模板,零幻觉风险
  • ✅ 格式固定,质量稳定
  • ✅ 所有信息都可追溯
  • ❌ 缺乏个性化和灵活性

何时触发Fallback?

python 复制代码
try:
    # AI生成
    llm_response = structured_llm.invoke(messages)
    
    # 校验
    self._validate_response(...)
    
    return llm_response
    
except (Exception, ValidationError) as e:
    print(f"LLM生成失败: {e},使用Fallback")
    return self._generate_fallback_response(...)

实际生成效果

来看一个完整的生成案例。

输入数据

用户查询: "不上火的奶粉,适合6个月宝宝"

商品上下文:

ini 复制代码
product_context = [
    ProductContext(
        sku_id="SKU_1001",
        title="美赞臣安儿宝A+婴幼儿配方奶粉3段",
        description="美国原装进口,添加DHA和ARA,支持宝宝智力发育",
        highlights=["进口奶源", "DHA配方", "易消化", "温和配方"],
        instructions="每次30ml温水加一勺奶粉,摇匀后喂养"
    ),
    ProductContext(
        sku_id="SKU_1002",
        title="惠氏启赋蓝钻婴幼儿奶粉3段",
        description="爱尔兰原装进口,添加OPO结构脂,亲和人体更易吸收",
        highlights=["进口奶源", "OPO结构脂", "促进吸收", "低乳糖"],
        instructions="温水冲调,避免过热破坏营养"
    )
]

real_time_data = {
    "SKU_1001": ProductRealTimeData(
        price=259.0,
        stock=45,
        promotion="限时8折"
    ),
    "SKU_1002": ProductRealTimeData(
        price=368.0,
        stock=12,
        promotion=None
    )
}

AI生成的回答

markdown 复制代码
根据您的需求,为您推荐以下适合6个月宝宝的温和配方奶粉:

1. **美赞臣安儿宝A+婴幼儿配方奶粉3段**
   推荐理由:这款奶粉采用温和配方,添加DHA和ARA支持宝宝智力发育,
   进口奶源品质有保障,易消化的特点特别适合宝宝娇嫩的肠胃。
   
   当前价格:¥259(限时8折优惠)
   库存状态:库存充足(45件)
   
2. **惠氏启赋蓝钻婴幼儿奶粉3段**
   推荐理由:爱尔兰进口奶源,添加OPO结构脂,更亲和人体,促进营养吸收。
   低乳糖配方温和不刺激,适合敏感宝宝。
   
   当前价格:¥368
   库存状态:库存有限(12件)

选择建议:如果您更看重性价比,美赞臣A+正在限时8折,是不错的选择。
如果希望更温和、更易吸收,惠氏启赋的OPO配方值得考虑。

校验通过:

  • ✅ 推荐的SKU都在给定范围内
  • ✅ 价格信息准确(259和368)
  • ✅ 库存描述准确(45件=库存充足,12件=库存有限)
  • ✅ 促销信息准确(8折)
  • ✅ 推荐理由可追溯到商品特性

对比:无约束生成

如果没有约束,AI可能这样回答:

markdown 复制代码
为您推荐以下不上火的奶粉:

1. **美赞臣安儿宝A+**
   含有独家专利的益生元配方,能有效防止宝宝上火便秘。❌ 编造"益生元"
   临床研究表明,92%的宝宝使用后排便顺畅。❌ 编造数据
   
   特价¥199!❌ 价格错误(实际259)
   全国包邮,48小时送达!❌ 编造服务

2. **惠氏启赋**
   添加天然乳脂球膜,模拟母乳成分。❌ 夸大功效
   荣获2023年度最佳婴儿奶粉奖。❌ 编造奖项
   
   原价¥499,现价¥299!❌ 价格错误(实际368)

3. **雀巢能恩** ❌ 系统里根本没有这款商品!
   瑞士原装进口,100%有机奶源...

看到区别了吗?无约束的AI会:

  • 编造成分和功效
  • 瞎编价格和促销
  • 推荐不存在的商品
  • 制造虚假数据

核心代码实现

完整的生成服务实现:

python 复制代码
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field

class LLMGenerateResponse(BaseModel):
    """结构化输出"""
    response: str = Field(description="导购回答")
    recommended_skus: List[str] = Field(description="推荐的SKU列表")

class LLMGenerateService:
    def __init__(self, llm: ChatOpenAI):
        self.llm = llm
        
        # System Prompt(约束)
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """你是专业的电商导购助手。
            
【严格约束】:
1. 只能推荐给定的商品
2. 不得编造信息
3. 价格、库存必须使用提供的数据
4. 推荐理由必须可追溯
5. 缺货商品必须明确告知

【回答格式】:
1. 理解用户需求
2. 推荐2-3款商品(名称、理由、价格、库存)
3. 给出选择建议"""),
            ("human", "用户查询: {query}\n\n商品信息:\n{product_info}")
        ])
    
    def generate(self, input_data):
        # 1. 格式化商品信息
        product_info = self._format_product_info(
            input_data.product_context,
            input_data.real_time_data
        )
        
        # 2. 调用LLM(结构化输出)
        messages = self.prompt.invoke({
            "query": input_data.query,
            "product_info": product_info
        })
        
        try:
            # 使用结构化输出
            structured_llm = self.llm.with_structured_output(
                LLMGenerateResponse
            )
            llm_response = structured_llm.invoke(messages)
            
            # 3. 校验
            valid_skus = {ctx.sku_id for ctx in input_data.product_context}
            self._validate_response(
                llm_response.response,
                llm_response.recommended_skus,
                valid_skus,
                input_data.real_time_data
            )
            
            # 4. 提取实际引用
            actual_refs = self._extract_actual_references(
                llm_response.response,
                valid_skus
            )
            
            return LLMGenerateOutput(
                response=llm_response.response,
                referenced_skus=actual_refs,
                recommended_skus=llm_response.recommended_skus,
                generation_type="llm"
            )
            
        except Exception as e:
            print(f"LLM生成失败: {e},使用Fallback")
            # 5. Fallback
            fallback_response = self._generate_fallback_response(
                input_data.query,
                input_data.product_context,
                input_data.real_time_data
            )
            return LLMGenerateOutput(
                response=fallback_response,
                generation_type="fallback"
            )

性能优化技巧

1. 控制上下文长度

商品信息可能很长,需要精简:

python 复制代码
def _format_product_info(self, product_context, real_time_data):
    """精简格式化"""
    info_list = []
    
    for ctx in product_context[:5]:  # 🔥 只取前5个商品
        # 限制描述长度
        desc = ctx.description[:200]  # 🔥 最多200字
        
        # 限制亮点数量
        highlights = ', '.join(ctx.highlights[:5])  # 🔥 最多5个
        
        info = f"【{ctx.title}】\n"
        info += f"SKU: {ctx.sku_id}\n"
        info += f"描述: {desc}\n"
        info += f"亮点: {highlights}\n"
        # ...
    
    return "\n---\n".join(info_list)

Token节省:

makefile 复制代码
优化前: 10个商品 × 500字 = 5000 tokens
优化后: 5个商品 × 250字 = 1250 tokens
节省75%

2. 缓存商品数据

避免每次生成都查数据库:

python 复制代码
# 模块级缓存
_cached_product_map = None

def _get_product_map():
    global _cached_product_map
    if _cached_product_map is None:
        db = ProductDatabase()
        _cached_product_map = {p["sku_id"]: p for p in db.products}
    return _cached_product_map

class LLMGenerateService:
    def __init__(self, llm):
        self.llm = llm
        self.product_map = _get_product_map()  # 使用缓存

3. 实际引用提取

结构化输出的recommended_skus是AI声称推荐的商品,但实际生成文本中可能没有提及。需要提取实际引用:

python 复制代码
def _extract_actual_references(self, response_text, available_skus):
    """从文本中提取实际引用的SKU"""
    referenced = []
    for sku in available_skus:
        if sku in response_text:
            referenced.append(sku)
    return referenced

# 使用
actual_refs = self._extract_actual_references(
    llm_response.response,
    valid_skus
)

# 这样可以知道AI真正推荐了哪些商品

为什么需要这个?

AI可能返回:

json 复制代码
{
  "recommended_skus": ["SKU_1001", "SKU_1002", "SKU_1003"],
  "response": "我推荐SKU_1001和SKU_1002..."
}

实际只提到了两个SKU,第三个没有在文本中出现。

效果对比

测试场景

100个真实用户查询,每个查询3-5个候选商品

指标 无约束生成 受控生成
幻觉率(编造商品) 18% 0% ✅
价格错误率 35% 2% ✅
库存错误率 28% 1% ✅
推荐准确率 72% 94% ✅
用户满意度 3.2/5 4.6/5 ✅
平均生成时间 1.2s 1.5s
每次成本 $0.0002 $0.00025

关键改进:

  • ✅ 幻觉率从18%降到0%
  • ✅ 价格准确率从65%提升到98%
  • ✅ 用户满意度从3.2提升到4.6

代价:

  • ❌ 生成时间略慢(增加了校验环节)
  • ❌ 成本略高(更详细的prompt)

但这个代价完全值得!

真实案例

Case 1:编造商品

arduino 复制代码
无约束:推荐了"SK-II神仙水洗面奶"(系统没有)
受控:只推荐给定的商品 ✅

Case 2:价格虚假

复制代码
无约束:实际¥259的商品说¥199
受控:严格使用实时价格数据 ✅

Case 3:夸大功效

arduino 复制代码
无约束:"临床证明可消除痘印"(编造)
受控:"温和配方,适合敏感肌"(基于商品描述) ✅

高级功能:动态调整回答风格

可以根据用户画像调整生成风格:

python 复制代码
def _build_system_prompt(self, user_profile=None):
    """根据用户画像调整prompt"""
    
    base_prompt = """你是专业的导购助手..."""
    
    if user_profile:
        # 价格敏感用户
        if user_profile.get("price_sensitive"):
            base_prompt += "\n特别注意:用户对价格敏感,优先推荐性价比高的商品。"
        
        # 品质追求用户
        if user_profile.get("quality_focused"):
            base_prompt += "\n特别注意:用户注重品质,优先推荐高端商品。"
        
        # 新手用户
        if user_profile.get("is_newbie"):
            base_prompt += "\n特别注意:用户是新手,解释要详细易懂。"
    
    return base_prompt

监控和告警

生产环境必须监控:

python 复制代码
import logging
from datetime import datetime

class GenerationMonitor:
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self.metrics = {
            "total_generations": 0,
            "llm_success": 0,
            "fallback_used": 0,
            "validation_failures": 0
        }
    
    def log_generation(self, result, input_data):
        """记录生成结果"""
        self.metrics["total_generations"] += 1
        
        if result.generation_type == "llm":
            self.metrics["llm_success"] += 1
        elif result.generation_type == "fallback":
            self.metrics["fallback_used"] += 1
            # 告警:Fallback使用率过高
            if self._fallback_rate() > 0.1:  # 超过10%
                self.logger.warning(f"Fallback rate high: {self._fallback_rate():.2%}")
        
        # 记录详细日志
        self.logger.info(f"""
        Generation Result:
        - Query: {input_data.query}
        - Type: {result.generation_type}
        - SKUs: {result.recommended_skus}
        - Timestamp: {datetime.now()}
        """)
    
    def _fallback_rate(self):
        total = self.metrics["total_generations"]
        return self.metrics["fallback_used"] / total if total > 0 else 0

踩坑经验

1. System Prompt要简洁

错误示范:

ini 复制代码
system_prompt = """
你是一个专业的电商商品导购助手,需要帮助用户选择最合适的商品...
(省略1000字详细说明)
"""

Token消耗太大,而且AI可能抓不住重点。

正确做法:

ini 复制代码
system_prompt = """
你是电商导购助手。

【严格约束】:
1. 只推荐给定商品
2. 不得编造信息
3. 价格库存使用提供数据

【格式】:
1. 理解需求
2. 推荐商品
3. 给出建议
"""

简洁、结构化、重点突出。

2. 价格校验要考虑精度

python 复制代码
# ❌ 直接比较浮点数
if price == rt_data.price:
    pass

# ✅ 允许小误差
if abs(price - rt_data.price) < 0.01:
    pass

浮点数精度问题可能导致校验失败。

3. 结构化输出的兼容性

旧版LangChain不支持with_structured_output

python 复制代码
def _use_structured_output(self):
    """检查是否支持结构化输出"""
    return hasattr(self.llm, 'with_structured_output')

if self._use_structured_output():
    # 使用新API
    structured_llm = self.llm.with_structured_output(...)
else:
    # 降级到传统方式
    response = self.llm.invoke(...)
    # 手动解析JSON

4. Fallback要足够简单

Fallback是兜底方案,不要太复杂:

python 复制代码
# ❌ Fallback还调用其他AI服务
def fallback():
    summary = another_llm.summarize(...)
    return template.format(summary)

# ✅ 纯模板,零风险
def fallback():
    return f"推荐{product.title},价格¥{price}"

写在最后

受控生成的核心是约束和验证

  1. 约束输入:System Prompt、格式化上下文
  2. 约束输出:结构化输出、格式规范
  3. 验证结果:SKU校验、价格校验、库存校验
  4. 降级保障:Fallback机制
  5. 持续监控:告警、日志

技术选型建议:

  • 电商导购:必须受控生成(法律风险)
  • 内容创作:可以宽松一点(追求创意)
  • 客服问答:中等约束(平衡准确和体验)

关键原则:

在需要准确性的场景,永远不要完全信任LLM。 给它约束,验证它的输出,准备好降级方案。

希望这篇文章能帮到正在做LLM应用的同学。欢迎交流~


参考资源

技术栈

bash 复制代码
langchain-openai    # LLM集成
pydantic           # 结构化输出
regex              # 内容校验

本文基于真实生产环境的LLM导购系统,代码示例经过简化处理。受控生成是LLM应用的核心能力之一。

相关推荐
西安老张(AIGC&ComfyUI)13 分钟前
第030章:ComfyUI视频制作LTX-2.3模型文生视频工作流详解(三)
人工智能·aigc·comfyui
深蓝AI7 小时前
NVIDIA 发布 Nemotron 3 Embed:开源嵌入模型登顶 RTEB,RAG 检索精度全面提升
aigc
TrisighT12 小时前
Skill 写成 2000 字说明书?Claude 根本不读——路由器思维与渐进式披露
aigc·ai编程·claude
食尘者13 小时前
MacBook Pro M5Max 本地大模型推理实践
macos·aigc
倾颜13 小时前
AI 聊天刷新后记录全丢?用浏览器本地存储给 AI Chat 加一层"离线记忆"
前端·aigc
刘棕霆14 小时前
企业测试造数为什么这么难?我把熟手经验做成了一个可交付的造数 Skill
aigc·agent·测试
武子康14 小时前
HunyuanVideo 全家族选型:原版 13B / I2V / Avatar / Foley / 1.5 8.3B 怎么分工
人工智能·llm·aigc
一孤程14 小时前
AI辅助测试用例生成实战——用大模型把测试设计效率提升300%
人工智能·测试工具·ai·aigc·测试用例·测试·测试覆盖率
怕浪猫14 小时前
AI Agent 应用开发实战封面大集合
aigc·openai·ai编程