Coverage for agentos/core/context.py: 100%
81 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 00:18 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 00:18 +0800
1"""
2上下文管理器 — 构建Agent所需的完整上下文。
3"""
5from __future__ import annotations
7from dataclasses import dataclass, field
8from typing import Any
11@dataclass
12class ToolCall:
13 """模型请求的工具调用。"""
15 name: str
16 arguments: dict[str, Any]
19@dataclass
20class ToolResult:
21 """工具调用的返回结果。"""
23 call_id: str
24 output: str | None = None
25 error: str | None = None
26 exit_code: int | None = None
28 @property
29 def is_error(self) -> bool:
30 return self.error is not None
33@dataclass
34class Message:
35 """对话中的单条消息。"""
37 role: str # system | user | assistant | tool
38 content: str
39 tool_calls: list[ToolCall] | None = None
40 tool_call_id: str | None = None
43@dataclass
44class AgentContext:
45 """传给模型的完整上下文。"""
46 messages: list[Message]
47 tools: list[dict] | None = None
48 model_type: str = "openai"
51class ContextManager:
52 """管理Agent会话的全部消息历史。"""
54 def __init__(self, system_prompt: str = "", max_history: int = 200):
55 self.system_prompt = system_prompt
56 self.max_history = max_history
57 self._messages: list[Message] = []
58 self.session_id: str = ""
59 self._current_task: str = ""
60 self._step_count: int = 0
61 self._plan: str = ""
63 async def init_session(self, session_id: str, task: str):
64 self.session_id = session_id
65 self._current_task = task
66 self._step_count = 0
67 self._plan = ""
68 self._messages = []
69 if self.system_prompt:
70 self._messages.append(Message(role="system", content=self.system_prompt))
71 self._messages.append(Message(role="user", content=task))
73 def build_context(
74 self, model_type: str = "openai", tools: list[dict] | None = None
75 ) -> AgentContext:
76 self._step_count += 1
77 messages = self._messages[-self.max_history:]
78 return AgentContext(messages=messages, tools=tools, model_type=model_type)
80 def update_plan(self, new_plan: str):
81 self._plan = new_plan
82 self._messages.append(
83 Message(role="system", content=f"[计划更新] {new_plan}")
84 )
86 def append_tool_results(self, results: list[ToolResult]):
87 for r in results:
88 self._messages.append(
89 Message(
90 role="tool",
91 content=r.error or r.output or "",
92 tool_call_id=r.call_id,
93 )
94 )
96 def add_assistant_message(self, content: str, tool_calls: list[ToolCall] | None = None):
97 self._messages.append(
98 Message(role="assistant", content=content, tool_calls=tool_calls)
99 )
101 def add_user_message(self, content: str):
102 self._messages.append(Message(role="user", content=content))
104 def estimate_context_usage(self) -> float:
105 """估算上下文使用比例 (0.0-1.0)。按 max_history 消息数 vs 最大 Token 估算。"""
106 if len(self._messages) == 0:
107 return 0.0
108 estimated_tokens = sum(len(m.content) for m in self._messages) // 4
109 max_tokens = self.max_history * 200 # 每消息 ~200 token 粗估
110 return min(estimated_tokens / max_tokens if max_tokens > 0 else 0.0, 1.0)
112 @property
113 def current_task(self) -> str:
114 return self._current_task
116 @property
117 def step_count(self) -> int:
118 return self._step_count
120 @property
121 def plan(self) -> str:
122 return self._plan
124 @property
125 def message_count(self) -> int:
126 return len(self._messages)
128 @property
129 def estimated_tokens(self) -> int:
130 """粗略估算当前上下文的总token数(按每4字符≈1token)。"""
131 total_chars = sum(len(m.content) for m in self._messages)
132 return total_chars // 4