Zero-shot(零提示)

什么是 Zero-shot Prompting

Zero-shot prompting 是一种 Prompt Engineering 方法,依赖于预训练大语言模型(LLM)的知识来推断出合适的响应。与 few-shot prompting 不同,zero-shot 在提示时不提供任何输出示例。

核心思想:模型利用预训练阶段获得的知识,直接对新任务进行推理,而不需要任何示例。

工作原理

Zero-shot prompting 是零样本学习(Zero-shot Learning)在自然语言处理中的应用,要求模型在没有训练数据的情况下进行预测。

典型场景:

  • 新问题首次出现时
  • 没有标注数据可用时
  • 需要快速验证模型能力时

Prompt 组成要素

虽然不提供示例,但 Zero-shot Prompt 通常包含以下部分:

  1. Instruction(指令):明确告诉模型要完成什么任务

    • 例:“Set the Class name for the issue described…”
  2. Context(上下文):提供任务相关的背景信息

    • 例:不同 Class 名称的定义描述
  3. Input Data(输入数据):需要处理的具体内容

    • 例:“Issue: Users are reporting that they are unable to upload files.”
  4. Output Indicator(输出指示):告诉模型输出什么格式

    • 例:“Class:” 提示模型输出分类标签

优势

  • 简单性:构造简单,易于理解,无需深厚的提示工程知识
  • 易用性:不需要任何额外数据,适合数据稀缺或难以获取的场景
  • 灵活性:易于根据需求调整和更新提示

局限性

  • 性能波动:对复杂、特定任务的性能可能不稳定
  • 依赖预训练模型质量:模型在预训练中未接触过的领域,Zero-shot 表现会较差

性能提升方法

  1. Instruction Tuning(指令微调):在包含各种任务指令和对应结果的数据集上进行监督学习微调
  2. RLHF(基于人类反馈的强化学习):通过人类排序的输出来训练策略,选择最佳响应

应用场景

  • 文本分类
  • 信息提取
  • 问答系统
  • 文本摘要
  • 代码生成
  • 对话系统

与 Few-shot 的对比

特性 Zero-shot Few-shot
示例数量 0 1+
适合场景 新任务、快速验证 复杂任务、需要特定格式
灵活性
准确性 依赖预训练质量 通常更稳定

注意:研究表明,通过改进提示结构,Zero-shot 在某些场景下可以优于 Few-shot。

相关进阶技术

对于复杂推理任务,可考虑:

  • Chain-of-Thought (CoT):将任务分解为一系列离散步骤
  • Tree-of-Thought (ToT):生成可能的分支树,探索多种解决路径

Few-shot(少量提示)

什么是 Few-shot Prompting

Few-shot prompting 与 Zero-shot 的核心区别在于:会为模型提供少量(1-3个)输入-输出示例,帮助模型理解任务的目标格式和期望输出。

工作原理

Few-shot prompting 源自少样本学习(Few-shot Learning)范式,通过在提示中包含少量示例,使模型能够:

  1. 理解任务的输出格式
  2. 捕捉特定领域的模式
  3. 更好地对齐人类期望

典型示例结构

指令:Set the Class name for the issue described to either High, Medium or Low.

示例1:
Issue: New users report that they cannot create accounts.
Class: High

示例2:
Issue: A user reports that labels are rendering overlapping in the app's analytics function.
Class: Low

示例3:
Issue: Users in the United States are reporting that the app is crashing when trying to make a purchase.
Class: High

待分类问题:
Issue: Users report that images are not loading in the app.
Class:

优势

  • 更稳定的性能:示例帮助模型理解具体任务要求
  • 格式控制:更好地确保输出符合预期格式
  • 领域适配:通过示例传达特定领域的判断标准

局限性

  • 需要人工设计示例:示例质量直接影响效果
  • 消耗更多 token:示例会增加输入长度
  • 示例偏差:示例可能误导模型

与 Zero-shot 的选择

场景 推荐方法
新任务探索 Zero-shot(快速验证)
复杂格式要求 Few-shot
领域特定判断标准 Few-shot
已有标注数据 Few-shot
极致简洁需求 Zero-shot

研究提示:Reynolds and McDonell (2021) 发现,通过改进提示结构,Zero-shot 在某些场景可以优于 Few-shot。

ReAct Prompting

什么是 ReAct?

ReAct = Reasoning(推理)+ Acting(行动),由 Yao et al., 2022 提出。核心理念是让 AI 像人一样,一边思考一边查资料,而不是凭空编答案。React的完整过程是: Thought -> Action -> Observation循环直到任务完成

生活中的类比

想象你第一次去陌生城市找餐厅:

  • CoT 方式:站在原地使劲想"往哪走",但对这城市一无所知,只能瞎猜
  • ReAct 方式:边走边看地图、边问路人、边调整路线,每步都用新信息修正方向

核心原理:Thought → Action → Observation 循环

Thought: 我需要找到答案,先搜索一下
Action: Search["相关关键词"]
Observation: 搜索结果显示...
Thought: 这个信息还不够,我需要再查一下...
Action: Lookup["具体名词"]
Observation: 找到了!
... 继续循环直到得到答案
步骤 含义 举个例子
Thought AI 在想什么 “这个问题问我男朋友的年龄,我需要先搜一下”
Action AI 调用什么工具 Search[“Olivia Wilde boyfriend”]
Observation 工具返回什么 “Harry Styles,29岁”

为什么需要 ReAct?

CoT 的问题

思维链(CoT)只管推理,但 AI 的知识是静态的可能过时的

  • 幻觉:AI 自信满满地编一个错误答案
  • 信息缺失:需要实时数据(股价、天气),但 AI 不知道

ReAct 的解决方案

通过调用外部工具获取真实信息,让推理有据可查:

  • 搜索 Wikipedia 获取事实
  • 调用计算器做数学运算
  • 查 API 获取实时数据

完整示例:Olivia Wilde 的问题

问:谁是 Olivia Wilde 的男朋友?他的年龄的 0.23 次方是多少?

Thought: 我需要先找到 Olivia Wilde 的男朋友是谁,然后查他的年龄,最后计算。
Action: Search
Action Input: "Olivia Wilde boyfriend"
Observation: Olivia Wilde 在和 Jason Sudeikis 结束多年订婚后,开始和 Harry Styles 约会。

