♻️ refactor(agent): 重构 Agent 状态管理与执行器流程,优化 Token 预估与自愈反思机制 (#2150)

- 统一使用 `run_context.run.messages` 作为消息历史的单一数据源,清理 `AgentState` 冗余字段
- 将工具消息装配逻辑 `assemble_tool_message` 提取并重构至 `ToolExecutor`
- 引入 `token_drift` 动态校准偏移量,并精确计算工具与系统提示词的 Token 开销
- 重构 `ReflexionCapability` 自愈反思引擎,基于异常多态与模板字典动态生成反馈提示词
- 支持通过 `resolve_model_capabilities` 解析并合并用户自定义的模型能力覆盖
- 在执行器循环中支持 `should_reset_cycle`,以优雅处理外部干预(如用户追加指示)
- 扩展 `capabilities` 中对 `gpt-[5-9]*` 等新型号模型的能力定义与上下文限制

Co-authored-by: webjoin111 <455457521@qq.com>
This commit is contained in:
Rumio
2026-07-16 09:09:59 +08:00
committed by GitHub
co-authored by webjoin111
parent 52f7dbdedf
commit cd5fa065d3
13 changed files with 416 additions and 300 deletions
+29 -8
View File
@@ -10,6 +10,7 @@ from zhenxun.services.ai.config import get_llm_config
from zhenxun.services.ai.context.knowledge.base import BaseKnowledge
from zhenxun.services.ai.context.memory.builder import MemoryBuilder
from zhenxun.services.ai.context.memory.models import MemoryConfig
from zhenxun.services.ai.core.engine.token_counter import token_counter
from zhenxun.services.ai.core.exceptions import (
ControlFlowExit,
)
@@ -29,6 +30,7 @@ from zhenxun.services.ai.flow.core.base import BaseRunnable
from zhenxun.services.ai.flow.core.models import InterventionPolicy
from zhenxun.services.ai.guardrails import GuardrailSource, parse_guardrails
from zhenxun.services.ai.llm.builder import IntentBuilder
from zhenxun.services.ai.llm.manager import resolve_model_capabilities
from zhenxun.services.ai.message_builder import MessageBuilder
from zhenxun.services.ai.run import (
AgentRunResult,
@@ -700,6 +702,10 @@ class Agent(
self.model_name() if callable(self.model_name) else self.model_name
)
resources.model_capabilities = await resolve_model_capabilities(
context.run.current_model
)
return state, resources
async def on_context_build(
@@ -752,10 +758,29 @@ class Agent(
if tool_payload.injected_prompts:
static_prompts_list.extend(tool_payload.injected_prompts)
final_tools = await ToolBuilder.prepare_effective_tools(
effective_tools, context, self.tool_filters, run_scoped_cap
)
base_overhead = 0
for sp in static_prompts_list:
if sp:
base_overhead += token_counter._count_text(str(sp))
for m in dynamic_messages:
base_overhead += token_counter.count_message(
m, context.run.current_model or ""
)
for t in final_tools:
t_def = getattr(t, "_dynamic_def", None)
if t_def and getattr(t_def, "parameters", None):
base_overhead += token_counter.count_tools_schema(t_def.parameters)
messages_for_run = (
await memory_context.read(
model_name=context.run.current_model or "",
capabilities=resources.model_capabilities,
override_history=resources.config.message_history,
base_overhead=base_overhead,
)
if memory_context
else []
@@ -772,17 +797,14 @@ class Agent(
if resources.memory_context:
await resources.memory_context.write([msgs[-1]])
final_tools = await ToolBuilder.prepare_effective_tools(
effective_tools, context, self.tool_filters, run_scoped_cap
)
context.session.append_only_manager.build(static_prompts_list, final_tools)
context.session.append_only_manager.sync_messages(messages_for_run)
state.messages = messages_for_run
context.run.messages = messages_for_run
state.tools = final_tools
state.static_system_prompt = static_prompts_list
state.dynamic_system_messages = dynamic_messages
state.origin_msg_len = len(messages_for_run)
state._origin_msg_len = len(messages_for_run)
async def on_execute(
self, state: AgentState, resources: AgentRunResources
@@ -793,7 +815,7 @@ class Agent(
for tk in resources.toolkits:
if hasattr(tk, "before_llm_request"):
await DependencyInjector.invoke(
tk.before_llm_request, {"messages": state.messages}, context
tk.before_llm_request, {"messages": context.run.messages}, context
)
config_exec = resources.config.executor if resources.config else None
@@ -802,12 +824,11 @@ class Agent(
or self.executor
or StandardAgentExecutor(directive_handlers=self.directive_handlers)
)
resources.model_name = context.run.current_model
raw_result: AgentRunResult[Any] = await executor.run(
state=state, resources=resources
)
new_msgs = raw_result.messages[state.origin_msg_len :]
new_msgs = raw_result.messages[state._origin_msg_len :]
if resources.memory_context:
await resources.memory_context.write(new_msgs)
@@ -65,10 +65,10 @@ async def handle_submit_structured(
)
logger.debug("✅ 拦截到结构化结果提交,结束循环。")
state.is_finished = True
state.final_result = model_construct(
state.pending_result = model_construct(
AgentRunResult,
output=None,
messages=state.messages,
messages=list(resources.run_context.run.messages),
structured_data=parsed_obj,
usage=state.usage,
)
@@ -85,10 +85,10 @@ async def handle_end_run(
)
logger.debug("✅ 捕获到工具发出的终止信号,提前结束推理循环。")
state.is_finished = True
state.final_result = model_construct(
state.pending_result = model_construct(
AgentRunResult,
output=output,
messages=state.messages,
messages=list(resources.run_context.run.messages),
usage=state.usage,
)
@@ -106,10 +106,10 @@ async def handle_handoff(
output_text = f"已触发控制权移交 -> {handoff.target}。原因: {handoff.reason}"
logger.info(f"✅ 拦截到移交(Handoff)信号: 移交给 -> {handoff.target}。结束循环。")
state.is_finished = True
state.final_result = model_construct(
state.pending_result = model_construct(
AgentRunResult,
output=output_text,
messages=state.messages,
messages=list(resources.run_context.run.messages),
usage=state.usage,
handoff=handoff,
)
+131 -168
View File
@@ -1,8 +1,7 @@
from abc import ABC, abstractmethod
import asyncio
from collections.abc import Awaitable, Callable
import json
from typing import Any, Literal, cast
from collections.abc import Iterable
from typing import Any, cast
from zhenxun.services.ai.capabilities import CombinedCapability
from zhenxun.services.ai.core.engine.context_renderer import ContextConverter
@@ -11,22 +10,15 @@ from zhenxun.services.ai.core.engine.token_counter import (
token_counter,
)
from zhenxun.services.ai.core.exceptions import (
ControlFlowExit,
UpstreamServerException,
)
from zhenxun.services.ai.core.messages import (
AgentMessage,
AssistantMessage,
AudioPart,
ChatRequest,
ChatResponse,
FilePart,
ImagePart,
LLMMessage,
TextPart,
ToolCallPart,
ToolReturnPart,
VideoPart,
)
from zhenxun.services.ai.core.models import CancellationToken, LLMContext, ToolChoice
from zhenxun.services.ai.core.options import GenerationConfig
@@ -40,11 +32,11 @@ from zhenxun.services.ai.flow.agent.models import AgentRunResources, AgentState
from zhenxun.services.ai.llm.engine.router import LLMOrchestrator
from zhenxun.services.ai.run import AgentRunResult, RunContext
from zhenxun.services.ai.run.models import OutputDataT
from zhenxun.services.ai.run.session import SessionInfo, session_manager
from zhenxun.services.ai.run.session import session_manager
from zhenxun.services.ai.tools.engine.executor import ToolExecutor
from zhenxun.services.ai.tools.models import ToolResult
from zhenxun.services.ai.utils.logger import log_agent as logger
from zhenxun.utils.pydantic_compat import dump_json_safely, model_construct
from zhenxun.utils.pydantic_compat import model_construct
from .directive import (
DirectiveHandlerFunc,
@@ -68,29 +60,42 @@ class BaseAgentExecutor(ABC):
await self.on_start(state, resources)
try:
for cycle_index in range(resources.config.max_cycles):
state.current_cycle = cycle_index
cycle_count = 0
while cycle_count < resources.config.max_cycles:
state.current_cycle = cycle_count
state.should_reset_cycle = False
await self.on_cycle_start(state, resources)
await self.build_llm_request(state, resources)
await self.execute_llm(state, resources)
await self.handle_llm_response(state, resources)
if state.should_reset_cycle:
cycle_count = 0
continue
if state.is_finished:
assert state.final_result is not None
return state.final_result
assert state.pending_result is not None
return state.pending_result
await self.filter_tool_calls(state, resources)
if state.should_reset_cycle:
cycle_count = 0
continue
if state.is_finished:
assert state.final_result is not None
return state.final_result
assert state.pending_result is not None
return state.pending_result
await self.execute_tools(state, resources)
await self.handle_tool_results(state, resources)
if state.should_reset_cycle:
cycle_count = 0
continue
if state.is_finished:
assert state.final_result is not None
return state.final_result
assert state.pending_result is not None
return state.pending_result
cycle_count += 1
return await self.on_fallback(state, resources)
except Exception as e:
@@ -179,23 +184,45 @@ class StandardAgentExecutor(BaseAgentExecutor):
directive_handlers or {}
)
@staticmethod
def _calculate_tool_overhead(tools: Iterable[Any] | None) -> int:
"""辅助方法:计算工具集合的 Schema Token 开销"""
if not tools:
return 0
overhead = 0
for t in tools:
if isinstance(t, dict) and "function" in t:
overhead += token_counter.count_tools_schema(
t["function"].get("parameters", {})
)
elif hasattr(t, "get_definition"):
t_def = getattr(t, "_dynamic_def", None)
if t_def and getattr(t_def, "parameters", None):
overhead += token_counter.count_tools_schema(t_def.parameters)
return overhead
def _can_retry_via_llm(self, result: ToolResult) -> bool:
"""通过新版的专属字段直接判断是否允许重试"""
return result.is_retryable
def _check_follow_up(
self, state: AgentState, resources: AgentRunResources, session_info: SessionInfo
async def _check_follow_up(
self, state: AgentState, resources: AgentRunResources
) -> bool:
"""检查追加队列,排空并合并数据到上下文,返回是否发现新消息"""
"""检查追加队列,排空并合并数据到上下文,更新状态机标志位"""
session_id = resources.run_context.session_id or "default_session"
session_info = await session_manager.get_or_create(session_id)
follow_ups = session_info.follow_up_queue.drain()
if follow_ups:
for fm in follow_ups:
state.messages.append(LLMMessage.user(f"💬 [用户追加指示]:{fm}"))
resources.run_context.run.messages.append(
LLMMessage.user(f"💬 [用户追加指示]:{fm}")
)
resources.run_context.session.append_only_manager.sync_messages(
state.messages
resources.run_context.run.messages
)
state.is_finished = False
state.final_result = None
state.pending_result = None
state.should_reset_cycle = True
return True
return False
@@ -224,13 +251,13 @@ class StandardAgentExecutor(BaseAgentExecutor):
await run_context.run.emit(
LLMStartEvent(
model_name=resources.model_name or "unknown",
model_name=resources.run_context.run.current_model or "unknown",
messages=flattened_messages,
)
)
response = await self._execute_model_request(
model_name=resources.model_name,
model_name=resources.run_context.run.current_model,
messages=flattened_messages,
config=resources.generation_config or GenerationConfig(),
run_context=run_context,
@@ -266,8 +293,16 @@ class StandardAgentExecutor(BaseAgentExecutor):
if usage_obj.completion_tokens > 0:
assistant_message.token_cost = usage_obj.completion_tokens
state.messages.append(assistant_message)
run_context.session.append_only_manager.sync_messages(state.messages)
if usage_obj.prompt_tokens > 0:
est_prompt_tokens = token_counter.count_context(
messages, resources.run_context.run.current_model or "", base_overhead=0
)
tool_overhead = self._calculate_tool_overhead(tools)
est_total = est_prompt_tokens + tool_overhead
state.token_drift = usage_obj.prompt_tokens - est_total
run_context.run.messages.append(assistant_message)
run_context.session.append_only_manager.sync_messages(run_context.run.messages)
return response
@@ -308,97 +343,53 @@ class StandardAgentExecutor(BaseAgentExecutor):
)
async def on_start(self, state: AgentState, resources: AgentRunResources) -> None:
resources.run_context.run.messages = state.messages
async def _execute_phase(
self,
phase_func: Callable[[AgentState, AgentRunResources], Awaitable[None]],
state: AgentState,
resources: AgentRunResources,
session_info: SessionInfo,
) -> Literal["NEXT", "RESET_CYCLE", "FINISH"]:
"""统一处理阶段执行与状态机完成/追加检查的高阶函数"""
await phase_func(state, resources)
if state.is_finished:
if self._check_follow_up(state, resources, session_info):
return "RESET_CYCLE"
return "FINISH"
return "NEXT"
async def run(
self, state: AgentState, resources: AgentRunResources
) -> AgentRunResult[OutputDataT]:
"""覆盖基类的模板方法,实现灵活的 while 控制流和 FOLLOW_UP 合并"""
await self.on_start(state, resources)
session_info = await session_manager.get_or_create(
resources.run_context.session_id or "default_session"
)
try:
cycle_count = 0
while cycle_count < resources.config.max_cycles:
state.current_cycle = cycle_count
await self.on_cycle_start(state, resources)
await self.build_llm_request(state, resources)
await self.execute_llm(state, resources)
status = await self._execute_phase(
self.handle_llm_response, state, resources, session_info
)
if status == "RESET_CYCLE":
cycle_count = 0
continue
if status == "FINISH":
assert state.final_result is not None
return state.final_result
status = await self._execute_phase(
self.filter_tool_calls, state, resources, session_info
)
if status == "RESET_CYCLE":
cycle_count = 0
continue
if status == "FINISH":
assert state.final_result is not None
return state.final_result
await self.execute_tools(state, resources)
status = await self._execute_phase(
self.handle_tool_results, state, resources, session_info
)
if status == "RESET_CYCLE":
cycle_count = 0
continue
if status == "FINISH":
assert state.final_result is not None
return state.final_result
cycle_count += 1
if self._check_follow_up(state, resources, session_info):
return await self.run(state, resources)
return await self.on_fallback(state, resources)
except Exception as e:
raise e
pass
async def on_cycle_start(
self, state: AgentState, resources: AgentRunResources
) -> None:
state.current_request_messages = []
state.current_response = None
state.current_tool_calls = []
state.current_tool_results = []
state.should_reset_cycle = False
cancellation_token = resources.run_context.run.cancellation_token
if cancellation_token:
cancellation_token.raise_if_cancelled()
try:
est_tokens = token_counter.count_context(
state.messages, resources.model_name or "", base_overhead=0
base_overhead = 0
if state.static_system_prompt:
sp_list = (
state.static_system_prompt
if isinstance(state.static_system_prompt, list)
else [state.static_system_prompt]
)
for sp in sp_list:
if sp:
base_overhead += token_counter._count_text(str(sp))
for m in state.dynamic_system_messages:
base_overhead += token_counter.count_message(
m, resources.run_context.run.current_model or ""
)
base_overhead += self._calculate_tool_overhead(state.tools)
est_tokens = (
token_counter.count_context(
resources.run_context.run.messages,
resources.run_context.run.current_model or "",
base_overhead=base_overhead,
)
+ state.token_drift
)
est_tokens = max(est_tokens, 0)
logger.debug(
f"(Iter {state.current_cycle + 1}) "
f"预估将消耗 {est_tokens} Token "
f"(Model: {resources.model_name or 'Unknown'})"
f"(Model: {resources.run_context.run.current_model or 'Unknown'})"
)
except Exception:
pass
@@ -413,8 +404,12 @@ class StandardAgentExecutor(BaseAgentExecutor):
steer_msgs = session_info.steer_queue.drain()
if steer_msgs:
for sm in steer_msgs:
state.messages.append(LLMMessage.user(f"💬 [用户实时修正指示]:{sm}"))
run_context.session.append_only_manager.sync_messages(state.messages)
run_context.run.messages.append(
LLMMessage.user(f"💬 [用户实时修正指示]:{sm}")
)
run_context.session.append_only_manager.sync_messages(
run_context.run.messages
)
messages_to_send = []
if state.static_system_prompt:
@@ -439,7 +434,7 @@ class StandardAgentExecutor(BaseAgentExecutor):
if prompt_text and prompt_text.strip():
messages_to_send.append(LLMMessage.system(prompt_text))
messages_to_send.extend(state.messages)
messages_to_send.extend(run_context.run.messages)
state.current_request_messages = messages_to_send
@@ -466,13 +461,16 @@ class StandardAgentExecutor(BaseAgentExecutor):
if not response.tool_calls:
logger.debug("✅ 模型未请求工具调用,推理循环结束。")
state.is_finished = True
state.final_result = model_construct(
state.pending_result = model_construct(
AgentRunResult,
output=response.text,
messages=state.messages,
messages=list(resources.run_context.run.messages),
usage=state.usage,
)
if state.is_finished:
await self._check_follow_up(state, resources)
async def filter_tool_calls(
self, state: AgentState, resources: AgentRunResources
) -> None:
@@ -522,14 +520,16 @@ class StandardAgentExecutor(BaseAgentExecutor):
logger.info("✅ 无本地客户端工具需执行,推理循环平滑结束。")
state.is_finished = True
state.final_result = model_construct(
state.pending_result = model_construct(
AgentRunResult,
output=response.text,
messages=state.messages,
messages=list(resources.run_context.run.messages),
usage=state.usage,
)
state.current_tool_calls = client_tool_calls
if state.is_finished:
await self._check_follow_up(state, resources)
async def execute_tools(
self, state: AgentState, resources: AgentRunResources
@@ -565,51 +565,6 @@ class StandardAgentExecutor(BaseAgentExecutor):
tool_results = await asyncio.gather(*exec_tasks, return_exceptions=True)
state.current_tool_results = tool_results
def _assemble_tool_message(
self,
original_call: ToolCallPart,
res_or_exc: BaseException | tuple[ToolCallPart, ToolResult],
tool_res: ToolResult | None,
state: AgentState,
) -> LLMMessage:
"""负责处理异常、解析多模态、序列化,并装配为最终的工具消息载体"""
media_parts = []
final_content = "Success"
if isinstance(res_or_exc, BaseException):
if isinstance(res_or_exc, ControlFlowExit):
raise res_or_exc
final_content = json.dumps(
{"error": str(res_or_exc), "status": "failed"},
ensure_ascii=False,
)
elif tool_res is not None:
if isinstance(tool_res.output, list):
texts = []
for item in tool_res.output:
if isinstance(item, ImagePart | AudioPart | VideoPart | FilePart):
media_parts.append(item)
elif isinstance(item, TextPart):
texts.append(item.text)
else:
texts.append(str(item))
final_content = " ".join(texts) if texts else "Success"
elif isinstance(tool_res.output, str):
final_content = tool_res.output
else:
final_content = dump_json_safely(tool_res.output, ensure_ascii=False)
tool_usage = getattr(tool_res, "usage", None)
if tool_usage is not None:
state.usage += tool_usage
msg = LLMMessage.tool_response(
original_call.id, original_call.tool_name, final_content
)
if media_parts:
msg.content.extend(media_parts)
return msg
async def handle_tool_results(
self, state: AgentState, resources: AgentRunResources
) -> None:
@@ -627,10 +582,12 @@ class StandardAgentExecutor(BaseAgentExecutor):
_, raw_tool_res = res_or_exc
tool_res = raw_tool_res
msg = self._assemble_tool_message(
original_call, res_or_exc, tool_res, state
msg, usage = ToolExecutor.assemble_tool_message(
original_call, res_or_exc, tool_res
)
state.messages.append(msg)
if usage:
state.usage += usage
resources.run_context.run.messages.append(msg)
if tool_res and getattr(tool_res, "directive", None):
ns = getattr(resources.run_context.session, "namespace", "global")
@@ -642,8 +599,10 @@ class StandardAgentExecutor(BaseAgentExecutor):
await handler(state, resources, tool_res)
if state.is_finished:
resources.run_context.session.append_only_manager.sync_messages(
state.messages
resources.run_context.run.messages
)
if state.is_finished:
await self._check_follow_up(state, resources)
return
else:
logger.warning(
@@ -651,7 +610,11 @@ class StandardAgentExecutor(BaseAgentExecutor):
f"的指令处理器 (Namespace: {ns})"
)
resources.run_context.session.append_only_manager.sync_messages(state.messages)
resources.run_context.session.append_only_manager.sync_messages(
resources.run_context.run.messages
)
if state.is_finished:
await self._check_follow_up(state, resources)
async def on_fallback(
self, state: AgentState, resources: AgentRunResources
@@ -680,12 +643,12 @@ class StandardAgentExecutor(BaseAgentExecutor):
"请**诚实地**向用户总结:你目前进行到了哪一步?遇到了什么困难导致循环耗尽?还有哪些预期步骤未能完成?\n"
"**绝对禁止**对用户撒谎声声称你已经完成了任务。严禁再次尝试调用任何工具!请直接输出纯文本结果。"
)
state.messages.append(fallback_msg)
run_context.run.messages.append(fallback_msg)
fallback_response = await self._invoke_and_record_llm(
state=state,
resources=resources,
messages=state.messages,
messages=run_context.run.messages,
tools=[],
tool_choice="none",
)
@@ -695,7 +658,7 @@ class StandardAgentExecutor(BaseAgentExecutor):
model_construct(
AgentRunResult,
output=fallback_response.text,
messages=state.messages,
messages=list(run_context.run.messages),
structured_data=None,
usage=state.usage,
),
+23 -23
View File
@@ -4,7 +4,7 @@ from collections.abc import Sequence
from pathlib import Path
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
from zhenxun.services.ai.capabilities import CapabilitySource, CombinedCapability
from zhenxun.services.ai.context.memory.builder import MemoryBuilder
@@ -18,10 +18,11 @@ from zhenxun.services.ai.core.messages import (
ToolCallPart,
UsageInfo,
)
from zhenxun.services.ai.core.models import ModelCapabilities
from zhenxun.services.ai.core.options import GenerationConfig
from zhenxun.services.ai.flow.core.models import BaseRuntimeConfig
from zhenxun.services.ai.run import RunContext
from zhenxun.services.ai.run.models import AgentRunResult, AgentTask, HandoffPayload
from zhenxun.services.ai.run.models import AgentRunResult, AgentTask
from zhenxun.services.ai.tools.core.toolkit import BaseToolkit
from zhenxun.services.ai.tools.engine.registry import ToolCollection
from zhenxun.utils.pydantic_compat import model_copy
@@ -121,27 +122,15 @@ class AgentState(BaseModel):
tools: ToolCollection | None = None
"""当前轮次生效的、已完成鉴权和过滤的工具集合"""
messages: list[AgentMessage] = Field(default_factory=list)
"""大模型将看到的完整历史消息列表 (执行历史)"""
usage: UsageInfo = Field(default_factory=UsageInfo)
"""累计的 Token 消耗"""
structured_result: Any | None = None
"""拦截到的结构化输出结果"""
early_result_output: Any | None = None
"""拦截到的早期终止输出结果"""
should_terminate: bool = False
"""标记是否应提前终止循环"""
handoff_triggered: HandoffPayload | None = None
"""标记是否触发了移交"""
is_finished: bool = False
"""标记大模型循环是否彻底结束"""
final_result: AgentRunResult[Any] | None = None
"""最终的运行结果 (AgentRunResult)"""
origin_msg_len: int = 0
"""初始进入循环时的消息历史长度 (用于增量保存记忆)"""
metadata: dict[str, Any] = Field(default_factory=dict)
"""供第三方开发者或生命周期钩子使用的自定义插槽,
在单个 Agent FSM 循环中存取临时变量"""
_origin_msg_len: int = PrivateAttr(default=0)
"""内部变量:初始进入循环时的消息历史长度 (用于增量保存记忆,防止被外部业务篡改)"""
current_cycle: int = 0
"""当前思考循环的轮次索引"""
current_request_messages: list[AgentMessage] = Field(default_factory=list)
"""当前即将发往大模型的实际请求消息"""
current_request_extra: dict[str, Any] = Field(default_factory=dict)
@@ -153,6 +142,17 @@ class AgentState(BaseModel):
current_tool_results: list[Any] = Field(default_factory=list)
"""当前轮次工具执行的结果或异常收集"""
should_reset_cycle: bool = False
"""标记是否需要重置当前思考循环(例如由于外部干预插入了新消息)"""
is_finished: bool = False
"""标记大模型循环是否彻底结束"""
pending_result: AgentRunResult[Any] | None = None
"""单一数据源:等待返回的最终运行结果 (SSOT)"""
usage: UsageInfo = Field(default_factory=UsageInfo)
"""累计的 Token 消耗"""
token_drift: int = 0
"""动态 Token 校准偏移量 (真实 - 预估)"""
class AgentRunResources(BaseModel):
"""大模型执行过程中的全局静态资源与配置载体"""
@@ -175,5 +175,5 @@ class AgentRunResources(BaseModel):
"""Agent 全局与运行时的统一策略配置"""
generation_config: GenerationConfig | None = None
"""大模型生成配置"""
model_name: str | None = None
"""当前实际调用的模型名称"""
model_capabilities: ModelCapabilities | None = None
"""当前底层大模型的能力配置(合并了用户自定义覆盖)"""