| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- """
- 工具系统完整示例
- 本文件展示 @tool 装饰器的所有用法,包括:
- ## 基础功能
- 1. 最简形式
- 2. 带 i18n 展示信息
- 3. 带可编辑参数
- 4. 需要用户确认
- 5. 带 context 参数
- 6. 同步工具
- 7. 复杂返回类型
- ## 高级功能
- 8. 域名过滤(URL Patterns)
- 9. 敏感数据处理(<secret> 占位符 + TOTP)
- 10. 工具使用统计
- 11. 组合所有功能
- 注意:
- - uid 参数会由框架自动注入,不需要用户传递
- - context 参数用于传递额外上下文(如浏览器会话、当前 URL 等)
- - 返回值可以是字符串、字典或 ToolResult
- """
- import asyncio
- import json
- from typing import List, Dict, Any, Optional
- from agent import tool, ToolResult, ToolContext, get_tool_registry
- # ============================================================
- # 基础功能示例
- # ============================================================
- # 1. 最简形式
- @tool()
- async def hello_world(name: str, uid: str = "") -> Dict[str, str]:
- """
- 最简单的工具示例
- Args:
- name: 要问候的名字
- uid: 用户ID(自动注入)
- Returns:
- 包含问候语的字典
- """
- return {"greeting": f"Hello, {name}!"}
- # 2. 带 i18n 展示信息的工具
- @tool(
- display={
- "zh": {
- "name": "搜索内容",
- "params": {
- "query": "搜索关键词",
- "limit": "返回数量"
- }
- },
- "en": {
- "name": "Search Content",
- "params": {
- "query": "Search query",
- "limit": "Number of results"
- }
- }
- }
- )
- async def search_content(
- query: str,
- limit: int = 10,
- uid: str = ""
- ) -> List[Dict[str, Any]]:
- """
- 搜索用户的内容
- 使用语义搜索查找相关内容。display 参数用于前端展示:
- - 工具名称会根据用户语言显示为"搜索内容"或"Search Content"
- - 参数名称也会相应翻译
- Args:
- query: 搜索查询文本
- limit: 返回结果数量(默认10)
- uid: 用户ID(自动注入)
- Returns:
- 搜索结果列表,每个包含 id, title, content, score
- """
- # 实际实现中会调用向量搜索
- return [
- {
- "id": "doc_001",
- "title": f"关于 {query} 的文档",
- "content": f"这是与 {query} 相关的内容...",
- "score": 0.95
- }
- ]
- # 3. 带可编辑参数的工具
- @tool(
- editable_params=["query", "filters"],
- display={
- "zh": {
- "name": "高级搜索",
- "params": {
- "query": "搜索关键词",
- "filters": "过滤条件",
- "sort_by": "排序方式"
- }
- }
- }
- )
- async def advanced_search(
- query: str,
- filters: Optional[Dict[str, Any]] = None,
- sort_by: str = "relevance",
- limit: int = 20,
- uid: str = ""
- ) -> Dict[str, Any]:
- """
- 高级搜索工具(允许用户编辑参数)
- editable_params 指定哪些参数允许用户在 LLM 生成后编辑:
- - LLM 会先生成 query 和 filters
- - 用户可以在确认前修改这些参数
- - 适用于搜索、创建等需要用户微调的场景
- Args:
- query: 搜索查询
- filters: 过滤条件(如 {"type": "note", "date_range": "7d"})
- sort_by: 排序方式(relevance/date/title)
- limit: 返回数量
- uid: 用户ID(自动注入)
- Returns:
- 搜索结果和元数据
- """
- return {
- "results": [
- {"id": "1", "title": "Result 1", "score": 0.9},
- {"id": "2", "title": "Result 2", "score": 0.8},
- ],
- "total": 42,
- "query": query,
- "filters_applied": filters or {},
- "sort_by": sort_by
- }
- # 4. 需要用户确认的危险操作
- @tool(
- requires_confirmation=True,
- display={
- "zh": {
- "name": "删除内容",
- "params": {
- "content_id": "内容ID",
- "permanent": "永久删除"
- }
- }
- }
- )
- async def delete_content(
- content_id: str,
- permanent: bool = False,
- uid: str = ""
- ) -> Dict[str, Any]:
- """
- 删除内容(需要用户确认)
- requires_confirmation=True 表示这是一个危险操作:
- - LLM 调用此工具时,不会立即执行
- - 会先向用户展示操作详情,等待确认
- - 用户确认后才会真正执行
- 适用场景:删除操作、发送消息、修改重要设置、任何不可逆操作
- Args:
- content_id: 要删除的内容ID
- permanent: 是否永久删除(False=移到回收站)
- uid: 用户ID(自动注入)
- Returns:
- 删除结果
- """
- return {
- "success": True,
- "content_id": content_id,
- "permanent": permanent,
- "message": f"内容 {content_id} 已{'永久删除' if permanent else '移到回收站'}"
- }
- # 5. 带 context 参数的工具
- @tool(
- display={
- "zh": {"name": "获取相关推荐", "params": {"top_k": "推荐数量"}}
- }
- )
- async def get_recommendations(
- top_k: int = 5,
- uid: str = "",
- context: Optional[Dict[str, Any]] = None
- ) -> List[Dict[str, Any]]:
- """
- 获取相关推荐(使用 context 获取额外信息)
- context 参数用于传递执行上下文,由框架自动注入:
- - 当前阅读位置 (current_location)
- - 当前会话 ID (session_id)
- - 排除的内容 ID (exclude_ids)
- Args:
- top_k: 返回推荐数量
- uid: 用户ID(自动注入)
- context: 执行上下文(自动注入)
- Returns:
- 推荐列表
- """
- current_location = None
- if context:
- current_location = context.get("current_location")
- return [
- {
- "id": "rec_001",
- "title": "推荐内容 1",
- "reason": f"基于当前位置 {current_location}" if current_location else "基于您的兴趣"
- }
- ]
- # 6. 同步工具(非 async)
- @tool()
- def format_text(
- text: str,
- format_type: str = "markdown",
- uid: str = ""
- ) -> str:
- """
- 格式化文本(同步工具)
- 不需要 async 的工具可以定义为普通函数。
- 框架会自动检测并正确调用。
- 适用于:纯计算操作、文本处理、不需要 I/O 的操作
- Args:
- text: 要格式化的文本
- format_type: 格式类型(markdown/plain/html)
- uid: 用户ID(自动注入)
- Returns:
- 格式化后的文本
- """
- if format_type == "markdown":
- return f"**{text}**"
- elif format_type == "html":
- return f"<p>{text}</p>"
- else:
- return text
- # 7. 使用 ToolResult 的工具
- @tool()
- async def analyze_content(
- content_id: str,
- analysis_types: Optional[List[str]] = None,
- uid: str = ""
- ) -> ToolResult:
- """
- 分析内容(使用 ToolResult)
- ToolResult 支持双层记忆管理:
- - output: 完整结果(可能很长)
- - long_term_memory: 简短摘要(永久保存)
- Args:
- content_id: 要分析的内容ID
- analysis_types: 分析类型列表(sentiment/keywords/summary)
- uid: 用户ID(自动注入)
- Returns:
- ToolResult 包含分析结果
- """
- types = analysis_types or ["sentiment", "keywords"]
- result = {
- "content_id": content_id,
- "analyses": {}
- }
- if "sentiment" in types:
- result["analyses"]["sentiment"] = {
- "score": 0.8,
- "label": "positive",
- "confidence": 0.92
- }
- if "keywords" in types:
- result["analyses"]["keywords"] = [
- {"word": "AI", "weight": 0.9},
- {"word": "学习", "weight": 0.7}
- ]
- return ToolResult(
- title=f"Analysis of {content_id}",
- output=json.dumps(result, indent=2, ensure_ascii=False),
- long_term_memory=f"Analyzed {content_id}: {', '.join(types)}",
- metadata={"types": types}
- )
- # ============================================================
- # 高级功能示例
- # ============================================================
- # 8. 域名过滤示例
- @tool(url_patterns=["*.google.com", "www.google.*"])
- async def google_search(query: str, uid: str = "") -> ToolResult:
- """
- Google 搜索(仅在 Google 页面可用)
- 使用 url_patterns 限制工具只在特定域名显示。
- 在 Google 页面时,此工具会出现在可用工具列表中。
- 在其他页面时,此工具会被过滤掉。
- Args:
- query: 搜索查询
- uid: 用户ID(自动注入)
- Returns:
- 搜索结果
- """
- return ToolResult(
- title="Google Search",
- output=f"Searching Google for: {query}",
- long_term_memory=f"Searched Google for '{query}'"
- )
- @tool(url_patterns=["*.github.com"])
- async def create_github_issue(
- title: str,
- body: str,
- uid: str = ""
- ) -> ToolResult:
- """
- 创建 GitHub Issue(仅在 GitHub 页面可用)
- Args:
- title: Issue 标题
- body: Issue 内容
- uid: 用户ID(自动注入)
- Returns:
- 创建结果
- """
- return ToolResult(
- title="Issue Created",
- output=f"Created issue: {title}",
- long_term_memory=f"Created GitHub issue: {title}"
- )
- @tool() # 无 url_patterns,所有页面都可用
- async def take_screenshot(uid: str = "") -> ToolResult:
- """截图(所有页面都可用)"""
- return ToolResult(
- title="Screenshot",
- output="Screenshot taken",
- attachments=["screenshot_001.png"]
- )
- # 9. 敏感数据处理示例
- @tool(url_patterns=["*.github.com"])
- async def github_login(
- username: str,
- password: str,
- totp_code: str,
- uid: str = ""
- ) -> ToolResult:
- """
- GitHub 登录(支持敏感数据占位符)
- LLM 会输出类似:
- {
- "username": "user@example.com",
- "password": "<secret>github_password</secret>",
- "totp_code": "<secret>github_2fa_bu_2fa_code</secret>"
- }
- 执行时会自动替换为实际值。
- Args:
- username: 用户名
- password: 密码(可以是占位符)
- totp_code: TOTP 验证码(可以是占位符,自动生成)
- uid: 用户ID(自动注入)
- Returns:
- 登录结果
- """
- # 注意:password 和 totp_code 在到达这里时已经被替换
- return ToolResult(
- title="Login Successful",
- output=f"Logged in as {username}",
- long_term_memory=f"Logged in to GitHub as {username}"
- )
- # 10. 组合所有功能
- @tool(
- url_patterns=["*.example.com"],
- requires_confirmation=True,
- editable_params=["message"],
- display={
- "zh": {
- "name": "发送认证消息",
- "params": {
- "recipient": "接收者",
- "message": "消息内容",
- "api_key": "API密钥"
- }
- }
- }
- )
- async def send_authenticated_message(
- recipient: str,
- message: str,
- api_key: str,
- ctx: ToolContext,
- uid: str = ""
- ) -> ToolResult:
- """
- 发送消息(组合多个功能)
- 展示所有高级功能:
- - 仅在 example.com 可用(域名过滤)
- - 需要用户确认(危险操作)
- - 消息可编辑(用户微调)
- - API key 使用敏感数据占位符
- - 使用 ToolContext 获取上下文
- Args:
- recipient: 接收者
- message: 消息内容
- api_key: API密钥(可以是占位符)
- ctx: 工具上下文
- uid: 用户ID(自动注入)
- Returns:
- 发送结果
- """
- # api_key 会从 <secret>api_key</secret> 替换为实际值
- # ctx 包含 page_url, browser_session 等信息
- return ToolResult(
- title="Message Sent",
- output=f"Sent to {recipient}: {message}",
- long_term_memory=f"Sent message to {recipient} on {ctx.page_url}",
- metadata={"recipient": recipient}
- )
- # ============================================================
- # 使用示例
- # ============================================================
- async def main():
- registry = get_tool_registry()
- print("=" * 60)
- print("工具系统完整示例")
- print("=" * 60)
- # ============================================================
- # 示例 1:基础工具调用
- # ============================================================
- print("\n1. 基础工具调用")
- print("-" * 60)
- result = await registry.execute("hello_world", {"name": "Alice"})
- print(f"hello_world: {result}")
- result = await registry.execute("search_content", {"query": "Python", "limit": 5})
- print(f"search_content: {result}")
- # ============================================================
- # 示例 2:域名过滤
- # ============================================================
- print("\n\n2. 域名过滤示例")
- print("-" * 60)
- # 在 Google 页面
- google_url = "https://www.google.com/search?q=test"
- google_tools = registry.get_tool_names(google_url)
- print(f"在 {google_url} 可用的工具:")
- print(f" 包含 google_search: {'google_search' in google_tools}")
- # 在 GitHub 页面
- github_url = "https://github.com/user/repo"
- github_tools = registry.get_tool_names(github_url)
- print(f"\n在 {github_url} 可用的工具:")
- print(f" 包含 create_github_issue: {'create_github_issue' in github_tools}")
- print(f" 包含 google_search: {'google_search' in github_tools}")
- # ============================================================
- # 示例 3:敏感数据处理
- # ============================================================
- print("\n\n3. 敏感数据处理示例")
- print("-" * 60)
- # 配置敏感数据
- sensitive_data = {
- "*.github.com": {
- "github_password": "my_secret_password",
- "github_2fa_bu_2fa_code": "JBSWY3DPEHPK3PXP" # TOTP secret
- }
- }
- # 模拟 LLM 输出(包含占位符)
- llm_output_args = {
- "username": "user@example.com",
- "password": "<secret>github_password</secret>",
- "totp_code": "<secret>github_2fa_bu_2fa_code</secret>"
- }
- print("LLM 输出的参数(包含占位符):")
- print(f" {llm_output_args}")
- # 执行工具(自动替换敏感数据)
- result = await registry.execute(
- "github_login",
- llm_output_args,
- context={"page_url": "https://github.com/login"},
- sensitive_data=sensitive_data
- )
- print(f"\n执行结果(密码已替换):")
- print(f" {result}")
- # ============================================================
- # 示例 4:工具统计
- # ============================================================
- print("\n\n4. 工具统计示例")
- print("-" * 60)
- # 模拟多次调用
- for i in range(5):
- await registry.execute("google_search", {"query": f"test {i}"})
- await registry.execute("take_screenshot", {})
- await registry.execute("take_screenshot", {})
- # 查看统计
- stats = registry.get_stats()
- print("工具使用统计:")
- for tool_name, tool_stats in stats.items():
- if tool_stats["call_count"] > 0:
- print(f"\n {tool_name}:")
- print(f" 调用次数: {tool_stats['call_count']}")
- print(f" 成功率: {tool_stats['success_rate']:.1%}")
- print(f" 平均执行时间: {tool_stats['average_duration']:.3f}s")
- # 获取 Top 工具
- print("\n\nTop 3 最常用工具:")
- top_tools = registry.get_top_tools(limit=3, by="call_count")
- for i, tool_name in enumerate(top_tools, 1):
- tool_stats = stats[tool_name]
- print(f" {i}. {tool_name} ({tool_stats['call_count']} 次调用)")
- if __name__ == "__main__":
- asyncio.run(main())
|