把 GPT-6 Astra、Sol 与 Luna 用于生产 Agent:Microsoft Foundry 的选型与路由方法

2026-09-23 24 预计阅读时间: 1 分钟
来源: azure.microsoft.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

GPT-6 Astra、Sol 与 Luna 为 Microsoft Foundry 中的生产级 AI Agent 提供了更多模型选择。真正影响上线效果的,并不是简单挑出一个“最强模型”,而是根据复杂工作流、高并发请求和风险等级建立可测量、可切换的模型路由机制。

由于现有摘要没有给出三款模型的具体上下文窗口、价格、延迟或工具调用能力,下面不会预设谁更快、谁更强,而是给出一套可以直接用于工程评估的落地方法。

不要按名字猜能力,用任务集做选择

生产环境中的模型选型至少要同时观察五个维度:

维度 建议指标 典型问题
任务质量 成功率、人工评分、事实错误率 Agent 是否真正完成了目标
工具调用 参数正确率、调用成功率、重试次数 是否会选错工具或构造错误参数
延迟 P50、P95、P99 高峰期是否拖慢整个工作流
成本 单次任务成本、每个成功任务成本 便宜的调用是否因重试反而更贵
稳定性 超时率、限流率、输出格式合规率 能否长期维持服务等级目标

评测时不要只发送几条通用问答。应从真实业务日志中脱敏抽取任务,例如:

  • 需要一次回答即可完成的分类、提取和摘要;
  • 包含检索、数据库查询或外部 API 的多步任务;
  • 需要生成并校验结构化 JSON 的任务;
  • 涉及退款、权限变更或代码执行的高风险任务;
  • 接近并发与配额上限的批量任务。

Astra、Sol 和 Luna 都应运行同一套数据集。只有这样,路由决策才来自测量结果,而不是模型名称或单次演示。

生产 Agent 应拆成工作流,而不是一次大调用

复杂 Agent 通常包含规划、执行、验证和总结等阶段。每个阶段的需求并不相同:规划可能更看重多步推理,批量提取更在意吞吐量,最终验证则要求稳定遵守规则。

可以将工作流抽象为三类路由:

  1. volume:短请求、低风险、请求量大的任务;
  2. complex:长上下文、多步骤或需要多次工具调用的任务;
  3. review:影响资金、权限、发布或客户承诺的任务。

这里的路由名称不是对 Astra、Sol、Luna 能力的官方定义。上线前应通过基准测试,把三个 Foundry 部署 ID 分别配置到最合适的路由;同一模型也可以同时承担多个角色。

这种解耦还有一个直接收益:模型升级或配额变化时,只需修改配置,不必改写 Agent 的业务代码。

可直接改造的 Python 模型路由器

下面示例假设你使用的是兼容聊天补全消息结构的 Microsoft Foundry 推理端点。不同项目的 URL、认证头和响应字段可能不同,运行前请以实际部署页面提供的调用格式为准。

将以下内容保存为 agent_router.py:

import json
import os
import sys
import time
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

ENDPOINT = os.environ['FOUNDRY_CHAT_URL']
API_KEY = os.environ['FOUNDRY_API_KEY']
AUTH_MODE = os.getenv('FOUNDRY_AUTH_MODE', 'api-key')

# 这些值应填写 Foundry 中真实的部署 ID。
# 根据自己的基准测试,让 Astra、Sol、Luna 对应到合适的路由。
MODELS = {
    'volume': os.environ['MODEL_VOLUME'],
    'complex': os.environ['MODEL_COMPLEX'],
    'review': os.environ['MODEL_REVIEW'],
}


def choose_route(text: str, expected_steps: int, risk: str) -> str:
    if risk == 'high':
        return 'review'
    if expected_steps >= 4 or len(text) >= 6000:
        return 'complex'
    return 'volume'


