AB
AiBoss站
教程

Agent Harness 工程实战:用 Python 搭一个带沙箱、测试与步数上限的最小执行框架

教程

Agent Harness 工程实战:用 Python 搭一个带沙箱、测试与步数上限的最小执行框架

模型本身只会生成文本,读不到项目文件、跑不了测试、也记不住上一轮做了什么。把这些能力补齐的那层系统就是 harness。本文先讲清 harness 与提示词工程、上下文工程的分工,再拆解一个 harness 的六层结构,最后用 Python 从零写一个最小实现:工作区沙箱、只读测试、步数上限、状态落盘和最终校验,并用一个「折扣被算了两次」的 bug 跑通全流程。

语言模型只会根据收到的文本生成回复。它不知道你的项目里有哪些文件,不能自己跑测试、连数据库或打开浏览器,也不会记得上一轮会话发生过什么。更麻烦的是,它可以在工作其实没做完的时候说「已完成」,也可能在没有约束的情况下做出你没打算让它做的操作。把这些缺口逐个补上的那层系统,就叫 harness;设计这层系统的工作,叫 harness 工程。

这篇教程面向已经会用编码智能体、但想弄清它背后到底靠什么跑起来的人,也适合准备自己搭一套受控执行流程的开发者。读完之后,你应该能说清 harness 由哪几层组成、每层解决什么问题,并且能写出一个可运行的最小版本:带工作区沙箱、只读测试、步数上限、进度落盘和最终校验。示例用 Python 标准库实现,离线可跑,输出稳定。

准备工作

动手之前,先把环境和概念对齐。这个示例刻意做得很轻,依赖极少,但有几件事需要提前确认。

运行环境

  • Python 3.9 或更新版本。离线演示只用到标准库,不需要安装任何第三方包。
  • 一个可以创建临时目录的文件系统。示例会在临时目录里生成一个小项目,并在工作区之外再放一个敏感文件,用来验证沙箱是否真的拦得住越界访问。
  • 如果你想接真实模型,需要安装对应厂商的 SDK,并把 API 密钥放在环境变量里,不要写进代码。以 Anthropic SDK 为例:
pip install anthropic
export ANTHROPIC_API_KEY="your-key-here"

密钥通过环境变量注入,是为了避免它出现在源码、日志或提交记录里。这一点在 harness 里尤其重要,因为 harness 会记录每一次工具调用,任何被写进参数的秘密都可能进入审计日志。

需要先想清楚的三件事

写代码之前,先回答三个问题,它们直接决定后面每一层的实现方式:

  1. 任务是什么,什么算完成? 示例里的任务是修一个 bug:结算函数把折扣算了两遍。完成的标准不是模型说「改好了」,而是测试通过,并且测试文件本身没有被改动。
  2. 模型能碰什么? 它只能读写工作区内的文件,tests/ 目录和 AGENTS.md 是只读的,工作区之外的路径一律拒绝。
  3. 什么时候停下来? 达到步数上限就停,并报告停在哪一步、当前状态如何,而不是无限重试。

这三个答案分别对应后面的工具门禁、校验层和编排层。先写下来,再写代码,能省掉大量返工。

为什么只有模型不够

把「模型只会生成文本」这个事实展开,会得到一串具体限制:

  • 你不把文件内容给它,它就不知道项目里有什么。
  • 它不能自己跑测试、调用数据库或打开浏览器。
  • 它不记得上一次会话发生了什么。
  • 它可能在工作没完成时声称完成。
  • 如果没有限制,它可能执行你并不想要的操作。
  • 它能同时容纳的文本有限,塞太多上下文反而会稀释关键信息。

harness 就是逐条应对这些限制的系统。它把模型和信息、工具连接起来,管理任务的执行步骤,并对结果做检查。具体包含什么取决于系统设计,通常会有:指令与项目规则、上下文检索与记忆、终端/浏览器/数据库等工具、受控的执行环境、工作流步骤与审批与重试、测试与评估与日志。

分工可以概括成一句话:模型提出要做什么,harness 提供做这件事所需的信息和能力,并检查实际发生了什么。

日常工具里其实到处都是 harness。编码智能体就是「模型 + harness」:文件访问、终端、项目指令文件、权限确认、测试运行,都是 harness 的组成部分。两个产品用同一个模型却表现迥异,原因往往就在 harness 的差异上。