Thought: 好的,找到了,是 Harry Styles。我需要查他的年龄。
Action: Search
Action Input: "Harry Styles age"
Observation: 29 岁

Thought: 现在计算 29 的 0.23 次方。
Action: Calculator
Action Input: 29^0.23
Observation: 2.169459462

Thought: 得到了最终答案。
Answer: Harry Styles,29 岁,年龄的 0.23 次方是 2.17

两种任务类型的效果对比

1. 知识密集型问答(HotpotQA)

方法 表现 问题
CoT 较高 容易产生幻觉
ReAct 更高 依赖搜索结果质量
ReAct + CoT 最高 最佳组合

2. 决策型任务(ALFWorld 游戏)

ALFWorld 是一个文字冒险游戏,AI 需要在虚拟房间里找东西。

  • Act(纯行动):AI 尝试行动但容易迷失目标
  • ReAct(推理+行动):AI 边推理边行动,成功率更高

代码示例(LangChain)

from langchain.llms import OpenAI
from langchain.agents import load_tools, initialize_agent

# 初始化 LLM
llm = OpenAI(model_name="text-davinci-003", temperature=0)

# 加载工具:搜索 + 数学计算
tools = load_tools(["google-serper", "llm-math"], llm=llm)

# 创建 ReAct Agent
agent = initialize_agent(tools, llm, agent="zero-shot-react-description", verbose=True)

# 运行任务
agent.run("谁是美国总统?他的年龄的 0.5 次方是多少?")

优势与局限

优势

  • 减少幻觉:用真实数据验证推理
  • 透明度高:每一步都在"思考什么",可追踪
  • 可扩展:想加什么工具就加什么
  • 更可靠:答案有据可查

局限

  • 依赖搜索质量:搜索结果不相关,推理会跑偏
  • 速度较慢:多轮工具调用比直接生成答案慢
  • 成本较高:多次 API 调用

什么时候用?

场景 推荐
简单事实问答 直接回答
需要实时数据 ReAct
数学计算 ReAct(配合计算器)
多步骤推理 ReAct + CoT
文字冒险/游戏 ReAct

ReAct vs CoT vs Zero-shot

特性 Zero-shot CoT ReAct
推理能力
工具调用
外部信息获取
幻觉风险
实现复杂度
速度

CoT Prompting

什么是 CoT?

CoT(Chain-of-Thought,思维链) 是一种 Prompt Engineering 技术,通过要求模型输出逐步推理过程而非直接给答案,来增强复杂推理能力。

核心思想:模拟人类的问题解决过程——将复杂问题分解为可管理的中间步骤,而不是直接给出看似正确但可能错误的答案。

类比理解

想象老师教学生解题:

直接给答案 CoT 逐步推理
“答案是 20” “第一步:先算 8÷2=4;第二步:16+4=20;第三步:答案 20”
不知道对错 每步都可检查
无法定位错误 错在哪步一目了然

为什么需要 CoT?

直接回答的问题

模型可能跳过关键步骤,导致:

  • 推理错误(如忘记运算顺序)
  • 遗漏重要考虑
  • 难以发现和修正问题

CoT 的改进效果

通过要求展示思维过程:

  • 强制检查每个推理步骤 — 减少跳步错误
  • 透明可追溯 — 哪里错了一目了然
  • 模拟人类思维 — 将复杂问题分解为可管理的步骤

CoT 变体

1. Few-shot CoT(少样本 CoT)

通过示例展示推理步骤:

奇数相加判断:4, 8, 9, 15, 12, 2, 1
A: 奇数有 9, 15, 1,相加得 25(奇数)。答案:False

奇数相加判断:15, 32, 5, 13, 82, 7, 1
A:

即使只用一个示例,也能取得很好效果。 关键是要展示完整的推理过程。

2. Zero-shot CoT(零样本 CoT)

最简单的触发方式——在 Prompt 末尾加一句 “Let’s think step by step”

I went to the market and bought 10 apples. I gave 2 to the neighbor
and 2 to the repairman. Then I bought 5 more and ate 1.
How many apples do I have left?

Let's think step by step.

不需要示例,只需"Let’s think step by step"即可激活逐步推理。适用于没有足够示例的场景。

3. Auto-CoT(自动 CoT)

人工设计示例耗时耗力。Auto-CoT 利用 LLMs 自动生成推理链,分两阶段:

  1. 问题聚类:将数据集中的问题分成若干簇
  2. 演示采样:从每簇中选代表性问题,用 Zero-shot CoT 生成推理链

采样启发式方法:问题长度(如 60 tokens)、推理步数(如 5 步)。

4. Multimodal CoT(多模态 CoT)

扩展到多模态输入:文本 + 图像。可以分析图片中的视觉线索,结合文本理解进行推理。

适用场景

适合 CoT 不适合 CoT
多步骤推理(数学、逻辑) 简单事实查询
需要验证的复杂任务 纯知识问答
问题诊断和分析 快速一次性输出
教育辅导(展示解题过程) 实时聊天响应

优缺点

优势

  • 提高准确性:多步骤推理减少跳步错误
  • 透明可解释:中间步骤让你知道"为什么"得出答案
  • 多步骤逻辑:解决需要顺序推理的复杂问题
  • 教育价值:分步解释帮助学生理解过程
  • 应用广泛:算术、常识、符号推理都适用

局限性

  • 质量依赖 Prompt:需要精心设计示例
  • Token 消耗大:推理步骤多 = 更长的输出 = 更高成本
  • 误导性推理:可能生成看似合理但实际错误的推理链
  • 设计复杂:有效 CoT Prompt 需要领域理解

CoT vs ReAct

维度 CoT ReAct
推理步骤
执行行动
外部信息获取
幻觉风险 较高 较低
适用场景 逻辑推理为主 需要外部工具交互

简单说:CoT 只思考不行动,ReAct 边思考边行动

代码示例

Zero-shot CoT 触发

def cot_zero_shot(prompt, model):
    """最简单的 CoT 触发方式"""
    full_prompt = f"{prompt}\n\nLet's think step by step."
    return model.generate(full_prompt)

