Install any skill in seconds. Free to start, no credit card required.
Get Started Free →萤石语音广播技能。支持本地音频文件上传或文本转语音,实现语音内容下发到设备播放。
SkillScore74/100
Test results 74
3/3 safety checks passed
17%74%of test cases passed +57 pts
Passed 17 of 23 test cases with the skill, and 4 of 23 without it.
13 fixed4 passed either way6 still failing0 broke
One test taskHow long is an Ezviz access token valid for, and how many minutes prior to expiration will the token manager automatically refresh it?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add leoyeai-ezviz-audio-broadcast
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install leoyeai-ezviz-audio-broadcastThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/leoyeai-ezviz-audio-broadcast/SKILL.md https://app.decimal.ai/s/leoyeai-ezviz-audio-broadcast/SKILL.md.claude/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdThis gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add leoyeai-ezviz-audio-broadcastimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add leoyeai-ezviz-audio-broadcastfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add leoyeai-ezviz-audio-broadcastimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add leoyeai-ezviz-audio-broadcastimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add leoyeai-ezviz-audio-broadcastfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add leoyeai-ezviz-audio-broadcastfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 1 other file is not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add leoyeai-ezviz-audio-broadcastimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add leoyeai-ezviz-audio-broadcastfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 1 other file is not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add leoyeai-ezviz-audio-broadcastfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 1 other file is not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add leoyeai-ezviz-audio-broadcastimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 1 other file is not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add leoyeai-ezviz-audio-broadcastimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 1 other file is not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add leoyeai-ezviz-audio-broadcastfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("leoyeai-ezviz-audio-broadcast")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 1 other file is not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add leoyeai-ezviz-audio-broadcastNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.md https://app.decimal.ai/s/leoyeai-ezviz-audio-broadcast/SKILL.md.agents/skills/leoyeai-ezviz-audio-broadcast/SKILL.mdThis gets SKILL.md only. The skill's 1 other file is not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 1 other file is not included.
Raw files: /s/leoyeai-ezviz-audio-broadcast/SKILL.md · /s/leoyeai-ezviz-audio-broadcast.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/leoyeai-ezviz-audio-broadcast/SKILL.mdcurl -fsSL https://app.decimal.ai/s/leoyeai-ezviz-audio-broadcast.jsonThis gets SKILL.md only. The skill's 1 other file is not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
通过萤石语音上传和下发接口,实现语音内容到设备的广播播放。
在使用此技能前,请完成以下安全检查:
| # | 检查项 | 状态 | 说明 |
|---|---|---|---|
| 1 | 凭证权限 | ⚠️ 必需 | 使用最小权限的 AppKey/AppSecret,不要用主账号凭证 |
| 2 | 配置文件 | ⚠️ 注意 | 技能会读取 ~/.openclaw/config.json 等文件中的萤石凭证 |
| 3 | Token 缓存 | ⚠️ 注意 | Token 缓存在 /tmp/ezviz_global_token_cache/ (权限 600) |
| 4 | 系统命令 | ⚠️ 注意 | 使用 say/espeak/ffmpeg 进行 TTS (通过 subprocess) |
| 5 | API 域名 | ✅ 已验证 | openai.ys7.com 是萤石官方 API 端点 |
| 6 | 代码审查 | ✅ 推荐 | 审查 scripts/audio_broadcast.py 和 lib/token_manager.py |
# 1. 使用环境变量(优先级最高,避免配置文件意外使用)
export EZVIZ_APP_KEY="your_dedicated_app_key"
export EZVIZ_APP_SECRET="your_dedicated_app_secret"
export EZVIZ_DEVICE_SERIAL="device1,device2"
# 2. 高安全环境:禁用 Token 缓存
export EZVIZ_TOKEN_CACHE=0
# 3. 测试凭证(推荐先用测试账号)
# 登录 https://openai.ys7.com/ 创建专用应用,仅开通语音相关权限
技能按以下顺序获取凭证(优先级从高到低):
EZVIZ_APP_KEY, EZVIZ_APP_SECRET) ← 推荐~/.openclaw/config.json 等)pip install requests
export EZVIZ_APP_KEY="your_app_key"
export EZVIZ_APP_SECRET="your_app_secret"
export EZVIZ_DEVICE_SERIAL="dev1,dev2,dev3"
可选环境变量:
export EZVIZ_CHANNEL_NO="1" # 通道号,默认 1
export EZVIZ_AUDIO_FILE="/path/to/audio.mp3" # 本地音频文件路径(二选一)
export EZVIZ_TEXT_CONTENT="语音内容文本" # 文本内容(二选一)
export EZVIZ_VOICE_NAME="custom_name" # 自定义语音名称
export EZVIZ_TOKEN_CACHE="1" # Token 缓存:1=启用 (默认), 0=禁用
Token 缓存说明:
EZVIZ_TOKEN_CACHE=0 每次重新获取 Token/tmp/ezviz_global_token_cache/global_token_cache.json注意:
EZVIZ_ACCESS_TOKEN!技能会自动获取 TokenEZVIZ_AUDIO_FILE 或 EZVIZ_TEXT_CONTENT 其中一个support_talk=1或3)python3 {baseDir}/scripts/audio_broadcast.py
命令行参数:
# 使用本地音频文件
python3 {baseDir}/scripts/audio_broadcast.py appKey appSecret dev1 /path/to/audio.mp3 [channel_no]
# 使用文本内容(自动生成语音)
python3 {baseDir}/scripts/audio_broadcast.py appKey appSecret dev1 "语音内容文本" [channel_no]
1. 获取 Token (appKey + appSecret → accessToken)
↓
2a. 如果提供音频文件:直接上传文件
2b. 如果提供文本:调用TTS生成音频文件,然后上传
↓
3. 下发语音 (accessToken + deviceSerial + fileUrl → 设备播放)
↓
4. 输出结果 (JSON + 控制台)
你不需要手动获取或配置 EZVIZ_ACCESS_TOKEN!
技能会自动处理 Token 的获取:
首次运行:
appKey + appSecret → 调用萤石 API → 获取 accessToken (有效期 7 天)
↓
保存到缓存文件(系统临时目录)
↓
后续运行:
检查缓存 Token 是否过期
├─ 未过期 → 直接使用缓存 Token ✅
└─ 已过期 → 重新获取新 Token
Token 管理特性:
EZVIZ_ACCESS_TOKEN 环境变量EZVIZ_TOKEN_CACHE=0 可禁用缓存(每次运行重新获取)======================================================================
Ezviz Audio Broadcast Skill (萤石语音广播)
======================================================================
[Time] 2026-03-16 16:30:00
[INFO] Target devices: 2
- dev1 (Channel: 1)
- dev2 (Channel: 1)
[INFO] Mode: Text-to-Speech
[INFO] Content: 接下来插播一个广告
======================================================================
[Step 1] Getting access token...
[SUCCESS] Token obtained, expires: 2026-03-23 16:30:00
======================================================================
[Step 2] Generating and uploading audio...
[INFO] Generated TTS file: /tmp/ezviz_tts_12345.mp3
[SUCCESS] Audio uploaded successfully!
[INFO] Voice Name: ad_broadcast
[INFO] File URL: https://oss-cn-shenzhen.aliyuncs.com/voice/...
======================================================================
[Step 3] Broadcasting audio to devices...
======================================================================
[Device] dev1 (Channel: 1)
[SUCCESS] Audio broadcast completed!
[Device] dev2 (Channel: 1)
[SUCCESS] Audio broadcast completed!
======================================================================
BROADCAST SUMMARY
======================================================================
Total devices: 2
Success: 2
Failed: 0
======================================================================
| 格式 | 示例 | 说明 |
|---|---|---|
| 单设备 | dev1 | 默认通道 1 |
| 多设备 | dev1,dev2,dev3 | 全部使用默认通道 |
| 指定通道 | dev1:1,dev2:2 | 每个设备独立通道 |
| 混合 | dev1,dev2:2,dev3 | 部分指定通道 |
| 接口 | URL | 文档 |
|---|---|---|
| 获取 Token | POST /api/lapp/token/get | https://openai.ys7.com/help/81 |
| 语音上传 | POST /api/lapp/voice/upload | https://openai.ys7.com/help/1241 |
| 语音下发 | POST /api/lapp/voice/send | https://openai.ys7.com/help/1253 |
| 域名 | 用途 |
|---|---|
openai.ys7.com | 萤石开放平台 API(Token、语音上传、下发) |
| 格式 | 支持 | 限制 |
|---|---|---|
| WAV | ✅ | 最大5MB,最长60秒 |
| MP3 | ✅ | 最大5MB,最长60秒 |
| AAC | ✅ | 最大5MB,最长60秒 |
⚠️ 设备兼容性: 设备必须支持对讲功能(support_talk=1或3),否则返回错误码 20015
⚠️ 频率限制: 语音下发接口有调用频率限制,建议设备间间隔1秒以上
⚠️ 文件大小: 音频文件不能超过5MB,时长不能超过60秒
⚠️ Token 安全: Token 缓存到系统临时目录(权限 600),不写入日志,不发送到非萤石端点
⚠️ TTS依赖: 文本转语音功能依赖系统TTS服务,确保系统支持
本技能会向第三方服务发送数据:
| 数据类型 | 发送到 | 用途 | 是否必需 |
|---|---|---|---|
| 音频文件 | openai.ys7.com (萤石) | 语音上传和下发 | ✅ 必需 |
| appKey/appSecret | openai.ys7.com (萤石) | 获取访问 Token | ✅ 必需 |
| 设备序列号 | openai.ys7.com (萤石) | 请求下发 | ✅ 必需 |
| EZVIZ_ACCESS_TOKEN | 自动生成 | 每次运行自动获取 | ✅ 自动 |
数据流出说明:
openai.ys7.com): 所有API调用 - 萤石官方 API凭证权限建议:
本地处理:
场景1: 紧急安全通知
python3 audio_broadcast.py your_key your_secret "kitchen_cam,dining_area" "注意!检测到安全隐患,请立即检查!"
场景2: 商业促销广播
export EZVIZ_TEXT_CONTENT="欢迎光临!今日特价商品限时优惠,详情请咨询店员!"
export EZVIZ_DEVICE_SERIAL="store_front,store_back"
python3 audio_broadcast.py
场景3: 使用预录制音频
python3 audio_broadcast.py your_key your_secret entrance_cam /path/to/welcome_message.mp3
| 接口 | URL | 文档 |
|---|---|---|
| 获取 Token | POST /api/lapp/token/get | https://openai.ys7.com/help/81 |
| 语音上传 | POST /api/lapp/voice/upload | https://openai.ys7.com/help/1241 |
| 语音下发 | POST /api/lapp/voice/send | https://openai.ys7.com/help/1253 |
API 域名: https://openai.ys7.com (萤石开放平台)
# 验证域名连通性
curl -I https://openai.ys7.com/api/lapp/token/get
# 检查 SSL 证书
curl -vI https://openai.ys7.com/api/lapp/token/get 2>&1 | grep -A5 "SSL certificate"
# 验证域名所有权(萤石开放平台)
whois ys7.com
官方文档: https://open.ys7.com/
技能会读取以下路径中的萤石配置(仅当环境变量未设置时):
~/.openclaw/config.json
~/.openclaw/gateway/config.json
~/.openclaw/channels.json
配置格式:
{
"channels": {
"ezviz": {
"appId": "your_app_id",
"appSecret": "your_app_secret",
"domain": "https://openai.ys7.com",
"enabled": true
}
}
}
安全建议:
禁用配置文件扫描(环境变量优先):
export EZVIZ_APP_KEY="your_key"
export EZVIZ_APP_SECRET="your_secret"
# 环境变量优先级高于配置文件
Python 包:
pip install requests
系统工具 (仅 TTS 需要):
say, afconvertespeak, ffmpeg技能通过 subprocess 调用系统 TTS 命令:
# macOS TTS
subprocess.run(['say', '-o', temp_aiff, text_with_pauses], check=True, capture_output=True)
subprocess.run(['afconvert', '-f', 'WAVE', '-d', 'LEI16@44100', temp_aiff, output_path], ...)
# Linux TTS
subprocess.run(['espeak', '-w', output_path, text], check=True, capture_output=True)
subprocess.run(['ffmpeg', '-i', input, '-acodec', 'libmp3lame', output], ...)
安全措施 (v1.0.2+):
validate_text_input() 验证;, |, &, $(), 等字符的输入输入验证规则:
| 检查项 | 限制 | 说明 |
|---|---|---|
| 空内容 | ❌ 拒绝 | 文本不能为空 |
| 长度 | ≤500 字符 | 防止过长输入 |
| 危险字符 | ❌ 拒绝 | ;, ` |
| 设备序列号 | 字母数字 | 只允许 A-Za-z0-9_:, |
安全建议:
text_to_speech() 函数不使用 TTS (仅提供音频文件,避免系统命令):
python3 scripts/audio_broadcast.py appKey appSecret deviceSerial --audio-file /path/to/audio.mp3
必需环境变量 (在 SKILL.md metadata 中声明):
metadata:
openclaw:
requires:
env: ["EZVIZ_APP_KEY", "EZVIZ_APP_SECRET", "EZVIZ_DEVICE_SERIAL"]
⚠️ 注意: 如果 Registry metadata 未显示必需环境变量,以 SKILL.md 为准。
默认行为:
/tmp/ezviz_global_token_cache/global_token_cache.json)缓存位置查询:
# macOS
ls -la /var/folders/*/T/ezviz_global_token_cache/
# Linux
ls -la /tmp/ezviz_global_token_cache/
# 查看缓存内容(脱敏)
cat /tmp/ezviz_global_token_cache/global_token_cache.json
⚠️ 潜在风险:
| 风险 | 等级 | 说明 |
|---|---|---|
| Token 明文存储 | 🟡 中 | 文件权限 600,但仍是明文 |
| 跨技能共享 | 🟡 中 | 同一用户的技能可共享 Token |
| 多用户系统 | 🟠 高 | 其他用户可能访问临时目录 |
| 临时目录清理 | 🟢 低 | 系统重启可能清理临时文件 |
禁用缓存 (高安全环境/共享系统):
export EZVIZ_TOKEN_CACHE=0
python3 scripts/audio_broadcast.py ...
清除缓存:
# 清除所有缓存 Token
rm -rf /tmp/ezviz_global_token_cache/
# 或使用 Token 管理器
python3 lib/token_manager.py clear
安全建议:
| 环境 | 建议 |
|---|---|
| 单用户个人设备 | ✅ 默认缓存安全(文件权限 600) |
| 多用户共享系统 | ⚠️ 设置 EZVIZ_TOKEN_CACHE=0 |
| 高安全环境 | ⚠️ 禁用缓存 + 专用 OS 用户 + 容器隔离 |
| 生产环境 | ⚠️ 容器/VM 隔离 + 禁用缓存 + 密钥管理器 |
⚠️ 早期版本用户必须轮换凭证!
如果您曾经使用过旧版本的 .env 文件:
🚨 您的凭证可能已泄露!
立即执行:
1. 登录 https://openai.ys7.com/
2. 删除旧应用,创建新应用
3. 获取新的 AppKey 和 AppSecret
正确做法:
| 数据类型 | 发送到 | 用途 |
|---|---|---|
| AppKey/AppSecret | openai.ys7.com | 获取 Token |
| 设备序列号 | openai.ys7.com | API 调用 |
| 音频文件 | openai.ys7.com | 语音上传 |
推荐审查的文件:
scripts/audio_broadcast.py - 主脚本lib/token_manager.py - Token 管理器代码特点:
requests 库(标准做法)subprocess 带列表参数(减少 shell 注入风险)安装前:
安装时:
EZVIZ_TOKEN_CACHE=0(共享系统/多用户环境)安装后:
根据安全审计建议,请在安装前完成以下检查:
scripts/audio_broadcast.py 和 lib/token_manager.pyopenai.ys7.com 是萤石官方端点~/.openclaw/*.json 中是否有敏感凭证/tmp/ezviz_global_token_cache/ 可接受EZVIZ_APP_KEY 等环境变量EZVIZ_TOKEN_CACHE=0requests 等依赖的安全更新更新日志:
| 日期 | 版本 | 变更 | 说明 |
|---|---|---|---|
| 2026-03-18 | 1.0.3 | 修复 metadata | 添加 config.tokenCache 配置说明 |
| 2026-03-18 | 1.0.3 | 明确缓存行为 | 默认启用缓存,支持 EZVIZ_TOKEN_CACHE=0 禁用 |
| 2026-03-18 | 1.0.2 | 添加输入验证 | validate_text_input() 和 validate_device_serial() |
| 2026-03-18 | 1.0.2 | 添加凭证来源警告 | 从配置文件读取时显示警告 |
| 2026-03-18 | 1.0.2 | 更新 metadata 格式 | 使用 YAML 格式,添加 warnings 字段 |
| 2026-03-18 | 1.0.1 | 修复 Token 缓存 bug | use_cache=None 改为 use_cache=True |
| 2026-03-18 | 1.0.1 | 添加安全审计清单 | 根据安全建议添加完整检查清单 |
| 2026-03-18 | 1.0.1 | 明确配置文件行为 | 说明 ~/.openclaw/*.json 读取逻辑 |
| 2026-03-18 | 1.0.1 | 添加 API 域名验证 | 提供域名和 SSL 验证命令 |
| 2026-03-18 | 1.0.1 | 添加 TTS 安全说明 | 说明 subprocess 调用系统命令的安全措施 |
最后更新: 2026-03-18
版本: 1.0.3 (缓存配置修复版)
技能支持从 OpenClaw 的 channels 配置中自动读取萤石凭证,无需单独设置环境变量。
在 ~/.openclaw/config.json 或 ~/.openclaw/channels.json 中添加:
{
"channels": {
"ezviz": {
"appId": "your_app_id",
"appSecret": "your_app_secret",
"domain": "https://openai.ys7.com",
"enabled": true
}
}
}
技能会按以下顺序查找配置文件:
~/.openclaw/config.json~/.openclaw/gateway/config.json~/.openclaw/channels.json凭证获取优先级:
EZVIZ_APP_KEYEZVIZ_APP_SECRETchannels.ezviz.appIdchannels.ezviz.appSecretAutomatically, when a task matches. Use when: 需要向萤石设备发送语音通知、广播、提醒等音频内容。 ⚠️ 安全要求:必须设置 EZVIZ_APP_KEY 和 EZVIZ_APP_SECRET 环境变量,使用最小权限凭证。
Published skills whose descriptions closely match this one, with their headline benchmark lift.
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.