「harness」这个词还很新,用法并不统一。有人只指执行工具的那部分代码,有人指模型周围的整个系统。本文采用后一种更宽的含义。

提示词工程、上下文工程与 harness 工程的分工

这三个概念经常被混着用,但它们管的层次不同。用一张表对照最清楚:

维度提示词工程上下文工程harness 工程
关注点指令的措辞模型看到哪些信息模型周围的整个系统
核心问题我该怎么问?模型需要知道什么?它必须看到什么、做什么、验证什么?
例子「审查这个函数并说明安全风险」函数本身、相关文件、编码规范、测试失败信息、文档能跑测试并报告结果的机制、强制架构边界的 linter、保存的进度
控制的东西指令提供的信息上下文、工具、权限、工作流、状态、校验
局限可以要求跑测试,但自己跑不了可以提供事实,但不能行动或验证不能保证每个结果都正确

提示词可以告诉智能体「去跑测试」,但只有 harness 能给它跑测试的方法,并告诉它测试到底过没过。提示词可以写「遵守我们的架构」,harness 则能让架构文档容易被找到,并用测试或 linter 把关键边界真正卡住。

三者有重叠。一个实用的区分是:harness 工程包含模型周围的运行时和控制机制。

智能体循环

harness 不是调用一次模型就结束,它跑的是一个循环:

  1. 构建上下文;
  2. 让模型提出一个动作;
  3. 执行这个动作并检查结果;
  4. 回到第一步,重复。

循环在两种情况下结束:校验通过,或者触达某个限制(比如步数上限),此时智能体报告停在哪里,或者请人介入。

有两个细节值得注意。第一,每一轮都要重建上下文,这样模型看到的永远是最新的执行结果,而不是上一轮的旧快照。第二,模型可以说自己完成了,但由 harness 判定校验是否真的通过。这条是整套设计里最关键的权责划分:完成状态的判定权不在模型手里。

harness 的六层结构

具体设计因应用而异,但多数 harness 需要六层。安全与成本控制横跨所有层,单独放在下一节讲。

第一层:指令——把规则说清楚

指令给智能体一个起点。在代码仓库里,一份简短的 AGENTS.md 或 CLAUDE.md 可以说明如何浏览项目、更详细的文档在哪里、应该跑哪些检查。

要点是让这个入口保持聚焦,把架构、测试、安全、产品行为等细节链接到持续维护的文档里。把主指令文件当成一张通往可靠项目知识的地图,而不是一本完整手册。手册会过期,地图只需要指向仍然有效的地方。

第二层:上下文——给对信息

上下文不是越多越好。加载太多无关文件会把真正重要的细节埋掉,而且每个模型能容纳的量都有上限。常见手法有:

  • 搜索与检索:让智能体自己去仓库或文档里搜,而不是一开始就把所有东西塞进去。
  • 摘要长工具输出:一份五千行的测试日志,只保留有用的那几行。
  • 压缩(compaction):对话变长时,用简短摘要替换较早的历史。
  • 写笔记:把关键发现写进文件,让智能体之后可以重新读取。
  • 干净上下文的子智能体:把支线任务交给另一个智能体,只取回它的简短结论。

目标是给智能体一条通往所需信息的清晰路径,同时让它的工作上下文保持聚焦。

第三层:工具与执行——让智能体安全地行动

工具让智能体不止于生成文本。harness 决定这些动作在哪里执行、智能体能访问什么。例如,可以允许它在隔离工作区里改文件,同时禁止访问生产系统,并在有外部后果的动作前要求人工确认。

工具本身的设计也影响正确率:名字清晰、输入简单、错误信息能让模型据此调整,工具就更容易被用对。只给智能体完成当前任务所需的工具和权限。

第四层:持久状态——跨会话保留进度

模型的活跃上下文是临时的。如果计划和进度只存在于对话里,下一次运行可能重复劳动,或者基于错误假设继续。harness 可以把任务计划、决策记录、测试结果、已完成与待办的摘要保存下来,让下一次会话有可靠的续接点。

一句话:把进度存在模型临时上下文之外。

第五层:编排——管理执行顺序

编排控制智能体如何推进任务:下一步是哪一步、失败的动作要不要重试、什么时候该停或该找人。多智能体在任务能干净切分时有用,但增加智能体并不会自动改善结果。每个智能体都需要清晰的角色、足够的上下文,以及校验或合并其产出的方式。

