Skip to content

Commit a02aaca

Browse files
authored
Merge pull request volcengine#529 from wujiaming-ai/feat/skill_observe
feat: 新增skill observe功能
2 parents 27af010 + 6d279ff commit a02aaca

File tree

7 files changed

+97
-3
lines changed

7 files changed

+97
-3
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "veadk-python"
3-
version = "0.5.22"
3+
version = "0.5.23"
44
description = "Volcengine agent development kit, integrations with Volcengine cloud services."
55
readme = "README.md"
66
requires-python = ">=3.10"

veadk/skills/skill.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Skill(BaseModel):
2323
skill_space_id: Optional[str] = None
2424
bucket_name: Optional[str] = None
2525
checklist: List[Dict[str, str]] = []
26+
id: Optional[str] = None
2627

2728
def get_checklist_items(self) -> List[str]:
2829
return [item.get("item", item.get("id", "")) for item in self.checklist]

veadk/skills/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ def load_skills_from_cloud(skill_space_ids: str) -> list[Skill]:
197197
skill_description = item.get("Description")
198198
tos_bucket = item.get("BucketName")
199199
tos_path = item.get("TosPath")
200+
skill_id = item.get("SkillId")
200201
if not skill_name:
201202
continue
202203

@@ -206,6 +207,7 @@ def load_skills_from_cloud(skill_space_ids: str) -> list[Skill]:
206207
path=tos_path,
207208
skill_space_id=skill_space_id,
208209
bucket_name=tos_bucket,
210+
id=skill_id,
209211
)
210212

211213
skills.append(skill)

veadk/tools/builtin_tools/execute_skills.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def execute_skills(
170170
"TOS_SKILLS_DIR": f"tos://agentkit-platform-{account_id}/skills/",
171171
"SKILL_SPACE_ID": skill_space_id,
172172
"TOOL_USER_SESSION_ID": tool_user_session_id,
173+
"PYTHONPATH": "$SRV_PYTHONPATH:$PYTHONPATH",
173174
}
174175