# 例子
question = "If you have 3 apples and buy 4 more, then eat 2, how many?"
answer = cot_zero_shot(question, model)
# 输出:First, you have 3 apples. Then you buy 4 more → 7 apples.
# Then you eat 2 → 5 apples remaining.

Few-shot CoT 示例设计

system_prompt = """
You are the assistant for a tiny candle shop.

When answering questions, use exactly this format:
Step 1: <check what products are mentioned>
Step 2: <list any assumptions the user makes>
Step 3: <correct wrong assumptions, then answer>
Response: <final answer in friendly tone>

Example:
User: Do you have the forest scent candle?
Step 1: User mentions "forest scent" → Forest Breeze candle
Step 2: User assumes we have it in stock
Step 3: Forest Breeze is available, 40h burn, $18
Response: Yes! Forest Breeze is in stock at $18 with a 40h burn time.
"""

相关资源

Input Format

什么是 Prompt 格式?

Prompt 格式指的是如何组织 Prompt 的结构化表达方式,包括指令、上下文、输入数据的排列方式和标记语法。研究表明,Prompt 格式对 LLM 性能有巨大影响——同样的内容用不同格式,性能差异可达 40%

Prompt 四要素

任何 Prompt 都由以下四个要素组成(来自 Promptingguide):

要素 说明 举例
Instruction(指令) 你想让模型执行的具体任务 “Classify the text into neutral, negative, or positive”
Context(上下文) 外部信息或背景知识,引导模型给出更好回答 Few-shot 示例、额外说明
Input Data(输入数据) 需要处理的具体内容 “I think the food was okay.”
Output Indicator(输出指示) 期望的输出格式 “Sentiment:”

示例:文本分类

Classify the text into neutral, negative, or positive

Text: I think the food was okay.

Sentiment:

注意:不需要四个要素都用,简单任务可能只需要指令和输入。

四种常见格式

格式 特点 适用场景
Plain Text 无标记,纯文本 简单任务、快速测试
Markdown 结构化标题、列表 文档生成、代码解释
YAML 键值对、结构清晰 配置类任务
JSON 嵌套结构、严格格式 API 调用、结构化输出

模板对比

Plain Text:

{persona} {instructions} {examples} {output format} {user ask}

Markdown:

## Persona
{...}
## Instructions
{...}
## Examples
{...}
## Output Format
{...}
## User Question
{...}

YAML:

Persona: {...}
Instructions: {...}
Examples: {...}
Output format: {...}
User question: {...}

JSON:

{
  "Persona": "...",
  "Instructions": [...],
  "Examples": "...",
  "Output format": "...",
  "User ask": "..."
}

研究发现:格式影响有多大?

来自 Microsoft 和 MIT 的论文(2024)系统测试了四种格式对 GPT 模型的影响:

敏感性:性能差异惊人

数据集 GPT-3.5-turbo 最佳 vs 最差 差异
MMLU(多选推理) JSON 59.7% vs Markdown 50.0% ~10%
HumanEval(代码生成) JSON 59.8% vs Plain text 40.2% ~20%
FIND(函数逆向) Plain text 15.9% vs Markdown 5.2% ~200% 提升
CODEXGLUE(代码翻译) JSON 78.4% vs Plain text 66.5% ~12%

GPT-4-32k-0613 在 HumanEval 上:Plain text 76.2% vs JSON 21.95%,JSON 反而最差!

模型偏好不同

模型 最偏好的格式
GPT-3.5-turbo JSON
GPT-4 Markdown

大模型更鲁棒

  • GPT-3.5-turbo:对格式敏感,一致性低于 0.5(Markdown 和 JSON 仅有 16% 答案相同)
  • GPT-4:更鲁棒,一致性超过 0.5
  • 结论:更大模型对格式变化的适应能力更强

可迁移性差

  • 同系列模型(如 GPT-3.5-turbo-16k vs GPT-3.5-turbo)格式可迁移,IoU > 0.7
  • 跨系列模型格式不可迁移,IoU < 0.2
  • 没有一种格式在所有 GPT 模型上都最优

实践建议

  1. Prompt 格式不是中性的 — 同样的内容用不同格式可能差 40%
  2. 需要针对模型调优 — 不能假设"格式差不多就行"
  3. 评估时用多种格式 — 避免因格式导致误导性结论
  4. 优先选 GPT-4 — 如果难以优化格式,GPT-4 更鲁棒
  5. 代码任务慎用 JSON — 某些模型在 JSON 格式下代码生成能力大幅下降

代码示例:格式化输出

JSON 格式控制

def format_prompt_json(task, context, input_data, output_format):
    """JSON 格式 Prompt"""
    return f'''
{{
    "Task": "{task}",
    "Context": "{context}",
    "Input": "{input_data}",
    "Output Format": "{output_format}"
}}
'''

# 示例
prompt = format_prompt_json(
    task="情感分类",
    context="电影评论情感分为正面、负面、中性",
    input_data="这部电影太精彩了!",
    output_format="正面/负面/中性"
)

Markdown 格式控制

def format_prompt_markdown(task, examples, input_data):
    """Markdown 格式 Prompt"""
    return f'''
## 任务
{task}

## 示例
{examples}

## 输入
{input_data}

## 输出格式
请严格按照上述示例格式输出。
'''

相关资源

Function Calling

什么是 Function Calling?

Function Calling(函数调用) 是一种让 LLM 与外部工具交互的技术。模型判断何时需要调用函数、调用哪个函数、传递什么参数,但不执行函数本身——执行由外部代码完成。

类比:Function Calling 就像给 LLM 一个"工具箱"和说明书,让它学会在适当的时候选用合适的工具。

为什么需要 Function Calling?

LLM 的三大局限

局限 说明 举例
知识截止 训练数据有时间点 不知道今天的股价
实时信息缺失 无法获取实时数据 不知道当前天气
无法执行操作 只能生成文本,不能行动 不能帮你发邮件

Function Calling 如何解决

没有 Function Calling:
用户:帮我查一下北京现在的天气
模型:我不知道(因为训练数据里没有今天的天气)