def call_model(model: str, prompt: str, attempts: int = 3) -> dict:
    payload = {
        'model': model,
        'messages': [
            {
                'role': 'system',
                'content': 'You are a production assistant. Be precise and do not invent tool results.'
            },
            {'role': 'user', 'content': prompt},
        ],
        'temperature': 0.2,
        'max_tokens': 800,
    }

    headers = {'Content-Type': 'application/json'}
    if AUTH_MODE == 'bearer':
        headers['Authorization'] = f'Bearer {API_KEY}'
    else:
        headers['api-key'] = API_KEY

    body = json.dumps(payload).encode('utf-8')

    for attempt in range(attempts):
        request = Request(ENDPOINT, data=body, headers=headers, method='POST')
        started = time.perf_counter()
        try:
            with urlopen(request, timeout=60) as response:
                result = json.loads(response.read().decode('utf-8'))
                result['_latency_ms'] = round(
                    (time.perf_counter() - started) * 1000, 1
                )
                return result
        except HTTPError as exc:
            retryable = exc.code == 429 or 500 <= exc.code < 600
            if not retryable or attempt == attempts - 1:
                detail = exc.read().decode('utf-8', errors='replace')
                raise RuntimeError(f'Foundry HTTP {exc.code}: {detail}') from exc
        except URLError as exc:
            if attempt == attempts - 1:
                raise RuntimeError(f'Foundry network error: {exc}') from exc

        time.sleep(2 ** attempt)

    raise RuntimeError('Request failed without a response')


def main() -> None:
    prompt = ' '.join(sys.argv[1:]).strip()
    if not prompt:
        raise SystemExit('Usage: python agent_router.py "your task"')

    steps = int(os.getenv('TASK_EXPECTED_STEPS', '1'))
    risk = os.getenv('TASK_RISK', 'low').lower()
    route = choose_route(prompt, steps, risk)
    model = MODELS[route]
    result = call_model(model, prompt)

    print(json.dumps({
        'route': route,
        'model': model,
        'latency_ms': result.pop('_latency_ms'),
        'response': result,
    }, ensure_ascii=False, indent=2))


if __name__ == '__main__':
    main()

配置部署信息后运行:

export FOUNDRY_CHAT_URL='https://YOUR-ENDPOINT.example/chat/completions'
export FOUNDRY_API_KEY='replace-with-your-key'
export FOUNDRY_AUTH_MODE='api-key'

export MODEL_VOLUME='your-benchmarked-deployment-id'
export MODEL_COMPLEX='your-benchmarked-deployment-id'
export MODEL_REVIEW='your-benchmarked-deployment-id'

TASK_EXPECTED_STEPS=2 TASK_RISK=low \
python agent_router.py '从这段客户反馈中提取产品名、问题类型和紧急程度,并返回 JSON。'

如果实际端点使用部署名而不是请求体中的 model 字段,可以删除该字段,并把部署 ID 放入 URL。生产环境还应使用密钥管理服务或托管身份,不要把 API Key 写进代码仓库。

路由之后,还要补上可观测性与安全边界

模型路由解决的是“调用谁”,并不能单独解决生产可靠性。每次执行至少应记录以下字段:

{
  "trace_id": "01J...",
  "workflow": "support-ticket-triage",
  "route": "complex",
  "model_deployment": "deployment-id",
  "latency_ms": 1840,
  "input_tokens": 2130,
  "output_tokens": 380,
  "tool_calls": 2,
  "retries": 0,
  "result": "success"
}

不要记录未经处理的敏感提示词、个人信息或工具凭据。对于能修改数据、发送邮件、执行代码或产生资金影响的 Agent,还应增加:

  • 工具参数白名单和 JSON Schema 校验;
  • 最小权限身份,不让模型直接持有长期凭据;
  • 高风险操作的人类确认或独立验证步骤;
  • 超时、限流、指数退避和熔断机制;
  • 防止提示词注入的内容隔离与工具授权检查;
  • 模型不可用时的降级路径,而不是无限重试。

上线前的决策清单

引入 Astra、Sol 和 Luna 时,可以按下面的顺序推进:

  • 用真实、脱敏的业务任务建立固定评测集;
  • 对三款模型测量质量、延迟、成本和工具调用成功率;
  • 用部署配置表达路由,不在代码中硬编码模型能力假设;
  • 对高风险操作设置验证器或人工审批;
  • 为 429、5xx、超时和配额耗尽设计降级方案;
  • 保存模型版本、提示词版本和评测结果,避免升级后静默退化;
  • 先用少量流量灰度发布,再逐步提高比例。

多模型的价值不在于让架构图出现更多方框,而在于把质量、吞吐量、延迟和风险变成可独立调节的工程参数。对生产 Agent 而言,最稳妥的策略通常不是永久押注某一个模型,而是让 Astra、Sol 与 Luna 在同一套评测、路由和监控体系中接受持续验证。


相关推荐