175176
code = f"""

veadk/tools/skills_tools/skills_tool.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@
1919

2020
from google.adk.tools import BaseTool, ToolContext
2121
from google.genai import types
22+
from opentelemetry import trace
23+
from opentelemetry.sdk.trace import _Span
24+
from opentelemetry.trace.status import Status, StatusCode
2225

2326
from veadk.skills.skill import Skill
2427
from veadk.tools.skills_tools.session_path import get_session_path
28+
from veadk.tracing.telemetry.telemetry import set_common_attributes_on_tool_span
2529
from veadk.utils.logger import get_logger
2630

31+
tracer = trace.get_tracer("veadk.skills_tool")
32+
2733
logger = get_logger(__name__)
2834

2935

@@ -94,7 +100,11 @@ async def run_async(
94100
if not skill_name:
95101
return "Error: No skill name provided"
96102

97-
return self._invoke_skill(skill_name, tool_context)
103+
with tracer.start_as_current_span(f"execute_skill {skill_name}") as span:
104+
result = self._invoke_skill(skill_name, tool_context)
105+
self._add_skill_span_attributes(span, skill_name, result)
106+
self._upload_skill_metrics(span, skill_name, result)
107+
return result
98108

99109
def _invoke_skill(self, skill_name: str, tool_context: ToolContext) -> str:
100110
"""Load and return the full content of a skill."""
@@ -345,3 +355,75 @@ def _format_skill_content(self, skill_name: str, content: str, skill_dir) -> str
345355
"The skill has been loaded. Follow the instructions above and use the bash tool to execute commands."
346356
)
347357
return header + content + footer
358+
359+
def _add_skill_span_attributes(
360+
self,
361+
span: _Span,
362+
skill_name: str,
363+
result: str,
364+
) -> None:
365+
"""Add attributes to the skill execution span."""
366+
try:
367+
set_common_attributes_on_tool_span(current_span=span)
368+
369+
if result:
370+
if result.startswith("Error:"):
371+
span.set_status(Status(StatusCode.ERROR, result))
372+
373+
span.set_attribute("skill.name", skill_name)
374+
span.set_attribute("tool.name", self.name)
375+
span.set_attribute("gen_ai.operation.name", "execute_skill")
376+
span.set_attribute("gen_ai.span.kind", "tool")
377+
if skill_name in self.skills:
378+
skill = self.skills[skill_name]
379+
if hasattr(skill, "skill_space_id") and skill.skill_space_id:
380+
span.set_attribute("skill.space_id", skill.skill_space_id)
381+
if hasattr(skill, "bucket_name") and skill.bucket_name:
382+
span.set_attribute("skill.bucket_name", skill.bucket_name)
383+
if hasattr(skill, "path") and skill.path:
384+
span.set_attribute("skill.path", skill.path)
385+
if hasattr(skill, "id") and skill.id:
386+
span.set_attribute("skill.id", skill.id)
387+
logger.debug(f"Added skill span attributes for {skill_name}")
388+
except Exception as e:
389+
logger.warning(f"Failed to add skill span attributes: {e}")
390+
391+
def _upload_skill_metrics(self, span: _Span, skill_name: str, result: str) -> None:
392+
"""Upload skill metrics to the telemetry system."""
393+
try:
394+
import time
395+
from veadk.tracing.telemetry.telemetry import meter_uploader
396+
397+
if meter_uploader:
398+
# 初始化属性,包含技能相关信息
399+
skill = self.skills.get(skill_name)
400+
attributes = {
401+
"skill_name": skill_name,
402+
"tool_name": self.name,
403+
"skill_space_id": (
404+
skill.skill_space_id if skill and skill.skill_space_id else ""
405+
),
406+
"skill_id": skill.id if skill and skill.id else "",
407+
"gen_ai.operation.name": "execute_skill",
408+
"error_type": (
409+
"skill_execution_error" if result.startswith("Error:") else ""
410+
),
411+
}
412+
413+
# 计算 span 执行耗时(秒)
414+
latency_seconds = 0
415+
if hasattr(span, "start_time"):
416+
# 计算耗时(秒)
417+
latency_seconds = (time.time_ns() - span.start_time) / 1e9 # type: ignore
418+
419+
# 记录技能执行延迟
420+
if hasattr(meter_uploader, "skill_invoke_latency"):
421+
# 使用 skill_invoke_latency 记录技能执行延迟(秒)
422+
meter_uploader.skill_invoke_latency.record(
423+
latency_seconds, attributes
424+
)
425+
logger.debug(
426+
f"Uploaded skill metrics for {skill_name} with latency {latency_seconds:.4f}s and attributes {attributes}"
427+
)
428+
except Exception as e:
429+
logger.warning(f"Failed to upload skill metrics: {e}")

veadk/tracing/telemetry/exporters/apmplus_exporter.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ class Meters:
151151
APMPLUS_SPAN_LATENCY = "apmplus_span_latency"
152152
# tool token usage
153153
APMPLUS_TOOL_TOKEN_USAGE = "apmplus_tool_token_usage"
154+
# skill invoke latency
155+
GEN_AI_SKILL_INVOKE_LATENCY = "gen_ai_skill_invoke_latency"
154156

155157

156158
class MeterUploader:
@@ -269,6 +271,12 @@ def __init__(
269271
unit="count",
270272
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS,
271273
)
274+
self.skill_invoke_latency = self.meter.create_histogram(
275+
name=Meters.GEN_AI_SKILL_INVOKE_LATENCY,
276+
description="Latency of skill invocations",
277+
unit="s",
278+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
279+
)
272280

273281
def record_call_llm(
274282
self,

veadk/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
VERSION = "0.5.22"
15+
VERSION = "0.5.23"

0 commit comments

Comments
 (0)