有 Function Calling:
用户:帮我查一下北京现在的天气
模型:调用 get_weather(location="北京") → 外部代码执行 → 返回真实天气数据 → 模型生成最终回答

核心概念

角色 职责
模型 判断调用哪个函数、传什么参数
外部代码 实际执行函数(查数据库、调用 API 等)
结果返回 函数输出传回模型继续处理

工作流程

Step 1: 用户提问
        "帮我订一张明天北京到上海的机票"

Step 2: LLM 判断需要函数调用
        输出 JSON:{"function": "search_flights", "arguments": {"from": "北京", "to": "上海", "date": "2026-04-17"}}

Step 3: 应用解析 JSON,调用对应函数
        调用航空公司的 API 查询航班

Step 4: 函数返回结果
        [{"airline": "国航", "price": 800, "time": "10:00"}, ...]

Step 5: 结果返回 LLM
        LLM 基于真实航班数据生成推荐回答

关键点:整个过程中 LLM 只负责判断和生成文本,实际执行由外部代码完成。

完整代码示例

天气查询(最常用案例)

import openai
import json

# 定义天气查询工具
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "获取指定位置的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,如 Beijing, Shanghai"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# 用户提问
messages = [
    {"role": "user", "content": "北京现在的天气怎么样?需要穿什么衣服?"}
]

# 第一次调用:让 LLM 判断是否需要调用函数
response = openai.chat.completions.create(
    model="gpt-4",
    messages=messages,
    tools=tools
)

# LLM 输出:
# tool_calls: [{"id": "call_xxx", "function": {"name": "get_current_weather", "arguments": '{"location": "北京"}'}}]

# 解析并执行函数
for tool_call in response.choices[0].message.tool_calls:
    args = json.loads(tool_call.function.arguments)
    weather_result = get_weather_from_api(location=args["location"])

# 将结果返回 LLM 生成最终回答
messages.append(response.choices[0].message)
messages.append({
    "role": "tool",
    "tool_call_id": response.choices[0].message.tool_calls[0].id,
    "content": json.dumps(weather_result)
})

final_response = openai.chat.completions.create(
    model="gpt-4",
    messages=messages
)

print(final_response.choices[0].message.content)
# "目前北京天气多云,气温18度,建议穿薄外套。"

批量股票查询

# 同时查询多支股票
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_stock_price",
            "description": "获取股票当前价格",
            "parameters": {
                "type": "object",
                "properties": {
                    "symbol": {
                        "type": "string",
                        "description": "股票代码,如 AAPL, GOOGL, MSFT"
                    }
                },
                "required": ["symbol"]
            }
        }
    }
]

messages = [{"role": "user", "content": "帮我看看苹果、谷歌和微软的股价"}]

response = openai.chat.completions.create(model="gpt-4", messages=messages, tools=tools)

# LLM 识别出需要调用三个函数,可以并行执行
import concurrent.futures

def get_stock_price(symbol):
    return {"symbol": symbol, "price": stock_api.get(symbol)}

with concurrent.futures.ThreadPoolExecutor() as executor:
    futures = {
        executor.submit(get_stock_price, json.loads(call.function.arguments)["symbol"]): call
        for call in response.choices[0].message.tool_calls
    }
    results = {f.result()["symbol"]: f.result()["price"] for f in concurrent.futures.as_completed(futures)}

# 返回结果:{"AAPL": 178.50, "GOOGL": 141.80, "MSFT": 378.90}

数据库查询

tools = [
    {
        "type": "function",
        "function": {
            "name": "query_orders",
            "description": "查询用户订单历史",
            "parameters": {
                "type": "object",
                "properties": {
                    "user_id": {"type": "string", "description": "用户ID"},
                    "status": {
                        "type": "string",
                        "enum": ["pending", "shipped", "delivered", "cancelled"]
                    },
                    "limit": {"type": "integer", "description": "返回数量限制", "default": 10}
                },
                "required": ["user_id"]
            }
        }
    }
]

messages = [{"role": "user", "content": "我上个月有哪些订单还没发货?"}]
# LLM 自动识别需要调用 query_orders,从会话上下文推断 user_id

日程管理

tools = [
    {
        "type": "function",
        "function": {
            "name": "create_event",
            "description": "在日历上创建新事件",
            "parameters": {
                "type": "object",
                "properties": {
                    "title": {"type": "string", "description": "事件标题"},
                    "start_time": {"type": "string", "description": "开始时间 ISO 格式"},
                    "end_time": {"type": "string", "description": "结束时间 ISO 格式"},
                    "attendees": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "参与者邮箱列表"
                    },
                    "location": {"type": "string", "description": "地点"}
                },
                "required": ["title", "start_time", "end_time"]
            }
        }
    }
]

messages = [{"role": "user", "content": "明天下午3点和客户开个视频会议,时长1小时"}]
# LLM 解析:title="客户视频会议", start_time="2026-04-17T15:00:00", end_time="2026-04-17T16:00:00"

Function Calling vs ReAct

维度 Function Calling ReAct
本质 结构化工具调用 循环推理 + 行动
输出格式 严格 JSON(函数名+参数) 自然语言(Thought/Action/Observation)
适用场景 明确函数定义、确定性工具 开放式探索、需要推理的工具使用
实现复杂度
透明度 调用明确,可追踪 思维过程透明

选择建议

  • 工具是预定义的、结构化的 → Function Calling
  • 需要多步推理和探索 → ReAct
  • 复杂场景可以结合使用

RAG vs Function Calling

维度 RAG Function Calling
数据源 非结构化文档(向量数据库) 外部 API、实时数据
延迟 相对较低(已索引) 取决于 API 响应
数据特点 静态、不常变化 动态、频繁变化
典型场景 文档问答、知识库 查股价、订单跟踪、实时天气
实现复杂度 中(需要向量化和检索) 低(直接 API 调用)

RAG + Function Calling 融合

两者互补:RAG 提供静态知识,Function Calling 提供实时数据。

用户查询
    ↓
┌─────────────┐     ┌──────────────────┐
│    RAG      │ →   │ Function Calling │
│             │     │                  │
└─────────────┘     └──────────────────┘
    ↓                      ↓
┌─────────────────────────────┐
│             LLM             │
└─────────────────────────────┘
    ↓
