spineagent 0.0.1__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.
- spineagent-0.0.1/.github/workflows/ci.yml +53 -0
- spineagent-0.0.1/.gitignore +5 -0
- spineagent-0.0.1/CLAUDE.md +64 -0
- spineagent-0.0.1/Makefile +49 -0
- spineagent-0.0.1/PKG-INFO +175 -0
- spineagent-0.0.1/README.md +138 -0
- spineagent-0.0.1/deploy/.dockerignore +23 -0
- spineagent-0.0.1/deploy/Dockerfile +30 -0
- spineagent-0.0.1/deploy/README.md +43 -0
- spineagent-0.0.1/examples/quickstart.py +189 -0
- spineagent-0.0.1/pyproject.toml +69 -0
- spineagent-0.0.1/src/spineagent/__init__.py +121 -0
- spineagent-0.0.1/src/spineagent/agent/__init__.py +1 -0
- spineagent-0.0.1/src/spineagent/agent/agent.py +122 -0
- spineagent-0.0.1/src/spineagent/agent/as_tool.py +30 -0
- spineagent-0.0.1/src/spineagent/agent/function_calling.py +129 -0
- spineagent-0.0.1/src/spineagent/agent/policy.py +121 -0
- spineagent-0.0.1/src/spineagent/agent/tool_using.py +107 -0
- spineagent-0.0.1/src/spineagent/conformance.py +128 -0
- spineagent-0.0.1/src/spineagent/llm/__init__.py +5 -0
- spineagent-0.0.1/src/spineagent/llm/bedrock_provider.py +164 -0
- spineagent-0.0.1/src/spineagent/llm/cohere_provider.py +96 -0
- spineagent-0.0.1/src/spineagent/llm/gemini_provider.py +175 -0
- spineagent-0.0.1/src/spineagent/llm/provider.py +283 -0
- spineagent-0.0.1/src/spineagent/orchestration/__init__.py +1 -0
- spineagent-0.0.1/src/spineagent/orchestration/chain.py +49 -0
- spineagent-0.0.1/src/spineagent/orchestration/coordinator.py +96 -0
- spineagent-0.0.1/src/spineagent/protocol/__init__.py +1 -0
- spineagent-0.0.1/src/spineagent/protocol/a2a/__init__.py +19 -0
- spineagent-0.0.1/src/spineagent/protocol/a2a/seam.py +126 -0
- spineagent-0.0.1/src/spineagent/protocol/mcp/__init__.py +21 -0
- spineagent-0.0.1/src/spineagent/protocol/mcp/seam.py +126 -0
- spineagent-0.0.1/src/spineagent/py.typed +0 -0
- spineagent-0.0.1/src/spineagent/tools/__init__.py +1 -0
- spineagent-0.0.1/src/spineagent/tools/function_tool.py +88 -0
- spineagent-0.0.1/src/spineagent/tools/tool.py +93 -0
- spineagent-0.0.1/tests/test_adapters.py +66 -0
- spineagent-0.0.1/tests/test_agent.py +51 -0
- spineagent-0.0.1/tests/test_as_tool.py +48 -0
- spineagent-0.0.1/tests/test_chain.py +74 -0
- spineagent-0.0.1/tests/test_conformance.py +96 -0
- spineagent-0.0.1/tests/test_coordinator.py +181 -0
- spineagent-0.0.1/tests/test_function_calling.py +173 -0
- spineagent-0.0.1/tests/test_import_clean.py +44 -0
- spineagent-0.0.1/tests/test_llm_provider.py +224 -0
- spineagent-0.0.1/tests/test_native_providers.py +220 -0
- spineagent-0.0.1/tests/test_policy.py +95 -0
- spineagent-0.0.1/tests/test_protocol_a2a.py +41 -0
- spineagent-0.0.1/tests/test_protocol_mcp.py +48 -0
- spineagent-0.0.1/tests/test_tool.py +60 -0
- spineagent-0.0.1/tests/test_tool_using.py +109 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# spineagent CI —— push + PR 上跑 lint + 测试,Python 多版本矩阵。
|
|
2
|
+
#
|
|
3
|
+
# 关键约束:spineagent 依赖兄弟薄核 corespine,而 corespine 尚未发到 PyPI。所以 CI 里把
|
|
4
|
+
# corespine【并排签出】到工作区,再【先于】spineagent path-install —— 复刻本地家族布局
|
|
5
|
+
# (spineagent 的 uv path source 指向 ../corespine)。之后用 Makefile 跑与本地同一套门。
|
|
6
|
+
name: CI
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
pull_request:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
ci:
|
|
14
|
+
name: lint + test (py${{ matrix.python }})
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
python: ["3.10", "3.11", "3.12"]
|
|
20
|
+
steps:
|
|
21
|
+
# 把 spineagent 自身签出到 ./spineagent,使其与 corespine 在工作区里【并排】。
|
|
22
|
+
- name: Checkout spineagent
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
path: spineagent
|
|
26
|
+
|
|
27
|
+
# corespine 还没上 PyPI(兄弟仓库),这里并排签出到 ./corespine。
|
|
28
|
+
# 从 ./spineagent 看过去就是 ../corespine —— Makefile 的 `uv pip install -e ../corespine`
|
|
29
|
+
# 与 pyproject 的 uv path source 都能原样命中。
|
|
30
|
+
#
|
|
31
|
+
# corespine 是公开兄弟仓库,直接并排签出,无需 token。
|
|
32
|
+
# 若将来转私有,补一行 `token: ${{ secrets.CORESPINE_CHECKOUT_TOKEN }}`
|
|
33
|
+
# (一个对 corespine 有读权限的 PAT / deploy key)。
|
|
34
|
+
- name: Checkout corespine (sibling, not yet on PyPI)
|
|
35
|
+
uses: actions/checkout@v4
|
|
36
|
+
with:
|
|
37
|
+
repository: VoldemortGin/corespine
|
|
38
|
+
path: corespine
|
|
39
|
+
# ref: main # 需锁定分支/标签时取消注释
|
|
40
|
+
|
|
41
|
+
- name: Set up uv (+ Python ${{ matrix.python }})
|
|
42
|
+
uses: astral-sh/setup-uv@v5
|
|
43
|
+
with:
|
|
44
|
+
python-version: ${{ matrix.python }}
|
|
45
|
+
|
|
46
|
+
# `make install` 建 venv 并先装 corespine、再装 spineagent[dev];`make ci` = lint + 测试。
|
|
47
|
+
- name: Install (corespine first, then spineagent[dev])
|
|
48
|
+
working-directory: spineagent
|
|
49
|
+
run: make install
|
|
50
|
+
|
|
51
|
+
- name: CI gate (lint + test)
|
|
52
|
+
working-directory: spineagent
|
|
53
|
+
run: make ci
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# CLAUDE.md — spineagent(宪章)
|
|
2
|
+
|
|
3
|
+
Spine 家族的 AI / 人类协作契约。先读家族 `../README.md` 与
|
|
4
|
+
`../docs/adr/0001-spine-family-boundaries-and-dependency-direction.md`,本文件是 spineagent 的操作指南。
|
|
5
|
+
|
|
6
|
+
## 这是什么
|
|
7
|
+
|
|
8
|
+
**spineagent —— 通用多 agent 协作框架**(ADR 0001 D1)。agent / tool / 编排 + MCP / A2A 等
|
|
9
|
+
agent 协议缝。它是家族里【演进最快】的成员,刻意单开独立包,**不**拖累薄核与兄弟包。它依赖薄核
|
|
10
|
+
`corespine`,复用其缝元模式与 observability / config 形状,但**不**含任何 RAG 概念。
|
|
11
|
+
|
|
12
|
+
## 宪章(不可违背)
|
|
13
|
+
|
|
14
|
+
- **离线优先、import-clean、零重依赖默认路径。** 核心默认路径只用标准库 + `corespine`;真实协议
|
|
15
|
+
SDK(MCP / A2A 等)经**可选 extra 延迟 import**(`corespine.lazy_extra_import`),缺 extra 时给
|
|
16
|
+
「pip install spineagent[<extra>]」友好报错。**import spineagent 绝不拉入任何网络 SDK**
|
|
17
|
+
(有 `tests/test_import_clean.py` 把这条钉死)。
|
|
18
|
+
- **每条缝长一个样**(家族统一元模式):**Protocol + 离线确定性默认 + Registry 工厂 + 参数化
|
|
19
|
+
conformance**。core 只 import Protocol,绝不 import SDK。
|
|
20
|
+
- **机制借 corespine,保证本包自绑**(ADR 0001 D6)。corespine 只给 conformance 基座;具体不变量
|
|
21
|
+
(agent 步 provenance、步级 trace 隐私安全、tool 结果 provenance)由 `spineagent/conformance.py`
|
|
22
|
+
绑定,并用参数化 conformance 测试钉死。
|
|
23
|
+
- **不在包层面依赖 ragspine。** 可在【运行时】把 ragspine 当一个 Tool / MCP server 组合调用
|
|
24
|
+
(ADR 0001 D4b),那是松耦合的可选组合,方向只能 spineagent→ragspine,绝不反向,也绝不写进
|
|
25
|
+
`dependencies`。
|
|
26
|
+
- **隐私 trace。** 任何步级 / 编排级 trace 只记 code / 计数 / 耗时,绝不记任务或输出正文。
|
|
27
|
+
|
|
28
|
+
## 模块地图(按文件夹定位)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/spineagent/
|
|
32
|
+
agent/agent.py Agent 协议 + LlmAgent(走 corespine LLMProvider)/ FunctionAgent(纯函数)
|
|
33
|
+
llm/provider.py 真实 LLM provider 适配器(对外统一 OpenAI chat-completions 形状):OpenAICompatProvider(openai SDK + base_url 吃下所有 OpenAI 兼容端点)+ AnthropicProvider(默认 claude-opus-4-8);llm_providers Registry。各走可选 extra 延迟 import,离线默认仍 MockProvider。
|
|
34
|
+
llm/cohere_provider.py CohereProvider:Cohere v2 native → OpenAI ChatCompletion([cohere] extra)
|
|
35
|
+
llm/gemini_provider.py GeminiProvider:Gemini generateContent native → OpenAI ChatCompletion([gemini] extra,同覆盖 Vertex Gemini)
|
|
36
|
+
llm/bedrock_provider.py BedrockConverseProvider:AWS Bedrock Converse native → OpenAI ChatCompletion([bedrock] extra,Converse 跨模型同形)
|
|
37
|
+
agent/policy.py ToolPolicy 缝:协议 + 离线确定性默认 SyntaxToolPolicy(`<tool>: <arg>` 语法路由,不假装 LLM 推理)+ tool_policies Registry
|
|
38
|
+
agent/tool_using.py ToolUsingAgent:离线确定性多步循环(SyntaxToolPolicy 语法路由),带 max_steps 守卫;实现 Agent 协议
|
|
39
|
+
agent/function_calling.py FunctionCallingAgent:真 LLM function-calling 多步循环(FunctionTool schema → chat(tools=) → tool_calls → 执行 → OpenAI tool 角色喂回 → 再 chat);实现 Agent 协议,底层换任意 provider 不改一行
|
|
40
|
+
agent/as_tool.py AgentTool:把 Agent 桥成 Tool(分层 / 督导式多 agent:督导 agent 通过工具调用派活给子 agent,可嵌套)
|
|
41
|
+
tools/tool.py Tool 协议 + EchoTool / CalcTool + tool_registry(spec 选工具 + entry-point 第三方工具发现,group corespine.tool);注:运行时可把 ragspine RAG 插为 Tool
|
|
42
|
+
tools/function_tool.py FunctionTool(带 JSON-schema、接 dict 参数,给真 function-calling 用)+ @function_tool 装饰器(从签名自动推 schema)
|
|
43
|
+
orchestration/coordinator.py Coordinator:顺序 / 并行 / 流水线(output→input)跑多 agent、保序收集;弹性容错 resilient 把异常归一为 AgentResult.error,坏 agent 不炸整批
|
|
44
|
+
orchestration/chain.py ChainAgent:把一串 agent 串成单个 Agent(流水线即一等可组合单元:可进 Coordinator / 当工具 / 套 chain)
|
|
45
|
+
protocol/mcp/seam.py McpClient / McpServer 协议 + OfflineMcpStub(离线回环)+ McpClientTool(MCP 工具→Tool)+ 延迟真实 SDK
|
|
46
|
+
protocol/a2a/seam.py A2AAgent 协议 + OfflineA2AStub(离线回环)+ A2AAgentAdapter(A2A→Agent)+ 延迟真实 SDK
|
|
47
|
+
conformance.py 本包绑定的不变量包(AGENT_INVARIANTS / TOOL_INVARIANTS / POLICY_INVARIANTS)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 跑(始终从包根)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv venv .venv
|
|
54
|
+
VIRTUAL_ENV="$(pwd)/.venv" uv pip install -e ../corespine # 本地兄弟包
|
|
55
|
+
VIRTUAL_ENV="$(pwd)/.venv" uv pip install -e ".[dev]"
|
|
56
|
+
.venv/bin/python -m pytest -q # 期望 GREEN
|
|
57
|
+
.venv/bin/python -c "import spineagent" # 期望 import-clean(无网络 SDK)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 约定
|
|
61
|
+
|
|
62
|
+
- Python **3.10+** 类型注解;import 顺序 **stdlib > 三方 > 本地**;简体中文 docstring/注释,匹配家族风格。
|
|
63
|
+
- **TDD**——测试即规格;**最小改动**——只改需求要求的部分。
|
|
64
|
+
- **深层、按领域分组**的布局:文件路径先定位职责,再读文件名。
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# spineagent —— 一键开发 / CI 命令(始终从包根跑)。
|
|
2
|
+
#
|
|
3
|
+
# `make` 或 `make help` 列出全部目标。默认走包内 venv;可覆盖解释器:
|
|
4
|
+
# make test PYTHON=python3.12
|
|
5
|
+
#
|
|
6
|
+
# 跨平台:用 $(VENV)/$(PYTHON) 变量(对齐家族 ragspine 的写法)。
|
|
7
|
+
|
|
8
|
+
.DEFAULT_GOAL := help
|
|
9
|
+
PYTHON ?= .venv/bin/python
|
|
10
|
+
VENV ?= .venv
|
|
11
|
+
|
|
12
|
+
# ---- 安装 --------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
.PHONY: install
|
|
15
|
+
install: ## 建 venv 并可编辑安装:先装兄弟薄核 corespine,再装 spineagent[dev]
|
|
16
|
+
uv venv $(VENV)
|
|
17
|
+
VIRTUAL_ENV="$(CURDIR)/$(VENV)" uv pip install -e ../corespine
|
|
18
|
+
VIRTUAL_ENV="$(CURDIR)/$(VENV)" uv pip install -e ".[dev]"
|
|
19
|
+
|
|
20
|
+
# ---- 质量门 ------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
.PHONY: ci
|
|
23
|
+
ci: lint test ## 本地 CI 门:lint + 测试(与 CI 同一套门)
|
|
24
|
+
|
|
25
|
+
.PHONY: test
|
|
26
|
+
test: ## 跑测试套件(离线、确定性)
|
|
27
|
+
$(PYTHON) -m pytest -q
|
|
28
|
+
|
|
29
|
+
.PHONY: lint
|
|
30
|
+
lint: ## ruff 静态检查(风格 + import 序 + 死代码)
|
|
31
|
+
$(PYTHON) -m ruff check src/spineagent tests examples
|
|
32
|
+
|
|
33
|
+
.PHONY: fmt
|
|
34
|
+
fmt: ## ruff 自动格式化
|
|
35
|
+
$(PYTHON) -m ruff format src/spineagent tests examples
|
|
36
|
+
|
|
37
|
+
# ---- demo --------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
.PHONY: demo
|
|
40
|
+
demo: ## 跑一键离线 demo(多 agent 顺序/并行 + 工具派发 + 隐私 trace,零网络)
|
|
41
|
+
$(PYTHON) examples/quickstart.py
|
|
42
|
+
|
|
43
|
+
# ---- meta --------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
.PHONY: help
|
|
46
|
+
help: ## 列出可用目标
|
|
47
|
+
@grep -hE '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) \
|
|
48
|
+
| sort \
|
|
49
|
+
| awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-12s\033[0m %s\n", $$1, $$2}'
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spineagent
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Spine 家族的通用多 agent 协作框架:agent / tool / 编排 + MCP / A2A 协议缝(离线优先,依赖 corespine)。
|
|
5
|
+
Author: lin han
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Keywords: a2a,agent,conformance,framework-free,mcp,multi-agent,offline,orchestration,seam
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: corespine
|
|
19
|
+
Provides-Extra: a2a
|
|
20
|
+
Requires-Dist: a2a-sdk; extra == 'a2a'
|
|
21
|
+
Provides-Extra: anthropic
|
|
22
|
+
Requires-Dist: anthropic; extra == 'anthropic'
|
|
23
|
+
Provides-Extra: bedrock
|
|
24
|
+
Requires-Dist: boto3; extra == 'bedrock'
|
|
25
|
+
Provides-Extra: cohere
|
|
26
|
+
Requires-Dist: cohere; extra == 'cohere'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
30
|
+
Provides-Extra: gemini
|
|
31
|
+
Requires-Dist: google-genai; extra == 'gemini'
|
|
32
|
+
Provides-Extra: mcp
|
|
33
|
+
Requires-Dist: mcp; extra == 'mcp'
|
|
34
|
+
Provides-Extra: openai
|
|
35
|
+
Requires-Dist: openai; extra == 'openai'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# spineagent
|
|
39
|
+
|
|
40
|
+
Spine 家族的**通用多 agent 协作框架**(见 [ADR 0001](../docs/adr/0001-spine-family-boundaries-and-dependency-direction.md))。
|
|
41
|
+
agent / tool / 编排 + **MCP / A2A** 等 agent 协议缝。依赖薄核 `corespine`,复用其缝元模式与
|
|
42
|
+
observability / config 形状;**默认路径离线可跑、import-clean、零网络 SDK**。
|
|
43
|
+
|
|
44
|
+
> 通用 ≠ 地基。真正的核是更薄的 `corespine`,spineagent 是它的兄弟消费者,**不**含任何 RAG 概念。
|
|
45
|
+
> 详见 [`CLAUDE.md`](CLAUDE.md) 宪章。
|
|
46
|
+
|
|
47
|
+
## 缝的元模式(家族统一)
|
|
48
|
+
|
|
49
|
+
每条缝都长一个样,核心 import 零 SDK、离线可跑:
|
|
50
|
+
|
|
51
|
+
**Protocol + 离线确定性默认 + `Registry` 工厂 + 参数化 conformance**
|
|
52
|
+
|
|
53
|
+
## 里面有什么
|
|
54
|
+
|
|
55
|
+
| 模块 | 原语 |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `agent/agent.py` | `Agent` 协议 + `LlmAgent`(走 corespine `LLMProvider`,离线用 `MockProvider`)/ `FunctionAgent`(纯函数节点);步级 trace 只记元数据 |
|
|
58
|
+
| `llm/provider.py` 等 | 真实 LLM provider 适配器(挂在 corespine `LLMProvider` 缝后面;**对外统一 OpenAI chat-completions 形状**):`OpenAICompatProvider`(`openai` SDK + `base_url`,一个吃下 OpenAI/Azure/Together/Groq/DeepSeek/Mistral/xAI/Qwen/Moonshot/Ollama/vLLM/OpenRouter/LiteLLM… 全部 OpenAI 兼容端点)+ 非 OpenAI 原生适配器把 native 转成 OpenAI 形状:`AnthropicProvider`(默认 `claude-opus-4-8`)/ `CohereProvider` / `GeminiProvider` / `BedrockConverseProvider`。`llm_providers` Registry(mock/openai/anthropic/cohere/gemini/bedrock)。各走可选 extra 延迟 import,离线默认仍是 `MockProvider`,各用原生 SDK 不 shim |
|
|
59
|
+
| `agent/policy.py` | `ToolPolicy` 协议 + 离线确定性默认 `SyntaxToolPolicy`(按 `<tool>: <arg>` 语法 + 工具名集合确定性路由,**不假装 LLM 推理**)+ `tool_policies` Registry(`llm` 位留真实推理式接入) |
|
|
60
|
+
| `agent/tool_using.py` | `ToolUsingAgent`:**离线确定性**多步循环(`SyntaxToolPolicy` 按 `<tool>: <arg>` 语法路由),带 `max_steps` 守卫;实现 `Agent` 协议 |
|
|
61
|
+
| `agent/function_calling.py` | `FunctionCallingAgent`:**真 LLM function-calling** 多步循环——把 `FunctionTool` schema 喂给 `chat(tools=)`,模型回 tool_calls → 执行 → 以 OpenAI tool 角色喂回 → 再 chat,直到出文本或触顶;底层换任意 provider 不改一行。实现 `Agent` 协议 |
|
|
62
|
+
| `tools/function_tool.py` | `FunctionTool`(带 JSON-schema、接 dict 参数的结构化工具)+ `@function_tool` 装饰器(从签名/注解/docstring 自动推 schema) |
|
|
63
|
+
| `agent/as_tool.py` | `AgentTool`:把一个 `Agent` 桥成 `Tool`,让督导 agent 通过工具调用把子任务派给专精子 agent(**分层 / 督导式多 agent**,可层层嵌套) |
|
|
64
|
+
| `tools/tool.py` | `Tool` 协议 + `EchoTool` / `CalcTool`(安全算术求值);结果带 provenance。`tool_registry`:spec 选工具 + **entry-point 第三方工具自动发现**(group `corespine.tool`)。**运行时可把 ragspine RAG 插为一个 Tool**(见下) |
|
|
65
|
+
| `orchestration/coordinator.py` | `Coordinator`:把多个 agent **顺序 / 并行 / 流水线**(output→input 链式)跑、保序收集 `AgentResult`;**弹性容错**(`resilient=True`)把单 agent 异常归一为家族错误 dict 塞进 `AgentResult.error`、一个坏 agent 不炸整批 |
|
|
66
|
+
| `orchestration/chain.py` | `ChainAgent`:把一串 agent 串成**单个 `Agent`**(流水线即一等可组合单元),可再进 `Coordinator` / 被 `AgentTool` 当工具 / 套 chain |
|
|
67
|
+
| `protocol/mcp/seam.py` | `McpClient` / `McpServer` 协议 + `OfflineMcpStub`(进程内回环)+ **`McpClientTool`(把 MCP 工具桥成 `Tool`)** + 真实 SDK 经 `[mcp]` extra 延迟 import |
|
|
68
|
+
| `protocol/a2a/seam.py` | `A2AAgent` 协议 + `OfflineA2AStub`(进程内回环)+ **`A2AAgentAdapter`(把 A2A agent 桥成 `Agent`)** + 真实 `a2a-sdk` 经 `[a2a]` extra 延迟 import |
|
|
69
|
+
| `conformance.py` | 本包绑定的不变量:`AGENT_INVARIANTS`(步产出 / provenance / 隐私 trace)、`TOOL_INVARIANTS`(结果 provenance)、`POLICY_INVARIANTS`(决策形状 / 不幻觉工具 / 可终止 / 纯函数) |
|
|
70
|
+
|
|
71
|
+
## 运行时组合 ragspine(ADR 0001 D4b)
|
|
72
|
+
|
|
73
|
+
spineagent **不**在包层面依赖 ragspine。但可在**运行时**把 ragspine 的 RAG 检索包成一个实现了
|
|
74
|
+
`Tool`(或 MCP server)协议的适配器,插给某个 agent 调用——松耦合、可选,方向只能 spineagent→ragspine。
|
|
75
|
+
本包 `dependencies` 永远不含 ragspine,也绝不在默认路径 import 它。
|
|
76
|
+
|
|
77
|
+
第三方工具(含 ragspine RAG)还可经 entry-point 在 `corespine.tool` group 下注册工具工厂,即被
|
|
78
|
+
`tool_registry.make / names` 自动发现、零改本包代码组合进 agent;而它们仍须过 `TOOL_INVARIANTS`
|
|
79
|
+
conformance 才算数——「敢放手让第三方填广度,却让脊柱不变量烂不掉」。
|
|
80
|
+
|
|
81
|
+
## 本地开发(始终从包根)
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
uv venv .venv
|
|
85
|
+
VIRTUAL_ENV="$(pwd)/.venv" uv pip install -e ../corespine
|
|
86
|
+
VIRTUAL_ENV="$(pwd)/.venv" uv pip install -e ".[dev]"
|
|
87
|
+
.venv/bin/python -m pytest -q
|
|
88
|
+
.venv/bin/python -c "import spineagent"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 30 秒上手
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from corespine import MockProvider, InProcessPrivacyTraceSink
|
|
95
|
+
from spineagent import LlmAgent, FunctionAgent, Coordinator, EchoTool, OfflineMcpStub
|
|
96
|
+
from spineagent.protocol.mcp.seam import McpTool
|
|
97
|
+
|
|
98
|
+
# 一个离线 agent:走 corespine 的确定性 MockProvider,跑单步
|
|
99
|
+
agent = LlmAgent("planner", MockProvider())
|
|
100
|
+
print(agent.step("列个计划").output) # 确定性、可复现
|
|
101
|
+
|
|
102
|
+
# 多 agent 编排:顺序 / 并行跑同一任务,保序收集
|
|
103
|
+
coord = Coordinator([FunctionAgent("a", lambda t: f"a:{t}"),
|
|
104
|
+
FunctionAgent("b", lambda t: f"b:{t}")])
|
|
105
|
+
print([r.output for r in coord.run_parallel("go")]) # ['a:go', 'b:go']
|
|
106
|
+
print([r.output for r in coord.run_pipeline("go")]) # ['a:go', 'b:a:go'](链式:上一个输出喂下一个)
|
|
107
|
+
|
|
108
|
+
# 弹性容错:坏 agent 不炸整批,异常归一为结构化 error,批次照常跑完
|
|
109
|
+
flaky = Coordinator([FunctionAgent("ok", lambda t: t),
|
|
110
|
+
FunctionAgent("bad", lambda t: 1 / 0)])
|
|
111
|
+
print([(r.agent, r.ok) for r in flaky.run_sequential("go", resilient=True)]) # [('ok', True), ('bad', False)]
|
|
112
|
+
|
|
113
|
+
# 工具:带 provenance 的结果
|
|
114
|
+
print(EchoTool().run("hi").tool) # 'echo'
|
|
115
|
+
|
|
116
|
+
# 工具缝注册表:按 spec 选工具(大小写/留白不敏感),第三方还能经 entry-point 自动发现
|
|
117
|
+
from spineagent import tool_registry
|
|
118
|
+
print(tool_registry.make("calc").run("6/2").output) # '3'
|
|
119
|
+
print("calc" in tool_registry.names()) # True
|
|
120
|
+
|
|
121
|
+
# 会用工具的多步 agent:离线确定性 policy 按 `<tool>: <arg>` 语法路由,$prev 把上一步输出喂回
|
|
122
|
+
from spineagent import ToolUsingAgent, SyntaxToolPolicy, CalcTool
|
|
123
|
+
solver = ToolUsingAgent("solver", SyntaxToolPolicy(), [CalcTool()])
|
|
124
|
+
print(solver.step("calc: 2 + 3\ncalc: $prev * 2").output) # '10'(2+3=5,再 *2=10)
|
|
125
|
+
|
|
126
|
+
# 分层督导式多 agent:把子 agent 用 AgentTool 暴露成工具,督导 agent 通过工具调用派活给它
|
|
127
|
+
from spineagent import AgentTool
|
|
128
|
+
calculator = ToolUsingAgent("calculator", SyntaxToolPolicy(), [CalcTool()])
|
|
129
|
+
supervisor = ToolUsingAgent("supervisor", SyntaxToolPolicy(), [AgentTool(calculator)])
|
|
130
|
+
print(supervisor.step("calculator: calc: 2+3").output) # '5'(督导派给子 agent,子 agent 再用工具)
|
|
131
|
+
|
|
132
|
+
# MCP 离线回环:注册 + 调用,零网络
|
|
133
|
+
stub = OfflineMcpStub()
|
|
134
|
+
stub.register_tool(McpTool("upper"), lambda a: {"result": a["s"].upper()})
|
|
135
|
+
print(stub.call_tool("upper", {"s": "hi"})) # {'result': 'HI'}
|
|
136
|
+
|
|
137
|
+
# 跨缝组合:把上面那个 MCP 工具桥成 Tool,交给会用工具的 agent 在循环里驱动(零网络)
|
|
138
|
+
from spineagent import McpClientTool
|
|
139
|
+
shouter = ToolUsingAgent("shouter", SyntaxToolPolicy(), [McpClientTool("upper", stub, arg_key="s")])
|
|
140
|
+
print(shouter.step("upper: hi").output) # 'HI'
|
|
141
|
+
|
|
142
|
+
# 隐私 trace:步级只记元数据;塞正文会被 corespine 的 sink 直接拒绝
|
|
143
|
+
sink = InProcessPrivacyTraceSink()
|
|
144
|
+
agent.step("敏感任务", trace=sink) # 只记 agent 名 / 长度 / token 数
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 换上真实模型(可选 extra)
|
|
148
|
+
|
|
149
|
+
**对外统一 OpenAI chat-completions 形状**(LiteLLM 模式):无论后端是谁,`chat(messages, tools)`
|
|
150
|
+
都回 OpenAI 形状的 `ChatCompletion`(`choices[0].message.content/.tool_calls`、`finish_reason`、
|
|
151
|
+
`usage.prompt_tokens`…)。`LlmAgent` 全程只认 corespine 的 `LLMProvider` 协议,把 `MockProvider`
|
|
152
|
+
换成真实适配器即可,其余代码(agent / 编排 / 工具循环)一行不改:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pip install "spineagent[openai]" # OpenAI 及一切「OpenAI 兼容」端点
|
|
156
|
+
pip install "spineagent[anthropic]" # 或 [cohere] / [gemini] / [bedrock]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from spineagent import OpenAICompatProvider, AnthropicProvider, GeminiProvider, LlmAgent
|
|
161
|
+
|
|
162
|
+
# 一个适配器吃下所有 OpenAI 兼容端点:OpenAI / Azure / Together / Groq / DeepSeek / Mistral /
|
|
163
|
+
# xAI / 通义 Qwen / Moonshot / Ollama / vLLM / OpenRouter / LiteLLM …(换 base_url + model 即可)
|
|
164
|
+
gpt = LlmAgent("gpt", OpenAICompatProvider("gpt-4o"))
|
|
165
|
+
local = LlmAgent("local", OpenAICompatProvider("llama3", base_url="http://localhost:11434/v1"))
|
|
166
|
+
|
|
167
|
+
# 非 OpenAI 原生模型:原生适配器在内部转成 OpenAI 形状,用户无感
|
|
168
|
+
claude = LlmAgent("claude", AnthropicProvider()) # 默认 claude-opus-4-8
|
|
169
|
+
gemini = LlmAgent("gemini", GeminiProvider(model="gemini-2.5-flash"))
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> 覆盖:**OpenAI 兼容生态(约 85% 主流市场)走 `OpenAICompatProvider` 一把梭**;真正非 OpenAI 形状的
|
|
173
|
+
> Anthropic / Cohere / Gemini / Bedrock 各有原生适配器,把 native 响应转成 OpenAI `ChatCompletion`
|
|
174
|
+
> (绝不把它们套进 OpenAI 形状 = 不 shim)。默认离线路径仍是 `MockProvider`,`import spineagent`
|
|
175
|
+
> 永远零网络 SDK(真实 SDK 仅在选用对应 extra 时延迟 import)。reasoning / citations 等扩展本期丢弃。
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# spineagent
|
|
2
|
+
|
|
3
|
+
Spine 家族的**通用多 agent 协作框架**(见 [ADR 0001](../docs/adr/0001-spine-family-boundaries-and-dependency-direction.md))。
|
|
4
|
+
agent / tool / 编排 + **MCP / A2A** 等 agent 协议缝。依赖薄核 `corespine`,复用其缝元模式与
|
|
5
|
+
observability / config 形状;**默认路径离线可跑、import-clean、零网络 SDK**。
|
|
6
|
+
|
|
7
|
+
> 通用 ≠ 地基。真正的核是更薄的 `corespine`,spineagent 是它的兄弟消费者,**不**含任何 RAG 概念。
|
|
8
|
+
> 详见 [`CLAUDE.md`](CLAUDE.md) 宪章。
|
|
9
|
+
|
|
10
|
+
## 缝的元模式(家族统一)
|
|
11
|
+
|
|
12
|
+
每条缝都长一个样,核心 import 零 SDK、离线可跑:
|
|
13
|
+
|
|
14
|
+
**Protocol + 离线确定性默认 + `Registry` 工厂 + 参数化 conformance**
|
|
15
|
+
|
|
16
|
+
## 里面有什么
|
|
17
|
+
|
|
18
|
+
| 模块 | 原语 |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `agent/agent.py` | `Agent` 协议 + `LlmAgent`(走 corespine `LLMProvider`,离线用 `MockProvider`)/ `FunctionAgent`(纯函数节点);步级 trace 只记元数据 |
|
|
21
|
+
| `llm/provider.py` 等 | 真实 LLM provider 适配器(挂在 corespine `LLMProvider` 缝后面;**对外统一 OpenAI chat-completions 形状**):`OpenAICompatProvider`(`openai` SDK + `base_url`,一个吃下 OpenAI/Azure/Together/Groq/DeepSeek/Mistral/xAI/Qwen/Moonshot/Ollama/vLLM/OpenRouter/LiteLLM… 全部 OpenAI 兼容端点)+ 非 OpenAI 原生适配器把 native 转成 OpenAI 形状:`AnthropicProvider`(默认 `claude-opus-4-8`)/ `CohereProvider` / `GeminiProvider` / `BedrockConverseProvider`。`llm_providers` Registry(mock/openai/anthropic/cohere/gemini/bedrock)。各走可选 extra 延迟 import,离线默认仍是 `MockProvider`,各用原生 SDK 不 shim |
|
|
22
|
+
| `agent/policy.py` | `ToolPolicy` 协议 + 离线确定性默认 `SyntaxToolPolicy`(按 `<tool>: <arg>` 语法 + 工具名集合确定性路由,**不假装 LLM 推理**)+ `tool_policies` Registry(`llm` 位留真实推理式接入) |
|
|
23
|
+
| `agent/tool_using.py` | `ToolUsingAgent`:**离线确定性**多步循环(`SyntaxToolPolicy` 按 `<tool>: <arg>` 语法路由),带 `max_steps` 守卫;实现 `Agent` 协议 |
|
|
24
|
+
| `agent/function_calling.py` | `FunctionCallingAgent`:**真 LLM function-calling** 多步循环——把 `FunctionTool` schema 喂给 `chat(tools=)`,模型回 tool_calls → 执行 → 以 OpenAI tool 角色喂回 → 再 chat,直到出文本或触顶;底层换任意 provider 不改一行。实现 `Agent` 协议 |
|
|
25
|
+
| `tools/function_tool.py` | `FunctionTool`(带 JSON-schema、接 dict 参数的结构化工具)+ `@function_tool` 装饰器(从签名/注解/docstring 自动推 schema) |
|
|
26
|
+
| `agent/as_tool.py` | `AgentTool`:把一个 `Agent` 桥成 `Tool`,让督导 agent 通过工具调用把子任务派给专精子 agent(**分层 / 督导式多 agent**,可层层嵌套) |
|
|
27
|
+
| `tools/tool.py` | `Tool` 协议 + `EchoTool` / `CalcTool`(安全算术求值);结果带 provenance。`tool_registry`:spec 选工具 + **entry-point 第三方工具自动发现**(group `corespine.tool`)。**运行时可把 ragspine RAG 插为一个 Tool**(见下) |
|
|
28
|
+
| `orchestration/coordinator.py` | `Coordinator`:把多个 agent **顺序 / 并行 / 流水线**(output→input 链式)跑、保序收集 `AgentResult`;**弹性容错**(`resilient=True`)把单 agent 异常归一为家族错误 dict 塞进 `AgentResult.error`、一个坏 agent 不炸整批 |
|
|
29
|
+
| `orchestration/chain.py` | `ChainAgent`:把一串 agent 串成**单个 `Agent`**(流水线即一等可组合单元),可再进 `Coordinator` / 被 `AgentTool` 当工具 / 套 chain |
|
|
30
|
+
| `protocol/mcp/seam.py` | `McpClient` / `McpServer` 协议 + `OfflineMcpStub`(进程内回环)+ **`McpClientTool`(把 MCP 工具桥成 `Tool`)** + 真实 SDK 经 `[mcp]` extra 延迟 import |
|
|
31
|
+
| `protocol/a2a/seam.py` | `A2AAgent` 协议 + `OfflineA2AStub`(进程内回环)+ **`A2AAgentAdapter`(把 A2A agent 桥成 `Agent`)** + 真实 `a2a-sdk` 经 `[a2a]` extra 延迟 import |
|
|
32
|
+
| `conformance.py` | 本包绑定的不变量:`AGENT_INVARIANTS`(步产出 / provenance / 隐私 trace)、`TOOL_INVARIANTS`(结果 provenance)、`POLICY_INVARIANTS`(决策形状 / 不幻觉工具 / 可终止 / 纯函数) |
|
|
33
|
+
|
|
34
|
+
## 运行时组合 ragspine(ADR 0001 D4b)
|
|
35
|
+
|
|
36
|
+
spineagent **不**在包层面依赖 ragspine。但可在**运行时**把 ragspine 的 RAG 检索包成一个实现了
|
|
37
|
+
`Tool`(或 MCP server)协议的适配器,插给某个 agent 调用——松耦合、可选,方向只能 spineagent→ragspine。
|
|
38
|
+
本包 `dependencies` 永远不含 ragspine,也绝不在默认路径 import 它。
|
|
39
|
+
|
|
40
|
+
第三方工具(含 ragspine RAG)还可经 entry-point 在 `corespine.tool` group 下注册工具工厂,即被
|
|
41
|
+
`tool_registry.make / names` 自动发现、零改本包代码组合进 agent;而它们仍须过 `TOOL_INVARIANTS`
|
|
42
|
+
conformance 才算数——「敢放手让第三方填广度,却让脊柱不变量烂不掉」。
|
|
43
|
+
|
|
44
|
+
## 本地开发(始终从包根)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv venv .venv
|
|
48
|
+
VIRTUAL_ENV="$(pwd)/.venv" uv pip install -e ../corespine
|
|
49
|
+
VIRTUAL_ENV="$(pwd)/.venv" uv pip install -e ".[dev]"
|
|
50
|
+
.venv/bin/python -m pytest -q
|
|
51
|
+
.venv/bin/python -c "import spineagent"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 30 秒上手
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from corespine import MockProvider, InProcessPrivacyTraceSink
|
|
58
|
+
from spineagent import LlmAgent, FunctionAgent, Coordinator, EchoTool, OfflineMcpStub
|
|
59
|
+
from spineagent.protocol.mcp.seam import McpTool
|
|
60
|
+
|
|
61
|
+
# 一个离线 agent:走 corespine 的确定性 MockProvider,跑单步
|
|
62
|
+
agent = LlmAgent("planner", MockProvider())
|
|
63
|
+
print(agent.step("列个计划").output) # 确定性、可复现
|
|
64
|
+
|
|
65
|
+
# 多 agent 编排:顺序 / 并行跑同一任务,保序收集
|
|
66
|
+
coord = Coordinator([FunctionAgent("a", lambda t: f"a:{t}"),
|
|
67
|
+
FunctionAgent("b", lambda t: f"b:{t}")])
|
|
68
|
+
print([r.output for r in coord.run_parallel("go")]) # ['a:go', 'b:go']
|
|
69
|
+
print([r.output for r in coord.run_pipeline("go")]) # ['a:go', 'b:a:go'](链式:上一个输出喂下一个)
|
|
70
|
+
|
|
71
|
+
# 弹性容错:坏 agent 不炸整批,异常归一为结构化 error,批次照常跑完
|
|
72
|
+
flaky = Coordinator([FunctionAgent("ok", lambda t: t),
|
|
73
|
+
FunctionAgent("bad", lambda t: 1 / 0)])
|
|
74
|
+
print([(r.agent, r.ok) for r in flaky.run_sequential("go", resilient=True)]) # [('ok', True), ('bad', False)]
|
|
75
|
+
|
|
76
|
+
# 工具:带 provenance 的结果
|
|
77
|
+
print(EchoTool().run("hi").tool) # 'echo'
|
|
78
|
+
|
|
79
|
+
# 工具缝注册表:按 spec 选工具(大小写/留白不敏感),第三方还能经 entry-point 自动发现
|
|
80
|
+
from spineagent import tool_registry
|
|
81
|
+
print(tool_registry.make("calc").run("6/2").output) # '3'
|
|
82
|
+
print("calc" in tool_registry.names()) # True
|
|
83
|
+
|
|
84
|
+
# 会用工具的多步 agent:离线确定性 policy 按 `<tool>: <arg>` 语法路由,$prev 把上一步输出喂回
|
|
85
|
+
from spineagent import ToolUsingAgent, SyntaxToolPolicy, CalcTool
|
|
86
|
+
solver = ToolUsingAgent("solver", SyntaxToolPolicy(), [CalcTool()])
|
|
87
|
+
print(solver.step("calc: 2 + 3\ncalc: $prev * 2").output) # '10'(2+3=5,再 *2=10)
|
|
88
|
+
|
|
89
|
+
# 分层督导式多 agent:把子 agent 用 AgentTool 暴露成工具,督导 agent 通过工具调用派活给它
|
|
90
|
+
from spineagent import AgentTool
|
|
91
|
+
calculator = ToolUsingAgent("calculator", SyntaxToolPolicy(), [CalcTool()])
|
|
92
|
+
supervisor = ToolUsingAgent("supervisor", SyntaxToolPolicy(), [AgentTool(calculator)])
|
|
93
|
+
print(supervisor.step("calculator: calc: 2+3").output) # '5'(督导派给子 agent,子 agent 再用工具)
|
|
94
|
+
|
|
95
|
+
# MCP 离线回环:注册 + 调用,零网络
|
|
96
|
+
stub = OfflineMcpStub()
|
|
97
|
+
stub.register_tool(McpTool("upper"), lambda a: {"result": a["s"].upper()})
|
|
98
|
+
print(stub.call_tool("upper", {"s": "hi"})) # {'result': 'HI'}
|
|
99
|
+
|
|
100
|
+
# 跨缝组合:把上面那个 MCP 工具桥成 Tool,交给会用工具的 agent 在循环里驱动(零网络)
|
|
101
|
+
from spineagent import McpClientTool
|
|
102
|
+
shouter = ToolUsingAgent("shouter", SyntaxToolPolicy(), [McpClientTool("upper", stub, arg_key="s")])
|
|
103
|
+
print(shouter.step("upper: hi").output) # 'HI'
|
|
104
|
+
|
|
105
|
+
# 隐私 trace:步级只记元数据;塞正文会被 corespine 的 sink 直接拒绝
|
|
106
|
+
sink = InProcessPrivacyTraceSink()
|
|
107
|
+
agent.step("敏感任务", trace=sink) # 只记 agent 名 / 长度 / token 数
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 换上真实模型(可选 extra)
|
|
111
|
+
|
|
112
|
+
**对外统一 OpenAI chat-completions 形状**(LiteLLM 模式):无论后端是谁,`chat(messages, tools)`
|
|
113
|
+
都回 OpenAI 形状的 `ChatCompletion`(`choices[0].message.content/.tool_calls`、`finish_reason`、
|
|
114
|
+
`usage.prompt_tokens`…)。`LlmAgent` 全程只认 corespine 的 `LLMProvider` 协议,把 `MockProvider`
|
|
115
|
+
换成真实适配器即可,其余代码(agent / 编排 / 工具循环)一行不改:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install "spineagent[openai]" # OpenAI 及一切「OpenAI 兼容」端点
|
|
119
|
+
pip install "spineagent[anthropic]" # 或 [cohere] / [gemini] / [bedrock]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from spineagent import OpenAICompatProvider, AnthropicProvider, GeminiProvider, LlmAgent
|
|
124
|
+
|
|
125
|
+
# 一个适配器吃下所有 OpenAI 兼容端点:OpenAI / Azure / Together / Groq / DeepSeek / Mistral /
|
|
126
|
+
# xAI / 通义 Qwen / Moonshot / Ollama / vLLM / OpenRouter / LiteLLM …(换 base_url + model 即可)
|
|
127
|
+
gpt = LlmAgent("gpt", OpenAICompatProvider("gpt-4o"))
|
|
128
|
+
local = LlmAgent("local", OpenAICompatProvider("llama3", base_url="http://localhost:11434/v1"))
|
|
129
|
+
|
|
130
|
+
# 非 OpenAI 原生模型:原生适配器在内部转成 OpenAI 形状,用户无感
|
|
131
|
+
claude = LlmAgent("claude", AnthropicProvider()) # 默认 claude-opus-4-8
|
|
132
|
+
gemini = LlmAgent("gemini", GeminiProvider(model="gemini-2.5-flash"))
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
> 覆盖:**OpenAI 兼容生态(约 85% 主流市场)走 `OpenAICompatProvider` 一把梭**;真正非 OpenAI 形状的
|
|
136
|
+
> Anthropic / Cohere / Gemini / Bedrock 各有原生适配器,把 native 响应转成 OpenAI `ChatCompletion`
|
|
137
|
+
> (绝不把它们套进 OpenAI 形状 = 不 shim)。默认离线路径仍是 `MockProvider`,`import spineagent`
|
|
138
|
+
> 永远零网络 SDK(真实 SDK 仅在选用对应 extra 时延迟 import)。reasoning / citations 等扩展本期丢弃。
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 忽略规则 —— 注意 build context 是【家族根】 ~/workspace/spine,故下列路径相对家族根。
|
|
2
|
+
#
|
|
3
|
+
# BuildKit 的忽略文件解析顺序:先找 `<-f 指向的 Dockerfile>.dockerignore`,再找 context 根的
|
|
4
|
+
# `.dockerignore`。用本文件名 `deploy/.dockerignore` 时,需让它对构建生效(二选一):
|
|
5
|
+
# 1) 把它放/链到 context 根: ln -sf spineagent/deploy/.dockerignore ~/workspace/spine/.dockerignore
|
|
6
|
+
# 2) 或改名让 BuildKit 自动识别: spineagent/deploy/Dockerfile.dockerignore
|
|
7
|
+
# 即便不忽略也能正确构建(镜像内是一次干净的 uv 安装,下列产物只是死重 / 拖慢上下文)。
|
|
8
|
+
|
|
9
|
+
# 各包本地虚拟环境 / 缓存 / 构建产物(镜像内会重新安装,无需带入)。
|
|
10
|
+
**/.venv/
|
|
11
|
+
**/.git/
|
|
12
|
+
**/__pycache__/
|
|
13
|
+
**/*.pyc
|
|
14
|
+
**/.pytest_cache/
|
|
15
|
+
**/.ruff_cache/
|
|
16
|
+
**/.mypy_cache/
|
|
17
|
+
**/*.egg-info/
|
|
18
|
+
**/build/
|
|
19
|
+
**/dist/
|
|
20
|
+
|
|
21
|
+
# 家族根的文档与说明,运行镜像用不到。
|
|
22
|
+
docs/
|
|
23
|
+
README.md
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# spineagent 容器镜像 —— 构建需要【两个】兄弟包:被依赖的薄核 corespine + spineagent 自身。
|
|
4
|
+
# 因此 build context 必须是【家族根】 ~/workspace/spine(而非 spineagent 包根),这样才能
|
|
5
|
+
# 同时把 corespine 与 spineagent COPY 进来,先装薄核、再装 spineagent。
|
|
6
|
+
#
|
|
7
|
+
# 构建(context = 家族根,-f 指向本文件):
|
|
8
|
+
# docker build -f spineagent/deploy/Dockerfile -t spineagent:latest ~/workspace/spine
|
|
9
|
+
#
|
|
10
|
+
# 运行(默认 CMD 跑离线 demo,零网络;退出码即健康信号):
|
|
11
|
+
# docker run --rm spineagent:latest
|
|
12
|
+
#
|
|
13
|
+
# 忽略规则见同目录 .dockerignore(详见 deploy/README.md 的 .dockerignore 说明)。
|
|
14
|
+
|
|
15
|
+
# uv 官方镜像:自带 Python 3.11 + uv,贴合家族「uv 管环境」的约定。
|
|
16
|
+
FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim
|
|
17
|
+
|
|
18
|
+
WORKDIR /app
|
|
19
|
+
|
|
20
|
+
# 先放被依赖的薄核 corespine,再放 spineagent(顺序与下面的安装顺序一致)。
|
|
21
|
+
COPY corespine/ /app/corespine/
|
|
22
|
+
COPY spineagent/ /app/spineagent/
|
|
23
|
+
|
|
24
|
+
# 先装 corespine —— 这样装 spineagent 时它的 `corespine` 依赖已被满足(离线、无需 PyPI);
|
|
25
|
+
# 再装 spineagent[dev]。--system 直接装进容器系统 Python,容器内无需再建 venv。
|
|
26
|
+
RUN uv pip install --system -e /app/corespine \
|
|
27
|
+
&& uv pip install --system -e "/app/spineagent[dev]"
|
|
28
|
+
|
|
29
|
+
# 默认跑 spineagent 的一键离线 demo(零网络),成功打印 "spineagent OK"。
|
|
30
|
+
CMD ["python", "/app/spineagent/examples/quickstart.py"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 部署 —— spineagent 容器镜像
|
|
2
|
+
|
|
3
|
+
镜像构建需要【两个】兄弟包:被依赖的薄核 `corespine` + `spineagent` 自身。所以镜像的
|
|
4
|
+
**build context 是家族根** `~/workspace/spine`(里面并排放着 `corespine/` 与 `spineagent/`),
|
|
5
|
+
而 `-f` 指向本目录的 `Dockerfile`。镜像内先装 `corespine`、再装 `spineagent`,默认 `CMD`
|
|
6
|
+
跑 spineagent 的一键离线 demo(零网络)。
|
|
7
|
+
|
|
8
|
+
## 构建
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# context = 家族根;-f 指向 spineagent 包内的 Dockerfile
|
|
12
|
+
docker build -f spineagent/deploy/Dockerfile -t spineagent:latest ~/workspace/spine
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 运行
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
docker run --rm spineagent:latest # 跑离线 demo,成功打印 "spineagent OK"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 为什么 context 是家族根而不是包根
|
|
22
|
+
|
|
23
|
+
`spineagent` 在 `dependencies` 里写着 `corespine`,但 `corespine` 还没发到 PyPI(本地是 uv
|
|
24
|
+
path source 指向 `../corespine`)。Docker 的 `COPY` 只能取 **build context 内**的文件,而包根
|
|
25
|
+
`spineagent/` 里看不到兄弟目录 `corespine/`。把 context 抬到家族根,镜像才能同时 `COPY` 进两个
|
|
26
|
+
包,并按 `corespine → spineagent` 的顺序可编辑安装 —— 与本地 `make install` 完全一致。
|
|
27
|
+
|
|
28
|
+
## 关于 `.dockerignore`
|
|
29
|
+
|
|
30
|
+
忽略规则在 `deploy/.dockerignore`,其路径相对 **context 根(家族根)**。BuildKit 解析忽略文件的
|
|
31
|
+
顺序是:先找 `<-f 指向的 Dockerfile>.dockerignore`,再找 context 根的 `.dockerignore` —— 因此
|
|
32
|
+
放在 `deploy/` 下的这份默认不会被自动应用。要让它生效,二选一:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# 1) 链到 context 根(推荐,保持单一来源)
|
|
36
|
+
ln -sf spineagent/deploy/.dockerignore ~/workspace/spine/.dockerignore
|
|
37
|
+
|
|
38
|
+
# 2) 或改名让 BuildKit 按 Dockerfile 旁路自动识别
|
|
39
|
+
cp spineagent/deploy/.dockerignore spineagent/deploy/Dockerfile.dockerignore
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
即便不应用忽略文件,构建结果依然正确:镜像内做的是一次干净的 `uv pip install`,COPY 进来的
|
|
43
|
+
`.venv/`、缓存、构建产物都不会被使用,只是徒增上下文体积、拖慢构建。
|