AI技能问题解析:从模型不确定性到工程化解决方案 如果你正在关注 AI 领域的最新动态可能已经注意到一个现象很多 AI 项目在演示时看起来无所不能但一到实际应用就暴露出各种问题。最近Stability AI 创始人 Emad Mostaque 在一次公开讨论中提到了一个关键问题——AI 系统中的技能问题Skill Issues。这不仅仅是技术层面的挑战更是当前 AI 应用落地的核心瓶颈。所谓技能问题并不是指 AI 模型本身的能力不足而是指在实际应用场景中如何让 AI 系统具备稳定、可靠、可预测的技能执行能力。这就像是一个天才的实习生理论知识很丰富但缺乏实际工作经验需要在具体任务中不断磨合。本文将深入分析 AI 系统中的技能问题从技术原理到实践解决方案帮助开发者理解为什么即使是最先进的 AI 模型也会出现技能执行不一致如何通过工程化手段提升 AI 技能的可靠性在实际项目中如何设计和测试 AI 技能链常见的技能失效场景及应对策略。1. AI 技能问题的本质是什么AI 技能问题本质上是一个系统工程问题。当我们谈论 AI 的技能时通常指的是模型完成特定任务的能力比如文本生成、代码编写、数据分析等。但问题在于这些技能在孤立测试时可能表现良好一旦放入真实的工作流程中就会出现各种意外情况。1.1 技能执行的不确定性传统软件的行为是可预测的相同的输入必然产生相同的输出。但 AI 系统基于概率模型即使是相同的输入也可能因为温度参数、随机种子等因素产生不同的输出。这种不确定性在创意场景中是优势但在需要可靠性的生产环境中就成为了问题。# 示例同样的提示词不同的输出结果 import openai prompt 请用 Python 计算斐波那契数列前10项 # 第一次执行 response1 openai.ChatCompletion.create( modelgpt-3.5-turbo, messages[{role: user, content: prompt}], temperature0.7 # 较高的创造性 ) # 第二次执行 response2 openai.ChatCompletion.create( modelgpt-3.5-turbo, messages[{role: user, content: prompt}], temperature0.7 # 同样的参数可能产生不同结果 ) print(结果1:, response1.choices[0].message.content) print(结果2:, response2.choices[0].message.content)1.2 上下文理解的局限性AI 模型对上下文的理解存在边界。当任务复杂度增加时模型可能无法保持对长对话或复杂指令的一致性理解。这就导致了技能执行中的注意力漂移现象。1.3 技能组合的协同问题单个技能可能运行良好但多个技能组合时就会出现接口不匹配、状态管理混乱等问题。比如一个数据分析任务需要先后执行数据提取、清洗、分析和可视化四个技能任何一个环节的失败都会导致整个流程中断。2. 技能问题的技术根源分析要解决技能问题首先需要理解其技术根源。这涉及到模型架构、训练数据、推理机制等多个层面。2.1 模型训练的分布偏移AI 模型在训练时接触的数据分布与真实应用场景的数据分布存在差异。这种分布偏移Distribution Shift导致模型在训练集上表现良好的技能在真实世界中可能失效。训练场景真实场景技能失效表现干净的标准数据集带有噪声的真实数据解析错误、理解偏差单一的任务类型多任务交叉环境任务混淆、优先级错误理想化的输入格式混乱的用户输入格式处理失败2.2 提示词工程的局限性当前大多数 AI 技能都依赖于提示词工程Prompt Engineering来激活。但提示词的效果存在很大的不稳定性# 不可靠的提示词设计 poor_prompt 请处理这个数据。 数据一些数字 # 更可靠的提示词设计 better_prompt 请执行以下数据清洗任务 输入数据格式CSV 字符串包含id,name,age三列 具体要求 1. 移除包含空值的行 2. 将age列转换为整数类型 3. 对name列进行去重处理 示例输入1,张三,25\n2,李四,\n3,张三,30 预期输出1,张三,25\n3,张三,30 实际需要处理的数据 2.3 推理过程中的累积误差在复杂的多步推理任务中错误会随着推理步骤的推进而累积。早期的微小错误可能导致最终结果的重大偏差这就是所谓的蝴蝶效应在 AI 推理中的体现。3. 构建可靠的 AI 技能工程体系解决技能问题需要建立完整的工程化体系。以下是构建可靠 AI 技能的关键组件。3.1 技能抽象层设计首先需要建立统一的技能抽象层将 AI 能力封装成可重用的组件。每个技能应该具备明确的输入输出规范、错误处理机制和性能指标。from abc import ABC, abstractmethod from typing import Any, Dict, Optional from dataclasses import dataclass dataclass class SkillResult: success: bool data: Any error_message: Optional[str] None execution_time: float 0.0 class BaseSkill(ABC): 技能基类 def __init__(self, name: str, version: str): self.name name self.version version self.metrics SkillMetrics() abstractmethod def execute(self, input_data: Dict) - SkillResult: 执行技能的核心逻辑 pass def validate_input(self, input_data: Dict) - bool: 输入验证 # 实现具体的验证逻辑 return True def handle_error(self, error: Exception) - SkillResult: 错误处理 return SkillResult( successFalse, dataNone, error_messagestr(error) ) class DataAnalysisSkill(BaseSkill): 数据分析技能实现 def execute(self, input_data: Dict) - SkillResult: try: if not self.validate_input(input_data): return SkillResult(False, None, 输入验证失败) # 执行数据分析逻辑 result self._perform_analysis(input_data) return SkillResult(True, result) except Exception as e: return self.handle_error(e) def _perform_analysis(self, data: Dict) - Dict: # 具体的数据分析实现 return {insights: [], summary: {}}3.2 技能编排与工作流引擎单个技能的可靠性是基础多个技能之间的协同工作更为重要。需要设计健壮的工作流引擎来管理技能之间的依赖关系和执行顺序。# 技能工作流定义示例 workflow: name: 电商数据分析流水线 version: 1.0 skills: - name: 数据提取 type: data_extraction config: source: database query: SELECT * FROM sales_data retry_policy: max_attempts: 3 backoff_factor: 2 - name: 数据清洗 type: data_cleaning depends_on: [数据提取] config: rules: - remove_duplicates: true - fill_missing: mean timeout: 300 - name: 趋势分析 type: trend_analysis depends_on: [数据清洗] config: methods: [seasonal_decomposition, anomaly_detection] error_handling: strategy: circuit_breaker fallback_skill: basic_reporting3.3 技能测试与验证框架建立完善的测试框架是确保技能可靠性的关键。应该包括单元测试、集成测试和端到端测试等多个层次。import pytest from unittest.mock import Mock, patch class TestDataAnalysisSkill: 数据分析技能测试用例 def setup_method(self): self.skill DataAnalysisSkill(test_analysis, 1.0) self.valid_input { data: [1, 2, 3, 4, 5], analysis_type: statistical } def test_execute_success(self): 测试成功执行场景 result self.skill.execute(self.valid_input) assert result.success is True assert summary in result.data assert result.error_message is None def test_execute_invalid_input(self): 测试无效输入处理 invalid_input {invalid: data} result self.skill.execute(invalid_input) assert result.success is False assert 验证 in result.error_message def test_execute_timeout(self): 测试超时处理 with patch.object(self.skill, _perform_analysis, side_effectTimeoutError): result self.skill.execute(self.valid_input) assert result.success is False pytest.mark.parametrize(input_data,expected, [ ({data: [], analysis_type: statistical}, True), ({data: None, analysis_type: statistical}, False), ]) def test_validate_input(self, input_data, expected): 参数化测试输入验证 assert self.skill.validate_input(input_data) expected4. 实战构建一个可靠的文本处理技能链让我们通过一个具体的例子来演示如何构建可靠的 AI 技能。我们将实现一个文本处理技能链包括文本提取、内容分析和报告生成三个环节。4.1 环境准备与依赖安装首先确保环境配置正确# 创建虚拟环境 python -m venv ai_skills_env source ai_skills_env/bin/activate # Linux/Mac # ai_skills_env\Scripts\activate # Windows # 安装核心依赖 pip install openai python-dotenv pandas numpy pytest pip install tenacity # 用于重试逻辑创建环境配置文件# .env 文件 OPENAI_API_KEYyour_api_key_here SKILL_TIMEOUT30 MAX_RETRY_ATTEMPTS3 LOG_LEVELINFO4.2 实现文本提取技能# skills/text_extraction.py import os import logging from tenacity import retry, stop_after_attempt, wait_exponential from .base_skill import BaseSkill, SkillResult class TextExtractionSkill(BaseSkill): 文本提取技能 def __init__(self): super().__init__(text_extraction, 1.0) self.logger logging.getLogger(__name__) retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def execute(self, input_data: dict) - SkillResult: try: self.logger.info(f执行文本提取输入: {input_data.keys()}) # 验证输入 if not self._validate_input(input_data): return SkillResult(False, None, 输入数据格式错误) # 提取文本内容 extracted_text self._extract_content(input_data) # 质量检查 if not self._quality_check(extracted_text): return SkillResult(False, None, 文本质量检查失败) return SkillResult(True, { text: extracted_text, metadata: self._generate_metadata(extracted_text) }) except Exception as e: self.logger.error(f文本提取失败: {str(e)}) return self.handle_error(e) def _validate_input(self, input_data: dict) - bool: required_fields [content, content_type] return all(field in input_data for field in required_fields) def _extract_content(self, input_data: dict) - str: content input_data[content] content_type input_data[content_type] if content_type plain_text: return str(content) elif content_type html: # 简单的 HTML 标签去除 import re return re.sub(.*?, , content) else: raise ValueError(f不支持的内容类型: {content_type}) def _quality_check(self, text: str) - bool: 基本的文本质量检查 if not text or len(text.strip()) 0: return False if len(text) 10: # 文本过短 return False return True def _generate_metadata(self, text: str) - dict: 生成文本元数据 words text.split() return { word_count: len(words), char_count: len(text), language: self._detect_language(text) } def _detect_language(self, text: str) - str: 简单的语言检测实际项目中应使用专业库 # 这里使用简单的启发式方法 if any(char in text for char in [的, 是, 在]): return chinese elif any(word in text.lower() for word in [the, is, and]): return english return unknown4.3 实现内容分析技能# skills/content_analysis.py import openai from tenacity import retry, stop_after_attempt, wait_exponential from .base_skill import BaseSkill, SkillResult class ContentAnalysisSkill(BaseSkill): 内容分析技能 def __init__(self, api_key: str): super().__init__(content_analysis, 1.0) openai.api_key api_key retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def execute(self, input_data: dict) - SkillResult: try: if text not in input_data: return SkillResult(False, None, 缺少文本数据) text input_data[text] analysis_type input_data.get(analysis_type, general) # 根据分析类型构建不同的提示词 prompt self._build_prompt(text, analysis_type) # 调用 AI 模型进行分析 response openai.ChatCompletion.create( modelgpt-3.5-turbo, messages[{role: user, content: prompt}], temperature0.1, # 低温度保证稳定性 max_tokens500 ) analysis_result self._parse_response(response.choices[0].message.content) return SkillResult(True, analysis_result) except Exception as e: return self.handle_error(e) def _build_prompt(self, text: str, analysis_type: str) - str: 构建分析提示词 base_prompt f 请对以下文本进行分析。文本内容 {text[:2000]} # 限制文本长度 请按照以下格式返回JSON分析结果 if analysis_type sentiment: return base_prompt { sentiment: positive/negative/neutral, confidence: 0.95, key_phrases: [短语1, 短语2], summary: 简要总结 } elif analysis_type technical: return base_prompt { complexity: high/medium/low, technical_terms: [术语1, 术语2], readability_score: 0.85 } else: # general analysis return base_prompt { main_topics: [主题1, 主题2], key_points: [要点1, 要点2], overall_quality: high/medium/low } def _parse_response(self, response_text: str) - dict: 解析AI响应 # 这里应该实现完整的JSON解析逻辑 # 简化示例实际项目需要更健壮的解析 import json try: return json.loads(response_text) except json.JSONDecodeError: # fallback: 尝试提取JSON部分 import re json_match re.search(r\{.*\}, response_text, re.DOTALL) if json_match: return json.loads(json_match.group()) return {raw_response: response_text}4.4 技能链编排与执行# skills/workflow_orchestrator.py import logging from typing import List, Dict, Any from .text_extraction import TextExtractionSkill from .content_analysis import ContentAnalysisSkill class WorkflowOrchestrator: 工作流编排器 def __init__(self, api_key: str): self.skills { text_extraction: TextExtractionSkill(), content_analysis: ContentAnalysisSkill(api_key) } self.logger logging.getLogger(__name__) def execute_workflow(self, workflow_config: Dict, input_data: Dict) - Dict[str, Any]: 执行完整的工作流 results {} current_input input_data for step_name, step_config in workflow_config[steps].items(): self.logger.info(f执行步骤: {step_name}) skill_name step_config[skill] skill self.skills.get(skill_name) if not skill: raise ValueError(f未找到技能: {skill_name}) # 合并步骤配置到输入数据 step_input {**current_input, **step_config.get(config, {})} # 执行技能 result skill.execute(step_input) if not result.success: self.logger.error(f步骤 {step_name} 执行失败: {result.error_message}) # 错误处理策略 if step_config.get(critical, True): raise Exception(f关键步骤失败: {step_name}) else: # 非关键步骤失败继续执行但记录错误 results[step_name] { success: False, error: result.error_message } continue # 保存结果并传递给下一步 results[step_name] { success: True, data: result.data } current_input {**current_input, previous_result: result.data} return { success: True, results: results, final_output: current_input.get(previous_result) } # 使用示例 def main(): orchestrator WorkflowOrchestrator(os.getenv(OPENAI_API_KEY)) workflow_config { steps: { extract_text: { skill: text_extraction, config: {content_type: plain_text}, critical: True }, analyze_content: { skill: content_analysis, config: {analysis_type: sentiment}, critical: True } } } input_data { content: 这是一段测试文本用于演示AI技能链的工作流程。, content_type: plain_text } result orchestrator.execute_workflow(workflow_config, input_data) print(工作流执行结果:, result) if __name__ __main__: main()5. 技能质量监控与持续改进构建可靠的 AI 技能不仅需要良好的初始设计还需要持续的监控和改进机制。5.1 技能性能指标监控建立全面的监控体系来跟踪每个技能的执行情况# monitoring/skill_metrics.py import time from dataclasses import dataclass from typing import Dict, List from datetime import datetime dataclass class SkillMetrics: 技能性能指标 total_executions: int 0 successful_executions: int 0 failed_executions: int 0 average_execution_time: float 0.0 last_execution_time: datetime None error_types: Dict[str, int] None def __post_init__(self): if self.error_types is None: self.error_types {} def record_execution(self, success: bool, execution_time: float, error_type: str None): 记录执行结果 self.total_executions 1 if success: self.successful_executions 1 else: self.failed_executions 1 if error_type: self.error_types[error_type] self.error_types.get(error_type, 0) 1 # 更新平均执行时间 if self.average_execution_time 0: self.average_execution_time execution_time else: self.average_execution_time ( self.average_execution_time * (self.total_executions - 1) execution_time ) / self.total_executions self.last_execution_time datetime.now() def get_success_rate(self) - float: 计算成功率 if self.total_executions 0: return 0.0 return self.successful_executions / self.total_executions class SkillMonitor: 技能监控器 def __init__(self): self.metrics_by_skill: Dict[str, SkillMetrics] {} def track_execution(self, skill_name: str, execution_func): 装饰器跟踪技能执行 def wrapper(*args, **kwargs): start_time time.time() try: result execution_func(*args, **kwargs) execution_time time.time() - start_time self._record_metric(skill_name, True, execution_time) return result except Exception as e: execution_time time.time() - start_time self._record_metric(skill_name, False, execution_time, type(e).__name__) raise return wrapper def _record_metric(self, skill_name: str, success: bool, execution_time: float, error_type: str None): 记录指标 if skill_name not in self.metrics_by_skill: self.metrics_by_skill[skill_name] SkillMetrics() self.metrics_by_skill[skill_name].record_execution(success, execution_time, error_type) def generate_report(self) - Dict: 生成监控报告 return { skill_name: { success_rate: metrics.get_success_rate(), avg_execution_time: metrics.average_execution_time, total_executions: metrics.total_executions, common_errors: metrics.error_types } for skill_name, metrics in self.metrics_by_skill.items() }5.2 A/B 测试与技能优化通过 A/B 测试来持续改进技能效果# optimization/skill_optimizer.py import random from typing import List, Dict, Any from dataclasses import dataclass dataclass class ABTestVariant: A/B测试变体 name: str skill_config: Dict[str, Any] weight: float 0.5 # 流量分配权重 class SkillOptimizer: 技能优化器 def __init__(self): self.variants: Dict[str, List[ABTestVariant]] {} self.results: Dict[str, List] {} def register_test(self, test_name: str, variants: List[ABTestVariant]): 注册A/B测试 # 归一化权重 total_weight sum(variant.weight for variant in variants) for variant in variants: variant.weight / total_weight self.variants[test_name] variants self.results[test_name] [] def select_variant(self, test_name: str) - ABTestVariant: 根据权重选择变体 if test_name not in self.variants: raise ValueError(f未注册的测试: {test_name}) variants self.variants[test_name] rand_val random.random() cumulative_weight 0 for variant in variants: cumulative_weight variant.weight if rand_val cumulative_weight: return variant return variants[-1] # 兜底返回最后一个变体 def record_result(self, test_name: str, variant_name: str, success: bool, metrics: Dict): 记录测试结果 self.results[test_name].append({ variant: variant_name, success: success, metrics: metrics, timestamp: time.time() }) def analyze_results(self, test_name: str) - Dict: 分析测试结果 if test_name not in self.results: return {} results self.results[test_name] variant_stats {} for variant in self.variants[test_name]: variant_results [r for r in results if r[variant] variant.name] if variant_results: success_count sum(1 for r in variant_results if r[success]) success_rate success_count / len(variant_results) avg_metrics self._calculate_average_metrics(variant_results) variant_stats[variant.name] { success_rate: success_rate, sample_size: len(variant_results), metrics: avg_metrics } return variant_stats def _calculate_average_metrics(self, results: List) - Dict: 计算平均指标 if not results: return {} metrics {} for key in results[0][metrics].keys(): values [r[metrics][key] for r in results if key in r[metrics]] if values: metrics[key] sum(values) / len(values) return metrics6. 常见技能问题与解决方案在实际应用中AI 技能会遇到各种典型问题。以下是常见问题及解决方案的详细分析。6.1 技能执行超时问题问题现象技能执行时间过长导致整个工作流阻塞。解决方案import signal from contextlib import contextmanager class TimeoutException(Exception): pass contextmanager def time_limit(seconds: int): 执行时间限制上下文管理器 def signal_handler(signum, frame): raise TimeoutException(技能执行超时) signal.signal(signal.SIGALRM, signal_handler) signal.alarm(seconds) try: yield finally: signal.alarm(0) class RobustSkill(BaseSkill): 具备超时处理能力的技能 def execute_with_timeout(self, input_data: Dict, timeout: int 30) - SkillResult: try: with time_limit(timeout): return self.execute(input_data) except TimeoutException: return SkillResult(False, None, f技能执行超时{timeout}秒)6.2 技能输出格式不一致问题现象AI 模型返回的数据格式不符合预期导致下游技能无法处理。解决方案def validate_output_format(self, output: Any, expected_schema: Dict) - bool: 验证输出格式是否符合预期模式 if not isinstance(output, dict): return False for key, expected_type in expected_schema.items(): if key not in output: return False if not isinstance(output[key], expected_type): return False return True def normalize_output(self, raw_output: Any, schema: Dict) - Dict: 标准化输出格式 normalized {} for key, expected_type in schema.items(): if key in raw_output: value raw_output[key] try: # 尝试类型转换 normalized[key] expected_type(value) except (ValueError, TypeError): # 转换失败使用默认值 normalized[key] self._get_default_value(expected_type) else: normalized[key] self._get_default_value(expected_type) return normalized6.3 技能间的数据传递问题问题现象上游技能的输出无法被下游技能正确理解。解决方案建立统一的数据契约# data_contracts/text_processing.yaml contracts: text_extraction_output: schema: text: string metadata: word_count: integer char_count: integer language: string required: [text] version: 1.0 content_analysis_input: schema: text: string analysis_type: string required: [text] version: 1.07. 生产环境最佳实践将 AI 技能部署到生产环境时需要遵循一系列最佳实践来确保系统的稳定性和可维护性。7.1 技能版本管理每个技能都应该有明确的版本控制策略# versioning/skill_version.py from semantic_version import Version class SkillVersionManager: 技能版本管理器 def __init__(self): self.registry {} def register_skill(self, skill_name: str, version: str, skill_instance: BaseSkill): 注册技能版本 if skill_name not in self.registry: self.registry[skill_name] {} self.registry[skill_name][version] skill_instance def get_skill(self, skill_name: str, version_constraint: str latest): 根据版本约束获取技能实例 if skill_name not in self.registry: raise ValueError(f技能未注册: {skill_name}) available_versions list(self.registry[skill_name].keys()) if version_constraint latest: # 获取最新版本 latest_version max(Version(v) for v in available_versions) return self.registry[skill_name][str(latest_version)] else: # 根据语义版本约束选择 constraint SimpleSpec(version_constraint) matching_versions [ v for v in available_versions if Version(v) in constraint ] if not matching_versions: raise ValueError(f没有满足约束的版本: {version_constraint}) latest_match max(Version(v) for v in matching_versions) return self.registry[skill_name][str(latest_match)]7.2 技能降级策略当主要技能失败时应该有备用的降级方案class FallbackSkillManager: 技能降级管理器 def __init__(self): self.fallback_chains {} def register_fallback_chain(self, primary_skill: str, fallback_skills: List[str]): 注册降级链 self.fallback_chains[primary_skill] fallback_skills def execute_with_fallback(self, skill_name: str, input_data: Dict) - SkillResult: 带降级的技能执行 if skill_name not in self.fallback_chains: # 没有配置降级链直接执行 skill self.get_skill(skill_name) return skill.execute(input_data) fallback_chain [skill_name] self.fallback_chains[skill_name] for current_skill in fallback_chain: try: skill self.get_skill(current_skill) result skill.execute(input_data) if result.success: # 记录使用的降级级别 result.metadata[fallback_level] current_skill return result except Exception as e: # 当前技能失败尝试下一个降级技能 continue # 所有技能都失败 return SkillResult(False, None, 所有技能执行失败)7.3 技能性能优化针对高频使用的技能进行性能优化# optimization/skill_cache.py import hashlib import pickle from datetime import datetime, timedelta class SkillResultCache: 技能结果缓存 def __init__(self, ttl: int 3600): # 默认1小时 self.cache {} self.ttl ttl def _generate_cache_key(self, skill_name: str, input_data: Dict) - str: 生成缓存键 input_str pickle.dumps(input_data, sort_keysTrue) return hashlib.md5(f{skill_name}:{input_str}.encode()).hexdigest() def get(self, skill_name: str, input_data: Dict) - Optional[SkillResult]: 获取缓存结果 cache_key self._generate_cache_key(skill_name, input_data) if cache_key in self.cache: cached_item self.cache[cache_key] if datetime.now() - cached_item[timestamp] timedelta(secondsself.ttl): return cached_item[result] else: # 缓存过期删除 del self.cache[cache_key] return None def set(self, skill_name: str, input_data: Dict, result: SkillResult): 设置缓存 cache_key self._generate_cache_key(skill_name, input_data) self.cache[cache_key] { result: result, timestamp: datetime.now() }8. 技能问题的未来展望随着 AI 技术的不断发展技能问题也将呈现新的发展趋势和解决方案。8.1 自适应技能学习未来的 AI 技能系统将具备自我学习和适应能力在线学习技能能够根据实际使用反馈自动调整参数迁移学习在一个领域学到的技能可以快速适配到相关领域元学习技能学会如何学习快速掌握新任务8.2 技能组合自动化当前技能组合需要人工设计未来将出现自动化的技能组合发现技能发现系统自动识别可用的技能组件工作流生成根据任务需求自动生成最优的技能执行路径动态调整根据执行结果实时调整技能组合策略8.3 技能可信度评估建立更完善的技能可信度评估体系不确定性量化对技能输出的不确定性进行精确度量可信度评分为每个技能结果提供可信度分数风险预警提前识别可能产生重大错误的技能执行Emad Mostaque 提到的技能问题确实指出了当前 AI 应用落地的关键挑战。通过建立工程化的技能管理体系我们可以显著提升 AI 系统的可靠性和实用性。本文介绍的方法和实践为构建生产级 AI 技能提供了可行的技术路径。在实际项目中建议从小的技能单元开始逐步建立监控和改进机制。记住可靠的 AI 系统不是一蹴而就的而是通过持续的工程化实践迭代出来的。