1. 这不是又一个“AI 网站”,而是一套可验证、可审计、可复用的 Agent 能力基础设施
“为了让 AI 更可靠,我做了个给 Agent 用的网站(含 skills)”——这句话乍看像一句轻描淡写的个人项目陈述,但拆开来看,它其实藏着当前大模型应用落地中最棘手的三个断层:可靠性断层(AI 输出飘忽、不可控)、能力断层(Agent 不会“动手”,只懂“动嘴”)、协作断层(不同 Agent 之间无法共享技能、复用逻辑)。我试过把技能写进 system prompt,也试过用 JSON Schema 强制约束工具调用,结果都一样:一旦任务链变长、外部服务响应波动、用户输入稍有歧义,整个流程就卡在某个环节,报错信息还全是“tool call failed”这种无效提示。后来在某跨平台系统里做智能客服模块时,我们团队被逼着重构了整套技能交付方式:不再让每个 Agent 自己“记住”怎么查订单、怎么发短信、怎么读 Excel,而是让它们统一去一个地方“认领任务”——那个地方,就是我现在说的这个网站。
它不是一个前端展示页,也不是一个 API 文档集合。它是一套面向 Agent 的技能操作系统:每个 skill 是一个带契约声明、带沙箱执行环境、带结构化反馈通道的独立单元;网站本身是这个操作系统的控制台、注册中心和可观测入口。关键词里没填内容,但实际运行中,最常被调用的三个核心词是skill contract(技能契约)、execution trace(执行轨迹)和human-in-the-loop fallback(人工兜底通道)。这三者共同构成了“更可靠”的底层支点。比如,当某个 skill 声明自己能“解析 PDF 表格并返回 JSON”,它就必须在契约中明确写出支持的 PDF 版本范围、最大页数、表格嵌套深度限制;网站不光提供调用接口,还会在每次执行后生成带时间戳、输入快照、中间日志、输出校验结果的完整 trace;更重要的是,如果某次解析失败且置信度低于阈值,系统不会直接抛异常,而是自动触发预设的人工审核队列,并把上下文打包推送给值班人员——这才是真正把“可靠”从口号落到动作上。
这个网站的目标用户非常明确:不是终端用户,而是正在构建复杂 Agent 工作流的工程师、产品经理和 AI 架构师。它解决的不是“能不能用”,而是“敢不敢在生产环境长期用”。你不需要把它理解成一个新框架,而应该看作一套可插拔的可靠性增强层——可以无缝集成到 LangChain、LlamaIndex、甚至自研的调度引擎里。后面我会一层层拆开它的设计逻辑、实操细节和那些踩出来的坑,告诉你为什么一个简单的“网站”能成为 Agent 可靠性的关键锚点。
2. Skill 不是函数,是带 SLA 的服务单元:契约驱动的设计哲学
很多人一听到“Agent 技能”,第一反应是写个 Python 函数,再包一层 tool wrapper 就完事。我最初也是这么干的,结果在压测阶段发现:同一个 PDF 解析 skill,在本地跑 100 次成功率 99.8%,但部署到 k8s 集群后,因为内存限制和 PDF 解析库的线程安全问题,失败率飙升到 12%。问题出在哪?不是代码写错了,而是我们根本没给 skill 定义“它到底承诺什么”。
真正的 skill 必须是一个带服务等级协议(SLA)的服务单元,而网站就是这个 SLA 的登记处与仲裁方。它强制要求每个 skill 在注册时提交一份机器可读、人类可审的契约(Contract),这份契约不是文档,而是运行时生效的约束规则。我们目前采用 YAML + JSON Schema 混合定义,结构分三层:
2.1 输入契约(Input Contract):拒绝模糊的“任意字符串”
传统 tool 参数定义往往只写"query": "string",这等于没说。我们的契约强制要求:
- 类型必须精确到子类(如
date_string: { type: "string", format: "date", pattern: "^\\d{4}-\\d{2}-\\d{2}$" }) - 必填字段必须标注
required: true - 字段长度、数值范围、枚举值必须显式声明(如
page_range: { type: "array", minItems: 1, maxItems: 5, items: { type: "integer", minimum: 1, maximum: 1000 } })
提示:我们曾因漏写
maximum: 1000,导致某次用户传入page_range: [1, 9999],PDF 解析器直接 OOM。契约不是形式主义,是第一道防线。
2.2 执行契约(Execution Contract):把“黑盒”变成“灰盒”
这是最常被忽略的部分。契约不仅要说明“能接受什么”,更要说明“在什么条件下能跑通”。我们要求每个 skill 注册时声明:
- 资源需求:CPU 核心数、内存上限(MB)、是否需要 GPU
- 依赖服务:调用的外部 API 地址、认证方式(API Key 或 OAuth)、超时时间(毫秒)
- 执行环境:Python 版本、必需的 pip 包及版本(如
pypdf==3.17.3)、是否允许网络访问
网站后台会根据这些声明,自动将 skill 分配到匹配的执行节点池。比如声明需要 GPU 的 skill,绝不会被调度到 CPU 节点;声明依赖https://api.xxx.com的 skill,其容器网络策略会自动放行该域名,其他外网请求一律拦截。这直接杜绝了“本地能跑,线上报错”的经典困境。
2.3 输出契约(Output Contract):让 Agent 真正“读懂”返回值
很多 Agent 失败,不是因为 skill 没执行,而是因为返回的 JSON 结构和 LLM 期望的不一致。我们的输出契约强制要求:
- 返回体必须是严格符合 JSON Schema 的对象(非数组、非原始类型)
- 每个字段必须有
description,说明业务含义(如order_items: { description: "用户下单的商品列表,不含已取消订单项" }) - 必须声明
error_code字段(枚举值:success,input_invalid,service_unavailable,rate_limited,human_review_required),Agent 可据此做分支决策
实测下来,这套契约机制让 skill 的复用率提升了 3.2 倍。原因很简单:当 A 团队开发的“航班状态查询”skill 契约里写着max_retries: 3, timeout_ms: 8000, error_codes: ["not_found", "delayed", "cancelled"],B 团队在编排旅行规划 Agent 时,就能直接基于这些字段设计重试逻辑和用户提示话术,无需再翻源码或猜行为。
3. 网站不是静态页面,而是 Agent 的“技能银行”与“运维看板”
把 skill 契约存进数据库,再做个 CRUD 管理界面,这谁都会。但真正让这个网站成为 Agent 可靠性基石的,是它作为动态运行中枢的能力。它不只是“存放技能的地方”,更是技能的“发行方”、“监控员”和“调解人”。我把网站的核心能力拆成三个不可分割的模块,它们共同构成了 Agent 的“技能银行”体系。
3.1 技能注册与版本管理:一次发布,全域生效
注册流程远不止上传代码。开发者通过网站 UI 或 CLI 工具提交:
- 契约文件(YAML)
- 执行代码(Python/JS 文件,或 Dockerfile)
- 测试用例集(至少 3 个覆盖边界条件的输入-期望输出对)
网站收到后,会启动全自动流水线:
- 静态检查:验证契约语法、Schema 合法性、字段描述完整性
- 沙箱执行:在隔离环境中运行测试用例,记录实际输出、耗时、内存占用
- 契约符合性审计:比对实际输出与契约声明的 schema 是否 100% 匹配
- 版本冻结:只有全部检查通过,才生成唯一版本号(如
v1.2.4-20240522-1432),并写入全局 registry
注意:网站禁止覆盖已发布的版本。任何修改都必须升版。这确保了 Agent 调用的 skill 版本永远可追溯、可回滚。我们曾因某次紧急修复跳过测试直接覆盖 v1.1,导致下游 7 个 Agent 的解析逻辑集体失效——这个教训让我们把“不可覆盖”写进了网站的宪法级配置。
3.2 统一调用网关:为每个 skill 建立“数字身份证”
Agent 不直接调用 skill 代码,而是通过网站提供的统一 HTTP 网关。调用 URL 格式为:POST https://skills.yourdomain.com/v1/execute/{skill_id}/{version}
网关在转发前完成三件事:
- 身份核验:验证调用方 Agent 的 token(绑定其所属项目、环境、权限组)
- 契约校验:检查请求 body 是否符合该 skill 当前版本的输入契约,不符合则立即返回
400 Bad Request并附详细错误路径(如$.page_range[1]: must be <= 1000) - 流量整形:根据 skill 契约声明的
rate_limit(如10 req/sec)和burst_capacity(如20),实施令牌桶限流
最关键的是,网关会为每次调用生成一个全局唯一的trace_id,并注入到 skill 执行环境的上下文中。这个 ID 贯穿整个生命周期:从 Agent 发起请求,到 skill 内部日志,再到最终返回给 Agent 的 response header 中。它让问题排查从“大海捞针”变成“按图索骥”。
3.3 实时可观测看板:把“黑盒执行”变成“透明流水线”
网站后台的 Dashboard 不是装饰品,而是运维决策依据。它实时聚合所有 skill 的 5 个核心指标:
| 指标 | 计算方式 | 可靠性意义 |
|---|---|---|
| Success Rate (1m) | 成功调用数 / 总调用数 | 发现瞬时抖动(如 DNS 故障) |
| P95 Latency (ms) | 响应时间第95百分位 | 识别慢技能拖垮整体链路 |
| Error Code Distribution | 各 error_code 占比 | 判断失败根因(是输入问题?服务问题?还是需人工介入?) |
| Resource Utilization | CPU/Mem 使用率峰值 | 预警资源瓶颈,指导扩容 |
| Human Review Queue Size | 待处理人工审核数 | 衡量自动化边界,驱动 skill 迭代 |
我们曾通过看板发现,“发票OCR”skill 的human_review_required错误率在每周二上午 10 点陡增 40%。下钻日志发现,该时段某合作方批量上传的发票 PDF 元数据损坏,导致解析器无法定位表格区域。于是我们立刻在契约中新增一条规则:“若检测到 PDF 元数据异常,自动降级为全页 OCR 模式”,并将此规则同步到所有调用方。没有这个看板,这个问题可能要等用户投诉才能暴露。
4. 可靠性不是堆参数,而是设计“失败剧本”:从兜底到进化
很多人以为提升 AI 可靠性就是加更多规则、设更严阈值、上更强硬件。但我在多个项目里反复验证过:最可靠的系统,不是永不失败的系统,而是失败时路径最清晰、恢复最迅速、学习最及时的系统。这个网站的核心价值,恰恰体现在它如何优雅地处理“失败”。
4.1 三级失败响应机制:从自动重试到人工接管
我们为每个 skill 配置了精细的失败响应策略,由网站网关在运行时执行:
Level 1:自动重试
仅对service_unavailable和rate_limited错误生效。重试次数(默认 2)、退避策略(指数退避,base=100ms)、最大总耗时(默认 3s)全部在契约中声明。网关自动注入X-Retry-Countheader,skill 代码可据此调整行为(如首次失败降级,二次失败直接报错)。Level 2:智能降级
对input_invalid错误,网关不直接返回错误,而是尝试“语义修复”:- 若
date_string格式错误但包含数字,尝试提取并格式化("2024/05/22"→"2024-05-22") - 若
page_range超出 PDF 实际页数,自动裁剪至有效范围 - 修复后的输入连同原始输入、修复说明,一并写入 trace log,供后续审计。
- 若
Level 3:人工兜底通道
当 skill 返回human_review_required,或连续 3 次 Level 1 重试失败,网关立即触发兜底流程:- 将完整 trace(含输入、输出、日志、截图)打包为工单
- 根据 skill 的
review_team字段,路由至对应值班组(如finance-team) - 通过企业微信/钉钉机器人推送告警,并附一键直达链接
- 工单处理完成后,人工填写“修正后输出”和“失败根因标签”(如
pdf_corrupted,ambiguous_input)
提示:这个通道不是摆设。我们要求所有人工处理必须在 15 分钟内响应,4 小时内闭环。它倒逼我们持续优化 skill,因为每一次人工介入,都意味着自动化流程的失败。
4.2 基于失败数据的 skill 进化闭环
网站不只是记录失败,更驱动 skill 进化。我们建立了“失败即训练数据”的闭环:
- 自动聚类:每天凌晨,系统扫描所有
human_review_required工单,用语义相似度(Sentence-BERT)聚类,识别高频失败模式(如“多页合同首尾页页眉页脚干扰表格识别”) - 生成合成数据:针对每个聚类,自动生成 50-100 条覆盖该场景的合成测试用例(输入+期望输出)
- 触发 CI 流水线:将合成数据注入 skill 的测试集,强制运行。若失败,则创建 GitHub Issue,标题为
[Auto-Generated] Fix for failure pattern: {cluster_name},并关联原始工单 - 效果验证:修复 PR 合并后,系统自动回放该聚类下所有历史失败工单,验证修复效果
这个闭环让我们的“合同解析”skill 在 3 个月内,human_review_required率从 18.7% 降至 2.3%。它证明:可靠性不是静态目标,而是由失败数据持续喂养的动态能力。
4.3 Agent 编排层的协同设计:让“网站”成为 Agent 的“常识库”
最后一点至关重要:网站的价值,只有当 Agent 的编排逻辑主动与之协同时,才能完全释放。我们要求所有接入网站的 Agent,在设计时必须内置三项能力:
- 契约感知:Agent 在调用前,先 GET
https://skills.yourdomain.com/v1/skills/{id}/contract,动态获取最新契约,而非硬编码参数结构。 - trace_id 透传:Agent 在发起下游调用(如调用另一个 skill 或外部 API)时,必须将上游传来的
trace_id注入到所有相关请求头中,形成全链路追踪。 - 失败意图理解:Agent 的 LLM 提示词中,必须包含对
error_code的明确解释和应对指令。例如,当收到rate_limited,提示词应写:“你已被限流,请向用户说明‘系统繁忙,请稍后再试’,并提供 30 秒后自动重试的选项”。
我们曾有个客服 Agent,因为没做契约感知,当“订单查询”skill 升级增加include_history: boolean字段后,它仍按旧契约传参,导致大量input_invalid。后来我们把它改造成:每次调用前,先用极简的 LLM(如 Phi-3)解析契约变更摘要,再动态构造参数。虽然多了一次小模型调用,但换来的是 99.99% 的兼容性保障。
5. 从零搭建:一个可运行的最小可行网站(含核心代码片段)
理论讲完,现在给你一个真正能跑起来的最小可行版本(MVP)。它不追求功能完备,但保证核心链路完整:契约注册、沙箱执行、网关调用、基础看板。技术栈选型原则是:够用、易部署、无 vendor lock-in。我们用 Python(FastAPI)+ SQLite(开发)/ PostgreSQL(生产)+ Redis(缓存/队列)+ Vue3(前端),所有组件均可单机 Docker Compose 一键启动。
5.1 核心数据模型:契约、技能、执行记录
数据库只需三张表,结构精简但覆盖全部关键信息:
-- 技能主表,存储元信息 CREATE TABLE skills ( id TEXT PRIMARY KEY, -- 唯一ID,如 "pdf-parser" name TEXT NOT NULL, -- 显示名称 description TEXT, -- 简介 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 契约版本表,每次发布生成新记录 CREATE TABLE skill_contracts ( id SERIAL PRIMARY KEY, skill_id TEXT NOT NULL REFERENCES skills(id), version TEXT NOT NULL, -- 如 "v1.0.0" input_schema JSONB NOT NULL, -- 输入JSON Schema output_schema JSONB NOT NULL, -- 输出JSON Schema execution_config JSONB, -- 执行契约:资源、超时、依赖等 is_active BOOLEAN DEFAULT TRUE, -- 是否为当前活跃版本 UNIQUE(skill_id, version) ); -- 执行记录表,记录每次调用详情 CREATE TABLE execution_logs ( id SERIAL PRIMARY KEY, trace_id TEXT UNIQUE NOT NULL, -- 全局唯一追踪ID skill_id TEXT NOT NULL, version TEXT NOT NULL, input_data JSONB NOT NULL, -- 原始输入 status TEXT NOT NULL, -- "success", "failed", "pending" output_data JSONB, -- 成功时的输出 error_code TEXT, -- 失败时的错误码 error_message TEXT, -- 失败时的详细信息 duration_ms INTEGER, -- 执行耗时(毫秒) started_at TIMESTAMP, -- 开始时间 finished_at TIMESTAMP, -- 结束时间 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );注意:
input_schema和output_schema字段用JSONB(PostgreSQL)或TEXT(SQLite,存 JSON 字符串),便于用jsonschema库直接校验。execution_config存储如{"timeout_ms": 5000, "cpu_limit": "0.5", "memory_limit_mb": 512}等键值对。
5.2 关键后端逻辑:契约校验与沙箱执行
FastAPI 的核心 endpoint 是/v1/execute/{skill_id}/{version}。以下是其核心逻辑骨架(省略异常处理和依赖注入):
# fastapi_app.py from fastapi import FastAPI, HTTPException, Depends from pydantic import BaseModel, ValidationError import jsonschema from jsonschema import validate import subprocess import tempfile import os import time import uuid app = FastAPI() class ExecuteRequest(BaseModel): input: dict # 动态结构,由契约决定 @app.post("/v1/execute/{skill_id}/{version}") async def execute_skill(skill_id: str, version: str, request: ExecuteRequest): # 1. 从DB加载契约 contract = get_contract_from_db(skill_id, version) # 伪代码 if not contract: raise HTTPException(404, f"Skill {skill_id} version {version} not found") # 2. 校验输入是否符合契约 try: validate(instance=request.input, schema=contract.input_schema) except ValidationError as e: raise HTTPException(400, f"Input validation failed: {e.message}") # 3. 生成唯一trace_id trace_id = str(uuid.uuid4()) # 4. 记录执行开始 log_id = insert_execution_log( trace_id=trace_id, skill_id=skill_id, version=version, input_data=request.input, status="pending" ) # 5. 沙箱执行(简化版:用subprocess调用Python脚本) start_time = time.time() try: # 创建临时目录存放skill代码和输入 with tempfile.TemporaryDirectory() as tmpdir: # 将skill代码(假设已存DB)写入 tmpdir/skill.py write_skill_code_to_file(tmpdir, skill_id, version) # 将输入写入 tmpdir/input.json with open(f"{tmpdir}/input.json", "w") as f: json.dump(request.input, f) # 执行:python skill.py input.json result = subprocess.run( ["python", f"{tmpdir}/skill.py", f"{tmpdir}/input.json"], capture_output=True, text=True, timeout=contract.execution_config.get("timeout_ms", 5000) / 1000 ) if result.returncode != 0: # 抛出skill内部错误 raise RuntimeError(f"Skill execution failed: {result.stderr}") # 解析输出 output_data = json.loads(result.stdout) # 6. 校验输出是否符合契约 validate(instance=output_data, schema=contract.output_schema) # 7. 记录成功 duration_ms = int((time.time() - start_time) * 1000) update_execution_log( log_id=log_id, status="success", output_data=output_data, duration_ms=duration_ms, finished_at=time.time() ) return {"trace_id": trace_id, "output": output_data} except jsonschema.ValidationError as e: # 输出校验失败 update_execution_log( log_id=log_id, status="failed", error_code="output_invalid", error_message=f"Output validation failed: {e.message}", finished_at=time.time() ) raise HTTPException(500, f"Output validation failed: {e.message}") except subprocess.TimeoutExpired: update_execution_log( log_id=log_id, status="failed", error_code="timeout", error_message="Execution timed out", finished_at=time.time() ) raise HTTPException(500, "Execution timed out") except Exception as e: # 兜底错误 update_execution_log( log_id=log_id, status="failed", error_code="internal_error", error_message=str(e), finished_at=time.time() ) raise HTTPException(500, f"Internal error: {str(e)}")这个骨架展示了最关键的三个动作:契约驱动的输入/输出校验、trace_id 全链路注入、沙箱超时控制。它足够简单,却已具备生产可用的可靠性基线。
5.3 前端看板:用 50 行 Vue 代码实现核心监控
Vue3 的 Dashboard 组件,核心是实时拉取指标并渲染图表。我们用 ECharts(轻量级)实现,关键代码如下:
<!-- Dashboard.vue --> <template> <div class="dashboard"> <h2>Skills Health Overview</h2> <div class="metrics-grid"> <MetricCard title="Overall Success Rate" :value="metrics.successRate" unit="%" :trend="metrics.successRateTrend" /> <MetricCard title="Avg. Latency (P95)" :value="metrics.p95Latency" unit="ms" :trend="metrics.p95LatencyTrend" /> <MetricCard title="Human Review Queue" :value="metrics.humanQueueSize" unit="items" :trend="metrics.humanQueueTrend" /> </div> <div class="chart-container"> <h3>Recent Error Distribution</h3> <div ref="errorChart" class="chart"></div> </div> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' import * as echarts from 'echarts' const errorChart = ref(null) let chartInstance = null let intervalId = null // 初始化图表 onMounted(() => { chartInstance = echarts.init(errorChart.value) renderErrorChart() // 每30秒刷新一次 intervalId = setInterval(() => { renderErrorChart() }, 30000) }) onUnmounted(() => { if (chartInstance) chartInstance.dispose() if (intervalId) clearInterval(intervalId) }) const renderErrorChart = async () => { try { const res = await fetch('/api/v1/metrics/error-distribution') const data = await res.json() const option = { tooltip: { trigger: 'item' }, series: [{ type: 'pie', data: data.map(item => ({ name: item.error_code, value: item.count })), radius: ['40%', '70%'] }] } chartInstance.setOption(option, true) } catch (err) { console.error('Failed to load error metrics:', err) } } </script>这个看板没有炫技,但它把最影响可靠性的三个信号——成功率、延迟、人工队列——放在最醒目的位置,并用饼图直观展示失败分布。工程师扫一眼,就能判断当前系统健康度。
6. 我的真实经验:哪些“可靠”是假象,哪些是真金
做了三年 Agent 基础设施,我亲手搭过 4 个类似网站,也接手维护过 2 个别人留下的“高可用”系统。有些看似很“可靠”的设计,实则是脆弱的假象;而有些不起眼的细节,才是真正的护城河。分享几个血泪换来的认知:
6.1 “100% 自动化”是最危险的幻觉
我见过最“完美”的系统:所有 skill 都有契约、都有测试、都有监控,失败率报表常年显示 99.99%。直到某天凌晨,一个human_review_required工单堆积到 200+,原因是合作方突然更改了 PDF 模板,而我们的“自动降级”逻辑只处理了 3 种已知模板变体,对第 4 种完全失能。那个系统的问题不在技术,而在心态——它把“自动化覆盖率”当成了可靠性指标,却忘了真实世界永远比契约更复杂。我的经验是:把 5% 的精力放在“如何让自动化更聪明”,把 95% 的精力放在“如何让人工兜底更快、更准、更少打扰用户”。现在我们网站的首页,最显眼的不是成功率数字,而是“当前人工队列平均响应时间:2.3 分钟”。
6.2 “强一致性”在 Agent 场景中往往是毒药
很多工程师第一反应是:契约必须强一致!输入必须 100% 符合 schema,否则立刻拒掉。这在金融交易场景是对的,但在 Agent 交互中,它制造了大量不必要的失败。比如用户说“查一下昨天的订单”,LLM 生成的输入是{"date_string": "yesterday"},而契约要求YYYY-MM-DD。强硬拒掉,Agent 只能尴尬回复“请提供标准日期格式”。我们现在的做法是:契约定义“可接受的模糊输入”,网关做“无损转换”。yesterday、last week、2 days ago都被网关识别并转换为标准日期,转换过程记录在 trace log 中。这大幅降低了用户侧摩擦,而真正的业务风险(如金额、ID)依然保持强校验。可靠性,有时恰恰藏在“恰到好处的宽容”里。
6.3 最有效的可靠性提升,来自“让 Agent 自己报告问题”
我们曾花大力气做复杂的日志分析、异常检测算法,效果平平。后来换了个思路:在所有 skill 的返回体中,强制增加一个diagnosis字段,由 skill 自己填写。例如:
{ "output": { "order_status": "shipped" }, "diagnosis": { "confidence": 0.92, "warnings": ["Found ambiguous tracking number, used fallback regex"], "suggestions": ["Consider adding more tracking number patterns to config"] } }Agent 的编排逻辑会读取diagnosis.confidence,低于阈值(如 0.85)就自动触发人工审核;读取warnings,就能在用户界面上给出更精准的提示(如“快递单号识别存在不确定性,结果仅供参考”)。这个字段不增加任何外部依赖,却让整个系统的“自省能力”跃升一个层级。它提醒我:最了解失败的,永远是失败发生的那个点本身,而不是远处的监控中心。
最后想说,做这个网站的初衷,从来不是为了造一个“酷炫的新玩意”。它是我在无数个深夜调试失败链路、安抚暴躁产品经理、向客户解释“为什么 AI 又没答对”之后,给自己、也给同行的一份务实答案:可靠性不是玄学,它是一条条契约、一次次失败、一个个被认真对待的error_code,最终沉淀下来的工程习惯。如果你也在为 Agent 的飘忽不定而头疼,不妨从定义第一个 skill 契约开始——那不是写代码,而是和你的 AI,签下第一份信任协议。