Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions docs/mkdocs/en/llm_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,102 @@ for query in demo_queries:

## Advanced Configuration and Control

### Limiting Work per Agent Invocation

Use `RunConfig` to limit LLM calls, loop iterations, and tool calls for each Agent invocation. These limits prevent an unexpected execution path from consuming resources indefinitely.

| Setting | Default | Meaning |
| --- | ---: | --- |
| `max_llm_calls` | `500` | Number of LLM calls that may be executed |
| `max_iterations` | `0` | Number of Agent loop iterations that may be executed; an iteration normally contains one LLM call and any tool execution requested by that call |
| `max_tool_calls` | `0` | Total number of tool calls that may be executed |

A value of `0` disables the corresponding limit. A limit allows the configured number of operations and raises `RunLimitException` when the Agent attempts the next operation. For example, `max_iterations=1` allows the first iteration to complete and raises before the second iteration starts, so the exception contains `configured_value=1` and `observed_value=2`.

`max_tool_calls` accumulates the number of tool calls returned by the LLM. If a batch would make the total exceed the limit, the framework raises before executing the batch, and none of the tools in that batch are executed.

The following configuration applies to every Agent involved in this `Runner.run_async()` call:

```python
from trpc_agent_sdk.configs import RunConfig

run_config = RunConfig(
max_llm_calls=10,
max_iterations=5,
max_tool_calls=5,
)
```

In a multi-Agent application, use `agent_limits` to configure different limits by `agent.name`:

```python
from trpc_agent_sdk.configs import AgentRunLimits, RunConfig

run_config = RunConfig(
max_llm_calls=10,
max_iterations=5,
max_tool_calls=5,
agent_limits={
"weather_agent": AgentRunLimits(
max_llm_calls=2,
max_iterations=2,
max_tool_calls=1,
),
"summary_agent": AgentRunLimits(
max_llm_calls=1,
max_tool_calls=0,
),
},
)
```

Each `agent_limits` key must exactly match the target `agent.name`; otherwise, that override has no effect. Fields omitted from `AgentRunLimits` inherit the top-level `RunConfig` value. Explicitly setting a field to `0` disables the inherited limit for that Agent.

Every call to an Agent's `run_async()` uses independent counters. Consequently, Agents involved in the same `Runner.run_async()` call are counted independently, and counters start over when a later invocation uses the same session.

When a limit is exceeded, the framework terminates the current invocation and raises `RunLimitException` to the Python caller:

```python
from trpc_agent_sdk.exceptions import RunLimitException

try:
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content,
run_config=run_config,
):
...
except RunLimitException as exc:
print(exc.error_code)
print(exc.agent_name)
print(exc.limit_type)
print(exc.configured_value, exc.observed_value)
```

The exception terminates only the current invocation; it does not close the session. The caller can pass a new `RunConfig` to a later `Runner.run_async()` call and continue using the same session. See [examples/llmagent_with_limit/run_agent.py](../../../examples/llmagent_with_limit/run_agent.py) for a complete, low-cost demonstration.

`RunConfig` only limits work performed inside the Agent loop. The caller should decide when a timeout starts and how to handle it. In addition, `Runner.run_async()` returns an asynchronous event stream, and the run is not complete until that stream has been consumed. Therefore, the framework does not add a time limit to `RunConfig`; instead, the caller should apply a timeout to the complete event-consumption coroutine:

```python
import asyncio

async def run_once() -> None:
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content,
run_config=run_config,
):
...

try:
await asyncio.wait_for(run_once(), timeout=120.0)
except TimeoutError:
# Handle the invocation timeout.
...
```

### GenerateContentConfig

Used to adjust LLM generation behavior, such as temperature, top-p, and other parameters:
Expand Down
84 changes: 84 additions & 0 deletions docs/mkdocs/zh/llm_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,90 @@ for query in demo_queries:

## 高级配置与控制

### 配置 Agent 的运行次数