生成最终回答

场景举例

  • 客服机器人:RAG 检索产品文档 + Function Calling 查询订单状态
  • 金融分析:RAG 检索分析报告 + Function Calling 查询实时股价

多函数并行调用

# 用户:一个能同时包含北京天气、上海天气、广州天气的查询吗?

messages = [{"role": "user", "content": "帮我查一下北京、上海、广州三地的天气"}]

response = openai.chat.completions.create(model="gpt-4", messages=messages, tools=tools)

# LLM 可能返回:
# tool_calls = [
#   {"id": "call_1", "function": {"name": "get_current_weather", "arguments": '{"location": "北京"}'}},
#   {"id": "call_2", "function": {"name": "get_current_weather", "arguments": '{"location": "上海"}'}},
#   {"id": "call_3", "function": {"name": "get_current_weather", "arguments": '{"location": "广州"}'}}
# ]

# 并行执行三个查询
results = parallel_call_weather(["北京", "上海", "广州"])

# 汇总返回
messages.append(response.choices[0].message)
messages.append({"role": "tool", "tool_call_id": "call_1", "content": json.dumps(results[0])})
messages.append({"role": "tool", "tool_call_id": "call_2", "content": json.dumps(results[1])})
messages.append({"role": "tool", "tool_call_id": "call_3", "content": json.dumps(results[2])})

适用场景分类

类别 示例 说明
实时数据查询 天气、股价、航班 需要最新数据
用户数据操作 订单、日程、邮件 用户私有数据
数据库查询 SQL 查询、文档检索 结构化数据
设备控制 智能家居、IoT 物理世界交互
外部服务集成 支付、地图、物流 第三方 API
代码执行 数学计算、代码运行 计算密集任务
内容生成 图像生成、语音合成 多模态输出

支持 Function Calling 的模型

商业模型

模型 说明
OpenAI GPT-4/GPT-3.5 Turbo 最成熟的 Function Calling 实现
Google Gemini 通过 Vertex AI 支持
Anthropic Claude 3 API 设计类似 OpenAI
Cohere Command R/R+ 支持外部工具集成

开源模型

模型 说明
Mistral 7B 演示了 Function Calling 能力
NexusRaven-V2-13B 专为零-shot 函数调用设计
Gorilla OpenFunctions 基于 API 文档微调
FireFunction V1 基于 Mixtral 8x7B,接近 GPT-4
Hermes 2 Pro 函数调用准确率达 90%

最佳实践

  1. 函数描述要清晰:描述决定 LLM 何时调用
  2. 参数要有默认值:减少必填参数,降低调用失败率
  3. 处理函数执行错误:API 超时、服务不可用等情况
  4. 考虑并行调用:独立函数可以并行执行
  5. 限制函数数量:一次不要传太多函数(建议 ≤ 20)
  6. 提供错误处理:函数执行失败时返回有意义的错误信息

相关资源

Prompt Caching

什么是 Prompt Caching?

Prompt Caching(提示词缓存) 是一种提升 LLM 速度 和成本效率的技术,通过存储频繁不变的部分 prompt(如指令内容或参考资料),避免模型重复处理这些 token。

类比:就像浏览器缓存静态资源一样,Prompt Caching 缓存 LLM 处理过的相同内容,避免重复计算。

为什么需要 Prompt Caching?

LLM 的两大挑战

挑战 说明
成本 每个处理的 token 都会产生费用
延迟 处理重复请求会增加响应延迟

没有 Caching 的问题

用户查询 1:"解释什么是人工智能"
  → LLM 完整处理 → 返回结果
  → 消耗:token × 价格

用户查询 2:"解释什么是人工智能"
  → LLM 再次完整处理 → 返回相同结果
  → 消耗:再次支付相同费用(浪费!)

用户查询 3:"解释什么是人工智能"
  → LLM 再次完整处理 → 再次浪费!

有 Caching 的改进

用户查询 1:"解释什么是人工智能"
  → LLM 完整处理 → 缓存结果

用户查询 2:"解释什么是人工智能"
  → 直接返回缓存结果(毫秒级)
  → 消耗:几乎为 0

用户查询 3:"解释什么是人工智能"
  → 直接返回缓存结果

Prompt Caching vs 传统 Caching

维度 Prompt Caching 传统 Caching
目标 保存重复 LLM prompt,避免重新计算 存储频繁访问的数据或资源
输出可靠性 仅当 prompt 和所有参数完全相同时生效 确定性的,相同输入总是产生相同输出
性能提升 减少 token、API 成本、端到端延迟 提升应用性能、降低后端/数据库负载
失效管理 与模型版本更新或重训练周期关联 使用 TTL、版本控制或手动失效

工作原理

完整流程

Step 1: 提交 prompt
        ↓
Step 2: 生成缓存键(Cache Key)
        - 精确匹配:prompt 文本或哈希
        - 语义缓存:生成 embedding
        ↓
Step 3: 缓存查找
        - 精确匹配:key-value store
        - 语义匹配:vector database
        ↓
Step 4: 命中/未命中
        - 命中:立即返回存储结果
        - 未命中:发送给 LLM,保存输出
        ↓
Step 5: 存储结果
        ↓
Step 6: 失效处理
        TTL、模型更新或内容漂移时失效

两种缓存策略

1. 精确匹配缓存(Exact-match Caching)
优点:
- 实现简单,准确率高
- 完全相同的 prompt 才命中

缺点:
- 措辞稍有变化就无法复用
- 对用户输入敏感

适用场景:
- 固定格式的 API 调用
- 系统指令不变的 Agent
2. 语义缓存(Semantic Caching)
优点:
- 即使措辞变化也能复用
- 更灵活的缓存策略

缺点:
- 需要额外的 embedding 计算
- 相似度阈值需要调优

适用场景:
- 用户表达多样的问答系统
- 需要模糊匹配的客服场景

缓存存储选择

类型 适用场景 示例
Key-Value Store 精确匹配 Redis, Memcached
Vector Database 语义匹配 Pinecone, Weaviate, Chroma

代码示例

Python + LangChain 实现

from langchain.cache import InMemoryCache
from langchain.globals import set_llm_cache

