axi-cli 0.0.3__tar.gz
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.
- axi_cli-0.0.3/.gitignore +14 -0
- axi_cli-0.0.3/CHANGELOG.md +34 -0
- axi_cli-0.0.3/PKG-INFO +128 -0
- axi_cli-0.0.3/README.md +114 -0
- axi_cli-0.0.3/pyproject.toml +43 -0
- axi_cli-0.0.3/src/axi/__init__.py +81 -0
- axi_cli-0.0.3/src/axi/cli.py +378 -0
- axi_cli-0.0.3/src/axi/config.py +140 -0
- axi_cli-0.0.3/src/axi/daemon/client.py +94 -0
- axi_cli-0.0.3/src/axi/daemon/protocol.py +42 -0
- axi_cli-0.0.3/src/axi/daemon/server.py +266 -0
- axi_cli-0.0.3/src/axi/executor.py +44 -0
- axi_cli-0.0.3/src/axi/models.py +74 -0
- axi_cli-0.0.3/src/axi/providers/__init__.py +0 -0
- axi_cli-0.0.3/src/axi/providers/mcp.py +284 -0
- axi_cli-0.0.3/src/axi/providers/native.py +66 -0
- axi_cli-0.0.3/src/axi/registry.py +111 -0
- axi_cli-0.0.3/src/axi/search/__init__.py +7 -0
- axi_cli-0.0.3/src/axi/search/bm25.py +50 -0
- axi_cli-0.0.3/src/axi/search/cache.py +53 -0
- axi_cli-0.0.3/src/axi/search/embedding.py +119 -0
- axi_cli-0.0.3/src/axi/search/hybrid.py +112 -0
- axi_cli-0.0.3/src/axi/search/regex.py +36 -0
- axi_cli-0.0.3/src/axi/search/tokenize.py +34 -0
axi_cli-0.0.3/.gitignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.0.3] - 2026-04-01
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- BM25 关键词搜索(bm25s + jieba 分词,支持中英文)
|
|
7
|
+
- Embedding 语义搜索(Jina/OpenAI,通过 LangChain 接入,可选)
|
|
8
|
+
- 混合搜索(BM25 + Embedding,RRF 融合排序,分数归一化 0-1)
|
|
9
|
+
- `axi grep` 命令:独立的正则表达式搜索
|
|
10
|
+
- `axi daemon status` 输出 JSON 格式状态信息(PID、运行时长、工具统计)
|
|
11
|
+
- Daemon idle 超时自动关闭机制
|
|
12
|
+
- `config.py` 统一配置模块(`axi.json` 解析 + Pydantic 模型)
|
|
13
|
+
- `docs/configuration.md` 配置参考文档
|
|
14
|
+
- Embedding 文件缓存(`.axi/` 目录)
|
|
15
|
+
- MCP 工具调用失败时自动重连一次
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- `axi search` 从子串匹配改为 BM25 混合搜索
|
|
19
|
+
- 搜索结果新增 `score` 字段
|
|
20
|
+
- `ToolResolveError` 拆分为 `ToolNotFoundError` 和 `AmbiguousToolError`
|
|
21
|
+
- Daemon 启动不再需要 `--config` 参数,统一从 `axi.json` 读取
|
|
22
|
+
- MCP/原生工具配置统一通过 `config.py` 管理,移除分散的配置解析逻辑
|
|
23
|
+
|
|
24
|
+
## [0.0.2] - 2026-03-30
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- 原生工具 schema 提取改用 Pydantic `create_model`,支持 Literal、Optional、list[T]、嵌套 BaseModel、Annotated[..., Field()] 等高级类型
|
|
28
|
+
- 版本号从 0.1.0 调整为 0.0.2,反映项目早期阶段
|
|
29
|
+
- 代码格式化统一(ruff format)
|
|
30
|
+
|
|
31
|
+
## [0.1.0] - 2026-03-29
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- 初始版本:原生工具注册、MCP 对接、daemon 模式
|
axi_cli-0.0.3/PKG-INFO
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: axi-cli
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Agent eXecution Interface - unified tool layer for AI Agents
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: bm25s>=0.3.3
|
|
7
|
+
Requires-Dist: jieba>=0.42.1
|
|
8
|
+
Requires-Dist: langchain-community>=0.4.1
|
|
9
|
+
Requires-Dist: langchain-openai>=1.1.12
|
|
10
|
+
Requires-Dist: mcp>=1.26.0
|
|
11
|
+
Requires-Dist: pydantic>=2.12.5
|
|
12
|
+
Requires-Dist: typer>=0.24.1
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# axi
|
|
16
|
+
|
|
17
|
+
Agent eXecution Interface — AI Agent 与外部系统之间的统一工具层。
|
|
18
|
+
|
|
19
|
+
通过 CLI 作为万能适配器,将 MCP server 和 Python 函数统一为可搜索、可描述、可调用的命令。Agent 无需猜测命令格式或解析人类可读输出,所有交互都是结构化 JSON。
|
|
20
|
+
|
|
21
|
+
## 核心理念
|
|
22
|
+
|
|
23
|
+
几乎所有主流 Agent 框架都把 bash 作为基础能力。axi 在这个"万能通道"之上建一层 Agent-native 的工具层:
|
|
24
|
+
|
|
25
|
+
- **渐进式披露** — `search → describe → run`,按需获取信息,不吃 context window
|
|
26
|
+
- **Agent-first 输出** — 统一紧凑 JSON,无表格、无颜色、无进度条
|
|
27
|
+
- **双来源统一** — MCP server 和原生 Python 工具,对 Agent 来说是同一套接口
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 需要 Python 3.12+
|
|
33
|
+
uv pip install -e .
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 快速开始
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 搜索工具
|
|
40
|
+
axi search "web"
|
|
41
|
+
|
|
42
|
+
# 查看工具详情
|
|
43
|
+
axi describe jina/jina_search
|
|
44
|
+
|
|
45
|
+
# 执行工具
|
|
46
|
+
axi run jina/jina_search --query "hello world" --count 3
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 配置
|
|
50
|
+
|
|
51
|
+
在项目根目录创建 `axi.json`(参考 `axi.json.example`):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"jina": {
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["jina-mcp-tools"],
|
|
59
|
+
"env": { "JINA_API_KEY": "your-key" }
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"nativeTools": [
|
|
63
|
+
{"module": "my_project.tools"},
|
|
64
|
+
{"module": "./scripts/tools.py", "name": "scripts"}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **mcpServers** — MCP server 配置,key 名作为工具命名空间
|
|
70
|
+
- **nativeTools** — 原生 Python 工具,`module` 支持文件路径和模块路径,`name` 可选(省略则自动推导)
|
|
71
|
+
|
|
72
|
+
## CLI 命令
|
|
73
|
+
|
|
74
|
+
| 命令 | 说明 |
|
|
75
|
+
|------|------|
|
|
76
|
+
| `axi list [server]` | 列出所有 server 及工具 |
|
|
77
|
+
| `axi search <query>` | 混合搜索工具(BM25 + Embedding,支持 `--top-k`) |
|
|
78
|
+
| `axi grep <pattern>` | 正则表达式搜索工具(支持 `--top-k`) |
|
|
79
|
+
| `axi describe <tool>` | 查看工具完整 schema |
|
|
80
|
+
| `axi run <tool> --key value` | 执行工具(也支持 `-j '{...}'` 传 JSON) |
|
|
81
|
+
| `axi daemon start\|status\|stop` | 管理 daemon 进程 |
|
|
82
|
+
|
|
83
|
+
所有输出统一为紧凑 JSON。`axi run` 返回统一信封:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{"status": "success", "data": "..."}
|
|
87
|
+
{"status": "error", "error": "错误信息"}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 注册原生工具
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from axi import tool
|
|
94
|
+
|
|
95
|
+
@tool(name="query_orders", description="按区域查询订单")
|
|
96
|
+
def query_orders(region: str, limit: int = 10) -> dict:
|
|
97
|
+
return {"orders": [...], "total": 42}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
装饰器自动从函数签名提取参数 schema。注册后同时获得 CLI 调用和 PTC 调用能力。
|
|
101
|
+
|
|
102
|
+
## PTC(Programmatic Tool Calling)
|
|
103
|
+
|
|
104
|
+
Agent 可以写 Python 代码批量调用工具,在本地做数据过滤和聚合,避免反复 LLM round-trip:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from axi import tool
|
|
108
|
+
|
|
109
|
+
search = tool("jina/jina_search")
|
|
110
|
+
results = search(query="python async", count=5)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Daemon 模式
|
|
114
|
+
|
|
115
|
+
MCP 工具通过后台 daemon 长连接执行,支持有状态 MCP server(如 browser MCP)。执行 `axi search/describe/run` 时自动启动 daemon,无需手动管理。
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
axi CLI ──(Unix socket)──> daemon ──(stdio)──> MCP server A
|
|
119
|
+
──(stdio)──> MCP server B
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 技术栈
|
|
123
|
+
|
|
124
|
+
Python 3.12+ / [Typer](https://typer.tiangolo.com/) / [Pydantic](https://docs.pydantic.dev/) / [MCP SDK](https://github.com/modelcontextprotocol/python-sdk)
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
axi_cli-0.0.3/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# axi
|
|
2
|
+
|
|
3
|
+
Agent eXecution Interface — AI Agent 与外部系统之间的统一工具层。
|
|
4
|
+
|
|
5
|
+
通过 CLI 作为万能适配器,将 MCP server 和 Python 函数统一为可搜索、可描述、可调用的命令。Agent 无需猜测命令格式或解析人类可读输出,所有交互都是结构化 JSON。
|
|
6
|
+
|
|
7
|
+
## 核心理念
|
|
8
|
+
|
|
9
|
+
几乎所有主流 Agent 框架都把 bash 作为基础能力。axi 在这个"万能通道"之上建一层 Agent-native 的工具层:
|
|
10
|
+
|
|
11
|
+
- **渐进式披露** — `search → describe → run`,按需获取信息,不吃 context window
|
|
12
|
+
- **Agent-first 输出** — 统一紧凑 JSON,无表格、无颜色、无进度条
|
|
13
|
+
- **双来源统一** — MCP server 和原生 Python 工具,对 Agent 来说是同一套接口
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 需要 Python 3.12+
|
|
19
|
+
uv pip install -e .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 快速开始
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 搜索工具
|
|
26
|
+
axi search "web"
|
|
27
|
+
|
|
28
|
+
# 查看工具详情
|
|
29
|
+
axi describe jina/jina_search
|
|
30
|
+
|
|
31
|
+
# 执行工具
|
|
32
|
+
axi run jina/jina_search --query "hello world" --count 3
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 配置
|
|
36
|
+
|
|
37
|
+
在项目根目录创建 `axi.json`(参考 `axi.json.example`):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"jina": {
|
|
43
|
+
"command": "npx",
|
|
44
|
+
"args": ["jina-mcp-tools"],
|
|
45
|
+
"env": { "JINA_API_KEY": "your-key" }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"nativeTools": [
|
|
49
|
+
{"module": "my_project.tools"},
|
|
50
|
+
{"module": "./scripts/tools.py", "name": "scripts"}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **mcpServers** — MCP server 配置,key 名作为工具命名空间
|
|
56
|
+
- **nativeTools** — 原生 Python 工具,`module` 支持文件路径和模块路径,`name` 可选(省略则自动推导)
|
|
57
|
+
|
|
58
|
+
## CLI 命令
|
|
59
|
+
|
|
60
|
+
| 命令 | 说明 |
|
|
61
|
+
|------|------|
|
|
62
|
+
| `axi list [server]` | 列出所有 server 及工具 |
|
|
63
|
+
| `axi search <query>` | 混合搜索工具(BM25 + Embedding,支持 `--top-k`) |
|
|
64
|
+
| `axi grep <pattern>` | 正则表达式搜索工具(支持 `--top-k`) |
|
|
65
|
+
| `axi describe <tool>` | 查看工具完整 schema |
|
|
66
|
+
| `axi run <tool> --key value` | 执行工具(也支持 `-j '{...}'` 传 JSON) |
|
|
67
|
+
| `axi daemon start\|status\|stop` | 管理 daemon 进程 |
|
|
68
|
+
|
|
69
|
+
所有输出统一为紧凑 JSON。`axi run` 返回统一信封:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{"status": "success", "data": "..."}
|
|
73
|
+
{"status": "error", "error": "错误信息"}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 注册原生工具
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from axi import tool
|
|
80
|
+
|
|
81
|
+
@tool(name="query_orders", description="按区域查询订单")
|
|
82
|
+
def query_orders(region: str, limit: int = 10) -> dict:
|
|
83
|
+
return {"orders": [...], "total": 42}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
装饰器自动从函数签名提取参数 schema。注册后同时获得 CLI 调用和 PTC 调用能力。
|
|
87
|
+
|
|
88
|
+
## PTC(Programmatic Tool Calling)
|
|
89
|
+
|
|
90
|
+
Agent 可以写 Python 代码批量调用工具,在本地做数据过滤和聚合,避免反复 LLM round-trip:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from axi import tool
|
|
94
|
+
|
|
95
|
+
search = tool("jina/jina_search")
|
|
96
|
+
results = search(query="python async", count=5)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Daemon 模式
|
|
100
|
+
|
|
101
|
+
MCP 工具通过后台 daemon 长连接执行,支持有状态 MCP server(如 browser MCP)。执行 `axi search/describe/run` 时自动启动 daemon,无需手动管理。
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
axi CLI ──(Unix socket)──> daemon ──(stdio)──> MCP server A
|
|
105
|
+
──(stdio)──> MCP server B
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 技术栈
|
|
109
|
+
|
|
110
|
+
Python 3.12+ / [Typer](https://typer.tiangolo.com/) / [Pydantic](https://docs.pydantic.dev/) / [MCP SDK](https://github.com/modelcontextprotocol/python-sdk)
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "axi-cli"
|
|
3
|
+
version = "0.0.3"
|
|
4
|
+
description = "Agent eXecution Interface - unified tool layer for AI Agents"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"bm25s>=0.3.3",
|
|
9
|
+
"jieba>=0.42.1",
|
|
10
|
+
"langchain-community>=0.4.1",
|
|
11
|
+
"langchain-openai>=1.1.12",
|
|
12
|
+
"mcp>=1.26.0",
|
|
13
|
+
"pydantic>=2.12.5",
|
|
14
|
+
"typer>=0.24.1",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
axi = "axi.cli:app"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/axi"]
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.sdist]
|
|
28
|
+
exclude = [
|
|
29
|
+
".github/",
|
|
30
|
+
".python-version",
|
|
31
|
+
"CLAUDE.md",
|
|
32
|
+
"axi.json.example",
|
|
33
|
+
"docs/",
|
|
34
|
+
"tests/",
|
|
35
|
+
"uv.lock",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=9.0.2",
|
|
41
|
+
"pytest-asyncio>=1.3.0",
|
|
42
|
+
"ruff>=0.15.8",
|
|
43
|
+
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""axi — Agent eXecution Interface."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Callable
|
|
5
|
+
|
|
6
|
+
from axi.cli import get_executor, get_registry
|
|
7
|
+
from axi.providers.native import register_tool
|
|
8
|
+
|
|
9
|
+
__all__ = ["tool"]
|
|
10
|
+
|
|
11
|
+
# 包级 logger:各子模块通过 logging.getLogger(__name__) 自动继承
|
|
12
|
+
_logger = logging.getLogger("axi")
|
|
13
|
+
_logger.addHandler(logging.NullHandler())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def tool(
|
|
17
|
+
name: str | None = None,
|
|
18
|
+
description: str | None = None,
|
|
19
|
+
output_example: Any | None = None,
|
|
20
|
+
) -> Callable:
|
|
21
|
+
"""装饰器:注册一个 Python 函数为 axi 工具。
|
|
22
|
+
|
|
23
|
+
用法:
|
|
24
|
+
@tool(name="query_orders", description="按区域查询订单")
|
|
25
|
+
def query_orders(region: str, limit: int = 10) -> dict:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
也可以作为函数调用获取 PTC 可调用对象:
|
|
29
|
+
query = tool("query_orders")
|
|
30
|
+
result = query(region="cn")
|
|
31
|
+
"""
|
|
32
|
+
# 当作为 PTC 调用时:tool("tool_name") 返回可调用对象
|
|
33
|
+
if name is not None and description is None and output_example is None:
|
|
34
|
+
# 先查本地原生工具
|
|
35
|
+
registry = get_registry()
|
|
36
|
+
meta = registry.get(name)
|
|
37
|
+
if meta is not None:
|
|
38
|
+
executor = get_executor()
|
|
39
|
+
|
|
40
|
+
def _native_caller(**kwargs: Any) -> Any:
|
|
41
|
+
result = executor.run(name, kwargs) # type: ignore[arg-type]
|
|
42
|
+
if result.status == "error":
|
|
43
|
+
raise RuntimeError(result.error)
|
|
44
|
+
return result.data
|
|
45
|
+
|
|
46
|
+
return _native_caller
|
|
47
|
+
|
|
48
|
+
# 再查 daemon(MCP 工具)
|
|
49
|
+
return _make_daemon_caller(name)
|
|
50
|
+
|
|
51
|
+
# 当作为装饰器时
|
|
52
|
+
def decorator(func: Callable) -> Callable:
|
|
53
|
+
meta = register_tool(
|
|
54
|
+
func,
|
|
55
|
+
name=name,
|
|
56
|
+
description=description,
|
|
57
|
+
output_example=output_example,
|
|
58
|
+
)
|
|
59
|
+
get_registry().register(meta)
|
|
60
|
+
return func
|
|
61
|
+
|
|
62
|
+
return decorator
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _make_daemon_caller(tool_name: str) -> Callable:
|
|
66
|
+
"""创建通过 daemon 调用 MCP 工具的 PTC 函数。"""
|
|
67
|
+
from axi.daemon.client import ensure_daemon, send_request
|
|
68
|
+
from axi.daemon.protocol import DaemonRequest
|
|
69
|
+
|
|
70
|
+
def _daemon_caller(**kwargs: Any) -> Any:
|
|
71
|
+
if not ensure_daemon():
|
|
72
|
+
raise RuntimeError("Daemon is not running. Start it with: axi daemon start")
|
|
73
|
+
|
|
74
|
+
resp = send_request(
|
|
75
|
+
DaemonRequest(method="call_tool", tool_name=tool_name, params=kwargs)
|
|
76
|
+
)
|
|
77
|
+
if resp.status == "error":
|
|
78
|
+
raise RuntimeError(resp.error)
|
|
79
|
+
return resp.data
|
|
80
|
+
|
|
81
|
+
return _daemon_caller
|