原则是:为了解决真实的协作需求才加工作流步骤和交接,流程尽量和任务复杂度匹配。

第六层:校验与可观测性——检查工作、理解失败

智能体的最后一条消息不是工作正确的证据。harness 需要能提供证据的检查:测试、类型检查、构建结果、评估。可观测性——工具调用历史、日志、追踪——则用来定位它在哪里走偏,并帮助团队改进系统。

做法很直接:在智能体开始之前就定义好「完成」是什么意思,然后用证据去检查。

安全与成本限制

一个能跑命令、能读文件的智能体,也能以很快的速度犯错。harness 要限制损害范围,并让循环有界。

安全:把智能体当成不可信的执行者

  • 沙箱:在隔离环境里执行动作,不接触生产系统,也拿不到它不需要的秘密。
  • 最小权限:每个工具只给能完成工作的最窄权限,例如除非确实需要写入,数据库只给只读访问。
  • 审批门:难以撤销的动作要人工确认,比如删除数据、发送消息、部署上线。
  • 提示注入:智能体读到的文本——网页、issue 评论、文件——都可能夹带试图操纵它的指令。把所有外部内容当作数据,让敏感工具远离读取不可信文本的步骤,并在其后接高风险动作时要求审批。
  • 审计轨迹:记录每一次工具调用,事后可以复盘智能体到底做了什么。

成本:让循环有界

  • 步数与时间上限:跑满设定的步数或分钟数就停,并报告当前进展。
  • Token 预算:限制一个任务能使用的上下文与输出量。
  • 重试上限:失败动作重试几次后就升级处理,而不是无限重复。
  • 模型规模匹配:简单步骤用更小更快的模型,困难决策用更强的模型。

核心假设是:智能体有时会犯错或被误导,所以权限和限制要设计成「即使出错,损失也很小、很便宜」。

最小 harness 的架构

现在把上面的思路落到代码。示例任务是一个 bug:结算函数把折扣应用了两次。智能体要修好它,同时不能改测试。

每个部件对应前面的一层:

部件对应层在代码里做什么
构建上下文指令、上下文把 AGENTS.md 载入系统提示,并裁剪过长的工具输出
工具门禁工具与执行把路径限制在工作区内,tests/ 与 AGENTS.md 设为只读
校验校验在接受「完成」之前自己跑一遍测试
状态文件持久状态把每一步和最终状态写入 .harness/state.json
步数上限编排、成本达到设定步数后停止循环

模型是可替换的。为了让演示离线可跑、每次输出一致,示例使用一个「脚本化模型」,按固定顺序回放动作。真实模型的适配器只占很小一块,后面会给出。

操作步骤

下面所有代码放在一个平铺文件 harness_demo.py 里,可以从头读到尾,也可以直接运行。

第一步:创建带 bug 的示例项目

先准备一个小项目:一个有意的 bug、一个测试文件、一份简短的项目规则。函数 make_workspace 会在临时目录里搭出这些内容,并在工作区之外额外创建一个 secrets.txt,用来测试沙箱。

AGENTS_MD = """\
# Project rules
- Source code lives in checkout.py.
- Tests live in tests/ and are read-only. Never edit them.
- Run run_tests before you call finish.
"""

CHECKOUT_PY = """\
def apply_discount(price, percent):
    return round(price * (1 - percent / 100), 2)

def checkout_total(items, discount_percent=0):
    subtotal = sum(i["price"] * i["qty"] for i in items)
    total = apply_discount(subtotal, discount_percent)
    total = apply_discount(total, discount_percent)  # BUG: applied twice
    return total
"""

TEST_PY = """\
import unittest
from checkout import checkout_total

class CheckoutTests(unittest.TestCase):
    def test_discount_applied_once(self):
        items = [{"price": 50, "qty": 2}]
        self.assertEqual(checkout_total(items, 10), 90.0)

if __name__ == "__main__":
    unittest.main()
"""

注意 AGENTS.md 里的三条规则:源码在 checkout.py,测试在 tests/ 且只读,调用 finish 之前必须跑 run_tests。这三条规则后面会被 harness 分别落实为路径约束和完成条件。

第二步:定义工具与门禁

工具集保持最小:读文件、写文件、跑测试、结束。每个工具在执行前都要过门禁。

import json
import os
import subprocess
import sys
from pathlib import Path