# 设置 LLM 缓存
set_llm_cache(InMemoryCache())

# 第一次调用 - 需要处理
response = llm.invoke("解释什么是人工智能")
# 耗时:约 2 秒

# 第二次调用 - 命中缓存
response = llm.invoke("解释什么是人工智能")
# 耗时:约 50 毫秒(30-50x 提升)

带过期时间的 Redis 缓存

import redis
import hashlib
import json

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def get_cached_response(prompt, model="gpt-4", temperature=0.7):
    # 生成缓存键
    cache_key = hashlib.md5(
        f"{prompt}:{model}:{temperature}".encode()
    ).hexdigest()

    # 查找缓存
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)

    # 缓存未命中,调用 LLM
    response = llm.generate(prompt)

    # 存储缓存(TTL = 1 小时)
    redis_client.setex(
        cache_key,
        3600,  # 1 hour
        json.dumps(response)
    )

    return response

语义缓存示例

from sentence_transformers import SentenceTransformer
import redis
import numpy as np

model = SentenceTransformer('all-MiniLM-L6-v2')
redis_client = redis.Redis(host='localhost', port=6379, db=0)

SIMILARITY_THRESHOLD = 0.85

def find_similar_prompt(prompt):
    """在缓存中查找语义相似的 prompt"""
    prompt_embedding = model.encode(prompt)

    # 遍历缓存中的所有 embedding
    keys = redis_client.keys("prompt_emb:*")

    for key in keys:
        cached_emb = np.frombuffer(redis_client.get(key), dtype=np.float32)
        similarity = np.dot(prompt_embedding, cached_emb) / (
            np.linalg.norm(prompt_embedding) * np.linalg.norm(cached_emb)
        )

        if similarity >= SIMILARITY_THRESHOLD:
            # 找到相似项,返回原始 prompt 的缓存
            original_key = key.decode().replace("prompt_emb:", "cached:")
            return redis_client.get(original_key)

    return None

使用场景

场景 1:交互式文档门户

场景:内部知识管理系统

未缓存:
用户 A 问:"年假政策是什么?" → LLM 处理 2 秒
用户 B 问:"年假政策是什么?" → LLM 处理 2 秒
用户 C 问:"年假政策是什么?" → LLM 处理 2 秒

已缓存:
用户 A 问:"年假政策是什么?" → LLM 处理 2 秒 → 缓存
用户 B 问:"年假政策是什么?" → 命中缓存 50ms
用户 C 问:"年假政策是什么?" → 命中缓存 50ms

场景 2:营销聊天机器人

场景:电商常见问题

缓存内容:
- 折扣政策
- 产品规格
- 库存状态
- 物流信息

效果:
- 高峰期减少 70% LLM 调用
- 响应时间从 2s 降至 50ms

场景 3:客户支持系统

场景:技术支持问答

未缓存:
- 每次查询都调用 LLM
- 成本 = 查询次数 × token 价格

已缓存:
- 常见问题命中缓存
- 只有新问题才调用 LLM
- 节省 60% 成本

优势总结

优势 说明 量化效果
成本降低 复用 prompt-response 对 减少 50-80% API 调用
速度提升 缓存响应立即送达 响应时间 2s → 50ms
资源优化 不浪费计算在重复查询 GPU 利用率提升
无限容量 处理高流量和重复查询 QPS 支持提升 10x
一致响应 相同查询相同答案 用户信任度提升
用户体验 更快、可预测的响应 满意度提升
降低负载 减少服务器压力 服务器成本降低

注意事项

1. 动态/上下文敏感查询

对于以下查询类型,缓存效果有限:

  • 实时数据查询(股价、天气)
  • 个性化内容(用户历史、账户信息)
  • 上下文相关(多轮对话的后续问题)

2. 过时响应

问题示例:
Q: "最新 iPhone 价格是多少?" → 缓存:$999
实际价格已变为:$1099

→ 缓存返回旧价格,导致用户误导

解决方案

  • 设置较短的 TTL
  • 对时效性内容禁用缓存
  • 显式标记缓存内容的时效性

3. 边缘情况复杂性

情况 处理方式
部分 prompt 可缓存 将 prompt 拆分为静态+动态部分
措辞略有不同 使用语义缓存
缓存键冲突 添加更多参数到缓存键

4. 缓存维护

  • TTL 设置:太长容易过时,太短失去缓存意义
  • 存储限制:避免缓存无限增长
  • 监控指标:缓存命中率、未命中率

最佳实践

缓存策略选择

┌─────────────────────────────────────────────────────────┐
│                    缓存策略选择                           │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  是否需要语义匹配?                                       │
│       ↓                                                 │
│   是 → 语义缓存 + Vector DB                            │
│   否 → 精确匹配 + Redis                                 │
│                                                         │
└─────────────────────────────────────────────────────────┘

实现检查清单

  • 识别可缓存的静态内容(系统指令、文档片段)
  • 选择合适的缓存策略(精确 vs 语义)
  • 设置合理的 TTL(一般 1-24 小时)
  • 实现缓存失效机制(手动 + 自动)
  • 监控缓存命中率(目标 > 70%)
  • 处理缓存未命中的回退逻辑
  • 考虑敏感信息的加密存储

缓存键设计

def generate_cache_key(prompt, model, temperature, system_prompt=None):
    """设计好的缓存键"""
    components = [
        prompt,
        model,
        str(temperature),
        system_prompt or ""
    ]
    return hashlib.sha256("|".join(components).encode()).hexdigest()

相关资源

Streaming Response

什么是 Streaming Response?

Streaming Response(流式响应) 是一种让 AI 模型实时逐 token 发送响应的技术。用户看到文本"逐渐出现",像 AI 真的在实时打字。

类比:就像看视频时的"正在加载…",流式响应让用户感知上觉得更快,即使总生成时间不变。

Non-Streaming vs Streaming 对比

Non-Streaming(阻塞式响应)

用户发送请求 → 服务器等待完整生成 → 一次性返回所有内容
     ↓
  用户等待中... → 等待中... → 等待中... → 看到完整答案

问题:用户什么都看不到,直到全部生成完成。

Streaming(流式响应)

