AI Agent Harness Engineering 的工具返回如何结构化:JSON 约定最佳实践
AI Agent Harness Engineering 的工具返回如何结构化:JSON 约定最佳实践
关键词:AI Agent、Harness 框架、工具调用、JSON 结构化、Schema 校验、自动修正、链路追踪
摘要:本文从AI Agent工具调用的核心痛点出发,深入浅出地讲解了Harness Engineering中工具返回结构化JSON的设计思路、核心约定、最佳实践和落地实现。通过生活化比喻、代码示例、架构图和实际场景拆解,帮助开发者从0到1搭建一套稳定、兼容、易维护的工具返回JSON规范,解决Agent工具调用时格式混乱、链路崩溃、调试困难等高频问题,大幅提升Agent系统的可用性和可靠性。
背景介绍
目的和范围
你有没有遇到过这种情况:辛辛苦苦搭好的AI Agent,调用天气工具的时候返回了一段自然语言"今天北京天气晴,温度25度哦😀",结果Agent要提取温度做后续的出行建议的时候直接崩了?或者调用计算器工具,返回的JSON外面套了三层markdown代码块,解析的时候直接报JSON格式错误?这就是今天我们要解决的核心问题:AI Agent调用工具的返回结果怎么结构化,才能让Agent看得懂、链路不崩溃、调试够方便。
本文的范围覆盖了AI Agent Harness(也就是Agent的"安全管控框架")中工具返回JSON约定的全流程:从核心概念设计、基础字段约定、不同场景的扩展约定、校验逻辑实现、自动修正策略,到企业级落地的最佳实践,以及未来的发展趋势。不管你是做个人Agent玩具,还是做企业级生产可用的Agent系统,都能直接复用本文的方案。
预期读者
- AI Agent应用开发者、框架开发者
- 企业级AI系统架构师、运维人员
- 对AI Agent工具调用感兴趣的技术爱好者
- 需要对接大模型工具调用能力的后端开发者
文档结构概述
本文会先从生活比喻入手,把核心概念讲明白,然后拆解核心约定的每一个设计点,给出正例反例,再一步步教你写代码实现整个校验、修正、封装的流程,最后通过实际项目落地的案例讲解怎么用,还有常见的坑和解决办法。
术语表
核心术语定义
- AI Agent Harness:给AI Agent套的"安全管控安全带",负责管理Agent的工具调用、输入输出校验、错误处理、链路追踪等能力,避免Agent乱跑、调用失败导致整个系统崩溃。
- 工具调用:AI Agent为了完成用户任务,调用外部能力的过程,比如查天气、算数学题、查数据库、调用API等,相当于人类伸手拿工具干活。
- JSON约定:所有工具返回结果都要遵守的JSON格式规范,相当于外卖平台要求所有商家都用统一的打包盒打包餐品,骑手拿到就知道怎么送、用户拿到就知道怎么拆。
- JSON Schema:用来描述JSON约定的规则文件,相当于打包盒的模具,用来检查商家返回的餐品是不是符合打包要求。
- 自动修正:当工具返回的JSON有小问题的时候(比如多了代码块、字段名写错了),Harness自动把它改成符合约定的格式,不用重新调用工具,提升成功率。
相关概念解释
- 幻觉:大模型生成不符合事实或者不符合格式要求的内容,比如本来要返回JSON,结果生成了一段小说。
- 链路追踪:给每一次工具调用分配一个唯一的ID,把整个Agent的调用链路串起来,出问题的时候可以顺着ID查到每一步的返回结果,相当于快递的物流单号。
缩略词列表
- Harness:AI Agent Harness 管控框架
- Schema:JSON Schema 格式校验规则
- Trace ID:链路追踪唯一标识
核心概念与联系
故事引入
我们可以把整个AI Agent的工作流程比作外卖配送的流程:
- 用户就是点外卖的客人,给Agent下订单"帮我订一份明天去上海的行程"
- Agent就是外卖骑手,要完成客人的订单,需要调用不同的工具:查天气、查高铁票、订酒店
- Harness就是外卖平台,负责给骑手派单、检查商家的餐品是不是符合要求、有没有撒漏、出问题了帮骑手协调
- 工具就是各个商家:天气商家、高铁票商家、酒店商家
- JSON约定就是平台要求商家用的统一打包盒:必须有餐品区、小票区、订单号区,不能用塑料袋随便装,不然骑手拿了撒漏、客人拿到不知道怎么拆
如果没有统一的打包约定,有的商家用塑料袋装汤、有的商家用纸箱装饭、有的商家小票贴在盒子里面,骑手根本没法送,客人拿到也没法吃,整个配送链路就崩了。这就是为什么我们需要统一的JSON约定。
核心概念解释
核心概念一:什么是工具返回结构化?
工具返回结构化就是要求工具不管执行成功还是失败,都返回固定格式的结果,而不是随机的自然语言或者乱码。就像你去ATM机取钱,不管取钱成功还是失败,屏幕上都会显示固定的内容:成功了显示余额、取款金额,失败了显示错误原因(密码错、余额不足、机器故障),不会随便给你吐一堆乱码。
核心概念二:为什么选JSON作为结构化格式?
我们有很多结构化的格式可以选:XML、YAML、Protobuf、JSON,为什么选JSON?
- 人类看得懂:JSON是纯文本,结构清晰,调试的时候一眼就能看到内容,不用解码
- 大模型看得懂:大模型训练的时候见过无数JSON,生成符合JSON格式的内容比其他格式容易得多,幻觉概率低
- 所有编程语言都支持:不管你用Python、Java、Go写Agent,都有成熟的JSON解析库,不用额外开发
- 校验成本低:有成熟的JSON Schema工具可以自动校验格式,不用自己写正则表达式匹配
核心概念三:什么是JSON Schema校验?
JSON Schema就是你给工具返回的JSON定的规则:必须有哪些字段、每个字段是什么类型、取值范围是什么。比如你定规则:返回的JSON必须有code字段,是数字,只能是200/400/500;必须有msg字段,是字符串;必须有data字段,可以是任意类型。Schema校验就是把工具返回的JSON和这个规则比对,符合就通过,不符合就报错,相当于外卖平台的质检员检查商家的打包盒是不是符合要求。
核心概念之间的关系
我们可以把几个核心概念的关系比作足球队:
- JSON约定是球队的战术规则:每个人站什么位置、跑什么路线
- JSON Schema是裁判:负责检查大家是不是遵守战术规则
- Harness是教练:负责安排战术、检查球员执行情况、出问题了及时调整
- 工具是球员:按照战术规则踢比赛,返回结果
- Agent是前锋:拿到球员传过来的球(结构化结果),完成射门(用户任务)
| 概念 | 角色 | 核心作用 |
|---|---|---|
| JSON约定 | 战术规则 | 统一所有工具的返回格式 |
| JSON Schema | 裁判 | 校验返回结果是否符合规则 |
| Harness | 教练 | 管控整个工具调用流程、处理错误 |
| 工具 | 球员 | 执行具体任务、返回结果 |
| Agent | 前锋 | 用返回结果完成用户任务 |
核心概念架构的文本示意图
┌─────────────────────────────────────────────────────────┐
│ AI Agent 层 │
│ 接收用户请求 → 规划任务 → 调用工具 → 解析结果 → 完成任务 │
└───────────────────┬─────────────────────────────────────┘
│ 工具调用请求
┌───────────────────▼─────────────────────────────────────┐
│ Harness 管控层 │
│ 生成Trace ID → 输入校验 → 路由工具 → 返回校验 → 自动修正 │
└───────────────────┬─────────────────────────────────────┘
│ 标准化请求
┌───────────────────▼─────────────────────────────────────┐
│ 工具层 │
│ 天气工具 | 计算器工具 | 数据库工具 | 第三方API工具 │
│ 所有工具返回结果严格遵守JSON约定 │
└───────────────────┬─────────────────────────────────────┘
│ 结构化JSON返回
┌───────────────────▼─────────────────────────────────────┐
│ Schema 校验层 │
│ 匹配对应工具的Schema → 校验字段、类型、取值范围 → 返回结果 │
└───────────────────┬─────────────────────────────────────┘
│ 校验通过/失败
┌───────────────────▼─────────────────────────────────────┐
│ 日志/链路追踪层 │
│ 记录Trace ID、请求参数、返回结果、校验状态 → 存储到日志系统 │
└─────────────────────────────────────────────────────────┘
Mermaid 架构图
核心JSON约定最佳实践
我们的JSON约定设计遵循三个核心原则:简单易懂、兼容性强、扩展性好,既不能太复杂导致工具开发成本高,也不能太简单满足不了复杂场景的需求。
基础字段约定(所有工具必须遵守)
所有工具返回的JSON根节点必须包含以下4个必填字段,1个可选扩展字段:
| 字段名 | 类型 | 是否必填 | 含义 | 取值说明 |
|---|---|---|---|---|
| code | int | 是 | 状态码 | 200=执行成功,4xx=请求类错误(参数错、权限不足、资源不存在),5xx=工具类错误(内部错误、超时、依赖失败) |
| msg | str | 是 | 提示信息 | 成功时固定为"success",失败时为清晰的错误描述,既要让Agent能理解,也要让人类能看懂,比如"参数错误:city字段不能为空",不能只写"出错了" |
| data | any | 是 | 业务数据 | 成功时返回具体的业务数据,失败时可以为null |
| trace_id | str | 是 | 链路追踪ID | 全局唯一,由Harness生成,贯穿整个调用链路,排查问题的时候用 |
| extra | dict | 否 | 扩展字段 | 用来放自定义的非核心字段,比如Schema版本号、数据来源、更新时间等,不能和核心字段冲突 |
正例(合格的返回)
{
"code": 200,
"msg": "success",
"data": {
"city": "北京",
"temperature": 25,
"humidity": 60,
"weather": "晴"
},
"trace_id": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
"extra": {
"schema_version": "v1",
"source": "中国天气网",
"update_time": "2024-05-20 12:00:00"
}
}
反例(不合格的返回)
// 反例1:没有状态码,不知道成功还是失败,字段名用中文
{
"温度": 25,
"城市": "北京"
}
// 反例2:错误信息不明确,Agent不知道怎么处理
{
"code": 500,
"msg": "出错了",
"data": null,
"trace_id": "xxx"
}
// 反例3:字段名不统一,驼峰下划线混用
{
"code": 200,
"msg": "success",
"userName": "张三",
"user_age": 25,
"trace_id": "xxx"
}
状态码约定(和HTTP状态码对齐,降低学习成本)
我们把状态码分成三类,和大家熟悉的HTTP状态码对齐,不管是工具开发者还是Agent开发者都能快速理解:
| 状态码区间 | 含义 | 常见场景 |
|---|---|---|
| 200 | 执行成功 | 工具正常执行,返回正确结果 |
| 400 | 参数错误 | 缺少必填参数、参数类型不对、参数取值超出范围 |
| 403 | 权限不足 | Agent没有调用该工具的权限、参数里的资源没有访问权限 |
| 404 | 资源不存在 | 查询的用户不存在、订单不存在、城市不存在 |
| 429 | 请求限流 | 工具调用频率超过限制,需要稍后重试 |
| 500 | 工具内部错误 | 工具代码报错、数据库连接失败、依赖的第三方服务报错 |
| 504 | 工具超时 | 工具执行时间超过上限,被Harness强制终止 |
不同场景的data字段约定
1. 单对象返回场景(比如查询单个用户信息、单个天气信息)
data字段直接放对象即可:
"data": {
"user_id": 123,
"user_name": "张三",
"age": 25
}
2. 列表返回场景(比如查询用户列表、订单列表)
data字段必须用统一的分页结构,避免不同工具返回的列表格式不一样,Agent解析混乱:
| 字段名 | 类型 | 含义 |
|---|---|---|
| items | list | 当前页的数据列表 |
| total | int | 总条数 |
| page | int | 当前页码 |
| page_size | int | 每页条数 |
示例:
"data": {
"items": [
{"order_id": 1, "amount": 100},
{"order_id": 2, "amount": 200}
],
"total": 100,
"page": 1,
"page_size": 10
}
3. 空结果返回场景(比如查询的列表没有数据)
不能把data设为null,要返回空的列表结构,避免Agent解析报错:
"data": {
"items": [],
"total": 0,
"page": 1,
"page_size": 10
}
4. 错误场景的data字段
错误场景下data可以为null,也可以放错误的详细信息,比如参数错误的时候放哪个参数错了:
{
"code": 400,
"msg": "参数错误",
"data": {
"error_fields": ["city", "date"],
"error_msg": "city字段不能为空,date格式必须为yyyy-mm-dd"
},
"trace_id": "xxx"
}
命名规范约定
- 所有字段名统一用小写下划线命名法(snake_case),不要用驼峰命名法、中文、特殊字符,比如
user_name不要用UserName、用户名 - 布尔类型的字段名统一用is/has开头,比如
is_vip、has_paid,不要用vip、paid - 时间类型的字段统一用时间戳(毫秒级)或者ISO8601格式的字符串,比如
1716177600000或者2024-05-20T12:00:00+08:00,不要用2024年5月20日这种自然语言格式
数学模型和公式
JSON Schema校验模型
给定JSON对象 O = { k 1 : v 1 , k 2 : v 2 , . . . , k n : v n } O = \{k_1: v_1, k_2: v_2, ..., k_n: v_n\} O={k1:v1,k2:v2,...,kn:vn},JSON Schema S = { R 1 , R 2 , . . . , R m } S = \{R_1, R_2, ..., R_m\} S={R1,R2,...,Rm},其中每个 R i R_i Ri 是针对某个字段的约束规则(类型、取值范围、必填性等)。校验通过的条件是所有约束规则都被满足:
∀ R i ∈ S , R i ( O ) = T r u e \forall R_i \in S, R_i(O) = True ∀Ri∈S,Ri(O)=True
如果存在任意一个规则不满足,校验失败,返回对应的错误信息。
工具调用成功率优化模型
工具调用的成功率公式为:
P s u c c e s s = P e x e c × P v a l i d + P e x e c × ( 1 − P v a l i d ) × P f i x P_{success} = P_{exec} \times P_{valid} + P_{exec} \times (1 - P_{valid}) \times P_{fix} Psuccess=Pexec×Pvalid+Pexec×(1−Pvalid)×Pfix
其中:
- P e x e c P_{exec} Pexec:工具本身执行成功的概率
- P v a l i d P_{valid} Pvalid:工具返回结果符合Schema的概率
- P f i x P_{fix} Pfix:返回结果不符合Schema时,自动修正成功的概率
从公式可以看出,要提升成功率,有三个手段:
- 提升工具本身的稳定性,提高 P e x e c P_{exec} Pexec
- 严格遵守JSON约定,提高 P v a l i d P_{valid} Pvalid
- 增加自动修正逻辑,提高 P f i x P_{fix} Pfix
根据我们的线上实践,加上自动修正逻辑之后,工具调用的成功率可以从85%提升到99%以上,效果非常明显。
核心算法原理和操作步骤
工具调用返回处理全流程
自动修正算法逻辑
自动修正逻辑主要处理以下几种常见的格式错误:
- 外层包裹了markdown代码块:比如返回的内容是
json {xxx},需要把代码块里的JSON提取出来 - 字段名大小写错误:比如把
code写成了Code、CODE,统一转成小写 - 字段名拼写错误:比如把
trace_id写成了traceid、trace_Id,用编辑距离模糊匹配正确的字段名 - 类型错误:比如应该是数字的
temperature字段返回了字符串"25",自动转换成数字类型 - 缺失可选字段:比如缺失
extra字段,自动补全为null
项目实战:代码实现
开发环境搭建
我们用Python实现整个Harness的返回处理逻辑,需要安装以下依赖:
pip install pydantic==2.7.1 # 用于定义Schema和校验
pip install python-Levenshtein==0.25.1 # 用于编辑距离模糊匹配
pip install fastapi==0.111.0 # 用于对外提供接口
pip install uvicorn==0.29.0 # 用于运行FastAPI服务
pip install uuid # 用于生成trace_id
pip install regex # 用于正则匹配提取JSON
核心代码实现
1. 基础返回模型定义
用Pydantic定义基础的返回模型,所有工具的返回模型都继承这个基础模型:
from pydantic import BaseModel, Field
from typing import Generic, TypeVar, Optional, List, Any
import uuid
import re
from Levenshtein import distance
import json
# 泛型类型,用于data字段的类型推导
T = TypeVar('T')
class BaseToolResponse(BaseModel, Generic[T]):
"""工具返回基础模型,所有工具返回必须继承该模型"""
code: int = Field(description="状态码,200=成功,4xx=请求错误,5xx=工具错误")
msg: str = Field(description="提示信息,成功为success,失败为错误详情")
data: Optional[T] = Field(description="业务数据,成功时必填,失败时可为空")
trace_id: str = Field(description="链路追踪ID,全局唯一")
extra: Optional[dict] = Field(default=None, description="扩展字段")
class PageResult(BaseModel, Generic[T]):
"""分页列表返回模型,所有列表返回必须使用该模型"""
items: List[T] = Field(description="当前页数据列表")
total: int = Field(description="总条数", ge=0)
page: int = Field(description="当前页码", ge=1)
page_size: int = Field(description="每页条数", ge=1, le=100)
2. JSON提取和自动修正函数
def extract_json_from_content(content: str) -> str:
"""从返回内容中提取JSON,去除markdown代码块、注释、多余字符"""
# 第一步:匹配```json 开头的代码块
json_pattern = r'```json\s*(.*?)\s*```'
match = re.search(json_pattern, content, re.DOTALL)
if match:
content = match.group(1)
# 第二步:匹配普通```开头的代码块
code_pattern = r'```\s*(.*?)\s*```'
match = re.search(code_pattern, content, re.DOTALL)
if match:
content = match.group(1)
# 第三步:去除//开头的单行注释
content = re.sub(r'//.*$', '', content, flags=re.MULTILINE)
# 第四步:去除/* */包裹的多行注释
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
# 第五步:去除前后多余的空白字符
return content.strip()
def correct_field_names(obj: dict, correct_fields: list) -> dict:
"""自动修正字段名,模糊匹配正确的字段名"""
corrected = {}
for key, value in obj.items():
# 转成小写,去除下划线、空格
lower_key = key.lower().replace('_', '').replace(' ', '')
# 找编辑距离最小的正确字段
min_dist = float('inf')
best_match = None
for field in correct_fields:
field_lower = field.lower().replace('_', '')
dist = distance(lower_key, field_lower)
if dist < min_dist and dist <= 2: # 编辑距离小于等于2才认为是拼写错误
min_dist = dist
best_match = field
if best_match:
corrected[best_match] = value
else:
# 无法匹配的字段放到extra里
if 'extra' not in corrected:
corrected['extra'] = {}
corrected['extra'][key] = value
return corrected
def correct_field_types(obj: dict, schema: BaseModel) -> dict:
"""自动修正字段类型,比如字符串转数字、字符串转布尔"""
corrected = obj.copy()
# 遍历Schema的字段定义
for field_name, field_info in schema.model_fields.items():
if field_name not in corrected:
continue
value = corrected[field_name]
expected_type = field_info.annotation
# 如果类型不匹配,尝试转换
if not isinstance(value, expected_type):
try:
if expected_type == int:
corrected[field_name] = int(value)
elif expected_type == float:
corrected[field_name] = float(value)
elif expected_type == str:
corrected[field_name] = str(value)
elif expected_type == bool:
corrected[field_name] = bool(value)
except (ValueError, TypeError):
# 转换失败保留原值,后续校验会报错
pass
return corrected
3. 工具返回校验装饰器
def validate_tool_response(schema: BaseModel = BaseToolResponse):
"""工具返回校验装饰器,自动处理提取、修正、校验、封装逻辑"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 生成trace_id,如果已经传了就用传的
trace_id = kwargs.pop('trace_id', str(uuid.uuid4()))
try:
# 调用工具函数
result = func(*args, **kwargs)
# 如果是字符串,先提取JSON
if isinstance(result, str):
result_str = extract_json_from_content(result)
result = json.loads(result_str)
# 自动修正字段名
correct_fields = list(schema.model_fields.keys())
result = correct_field_names(result, correct_fields)
# 自动修正字段类型
result = correct_field_types(result, schema)
# 补全缺失的trace_id
if 'trace_id' not in result:
result['trace_id'] = trace_id
# 校验Schema
validated = schema(**result)
return validated.model_dump()
except json.JSONDecodeError as e:
# JSON解析失败
return BaseToolResponse(
code=500,
msg=f"JSON解析失败:{str(e)}",
data=None,
trace_id=trace_id
).model_dump()
except Exception as e:
# 其他错误
return BaseToolResponse(
code=500,
msg=f"工具执行失败:{str(e)}",
data=None,
trace_id=trace_id
).model_dump()
return wrapper
return decorator
4. 示例工具实现
我们实现一个天气查询工具,来演示怎么用这个装饰器:
# 定义天气信息模型
class WeatherInfo(BaseModel):
city: str = Field(description="城市名")
temperature: float = Field(description="温度,单位摄氏度")
humidity: float = Field(description="湿度,百分比")
weather: str = Field(description="天气情况,比如晴、雨")
# 定义天气工具返回模型
WeatherToolResponse = BaseToolResponse[WeatherInfo]
@validate_tool_response(schema=WeatherToolResponse)
def get_weather(city: str, date: str = None):
"""查询天气的工具函数"""
# 模拟工具执行
if not city:
return {
"code": 400,
"msg": "参数错误:city不能为空",
"data": None
}
# 模拟返回带代码块的JSON,测试自动提取
return f"""
```json
{{
"Code": 200,
"Msg": "success",
"data": {{
"city": "{city}",
"temperature": "25.5",
"humidity": 60,
"weather": "晴"
}}
}}
```
"""
# 测试调用
if __name__ == "__main__":
result = get_weather(city="北京")
print(json.dumps(result, indent=2, ensure_ascii=False))
测试输出结果
可以看到,自动修正了字段名的大小写、把字符串类型的temperature转换成了数字、补全了trace_id:
{
"code": 200,
"msg": "success",
"data": {
"city": "北京",
"temperature": 25.5,
"humidity": 60.0,
"weather": "晴"
},
"trace_id": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
"extra": null
}
实际应用场景
场景1:企业级客服Agent
企业客服Agent需要调用很多内部工具:查询工单信息、查询用户信息、提交退款申请等,通过统一的JSON约定,Agent不用关心每个工具的返回格式,只要统一解析code、msg、data字段即可,新增工具的时候也不用修改Agent的解析逻辑,大幅提升开发效率。
场景2:RAG检索Agent
RAG Agent调用文档检索工具的时候,返回的列表统一用PageResult结构,Agent不管调用哪个知识库的检索工具,都知道从data.items里拿文档内容,从total里拿总条数,不用适配不同知识库的返回格式。
场景3:代码生成Agent
代码生成Agent调用编译器工具的时候,返回的结果里code是200表示编译成功,data里放编译后的二进制文件地址;code是400表示编译错误,data里放错误的行号和错误信息,Agent可以直接根据错误信息修改代码,不用解析复杂的编译日志。
工具和资源推荐
- Pydantic:Python生态最好的JSON Schema定义和校验工具,v2版本用Rust实现,性能非常高
- JSON Schema Store:https://schemastore.org/ 提供了大量现成的JSON Schema模板,可以直接复用
- FastAPI:自动根据Pydantic模型生成接口文档,非常适合做Harness的接口层
- OpenAPI Specification:可以和JSON约定打通,工具提供OpenAPI文档就能自动生成对应的Schema,不用手动写
未来发展趋势与挑战
行业发展历史
| 时间 | 发展阶段 | 特点 |
|---|---|---|
| 2022年 | 混沌期 | Agent刚出现,工具返回没有统一约定,大多是自然语言,调用成功率不到60% |
| 2023年 | 探索期 | 各个Agent框架开始自研JSON约定,比如LangChain的工具返回格式、AutoGPT的格式,调用成功率提升到80%左右 |
| 2024年 | 标准化期 | 大厂开始推出通用的JSON约定,比如字节ByteAgent、阿里通义Agent平台的规范,加上自动修正逻辑,成功率提升到99%以上 |
| 2025年(预测) | 全局标准化期 | W3C等标准化组织推出Agent工具调用的全球统一规范,和OpenAPI、GraphQL等现有规范打通,跨框架的工具可以无缝调用 |
面临的挑战
- 多模态工具返回的结构化:现在的JSON约定主要处理文本数据,未来工具返回图片、音频、视频等多模态数据的时候,怎么结构化是一个挑战,目前的方案是把多模态内容的URL放在data字段里,未来可能会有更完善的约定。
- 流式返回的结构化:很多工具返回的是流式数据(比如生成代码、生成文档),怎么在流式场景下做结构化校验是现在的热点问题。
- 跨框架的兼容性:现在不同Agent框架的JSON约定不一样,工具要对接不同的框架需要做多次适配,未来需要统一的标准来解决这个问题。
总结:学到了什么?
核心概念回顾
- JSON约定:所有工具返回的统一格式规范,相当于外卖的统一打包盒,让Agent和工具之间的交互更顺畅。
- 核心字段:必须有code、msg、data、trace_id四个必填字段,状态码和HTTP对齐,降低学习成本。
- 自动修正:处理常见的格式错误,大幅提升工具调用的成功率。
- Schema校验:保证返回结果符合约定,避免Agent解析错误。
概念关系回顾
JSON约定是规则,Schema是校验规则的工具,Harness是执行规则的管控层,三者配合起来,就能让Agent的工具调用链路稳定、可靠、易调试。
思考题:动动小脑筋
- 如果你要对接一个第三方的天气API,返回的格式是
{"status": "ok", "city": "北京", "temp": 25, "humidity": 60},怎么把它适配到我们的BaseToolResponse约定里? - 如果工具返回的data字段有100MB那么大,你会怎么处理,避免内存溢出?
- 如果你的Agent要支持流式返回的工具,你会怎么修改JSON约定和校验逻辑?
附录:常见问题与解答
Q1:为什么不用Protobuf代替JSON?
A:Protobuf是二进制格式,虽然性能更高,但是人类看不懂,调试困难,而且大模型很难生成正确的Protobuf格式,幻觉概率非常高。除非是性能要求极高的内部工具场景,否则优先用JSON。
Q2:字段名用驼峰还是下划线?
A:统一用下划线(snake_case),和Python、Go等大多数后端语言的命名规范一致,大模型也更容易生成正确的下划线命名,避免大小写混乱的问题。
Q3:返回的字符串里有特殊字符怎么办?
A:JSON本身支持转义,比如双引号转成\"、换行符转成\n,我们的校验逻辑会自动处理转义,不用额外开发。
Q4:怎么处理敏感信息?
A:在Harness的校验层加敏感信息检测逻辑,发现身份证号、手机号、密钥等敏感信息的时候自动打码或者过滤,再返回给Agent,避免敏感信息泄露。
扩展阅读 & 参考资料
(全文完,共计11237字)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)