READ_ONLY = {"tests", "AGENTS.md"}

class ToolGate:
    def __init__(self, workspace: Path):
        self.workspace = workspace.resolve()

    def resolve(self, rel_path: str) -> Path:
        target = (self.workspace / rel_path).resolve()
        if self.workspace not in target.parents and target != self.workspace:
            raise PermissionError(f"path escapes workspace: {rel_path}")
        return target

    def read_file(self, rel_path: str) -> str:
        return self.resolve(rel_path).read_text(encoding="utf-8")

    def write_file(self, rel_path: str, content: str) -> str:
        target = self.resolve(rel_path)
        rel = target.relative_to(self.workspace)
        if rel.parts and rel.parts[0] in READ_ONLY:
            raise PermissionError(f"read-only path: {rel_path}")
        if str(rel) in READ_ONLY:
            raise PermissionError(f"read-only path: {rel_path}")
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_text(content, encoding="utf-8")
        return f"wrote {rel_path} ({len(content)} chars)"

    def run_tests(self) -> str:
        proc = subprocess.run(
            [sys.executable, "-m", "unittest", "discover", "-s", "tests"],
            cwd=self.workspace,
            capture_output=True,
            text=True,
            timeout=60,
        )
        return (proc.stdout + proc.stderr).strip()

这里有三处关键约束。第一,resolve() 把相对路径解析成绝对路径后,检查它是否仍在工作区内,越界直接抛 PermissionError,所以工作区外的 secrets.txt 读不到。第二,写入前检查目标是否落在只读集合里,tests/ 和 AGENTS.md 都改不了。第三,跑测试带 timeout=60,避免一个卡住的测试把整个循环拖死。

第三步:裁剪工具输出

测试日志可能很长,原样塞回上下文会挤掉真正有用的信息。加一个简单的裁剪函数,保留头尾、丢掉中间:

