tinet-agent-cli 0.1.0.dev36__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. taco/__init__.py +1 -0
  2. taco/agent_openapi/__init__.py +1 -0
  3. taco/agent_openapi/catalog.py +198 -0
  4. taco/agent_openapi/models.py +47 -0
  5. taco/agent_openapi/parameters.py +111 -0
  6. taco/agent_openapi/service.py +73 -0
  7. taco/aikb/__init__.py +4 -0
  8. taco/aikb/catalog.py +380 -0
  9. taco/aikb/executors.py +157 -0
  10. taco/aikb/file_input.py +55 -0
  11. taco/aikb/models.py +42 -0
  12. taco/aikb/parameters.py +135 -0
  13. taco/aikb/service.py +68 -0
  14. taco/assets/__init__.py +1 -0
  15. taco/assets/agent_openapi/catalog.json +14 -0
  16. taco/assets/agent_openapi/operations/conversation/list-conversations.json +67 -0
  17. taco/assets/agent_openapi/operations/message/list-conversation-messages.json +80 -0
  18. taco/assets/agent_openapi/operations/trace/list-message-traces.json +72 -0
  19. taco/assets/aikb/catalog.json +41 -0
  20. taco/assets/aikb/operations/conversation/chat-conversation-on-open.json +99 -0
  21. taco/assets/aikb/operations/directory/delete-directory.json +81 -0
  22. taco/assets/aikb/operations/directory/edit-directory.json +97 -0
  23. taco/assets/aikb/operations/directory/list-directory-tree.json +106 -0
  24. taco/assets/aikb/operations/directory/save-directory.json +109 -0
  25. taco/assets/aikb/operations/faq/create-faq.json +193 -0
  26. taco/assets/aikb/operations/faq/delete-faq.json +81 -0
  27. taco/assets/aikb/operations/faq/describe-faq.json +82 -0
  28. taco/assets/aikb/operations/faq/edit-faq.json +193 -0
  29. taco/assets/aikb/operations/faq/list-faqs.json +136 -0
  30. taco/assets/aikb/operations/file/create-file.json +145 -0
  31. taco/assets/aikb/operations/file/delete-files.json +110 -0
  32. taco/assets/aikb/operations/file/describe-file.json +82 -0
  33. taco/assets/aikb/operations/file/get-file-upload-url.json +154 -0
  34. taco/assets/aikb/operations/file/list-files.json +146 -0
  35. taco/assets/aikb/operations/media/describe-faq-media-url.json +98 -0
  36. taco/assets/aikb/operations/media/describe-file-media-url.json +90 -0
  37. taco/assets/aikb/operations/oss/signature-for-upload.json +85 -0
  38. taco/assets/aikb/operations/recycle-bin/list-recycled-items.json +151 -0
  39. taco/assets/aikb/operations/repository/describe-bot-repository.json +71 -0
  40. taco/assets/aikb/operations/repository/list-private-repositories.json +75 -0
  41. taco/assets/aikb/operations/repository/list-public-repositories.json +75 -0
  42. taco/assets/aikb/operations/search/search-knowledge-on-open.json +180 -0
  43. taco/assets/examples/README.md +3 -0
  44. taco/assets/examples/agent-basic.json +16 -0
  45. taco/assets/examples/agent-builtin-tool.json +28 -0
  46. taco/assets/examples/agent-workflow-tool.json +30 -0
  47. taco/assets/examples/chatflow-basic.json +24 -0
  48. taco/assets/examples/workflow-http.json +34 -0
  49. taco/assets/manifest.json +12 -0
  50. taco/assets/scenarios/README.md +3 -0
  51. taco/assets/scenarios/agent-basic.json +80 -0
  52. taco/assets/scenarios/agent-builtin-tool.json +116 -0
  53. taco/assets/scenarios/agent-workflow-tool.json +277 -0
  54. taco/assets/schemas/README.md +3 -0
  55. taco/assets/schemas/agent-tool.json +71 -0
  56. taco/assets/schemas/agent.json +199 -0
  57. taco/assets/schemas/chatflow.json +1048 -0
  58. taco/assets/schemas/workflow.http.json +1052 -0
  59. taco/assets/schemas/workflow.json +1052 -0
  60. taco/assets/skills/README.md +3 -0
  61. taco/assets/skills/taco/SKILL.md +68 -0
  62. taco/assets/skills/taco/manifest.json +10 -0
  63. taco/cli.py +127 -0
  64. taco/commands/__init__.py +1 -0
  65. taco/commands/agent.py +760 -0
  66. taco/commands/aikb.py +132 -0
  67. taco/commands/app.py +151 -0
  68. taco/commands/category.py +37 -0
  69. taco/commands/chatflow.py +183 -0
  70. taco/commands/credential.py +222 -0
  71. taco/commands/discovery.py +409 -0
  72. taco/commands/model.py +55 -0
  73. taco/commands/profile.py +124 -0
  74. taco/commands/tool.py +61 -0
  75. taco/commands/workflow.py +714 -0
  76. taco/core/__init__.py +1 -0
  77. taco/core/access_token_manager.py +98 -0
  78. taco/core/api_client.py +263 -0
  79. taco/core/assets.py +81 -0
  80. taco/core/config_store.py +209 -0
  81. taco/core/context.py +19 -0
  82. taco/core/developer_center_client.py +165 -0
  83. taco/core/errors.py +19 -0
  84. taco/core/file_lock.py +80 -0
  85. taco/core/http_headers.py +21 -0
  86. taco/core/openapi_signer.py +221 -0
  87. taco/core/output.py +72 -0
  88. taco/core/profile_manager.py +217 -0
  89. taco/core/profile_resolver.py +151 -0
  90. taco/core/secure_file.py +76 -0
  91. taco/release.py +363 -0
  92. taco/services/__init__.py +1 -0
  93. taco/services/agent_service.py +314 -0
  94. taco/services/app_service.py +153 -0
  95. taco/services/category_service.py +57 -0
  96. taco/services/model_service.py +117 -0
  97. taco/services/tool_service.py +482 -0
  98. taco/services/workflow_compiler.py +1665 -0
  99. taco/services/workflow_service.py +652 -0
  100. taco/services/workflow_tool_service.py +439 -0
  101. tinet_agent_cli-0.1.0.dev36.dist-info/METADATA +158 -0
  102. tinet_agent_cli-0.1.0.dev36.dist-info/RECORD +104 -0
  103. tinet_agent_cli-0.1.0.dev36.dist-info/WHEEL +4 -0
  104. tinet_agent_cli-0.1.0.dev36.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,439 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import re