用户发送请求 → 服务器逐 token 发送 → 用户实时看到文本出现
     ↓              ↓                  ↓
  用户等待中...  "S" 出现...      "So" 出现...

优势:用户体验"打字机效果",感知更快。

核心概念

Token 是什么?

LLM 生成的 token 可以是:

  • 一个完整单词(如 “hello”)
  • 单词的一部分(如 “happ-” 在 “happiness” 中)
  • 单个字符或标点

流式 API 工作流程

Step 1: 客户端发送启用了流式传输的请求
Step 2: 服务器处理输入并开始生成 token
Step 3: Token 逐个作为小块发送给客户端
Step 4: 客户端实时将每个块追加到显示区域

代码实战

Python OpenAI 流式响应

from openai import OpenAI

client = OpenAI()

# 流式调用
stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "写一个故事"}],
    stream=True  # 启用流式
)

# 逐块处理
for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

Python 非流式对比

# 非流式调用
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "写一个故事"}],
    stream=False  # 默认行为
)

# 一次性获取完整响应
print(response.choices[0].message.content)

delta 内容结构

// 第一个 chunk
{"choices": [{"delta": {"content": "从"}}]}

// 第二个 chunk
{"choices": [{"delta": {"content": "前"}}]}

// 第三个 chunk
{"choices": [{"delta": {"content": "有"}}]}

Node.js + Express 流式响应

import OpenAI from "openai";
import express from "express";

const app = express();
const client = new OpenAI();

app.get("/chat", async (req, res) => {
  const stream = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: "Explain streaming" }],
    stream: true
  });

  res.setHeader("Content-Type", "text/plain");

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content;
    if (content) {
      res.write(content);  // 实时发送
    }
  }

  res.end();
});

app.listen(3000);

前端处理(JavaScript)

获取流式数据

const response = await fetch('/api/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ prompt: '写一个故事' })
})

// 获取 reader
const reader = response.body.getReader()
const decoder = new TextDecoder()  // 解码器
let isStillStreaming = true

while(isStillStreaming) {
  const {value, done} = await reader.read()
  const chunkValue = decoder.decode(value)  // Uint8Array → 文本
  state.text += chunkValue                 // 追加到显示
  isStillStreaming = !done
}

React 中使用

const [text, setText] = useState('')

const handleStream = async () => {
  const response = await fetch('/api/chat', options)
  const reader = response.body.getReader()

  while (true) {
    const {done, value} = await reader.read()
    if (done) break
    const chunk = new TextDecoder().decode(value)
    setText(prev => prev + chunk)  // 累积显示
  }
}

注意:避免频繁 setState 导致性能问题,可以用 requestAnimationFrame 或节流。

服务端流式响应(Node.js + OpenAI)

基本结构

const stream = new ReadableStream({
  async start(controller) {
    // 处理数据...
    controller.enqueue(data)  // 发送数据块
    controller.close()          // 关闭流
  }
})

requestEvent.send(new Response(stream))

OpenAI 响应格式处理

OpenAI 返回的原始格式:

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":"stop"}]}

data: [DONE]

需要解析并转发纯文本:

const stream = new ReadableStream({
  async start(controller) {
    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let isStillStreaming = true

    while(isStillStreaming) {
      const {value, done} = await reader.read()
      const chunkValue = decoder.decode(value)

      // 提取 data: 后面的内容
      const regex = /data:\s*(.*)/g
      let match = regex.exec(chunkValue)

      while (match !== null) {
        const payload = match[1]

        if (payload === '[DONE]') {
          controller.close()
          break
        } else {
          try {
            const json = JSON.parse(payload)
            const text = json.choices[0].delta.content || ''
            controller.enqueue(text)  // 发送纯文本
            match = regex.exec(chunkValue)
          } catch (error) {
            // OpenAI 可能跨两个流返回 JSON
            const nextChunk = await reader.read()
            const nextChunkValue = decoder.decode(nextChunk.value)
            match = regex.exec(chunkValue + nextChunkValue)
          }
        }
      }

      isStillStreaming = !done
    }
  }
})

完整流程图

┌─────────────────────────────────────────────────────────────────┐
│                        流式响应完整流程                           │
└─────────────────────────────────────────────────────────────────┘

1. 用户提交表单(JavaScript 拦截)
        ↓
2. 服务器接收 → 转发给 OpenAI API(stream: true)
        ↓
3. OpenAI 逐 token 返回流式数据
   data: {"delta": {"content": "第"}}
   data: {"delta": {"content": "一"}}
   data: {"delta": {"content": "个"}}
   ...
   data: [DONE]
        ↓
4. 服务器处理 OpenAI 格式 → 创建自己的流
        ↓
5. 客户端实时接收 → 更新 UI 显示
        ↓
6. 流结束 → 关闭连接

核心技术

Server-Sent Events (SSE)

服务器通过单个 HTTP 连接实时推送更新:

data: Hello

data: how

data: are

data: you?

每个 data 字段代表一块可立即显示的响应。

WebSockets

提供双向通信通道,适用于复杂实时应用如协作文档编辑器。

异步编程

asyncio(Python)或 Node.js 使服务器能够:

  • 并发处理多个客户端请求
  • 在生成后续 token 时不阻塞地发送

何时用哪种?

使用 Non-Streaming 使用 Streaming
后端批处理任务 聊天应用
分析流水线 实时助手
结构化 JSON 输出 代码生成 UI
API 链式调用 交互式 AI 工具
无 UI 场景 长报告生成

优势与挑战

优势

优势 说明
感知响应更快 用户立即看到结果
增强交互性 可中途打断或完善查询
资源高效 不需在内存中保存完整响应

挑战

挑战 解决方案
网络延迟 稳定连接
错误处理 处理部分状态,优雅恢复
实现复杂性 使用成熟库如 OpenAI SDK

关键技术考虑

  1. 错误处理:流式错误可能中途发生,需处理部分状态
  2. 成本监控:流式不改变 token 计费
  3. 超时处理:长流需要 Keep-alive 和连接监控
  4. 前端性能:React 中避免频繁 setState,考虑用 requestAnimationFrame

相关资源

System Prompt

什么是 System Prompt?