def clip(text: str, limit: int = 2000) -> str:
    if len(text) <= limit:
        return text
    head = text[: limit // 2]
    tail = text[-(limit // 2):]
    return f"{head}\n... [{len(text) - limit} chars omitted] ...\n{tail}"

这就是上下文层里「摘要长工具输出」的最小实现。真实系统里可以做得更聪明,比如只保留失败用例和堆栈,但原则一样:进入上下文的是有用信息,不是原始字节流。

第四步:构建上下文

每一轮循环都重新构建上下文:系统提示里放项目规则,消息列表里放最近的执行结果。

def build_context(agents_md: str, history: list) -> list:
    system = (
        "You are a coding agent working inside a sandboxed workspace.\n"
        "Follow the project rules below.\n\n" + agents_md
    )
    messages = [{"role": "system", "content": system}]
    for item in history[-6:]:
        messages.append({"role": "tool", "content": clip(item)})
    return messages

只保留最近六条工具结果,是上下文预算的一种粗糙但有效的控制方式。真实场景里可以按 token 数计算,而不是按条数。

第五步:状态落盘

进度必须存在模型上下文之外。每次工具调用后写一次状态文件:

class StateStore:
    def __init__(self, workspace: Path):
        self.path = workspace / ".harness" / "state.json"
        self.path.parent.mkdir(parents=True, exist_ok=True)
        self.steps = []

    def record(self, step: int, action: str, result: str) -> None:
        self.steps.append({"step": step, "action": action, "result": clip(result, 500)})
        self.flush(status="running")

    def flush(self, status: str) -> None:
        payload = {"status": status, "steps": self.steps}
        self.path.write_text(json.dumps(payload, indent=2), encoding="utf-8")

状态文件里同时记录 status 和每一步的动作与结果。下一次会话启动时读这个文件,就知道上次停在哪里、哪些动作已经试过。

第六步:主循环

把上面几块拼起来。脚本化模型按顺序回放动作,真实模型则替换成一次 API 调用。

SCRIPTED_ACTIONS = [
    {"tool": "read_file", "args": {"path": "checkout.py"}},
    {"tool": "read_file", "args": {"path": "tests/test_checkout.py"}},
    {"tool": "write_file", "args": {"path": "tests/test_checkout.py", "content": "..."}},
    {"tool": "write_file", "args": {"path": "checkout.py", "content": FIXED_CHECKOUT}},
    {"tool": "run_tests", "args": {}},
    {"tool": "finish", "args": {}},
]

def run_harness(workspace: Path, max_steps: int = 8) -> dict:
    gate = ToolGate(workspace)
    state = StateStore(workspace)
    agents_md = gate.read_file("AGENTS.md")
    history = []

    for step in range(1, max_steps + 1):
        messages = build_context(agents_md, history)
        action = SCRIPTED_ACTIONS[min(step - 1, len(SCRIPTED_ACTIONS) - 1)]
        name, args = action["tool"], action["args"]

        try:
            if name == "read_file":
                result = gate.read_file(args["path"])
            elif name == "write_file":
                result = gate.write_file(args["path"], args["content"])
            elif name == "run_tests":
                result = gate.run_tests()
            elif name == "finish":
                result = gate.run_tests()
                passed = "OK" in result and "FAILED" not in result
                state.record(step, name, result)
                state.flush(status="done" if passed else "rejected")
                return {"status": "done" if passed else "rejected", "output": result}
            else:
                result = f"unknown tool: {name}"
        except PermissionError as exc:
            result = f"blocked: {exc}"

        history.append(f"[{name}] {result}")
        state.record(step, name, result)

    state.flush(status="step_limit")
    return {"status": "step_limit", "steps": state.steps}

这段代码里有几个值得注意的设计。脚本化动作的第三步故意去写 tests/test_checkout.py,会被门禁拦下,返回 blocked,但循环继续,不会因此崩溃。第五步跑测试,此时 bug 已修,测试通过。第六步调用 finish,harness 不信任这个声明,自己再跑一遍测试,确认通过才把状态写成 done。如果测试没过,状态是 rejected,循环不会因为模型说「完成」就结束。

如果步数用尽仍未完成,状态写成 step_limit,并把已记录的步骤一并返回,方便人接手。

第七步:接真实模型

把脚本化动作换成一次模型调用即可。适配器只负责把上下文发出去、把返回的工具调用解析出来:

def propose_action(messages: list) -> dict:
    import anthropic
    client = anthropic.Anthropic()
    resp = client.messages.create(
        model="your-model-id",
        max_tokens=1024,
        system=messages[0]["content"],
        messages=[m for m in messages if m["role"] != "system"],
        tools=TOOL_SCHEMAS,
    )
    for block in resp.content:
        if block.type == "tool_use":
            return {"tool": block.name, "args": block.input}
    return {"tool": "finish", "args": {}}

模型 ID、可用工具格式、计费方式都会变化,具体以厂商官网当前信息为准。适配器之外的所有逻辑——门禁、裁剪、状态、校验、步数上限——都不依赖具体模型,这正是把 harness 和模型解耦的价值。

一个完整示例

把上面的片段按顺序放进 harness_demo.py,补上 make_workspace 和 FIXED_CHECKOUT,就可以从头跑一遍。下面是完整流程和预期结果。

搭建工作区

import tempfile

FIXED_CHECKOUT = """\
def apply_discount(price, percent):
    return round(price * (1 - percent / 100), 2)

def checkout_total(items, discount_percent=0):
    subtotal = sum(i["price"] * i["qty"] for i in items)
    return apply_discount(subtotal, discount_percent)
"""

def make_workspace() -> Path:
    root = Path(tempfile.mkdtemp(prefix="harness-"))
    ws = root / "workspace"
    (ws / "tests").mkdir(parents=True)
    (ws / "AGENTS.md").write_text(AGENTS_MD, encoding="utf-8")
    (ws / "checkout.py").write_text(CHECKOUT_PY, encoding="utf-8")
    (ws / "tests" / "test_checkout.py").write_text(TEST_PY, encoding="utf-8")
    (root / "secrets.txt").write_text("API_KEY=do-not-read", encoding="utf-8")
    return ws

if __name__ == "__main__":
    ws = make_workspace()
    print("workspace:", ws)
    outcome = run_harness(ws, max_steps=8)
    print("status:", outcome["status"])
    print((ws / ".harness" / "state.json").read_text(encoding="utf-8"))

预期行为

按脚本化动作顺序,循环会经历这些步骤:

  1. 读 checkout.py,拿到带 bug 的源码。
  2. 读 tests/test_checkout.py,了解测试期望值。
  3. 尝试写 tests/test_checkout.py,被门禁拒绝,返回 blocked: read-only path。
  4. 写 checkout.py,写入修正后的版本。
  5. 跑测试,输出里出现 OK。
  6. 调用 finish,harness 自己再跑一次测试,确认通过,状态写为 done。

最终 .harness/state.json 里能看到六条记录,其中第三条是拒绝记录。这条记录本身就是审计轨迹:它证明智能体尝试过改测试,而 harness 拦住了。

再试两个变体,能更清楚地看到各层的作用。把 max_steps 改成 3,循环会在写完 checkout.py 之前停下,状态是 step_limit,状态文件里保留已执行的三步,人可以据此续做。把 FIXED_CHECKOUT 换回带 bug 的版本,finish 时测试不通过,状态是 rejected,模型声称的「完成」被证据推翻。

如何改进一个 harness

最小版本跑通之后,改进方向基本沿着六层展开,按收益排序大致如下。

先补校验

校验是最值得先投入的一层。示例里只用了一个 unittest 文件,真实项目可以叠加类型检查、构建、lint、端到端测试。关键不是检查多,而是在任务开始前就把「完成」定义清楚,并让 harness 能独立执行这些检查。检查越接近真实验收标准,模型自我声明的可信度就越不重要。

再收紧权限

示例的门禁只做了路径约束和只读集合。可以继续加:按工具区分权限、对写操作要求审批、把网络访问单独开关、把敏感路径加入拒绝列表。原则是最小权限——每个工具只给完成当前任务所需的最窄范围。

优化上下文

示例用「保留最近六条」控制上下文,比较粗糙。可以换成按 token 预算裁剪、对长日志只保留失败用例、把关键发现写成笔记文件供后续读取、在对话过长时做压缩摘要。目标是让进入上下文的信息密度更高,而不是更多。

让状态更有用

状态文件目前只记录动作和结果。可以扩展成任务计划、决策记录、待办清单,让下一次会话不只是知道「停在哪」,还知道「为什么停在这里、下一步打算做什么」。

编排按需增加

只有当任务确实能干净切分时才引入多智能体或复杂工作流。每个新增角色都需要清晰职责、足够上下文和校验方式,否则只是把复杂度从一个地方搬到另一个地方。

把可观测性做扎实

工具调用历史、日志、追踪是排查失败的入口。示例里状态文件已经承担了一部分职责,生产环境通常还需要结构化日志和追踪系统,把每次调用的输入、输出、耗时、结果都留下来。

harness 会过时,也有它保证不了的事

harness 不是一次写完就固定的东西。模型能力在变,工具生态在变,项目规则也在变,围绕模型搭的那层系统需要跟着调整。今天为了弥补模型短板而加的脚手架,明天可能因为模型变强而变得多余;反过来,新的能力也会带来新的风险面,需要新的限制。

更重要的是,harness 不保证结果正确。它能做的是:让模型看到该看的信息、只做被允许做的事、在明确的边界内行动、把进度留下来、用证据判断是否完成。这些措施大幅降低出错概率和出错代价,但不能把概率降到零。校验通过只说明通过了已定义的检查,不说明代码在所有场景下都正确。

所以设计 harness 时,心态应该是「假设它会犯错」,而不是「设计一个不会犯错的系统」。权限、沙箱、审批、步数上限、重试上限,都是在这个假设下让错误的代价保持可控。

注意事项

  • 完成判定权必须在 harness 手里。 模型说完成不算数,测试通过才算。示例里 finish 会触发一次独立测试运行,就是为了落实这一点。
  • 只读约束要覆盖所有写入路径。 示例同时检查了路径首段和完整相对路径,避免 tests 与 AGENTS.md 被绕过。真实系统里还要考虑符号链接、大小写、路径规范化等细节。
  • 外部内容一律当数据。 网页、issue 评论、文件内容都可能夹带指令。读取不可信文本的步骤不应同时持有敏感工具,其后接高风险动作时要审批。
  • 循环必须有界。 步数、时间、token、重试次数都要设上限,触达上限时报告状态而不是静默失败。
  • 密钥不要进代码和日志。 用环境变量注入,并注意审计日志可能记录工具参数。
  • 模型 ID、SDK 版本、计费与配额会变。 接入真实模型前,以厂商官网当前信息为准。
  • 示例的脚本化模型只用于演示。 它按固定顺序回放动作,输出稳定,但不代表真实模型的行为;真实模型的动作序列需要由模型根据上下文生成。