5
+ from collections.abc import Mapping
6
+ from copy import deepcopy
7
+ from typing import Any
8
+ from uuid import UUID
9
+
10
+ from taco.core.api_client import ApiClient
11
+ from taco.core.errors import TacoError
12
+
13
+
14
+ _WORKFLOW_TOOL_PATH = "/workspaces/current/tool-provider/workflow"
15
+ _TOOL_NAME = re.compile(r"^[A-Za-z0-9_]{1,64}$")
16
+ _DIFY_SELECTOR = re.compile(r"^[A-Za-z_][A-Za-z0-9_]{0,29}$")
17
+ _SUPPORTED_WORKFLOW_INPUT_TYPES = frozenset({"string", "number", "select"})
18
+ _UNSUPPORTED_FILE_TYPES = frozenset({"file", "files", "system-files"})
19
+ _DEFAULT_ICON = {"background": "#E4FBCC", "content": "🔀"}
20
+
21
+
22
+ class WorkflowToolService:
23
+ def __init__(self, api_client: ApiClient | None = None) -> None:
24
+ self.api_client = api_client or ApiClient()
25
+
26
+ def get_tool(
27
+ self,
28
+ workflow_app_id: str | None = None,
29
+ workflow_tool_id: str | None = None,
30
+ profile: str | None = None,
31
+ ) -> dict[str, Any]:
32
+ app_id = _non_empty_string(workflow_app_id)
33
+ tool_id = _non_empty_string(workflow_tool_id)
34
+ if (app_id is None) == (tool_id is None):
35
+ raise TacoError(
36
+ code="WORKFLOW_TOOL_QUERY_INVALID",
37
+ message="查询 Workflow Tool 必须且只能提供 app ID 或 tool ID。",
38
+ exit_code=2,
39
+ )
40
+
41
+ params = (
42
+ {"workflow_app_id": app_id}
43
+ if app_id is not None
44
+ else {"workflow_tool_id": tool_id}
45
+ )
46
+ payload = self.api_client.get(
47
+ f"{_WORKFLOW_TOOL_PATH}/get",
48
+ params=params,
49
+ profile=profile,
50
+ )
51
+ _validate_detail_identity(payload)
52
+ if (
53
+ tool_id is not None
54
+ and not _identifiers_match(tool_id, payload["workflow_tool_id"])
55
+ ) or (
56
+ app_id is not None
57
+ and not _identifiers_match(app_id, payload["workflow_app_id"])
58
+ ):
59
+ raise _invalid_response()
60
+ return payload
61
+
62
+ def publish_tool(
63
+ self,
64
+ app_id: str,
65
+ workflow: Mapping[str, Any],
66
+ name: str,
67
+ label: str,
68
+ description: str,
69
+ profile: str | None = None,
70
+ ) -> Any:
71
+ _validate_machine_name(name)
72
+ _validate_label(label)
73
+ _validate_description(description)
74
+ parameters = _derive_parameters(workflow)
75
+ response = self.api_client.post(
76
+ f"{_WORKFLOW_TOOL_PATH}/create",
77
+ json={
78
+ "workflow_app_id": app_id,
79
+ "name": name,
80
+ "label": label,
81
+ "description": description,
82
+ "icon": dict(_DEFAULT_ICON),
83
+ "parameters": parameters,
84
+ "privacy_policy": "",
85
+ "labels": [],
86
+ },
87
+ profile=profile,
88
+ )
89
+ return _validated_write_response(response)
90
+
91
+ def update_tool(
92
+ self,
93
+ tool_id: str,
94
+ workflow: Mapping[str, Any],
95
+ name: str | None = None,
96
+ label: str | None = None,
97
+ description: str | None = None,
98
+ profile: str | None = None,
99
+ ) -> Any:
100
+ detail = self.get_tool(workflow_tool_id=tool_id, profile=profile)
101
+ if not _identifiers_match(tool_id, detail["workflow_tool_id"]):
102
+ raise _invalid_response()
103
+ canonical_tool_id = _canonical_identifier(detail["workflow_tool_id"])
104
+ merged_name = detail["name"] if name is None else name
105
+ merged_label = _merged_string(detail, "label", label)
106
+ merged_description = _merged_string(
107
+ detail,
108
+ "description",
109
+ description,
110
+ allow_empty=True,
111
+ )
112
+ _validate_machine_name(merged_name)
113
+ _validate_label(merged_label)
114
+ _validate_description(merged_description)
115
+
116
+ icon = detail.get("icon")
117
+ privacy_policy = detail.get("privacy_policy", "")
118
+ labels = _detail_labels(detail)
119
+ if not isinstance(icon, dict) or not isinstance(privacy_policy, str):
120
+ raise _invalid_response()
121
+
122
+ response = self.api_client.post(
123
+ f"{_WORKFLOW_TOOL_PATH}/update",
124
+ json={
125
+ "workflow_tool_id": canonical_tool_id,
126
+ "name": merged_name,
127
+ "label": merged_label,
128
+ "description": merged_description,
129
+ "icon": deepcopy(icon),
130
+ "parameters": _derive_parameters(workflow),
131
+ "privacy_policy": privacy_policy,
132
+ "labels": labels,
133
+ },
134
+ profile=profile,
135
+ )
136
+ return _validated_write_response(response)
137
+
138
+ def resolve_agent_tool(
139
+ self,
140
+ workflow_tool_id: str,
141
+ tool_name: str,
142
+ parameters: dict[str, Any],
143
+ profile: str | None = None,
144
+ ) -> dict[str, Any]:
145
+ detail = self.get_tool(
146
+ workflow_tool_id=workflow_tool_id,
147
+ profile=profile,
148
+ )
149
+ if (
150
+ not _identifiers_match(
151
+ workflow_tool_id,
152
+ detail["workflow_tool_id"],
153
+ )
154
+ or detail["name"] != tool_name
155
+ ):
156
+ raise _tool_mismatch()
157
+
158
+ tool = detail.get("tool")
159
+ if tool is not None and not isinstance(tool, dict):
160
+ raise _invalid_response()
161
+ if isinstance(tool, dict):
162
+ discovered_name = tool.get("name")
163
+ if discovered_name is not None and discovered_name != tool_name:
164
+ raise _tool_mismatch()
165
+
166
+ definitions = (
167
+ tool.get("parameters")
168
+ if isinstance(tool, dict) and "parameters" in tool
169
+ else detail.get("parameters")
170
+ )
171
+ resolved_parameters = _resolve_parameters(definitions, parameters)
172
+ tool_label = _localized_label(
173
+ tool.get("label") if isinstance(tool, dict) else None
174
+ )
175
+ if tool_label is None:
176
+ tool_label = _non_empty_string(detail.get("label")) or tool_name
177
+
178
+ return {
179
+ "provider_type": "workflow",
180
+ "provider_id": _canonical_identifier(detail["workflow_tool_id"]),
181
+ "provider_name": detail["name"],
182
+ "tool_name": tool_name,
183
+ "tool_label": tool_label,
184
+ "tool_parameters": resolved_parameters,
185
+ "enabled": True,
186
+ }
187
+
188
+
189
+ def _derive_parameters(workflow: Mapping[str, Any]) -> list[dict[str, str]]:
190
+ if not isinstance(workflow, Mapping):
191
+ raise _parameters_invalid()
192
+ inputs = workflow.get("inputs")
193
+ if not isinstance(inputs, Mapping):
194
+ raise _parameters_invalid()
195
+
196
+ parameters: list[dict[str, str]] = []
197
+ seen: set[str] = set()
198
+ for name, configuration in inputs.items():
199
+ if (
200
+ not isinstance(name, str)
201
+ or _DIFY_SELECTOR.fullmatch(name) is None
202
+ or name in seen
203
+ or not isinstance(configuration, Mapping)
204
+ or configuration.get("type") not in _SUPPORTED_WORKFLOW_INPUT_TYPES
205
+ ):
206
+ raise _parameters_invalid()
207
+ description = configuration.get("description")
208
+ if description is not None and not isinstance(description, str):
209
+ raise _parameters_invalid()
210
+ seen.add(name)
211
+ parameters.append(
212
+ {
213
+ "name": name,
214
+ "description": description or name,
215
+ "form": "llm",
216
+ "required": bool(configuration.get("required", False)),
217
+ }
218
+ )
219
+ return parameters
220
+
221
+
222
+ def _resolve_parameters(
223
+ definitions: Any,
224
+ supplied: dict[str, Any],
225
+ ) -> dict[str, Any]:
226
+ if (
227
+ not isinstance(definitions, list)
228
+ or any(not isinstance(item, dict) for item in definitions)
229
+ or not isinstance(supplied, dict)
230
+ ):
231
+ raise _invalid_response()
232
+
233
+ normalized_definitions: list[tuple[str, dict[str, Any]]] = []
234
+ seen: set[str] = set()
235
+ form_names: set[str] = set()
236
+ for definition in definitions:
237
+ name = definition.get("name")
238
+ form = definition.get("form")
239
+ parameter_type = definition.get("type", "string")
240
+ if (
241
+ not isinstance(name, str)
242
+ or _DIFY_SELECTOR.fullmatch(name) is None
243
+ or name in seen
244
+ or form not in {"llm", "form"}
245
+ ):
246
+ raise _invalid_response()
247
+ if parameter_type in _UNSUPPORTED_FILE_TYPES:
248
+ raise TacoError(
249
+ code="WORKFLOW_TOOL_FILE_PARAMETER_UNSUPPORTED",
250
+ message=f"Workflow Tool 参数 {name} 是不支持的文件类型。",
251
+ exit_code=6,
252
+ )
253
+ if parameter_type == "secret-input":
254
+ raise TacoError(
255
+ code="WORKFLOW_TOOL_SECRET_PARAMETER_UNSUPPORTED",
256
+ message=f"Workflow Tool 参数 {name} 是不能写入 Agent DSL 的密钥类型。",
257
+ exit_code=6,
258
+ )
259
+ seen.add(name)
260
+ if form == "form":
261
+ form_names.add(name)
262
+ normalized_definitions.append((name, definition))
263
+
264
+ unknown = sorted(set(supplied) - form_names)
265
+ if unknown:
266
+ raise _parameters_invalid("用户参数包含未发现的 form 字段。")
267
+
268
+ resolved: dict[str, Any] = {}
269
+ for name, definition in normalized_definitions:
270
+ if definition["form"] == "llm":
271
+ resolved[name] = ""
272
+ continue
273
+ if name in supplied:
274
+ value = supplied[name]
275
+ elif definition.get("default") is not None:
276
+ value = definition["default"]
277
+ elif definition.get("required"):
278
+ raise _parameters_invalid("缺少必填 form 参数。")
279
+ else:
280
+ continue
281
+ resolved[name] = _normalize_parameter(definition, value)
282
+ return resolved
283
+
284
+
285
+ def _normalize_parameter(definition: dict[str, Any], value: Any) -> Any:
286
+ parameter_type = definition.get("type", "string")
287
+ valid = False
288
+ if parameter_type == "string":
289
+ valid = isinstance(value, str)
290
+ elif parameter_type == "number":
291
+ valid = (
292
+ isinstance(value, int | float)
293
+ and not isinstance(value, bool)
294
+ and (not isinstance(value, float) or math.isfinite(value))
295
+ )
296
+ elif parameter_type == "boolean":
297
+ valid = isinstance(value, bool)
298
+ elif parameter_type == "select":
299
+ options = definition.get("options") or []
300
+ if not isinstance(options, list):
301
+ raise _invalid_response()
302
+ choices = [
303
+ option.get("value") if isinstance(option, dict) else option
304
+ for option in options
305
+ ]
306
+ if any(not isinstance(choice, str) for choice in choices):
307
+ raise _invalid_response()
308
+ valid = isinstance(value, str) and (not choices or value in choices)
309
+ if not valid:
310
+ raise _parameters_invalid("form 参数类型或选项不合法。")
311
+ return value
312
+
313
+
314
+ def _validate_detail_identity(payload: Any) -> None:
315
+ if not isinstance(payload, dict) or any(
316
+ _non_empty_string(payload.get(field)) is None
317
+ for field in ("workflow_tool_id", "workflow_app_id", "name")
318
+ ):
319
+ raise _invalid_response()
320
+
321
+
322
+ def _identifiers_match(expected: str, actual: str) -> bool:
323
+ try:
324
+ expected_uuid = UUID(expected)
325
+ actual_uuid = UUID(actual)
326
+ except ValueError:
327
+ return expected == actual
328
+ return expected_uuid == actual_uuid
329
+
330
+
331
+ def _canonical_identifier(identifier: str) -> str:
332
+ try:
333
+ return str(UUID(identifier))
334
+ except ValueError:
335
+ return identifier
336
+
337
+
338
+ def _validated_write_response(payload: Any) -> dict[str, Any]:
339
+ if not isinstance(payload, dict) or payload.get("result") != "success":
340
+ raise _invalid_response()
341
+ return payload
342
+
343
+
344
+ def _detail_labels(detail: dict[str, Any]) -> list[str]:
345
+ labels = detail.get("labels")
346
+ if labels is None:
347
+ tool = detail.get("tool")
348
+ labels = tool.get("labels") if isinstance(tool, dict) else []
349
+ if labels is None:
350
+ labels = []
351
+ if not isinstance(labels, list) or any(
352
+ not isinstance(item, str) for item in labels
353
+ ):
354
+ raise _invalid_response()
355
+ return list(labels)
356
+
357
+
358
+ def _merged_string(
359
+ detail: dict[str, Any],
360
+ field: str,
361
+ override: str | None,
362
+ *,
363
+ allow_empty: bool = False,
364
+ ) -> str:
365
+ value = detail.get(field) if override is None else override
366
+ if not isinstance(value, str) or (not allow_empty and not value.strip()):
367
+ raise _invalid_response()
368
+ return value
369
+
370
+
371
+ def _validate_machine_name(name: Any) -> None:
372
+ if not isinstance(name, str) or _TOOL_NAME.fullmatch(name) is None:
373
+ raise TacoError(
374
+ code="WORKFLOW_TOOL_NAME_INVALID",
375
+ message="Workflow Tool 机器名必须是 1-64 位 ASCII 字母、数字或下划线。",
376
+ exit_code=2,
377
+ )
378
+
379
+
380
+ def _validate_label(label: Any) -> None:
381
+ if not isinstance(label, str) or not label.strip():
382
+ raise TacoError(
383
+ code="WORKFLOW_TOOL_LABEL_INVALID",
384
+ message="Workflow Tool label 不能为空。",
385
+ exit_code=2,
386
+ )
387
+
388
+
389
+ def _validate_description(description: Any) -> None:
390
+ if not isinstance(description, str):
391
+ raise TacoError(
392
+ code="WORKFLOW_TOOL_DESCRIPTION_INVALID",
393
+ message="Workflow Tool description 必须是字符串。",
394
+ exit_code=2,
395
+ )
396
+
397
+
398
+ def _localized_label(label: Any) -> str | None:
399
+ if isinstance(label, str):
400
+ return label or None
401
+ if isinstance(label, dict):
402
+ for locale in ("zh_Hans", "en_US"):
403
+ value = _non_empty_string(label.get(locale))
404
+ if value is not None:
405
+ return value
406
+ return None
407
+
408
+
409
+ def _non_empty_string(value: Any) -> str | None:
410
+ if not isinstance(value, str) or not value.strip():
411
+ return None
412
+ return value
413
+
414
+
415
+ def _parameters_invalid(reason: str | None = None) -> TacoError:
416
+ message = "Workflow Tool 参数配置不合法。"
417
+ if reason:
418
+ message = f"{message}{reason}"
419
+ return TacoError(
420
+ code="WORKFLOW_TOOL_PARAMETERS_INVALID",
421
+ message=message,
422
+ exit_code=6,
423
+ )
424
+
425
+
426
+ def _tool_mismatch() -> TacoError:
427
+ return TacoError(
428
+ code="WORKFLOW_TOOL_MISMATCH",
429
+ message="Workflow Tool 详情与请求的 tool ID 或名称不一致。",
430
+ exit_code=6,
431
+ )
432
+
433
+
434
+ def _invalid_response() -> TacoError:
435
+ return TacoError(
436
+ code="WORKFLOW_TOOL_RESPONSE_INVALID",
437
+ message="Workflow Tool 接口返回结构不合法。",
438
+ exit_code=4,
439
+ )
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: tinet-agent-cli
3
+ Version: 0.1.0.dev36
4
+ Summary: Tinet Agent CLI Orchestrator
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: filelock<4.0.0,>=3.32.5
7
+ Requires-Dist: httpx>=0.27.0
8
+ Requires-Dist: jsonschema<5.0.0,>=4.23.0
9
+ Requires-Dist: pyyaml<7.0.0,>=6.0.2
10
+ Requires-Dist: typer<0.27.0,>=0.26.0
11
+ Provides-Extra: dev
12
+ Requires-Dist: build>=1.2.0; extra == 'dev'
13
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
14
+ Requires-Dist: twine>=5.0.0; extra == 'dev'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # TACO:智能体平台 CLI
18
+
19
+ TACO(Tinet Agent CLI Orchestrator)是天润智能体平台的命令行工具,可供开发者、自动化
20
+ 流水线,以及 Cursor、Codex、Claude Code 等 AI Agent 使用。
21
+
22
+ 它将智能体、应用和智能知识库能力组织成稳定、可发现的命令,并同时提供人工可读输出与 JSON
23
+ 输出。
24
+
25
+ ## 主要能力
26
+
27
+ - 使用 profile 管理和切换企业授权
28
+ - 查询、创建和管理智能体应用
29
+ - 从应用模板创建应用
30
+ - 拉取、校验、调试和发布智能体 DSL
31
+ - 管理 Workflow、Chatflow 和智能体工具
32
+ - 发现并调用 Agent OpenAPI
33
+ - 发现并调用智能知识库 OpenAPI,包括文档上传
34
+ - 向 AI Agent 提供命令、参数、场景和 Schema 的结构化说明
35
+
36
+ ## 安装
37
+
38
+ 要求 Python 3.12 或更高版本。推荐使用 `pipx` 安装:
39
+
40
+ ```bash
41
+ pipx install tinet-agent-cli
42
+ taco --version
43
+ ```
44
+
45
+ 也可以使用 `pip`:
46
+
47
+ ```bash
48
+ python -m pip install tinet-agent-cli
49
+ ```
50
+
51
+ ## 配置授权
52
+
53
+ 使用前需要准备开发者中心 AccessKeyId 和 AccessKeySecret,并为接口密钥分配“智能体 > CLI
54
+ 授权”权限。
55
+
56
+ CLINK2 endpoint 示例:
57
+
58
+ - 北京平台:`https://api-bj.clink.cn`
59
+ - 上海平台:`https://api-sh.clink.cn`
60
+
61
+ 创建 profile:
62
+
63
+ ```bash
64
+ TACO_ACCESS_KEY_SECRET='<access-key-secret>' taco profile set \
65
+ --name clink-bj \
66
+ --endpoint https://api-bj.clink.cn \
67
+ --access-key-id '<access-key-id>'
68
+ ```
69
+
70
+ 列出 Zenava 和 TACO profile。带 `current=true` 的配置会优先展示,Zenava 当前配置排在 TACO
71
+ 当前配置之前:
72
+
73
+ ```bash
74
+ taco --json profile list
75
+ ```
76
+
77
+ 多企业场景可以列出、切换 profile,或为单次命令指定 profile:
78
+
79
+ ```bash
80
+ taco profile list
81
+ taco profile use clink-bj
82
+ taco --profile clink-sh --json agent app list
83
+ ```
84
+
85
+ ## 智能体
86
+
87
+ ### 应用与模板
88
+
89
+ ```bash
90
+ taco agent app list --limit 20
91
+ taco --json agent app get <app-id>
92
+ taco agent app tag list --keyword 客服
93
+ taco agent app template list
94
+ taco agent app template create <template-app-id> --name 新应用 --yes
95
+ ```
96
+
97
+ ### 智能体 DSL
98
+
99
+ ```bash
100
+ taco agent create \
101
+ --name 示例智能体 \
102
+ --category <category-code> \
103
+ --output agent.json \
104
+ --yes
105
+
106
+ taco agent pull <app-id> --output agent.json
107
+ taco agent validate --dsl agent.json
108
+ taco agent publish <app-id> --dsl agent.json --yes
109
+ ```
110
+
111
+ 模型、分类、Workflow、Chatflow、工具和内置示例可以从 `taco agent --help` 继续发现。
112
+
113
+ ### Agent OpenAPI
114
+
115
+ ```bash
116
+ taco agent operation search <关键词>
117
+ taco agent operation describe <operation-id>
118
+ taco --json agent call <operation-id> --param key=value
119
+ ```
120
+
121
+ ## 智能知识库
122
+
123
+ ```bash
124
+ taco aikb operation search <关键词>
125
+ taco aikb operation describe <operation-id>
126
+ taco --json aikb call <operation-id> --param key=value
127
+ ```
128
+
129
+ 复杂参数可以通过 `--params-file` 或 `--body-file` 提供。文档上传 operation 支持使用 `--file`
130
+ 指定本地文件,由 TACO 完成上传流程。
131
+
132
+ ## 提供给 AI Agent 使用
133
+
134
+ AI Agent 或 CI 推荐使用 `--json --no-input`,避免依赖终端文本或等待交互输入:
135
+
136
+ ```bash
137
+ taco --json --no-input profile list
138
+ taco --json --no-input agent app list --limit 10
139
+ taco --json --no-input aikb operation search 文档
140
+ ```
141
+
142
+ TACO 可以输出完整命令契约及内置场景:
143
+
144
+ ```bash
145
+ taco help -o json
146
+ taco agent scenario list -o json
147
+ taco agent schema list -o json
148
+ ```
149
+
150
+ 推荐先搜索候选 operation,再查看参数契约,最后执行调用。远端创建、修改、删除、上传和发布
151
+ 操作应先取得用户批准,再显式传入 `--yes`。
152
+
153
+ ## 安全提示
154
+
155
+ - 不要把 AccessKeySecret 或 token 写入代码、DSL、日志和对话内容。
156
+ - 优先通过隐藏输入、环境变量或 CI 密钥管理提供 AccessKeySecret。
157
+ - 写操作前确认当前 profile、目标资源和变更范围。
158
+ - 自动化程序应判断退出码、`ok` 和 `error.code`。