cloudwego / cloudwego/eino

[RFC] Structured tool result format for LLM-friendly error handling

Open
#1,010 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
13k
Forks
1.1k
Avg merge
4h 6m
Merged PRs (30d)
41

Description

## 问题描述

当前 `InvokableTool` 接口返回 `(string, error)`,对字符串结果没有任何结构约束。
这意味着 Tool 的错误信息是非结构化的自由文本,LLM 无法据此做出可靠的决策。

社区中已有真实案例(见 #432):当 Tool 返回 Go `error` 时,react Agent 直接停止,报错如下:

```
[NodeRunError] failed to invoke tool[name:get_weather]: not implemented
```

LLM 完全看不到这个错误——它在框架层就被终止了。
即便错误以字符串形式传回给 LLM,LLM 也无法判断:

- 是否应该重试?
- 是否需要修改入参?
- 是否应该换一个 Tool?
- 应该给用户回复什么?

## 问题根因

这个问题分两层:

1. **框架层**:`InvokableRun` 返回 Go `error` 导致 Agent 直接中止(相关:#432)
2. **内容层**:即使错误传到了 LLM,也是非结构化、无法被 LLM 利用的字符串

本提案解决第二层——提供一个标准结构化返回格式,
让 Tool 开发者能够写出对 LLM 友好的错误信息。

## 提案

在 `eino-ext` 中新增 `toolutil` 包,提供标准结构体和 helper 函数。
**不修改任何现有接口**,纯粹向后兼容的新增。

### 结构定义

```go
type Result struct {
Success bool `json:"success"`
Data any `json:"data,omitempty"`
Error *ToolError `json:"error,omitempty"`
}

type ToolError struct {
Code string `json:"code"` // 机器可读的错误码
Message string `json:"message"` // LLM 可读的描述
Retryable bool `json:"retryable"` // LLM 是否应该重试
Suggestion string `json:"suggestion,omitempty"` // 建议 LLM 的下一步行动
}
```

### Helper 函数

```go
// 成功
toolutil.OK(data)

// 失败
toolutil.Fail("ORDER_NOT_FOUND", "订单不存在", false,
"请让用户确认订单号,或调用 list_orders 查询")
```

### Tool 使用示例

```go
func (t *MyTool) InvokableRun(ctx context.Context, args string) (string, error) {
order, err := t.db.GetOrder(ctx, id)
if err != nil {
return toolutil.Fail("ORDER_NOT_FOUND", "order does not exist", false,
"Confirm the order ID with the user or call list_orders")
}
return toolutil.OK(order)
}
```

LLM 收到的是可直接用于决策的结构化内容:

```json
{
"success": false,
"error": {
"code": "ORDER_NOT_FOUND",
"message": "order ORD-999 does not exist",
"retryable": false,
"suggestion": "Confirm the order ID with the user or call list_orders"
}
}
```

## 动机

Tool 的错误信息应该是为 LLM 设计的,而不是为开发者设计的。
`retryable` 和 `suggestion` 字段让 Agent 拥有足够的上下文做出正确决策,
而不是凭空猜测恢复路径。

## 实现范围

- 新增包:`eino-ext/components/tool/util`
- 不修改任何现有接口,完全向后兼容
- 包含单元测试和使用示例

如果 maintainer 认可这个方向,我可以负责实现。

## 相关 Issue

- #432:Tool error 导致 react Agent 直接停止——本提案从内容层解决该问题的根因
- #997:ADK AgentEvent 结构化——同属"结构化"大方向,但受众不同(开发者 vs LLM)

Contributor guide

Open the contributing guide

Research direction

Start by reviewing the existing eino-ext/components/tool/util package area and the InvokableTool and InvokableRun interfaces referenced in the proposal. Define the Result and ToolError structures plus OK and Fail helpers without changing existing interfaces, then add unit tests and a usage example. Done means the structured success and failure JSON matches the proposed format and the tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
ai
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.