Skip to content

运行时适配器与统一事件合同 ​

Agent 服务不应把供应商 SDK 响应直接暴露给业务。适配器负责把模型响应映射成内部事件:文本增量、工具调用、工具结果、用量、完成、失败和取消。内部事件保留 run_id、sequence、provider request ID 与原始类型,才能实现去重、重放和排障。平台特有能力通过 capability 声明或显式扩展表达,不能静默丢失。

第一个可运行实验:事件归一化与序号去重 ​

保存为 adapter.py,用 Python 3.12 运行。

python
def normalize(raw):
    kind = raw['type']
    table = {'text.delta': 'delta', 'response.done': 'done', 'response.error': 'error'}
    if kind not in table:
        return {'type': 'extension', 'raw_type': kind, 'data': raw}
    return {'type': table[kind], 'sequence': raw['sequence'], 'request_id': raw['request_id'],
            **({'text': raw['text']} if kind == 'text.delta' else {})}

def consume(events):
    seen, out = set(), []
    for event in events:
        if event['sequence'] in seen:
            continue
        seen.add(event['sequence'])
        out.append(normalize(event))
    return out

raw = [
    {'type': 'text.delta', 'sequence': 1, 'request_id': 'r1', 'text': 'A'},
    {'type': 'text.delta', 'sequence': 1, 'request_id': 'r1', 'text': 'A'},
    {'type': 'response.done', 'sequence': 2, 'request_id': 'r1'},
]
assert [e['type'] for e in consume(raw)] == ['delta', 'done']
assert consume([{'type': 'tool.delta', 'sequence': 3, 'request_id': 'r1'}])[0]['type'] == 'extension'
print('adapter contract passed')

去重依据应是供应商保证的事件序号或内部组合键,不能靠文本内容比较;两个相同文字片段可能是合法输出。适配器不负责业务成功判定,完成事件还要经过结构化校验、工具状态和权限检查。

变体实验 ​

交换事件到达顺序,要求消费者按序号检测缺口并进入恢复订阅。再加入不同 request ID 的同一序号,要求拒绝混合两次运行。

练习 ​

  1. 设计 usage、tool_call 和 cancelled 的内部事件形状。
  2. 区分供应商事件重放与业务重试。
  3. 列出 capability 矩阵中必须记录的模型、版本、工具和流式字段。

用心记录每一步成长