使用 `RunConfig` 可以限制一次 Agent 调用中的 LLM 调用次数、循环次数和工具调用次数。

| 配置项 | 默认值 | 含义 |
| --- | ---: | --- |
| `max_llm_calls` | `500` | LLM 最多调用多少次 |
| `max_iterations` | `0` | Agent 最多循环多少次 |
| `max_tool_calls` | `0` | 工具最多调用多少次 |

值为 `0` 表示不限制。配置的次数可以正常执行,下一次执行时才会抛出 `RunLimitException`。例如,`max_iterations=1` 时第一次循环可以正常执行,开始第二次循环时抛出异常,因此异常中的 `configured_value` 是 `1`,`observed_value` 是 `2`。

如果 LLM 一次返回多个工具调用,框架会一起计算这些工具调用。总数超过 `max_tool_calls` 时,这批工具都不会执行。

下面的配置会应用到本次调用中的所有 Agent:

```python
from trpc_agent_sdk.configs import AgentRunLimits, RunConfig

run_config = RunConfig(
max_llm_calls=10,
max_iterations=5,
max_tool_calls=5,
agent_limits={
"weather_agent": AgentRunLimits(
max_llm_calls=2,
max_iterations=2,
max_tool_calls=1,
),
"summary_agent": AgentRunLimits(
max_llm_calls=1,
max_tool_calls=0,
),
},
)
```

`agent_limits` 用于给不同 Agent 设置单独的限制。字典中的名称必须与 `agent.name` 完全一致,否则配置不会生效。`AgentRunLimits` 中没有填写的配置会沿用 `RunConfig` 中的值;设置为 `0` 表示该 Agent 不受这项限制。

每个 Agent 单独计数。再次调用 Agent 时会重新计数,即使继续使用同一个会话也不会沿用上一次的次数。

超过限制时,当前调用会停止,并抛出 `RunLimitException`:

```python
from trpc_agent_sdk.exceptions import RunLimitException

try:
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content,
run_config=run_config,
):
...
except RunLimitException as exc:
print(exc.error_code)
print(exc.agent_name)
print(exc.limit_type)
print(exc.configured_value, exc.observed_value)
```

这个异常不会关闭会话。后续调用可以传入新的 `RunConfig`,继续使用原来的会话。完整示例请参考 [examples/llmagent_with_limit/run_agent.py](../../../examples/llmagent_with_limit/run_agent.py)。

`RunConfig` 只负责限制 Agent 内部的运行次数。超时从何时开始计算、超时后如何处理,应由调用方决定;而且 `Runner.run_async()` 返回的是异步事件流,需要消费事件后才算运行结束。因此,框架没有在 `RunConfig` 中增加时间限制,而是由调用方为完整的事件消费过程设置超时:

```python
import asyncio

async def run_once() -> None:
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content,
run_config=run_config,
):
...

try:
await asyncio.wait_for(run_once(), timeout=120.0)
except TimeoutError:
# 处理超时。
...
```

### GenerateContentConfig

用于调整LLM的生成行为,如temperature、top-p等参数:
Expand Down
4 changes: 4 additions & 0 deletions examples/llmagent_with_limit/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Set TRPC_AGENT_API_KEY, TRPC_AGENT_BASE_URL, and TRPC_AGENT_MODEL_NAME
TRPC_AGENT_API_KEY=your-api-key
TRPC_AGENT_BASE_URL=your-base-url
TRPC_AGENT_MODEL_NAME=your-model-name
59 changes: 59 additions & 0 deletions examples/llmagent_with_limit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# LLM Agent 运行次数限制示例

本示例演示如何通过 `RunConfig` 限制一次 Agent 调用中的 LLM 调用次数、循环次数和工具调用次数。

## 验证内容

示例包含三个独立场景:

- `max_llm_calls=1`:允许一次 LLM 调用,在第二次调用前抛出异常
- `max_iterations=1`:允许 Agent 执行一次循环,在第二次循环开始前抛出异常
- `max_tool_calls=1`:让 LLM 一次请求两个工具,超过限制后两个工具都不执行

