方法介绍
SkillClient 是 V3 Skill 管理面的 Python 客户端,用于 Skill 的全生命周期管理,包括检索和发现可用 Skill、创建与修改 Skill、管理资源文件、以及从对话中自动提取 Skill。说明:
Skill 一经创建即处于可用状态,Agent 可通过
listing 和 search 方法检索和发现 Skill,无需额外的部署或启用操作。导入
from tencentdb_agent_memory.v3 import SkillClient, AsyncSkillClientfrom tencentdb_agent_memory.v3.skill_client import encode_utf8, encode_base64
构造
from tencentdb_agent_memory.v3 import SkillClientclient = SkillClient(endpoint="https://memory.tdai.tencentyun.com",api_key="sk-xxxxxxxxxxxxxxxx",service_id="tdai-mem-xxxxxxxx",team_id="team-abc123",agent_id="agt-xyz789",user_id="usr-456",task_id="task-2026q3",timeout=30,verify=False,)
参数名 | 类型 | 必填 | 描述说明 |
endpoint | str | 是 | Memory 服务接入地址 |
api_key | str | 是 | API Key,格式 sk-... |
service_id | str | 是 | 实例 ID,如 tdai-mem-xxxxxxxx。使用自定义传输通道构造时可省略 |
team_id | str | 否 | 团队 ID,可在各方法中按需覆盖 |
agent_id | str | 否 | Agent ID,可在各方法中按需覆盖 |
user_id | str | 否 | 用户 ID,可在各方法中按需覆盖 |
task_id | str | 否 | Task ID,可在各方法中按需覆盖 |
timeout | float | 否 | 请求超时时间(秒),默认 30 |
verify | bool | 否 | 是否验证 SSL 证书,默认 False |
stub | Stub | 否 | 自定义网络传输通道,用于测试场景(如注入 mock) |
查询与发现
语义检索
按查询文本搜索匹配的 Skill,支持关键词、语义向量和混合搜索三种模式。
resp = client.search(query="Python type hints usage",top_k=10,mode="hybrid",)for hit in resp["items"]:print(f"[{hit['score']:.2f}] {hit['name']}")
参数名 | 类型 | 必填 | 描述说明 |
query | str | 是 | 搜索查询文本 |
top_k | int | 否 | 返回结果数量上限 |
mode | str | 否 | 搜索模式: bm25(关键词)、embedding(语义)或 hybrid(混合) |
scope | str | 否 | 搜索范围,传 "team" 时跨当前团队下全部 Agent 搜索 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
列表查询
按过滤条件分页查询 Skill 列表。
resp = client.list(filters={"team_id": "team-abc123"},pagination={"limit": 20, "offset": 0},)for skill in resp["items"]:print(f"{skill['name']} v{skill['version']}")
参数名 | 类型 | 必填 | 描述说明 |
filters | Dict | 否 | 过滤条件,支持按 team_id、agent_id、name 等字段筛选 |
pagination | Dict | 否 | 分页参数,含 limit 和 offset |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
查询单个 Skill
按 Skill ID 查询 Skill 的详细信息,支持按版本号查询历史版本。
# 查询最新版本resp = client.get("skill-abc123")print(resp["name"], resp["content"])# 查询指定版本resp = client.get("skill-abc123", version=3)# 仅查询元数据,不返回 contentresp = client.get("skill-abc123", include_content=False)
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
version | int | 否 | 指定版本号,不传则返回最新版本 |
include_content | bool | 否 | 是否返回 content 字段,默认 True |
include_manifest | bool | 否 | 是否返回文件清单,默认 False |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
查询版本历史
查看 Skill 的历史变更记录,可按分页查询历史版本。
resp = client.versions(skill_id="skill-abc123",pagination={"limit": 10, "offset": 0},)for v in resp["items"]:print(f"v{v['version']}: {v['created_at']}")
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
pagination | Dict | 否 | 分页参数,含 limit 和 offset |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
读取资源文件
读取 Skill 中某个资源文件的内容。
resp = client.read_file(skill_id="skill-abc123",path="examples/usage.py",version=3,)print(resp["content"])
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
path | str | 是 | 文件路径 |
version | int | 否 | 指定版本号,不传返回最新版本 |
encoding | str | 否 | 内容编码方式,默认返回原始格式 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
渲染可用 Skill 列表
为 Agent 生成可用 Skill 的 XML 格式描述块,便于拼入 Agent prompt 中的
<available_skills> 区块。resp = client.listing(query="coding helper",char_budget=2000,)print(resp["block"])
参数名 | 类型 | 必填 | 描述说明 |
query | str | 否 | 用于检索和筛选 Skill 的查询文本 |
char_budget | int | 否 | 返回的 XML 块最大字符数 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
创建 Skill
创建全新 Skill
向指定团队和 Agent 下创建一个新 Skill。创建后 Skill 即处于可用状态,可通过
listing 和 search 被 Agent 发现和调用。# 构建资源文件resource = client.encode_utf8("README.md","# Python Best Practices\\n\\n- Use type hints\\n- Follow PEP 8",mime_type="text/markdown",)# 创建 Skillresp = client.create(name="python-tips",content="---\\nname: python-tips\\ndescription: Python coding tips\\n---\\n# Tips\\n",resources=[resource],metadata={"category": "programming"},)print(resp["skill_id"]) # "skill-abc123"
参数名 | 类型 | 必填 | 描述说明 |
name | str | 是 | Skill 名称 |
content | str | 是 | Skill 内容,推荐使用 YAML frontmatter 格式 |
resources | List[Dict] | 否 | 附加资源文件列表,通过 encode_utf8 或 encode_base64 构建 |
metadata | Dict | 否 | 自定义元数据 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
响应参数:
参数名 | 类型 | 参数含义 |
skill_id | str | 新创建的 Skill ID |
version | int | 初始版本号,固定为 1 |
从对话提取 Skill
从已有对话中自动提取可复用的 Skill。支持两种使用方式:
即时提取:直接在
messages 参数中传入对话内容。缓冲区提取:先通过
conversation_add 持续积累对话数据,再调用 extract(不传 messages)从缓冲区中提取。Extract 为异步操作,调用后返回任务 ID,提取完成的 Skill 可通过
list 或 search 方法按 task_ref_id 过滤获取。resp = client.extract(messages=[{"role": "user", "content": "请帮我写一个 Python 脚本来处理 CSV 文件"},{"role": "assistant", "content": "好的,以下是处理 CSV 的脚本..."},],session_id="agent-main:sess-001",)print(resp["task_id"]) # 异步提取任务 ID
参数名 | 类型 | 必填 | 描述说明 |
messages | List[Dict] | 是 | 对话消息列表,每项含 role 和 content |
session_id | str | 否 | 会话 ID |
reason | str | 否 | 提取触发原因,如 tool_calls、bytes |
options | Dict | 否 | 提取选项 |
space_id | str | 否 | 实例 ID,不传则使用构造时的 service_id |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
响应参数:
参数名 | 类型 | 参数含义 |
ok | bool | 是否受理成功 |
task_id | str | 提取任务 ID,用于后续查询结果 |
archived_at_ms | int | 归档时间戳(毫秒) |
archive_key | str | 归档键 |
缓冲区模式:累积对话后提取
如果希望持续积累多轮对话后再统一提取 Skill,可使用
conversation_add 将对话内容写入缓冲区,积累到一定量后通过 extract(不传 messages)从缓冲区提取。client.conversation_add(session_id="agent-main:sess-001",user_id="usr-456",team_id="team-abc123",agent_id="agt-xyz789",messages=[{"role": "user", "content": "帮我查一下部署配置"},{"role": "assistant", "content": "以下是当前服务的部署参数..."},],)# 积累足够对话后,不传 messages,从缓冲区提取resp = client.extract(session_id="agent-main:sess-001")
参数名 | 类型 | 必填 | 描述说明 |
session_id | str | 是 | 会话 ID |
user_id | str | 是 | 用户 ID。此参数为服务端必填,不会自动使用构造时的默认值 |
team_id | str | 是 | 团队 ID。同上,不会自动使用构造时的默认值 |
agent_id | str | 是 | Agent ID。同上,不会自动使用构造时的默认值 |
messages | List[Dict] | 是 | 对话消息列表 |
space_id | str | 否 | 实例 ID,覆盖构造时的 service_id |
task_id | str | 否 | Task ID |
修改 Skill
更新 Skill 内容
更新已有 Skill 的文本内容。通过版本号校验防止并发修改冲突。
resp = client.update(skill_id="skill-abc123",expected_version=2,content="---\\nname: python-tips\\n---\\n# Updated Python Tips\\n",)
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
expected_version | int | 是 | 预期当前版本号,用于防止并发覆盖 |
content | str | 是 | 新的 Skill 内容 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
局部文本替换
对 Skill 内容进行局部修改,适合仅需修改某个段落或字段的情况,无需传输完整内容。
resp = client.patch(skill_id="skill-abc123",expected_version=3,old_string="Use type hints",new_string="Use type hints (PEP 484)",)
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
expected_version | int | 是 | 预期当前版本号 |
old_string | str | 是 | 被替换的文本 |
new_string | str | 是 | 替换后的文本 |
replace_all | bool | 否 | 是否替换所有匹配项,默认仅替换首次出现的 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
写入资源文件
向 Skill 追加或更新资源文件,适用于 Skill 附带的代码示例、配置文件、图片等。
resp = client.write_files(skill_id="skill-abc123",expected_version=3,files=[client.encode_utf8("examples/usage.py", "print('hello')", mime_type="text/x-python"),client.encode_base64("assets/logo.png", b"...", mime_type="image/png"),],)
encode_utf8 和 encode_base64 均为 SkillClient 的静态辅助方法。方法 | 用途 | 参数 |
encode_utf8(path, content, mime_type) | 编码文本文件 | path 文件路径;content 文本内容(str);mime_type MIME 类型 |
encode_base64(path, content, mime_type) | 编码二进制文件 | path 文件路径;content 二进制内容(bytes);mime_type MIME 类型 |
请求参数:
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
expected_version | int | 是 | 预期当前版本号 |
files | List[Dict] | 是 | 文件列表 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
删除资源文件
从 Skill 中移除指定路径的资源文件。
resp = client.remove_files(skill_id="skill-abc123",expected_version=4,paths=["examples/usage.py", "assets/logo.png"],)
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
expected_version | int | 是 | 预期当前版本号 |
paths | List[str] | 是 | 要删除的文件路径列表 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
删除 Skill
删除指定版本号的 Skill。删除操作受版本号校验保护,需传入当前版本号。
resp = client.delete(skill_id="skill-abc123",expected_version=5,)
参数名 | 类型 | 必填 | 描述说明 |
skill_id | str | 是 | Skill ID |
expected_version | int | 是 | 预期当前版本号 |
team_id | str | 否 | 团队 ID,覆盖默认值 |
agent_id | str | 否 | Agent ID,覆盖默认值 |
user_id | str | 否 | 用户 ID,覆盖默认值 |
task_id | str | 否 | Task ID,覆盖默认值 |
切换上下文
当需要用不同的团队或用户身份调用 Skill 接口时,可通过
with_defaults 生成一个共享同一网络连接的客户端副本,无需重复传入 endpoint、api_key 和 service_id。client = SkillClient(endpoint="https://memory.tdai.tencentyun.com",api_key="sk-xxx",service_id="tdai-mem-xxx",team_id="team-a",user_id="usr-one",)# 切换到不同团队和用户client_b = client.with_defaults(team_id="team-b", user_id="usr-two")result = client_b.search(query="Python best practices", top_k=5)# 仅切换用户,团队沿用原始客户端的默认值client_c = client.with_defaults(user_id="usr-three")
上下文管理器
推荐使用上下文管理器自动管理连接生命周期:
with SkillClient(endpoint="https://memory.tdai.tencentyun.com",api_key="sk-xxxxxxxxxxxxxxxx",service_id="tdai-mem-xxxxxxxx",) as client:resp = client.search(query="Python tips", top_k=5)
退出
with 块时自动调用 close() 释放网络连接。异步版本
AsyncSkillClient 的方法签名与 SkillClient 完全一致,所有方法均为 async:import asynciofrom tencentdb_agent_memory.v3 import AsyncSkillClientasync def main():async with AsyncSkillClient(endpoint="https://memory.tdai.tencentyun.com",api_key="sk-...",service_id="tdai-mem-xxxxxxxx",) as client:created = await client.create(name="python-tips",content="---\\nname: python-tips\\n---\\n# Python tips\\n",)print(f"创建成功: {created['skill_id']}")result = await client.search(query="Python tips", top_k=5)for hit in result["items"]:print(f"[{hit['score']:.2f}] {hit['name']}")asyncio.run(main())