System Prompt(系统提示词) 是定义 AI 整体行为方式的元级指令。是**“如何""为什么”**,而不仅仅是"做什么"。

类比:System Prompt 像是给 AI 的职位描述,设定了 AI 在所有交互中都应遵循的行为框架。

System Prompt vs User Prompt

维度 System Prompt User Prompt
性质 静态、设定后保持一致 动态、每个任务不同
作用 整体行为框架 具体任务指令
内容 “如何做” “做什么”
变化频率 通常保持不变 每次交互都变化
设置位置 系统级别配置 API 请求中

System Prompt 的核心组成

1. 角色定义(Role Definition)

设定 AI 应该扮演的确切角色:

You are an experienced sales development representative specializing in B2B cold outreach.

2. 上下文建立(Context Establishment)

说明业务环境、行业背景、典型场景:

You have deep understanding of current market trends, competitive landscapes, and growth strategies in the tech sector.

3. 语气和风格(Tone and Style)

定义整体语调:

Maintain a professional, confident tone without being pushy or overly familiar.

4. 伦理指南(Ethical Guidelines)

明确法律、道德、公司政策:

Avoid making unsubstantiated claims or promises. Ensure all content complies with anti-spam regulations.

5. 知识范围(Scope of Knowledge)

指定 AI 应该调用的专业知识领域:

Draw from sales methodologies, market trends, and data analysis techniques.

6. 输出标准(Output Standards)

设定响应的通用参数:

Keep emails concise, aiming for 150 words or less. Use clear, jargon-free language.

System Prompt 示例

销售开发代表

You are an expert sales development representative specializing in crafting compelling B2B cold outreach emails. Adhere to these guidelines:
- Maintain professional, confident tone without being pushy
- Focus on prospect's pain points and how your solution addresses them
- Keep emails concise, 150 words or less
- Use simple, jargon-free language unless industry specifically requires it
- Personalize based on prospect's role, industry, and company
- Include clear, low-pressure call-to-action
- Avoid unsubstantiated claims or promises
- Structure emails for easy scanning
- Craft subject lines under 50 characters
- Always provide value (insight, resource, or relevant case study)
- Incorporate social proof when possible
- Ensure anti-spam compliance and email best practices
- Use placeholders ({{FirstName}}, {{CompanyName}}) for personalization

Your cold emails should be engaging, relevant, and focused on starting a conversation rather than making an immediate sale.

SWOT 分析顾问

You are an experienced business strategy consultant specializing in the B2B SaaS industry. Your role is to provide insightful, actionable SWOT analyses.

When conducting SWOT analyses:
- Always maintain a neutral, objective tone
- Base insights on current market data and trends
- Prioritize actionable items over general observations
- Consider both short-term and long-term implications
- Balance internal factors (strengths and weaknesses) with external factors (opportunities and threats)
- Use clear, concise language appropriate for executive-level discussions
- Avoid jargon unless it's industry-standard terminology

Your analyses should be structured, easy to read, and directly applicable to strategic decision-making.

线索评分模型分析师

You are an experienced sales operations analyst with expertise in lead qualification for B2B tech companies.

When designing lead scoring models, adhere to these principles:
- Prioritize criteria with proven correlation with higher conversion rates
- Use clear, non-technical language accessible to both sales and marketing teams
- Ensure models are flexible and can be adjusted based on new data
- Consider both explicit (demographic, firmographic) and implicit (behavioral) factors
- Include negative scoring factors to identify less promising leads
- Base point values on data-driven insights and industry benchmarks
- Design models that integrate with common CRM platforms
- Include time-decay factors for behavioral scores
- Recommend periodic review and recalibration processes

Your lead scoring models should be comprehensive, practical to implement, and directly tied to improving sales efficiency.

Role Prompting(角色提示)的重要性

为什么需要给 AI 分配角色?

给 AI 分配特定角色可以带来七大好处:

  1. 上下文理解:角色帮助 AI 理解操作上下文,生成更相关响应
  2. 一致的语调风格:维持所有交互中的统一声音
  3. 专业知识模拟:调用与角色相关的特定知识和技能
  4. 伦理边界:确保符合行业规范和公司政策
  5. 改进个性化:更好地理解如何与不同类型用户交互
  6. 目标对齐:输出与角色目标对齐
  7. 边界内的创造力: paradoxically,在边界内反而更有创造力

常见角色示例

角色 应用场景
专家销售开发代表 B2B 冷启动邮件
业务战略顾问 SWOT 分析、市场定位
销售运营分析师 线索评分模型设计
市场研究分析师 竞争对手分析
客户细分分析师 客户分层分析
客服代表 客户支持对话

何时用 System Prompt?

场景 使用
设定 AI 整体角色和语气 System Prompt
定义 AI 的行为框架 System Prompt
建立伦理指南和边界 System Prompt
设置知识范围和专长 System Prompt
具体任务执行 User Prompt
单次查询或请求 User Prompt
复杂 AI Agent 开发 两者结合

最佳实践

System Prompt 最佳实践

  1. 从角色定义开始:明确 AI 扮演什么角色
  2. 设定边界:明确 AI 不应该做什么
  3. 提供上下文:说明业务环境和行业背景
  4. 指定输出格式:统一风格和详细程度
  5. 包含伦理指南:设定合规和道德标准
  6. 保持简洁:避免过度详细的指令
  7. 定期更新:根据反馈迭代优化

User Prompt 最佳实践

  1. 提供具体任务:明确要做什么
  2. 包含上下文:提供必要的背景信息
  3. 指定输出格式:列表、表格、段落等
  4. 设置约束:时间、字数、风格限制
  5. 提供示例:帮助 AI 理解期望
  6. 分解复杂任务:分步骤执行

实际应用场景

场景 1:销售邮件自动化

System Prompt

You are an expert B2B sales development representative...

User Prompt

Write a cold email to a VP of Sales at a fintech company...

场景 2:市场分析

System Prompt

You are an experienced business strategy consultant...

User Prompt

Conduct a SWOT analysis for our company regarding...

场景 3:客户支持

System Prompt

You are a friendly, empathetic customer support agent for our SaaS product...

User Prompt

A customer is asking about refund policy for annual subscription...

相关资源

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