每个场景使用一个单独的会话,并连续调用两次:

1. 第一次调用触发 `RunLimitException`
2. 第二次调用关闭限制,在同一个会话中询问 `What did we do previously?`

第二次调用可以正常完成,说明异常只会停止当前调用,不会关闭会话。

## 关键配置

```python
run_config = RunConfig(
agent_limits={
root_agent.name: AgentRunLimits(
max_llm_calls=1,
max_iterations=0,
max_tool_calls=0,
),
},
)
```

`agent_limits` 中的名称需要与 `agent.name` 完全一致。值为 `0` 表示不限制。

## 运行示例

先在 `.env` 中配置以下环境变量:

- `TRPC_AGENT_API_KEY`
- `TRPC_AGENT_BASE_URL`
- `TRPC_AGENT_MODEL_NAME`

然后运行:

```bash
cd examples/llmagent_with_limit
python3 run_agent.py
```

每个场景的预期结果如下:

```text
⛔ [max_llm_calls_exceeded: Agent 'weather_agent' reached max_llm_calls=1.]
✅ Invocation 1 raised the expected limit: configured=1, observed=2
✅ Invocation 2 continued and completed normally
```

另外两个场景会分别输出 `max_iterations_exceeded` 和 `max_tool_calls_exceeded`。
5 changes: 5 additions & 0 deletions examples/llmagent_with_limit/agent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Agent-Python is licensed under Apache-2.0.
43 changes: 43 additions & 0 deletions examples/llmagent_with_limit/agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Agent-Python is licensed under Apache-2.0.
"""Agent used by the run-limit example."""

from trpc_agent_sdk.agents import LlmAgent
from trpc_agent_sdk.models import LLMModel
from trpc_agent_sdk.models import OpenAIModel
from trpc_agent_sdk.tools import FunctionTool

from .config import get_model_config
from .prompts import INSTRUCTION
from .tools import get_weather_forecast
from .tools import get_weather_report


def _create_model() -> LLMModel:
"""Create the model configured for this example."""
api_key, base_url, model_name = get_model_config()
return OpenAIModel(
model_name=model_name,
api_key=api_key,
base_url=base_url,
)


def create_agent() -> LlmAgent:
"""Create the weather Agent used to trigger the run limits."""
return LlmAgent(
name="weather_agent",
description="A weather assistant used to demonstrate run limits.",
model=_create_model(),
instruction=INSTRUCTION,
tools=[
FunctionTool(get_weather_report),
FunctionTool(get_weather_forecast),
],
)


root_agent = create_agent()
19 changes: 19 additions & 0 deletions examples/llmagent_with_limit/agent/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Agent-Python is licensed under Apache-2.0.
"""Model configuration for the run-limit example."""

import os


def get_model_config() -> tuple[str, str, str]:
"""Read the model configuration from environment variables."""
api_key = os.getenv("TRPC_AGENT_API_KEY", "")
base_url = os.getenv("TRPC_AGENT_BASE_URL", "")
model_name = os.getenv("TRPC_AGENT_MODEL_NAME", "")
if not api_key or not base_url or not model_name:
raise ValueError("TRPC_AGENT_API_KEY, TRPC_AGENT_BASE_URL, and "
"TRPC_AGENT_MODEL_NAME must be set.")
return api_key, base_url, model_name
16 changes: 16 additions & 0 deletions examples/llmagent_with_limit/agent/prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Agent-Python is licensed under Apache-2.0.
"""Prompt for the run-limit example."""

INSTRUCTION = """
You are a weather assistant for {user_name}, whose default city is {user_city}.

Use `get_weather_report` for current weather and `get_weather_forecast` for a
multi-day forecast. Follow the user's tool-call instructions exactly.

When asked what happened previously, answer from the conversation history
without calling a tool.
"""
Loading
Loading