psi-agent 0.0.1a2__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.
- psi_agent-0.0.1a2/.git +1 -0
- psi_agent-0.0.1a2/.github/workflows/ci.yml +90 -0
- psi_agent-0.0.1a2/.gitignore +67 -0
- psi_agent-0.0.1a2/CLAUDE.md +313 -0
- psi_agent-0.0.1a2/LICENSE.md +660 -0
- psi_agent-0.0.1a2/PKG-INFO +18 -0
- psi_agent-0.0.1a2/README.md +169 -0
- psi_agent-0.0.1a2/SPEC.md +573 -0
- psi_agent-0.0.1a2/examples/simple_example/AGENT.md +5 -0
- psi_agent-0.0.1a2/examples/simple_example/skills/example/SKILL.md +14 -0
- psi_agent-0.0.1a2/examples/simple_example/systems/builder.py +39 -0
- psi_agent-0.0.1a2/examples/simple_example/tools/bash.py +32 -0
- psi_agent-0.0.1a2/examples/simple_example/tools/read_file.py +26 -0
- psi_agent-0.0.1a2/pyproject.toml +57 -0
- psi_agent-0.0.1a2/src/psi_agent/__init__.py +1 -0
- psi_agent-0.0.1a2/src/psi_agent/ai/__init__.py +5 -0
- psi_agent-0.0.1a2/src/psi_agent/ai/openai/__init__.py +197 -0
- psi_agent-0.0.1a2/src/psi_agent/channel/__init__.py +5 -0
- psi_agent-0.0.1a2/src/psi_agent/channel/tui/__init__.py +117 -0
- psi_agent-0.0.1a2/src/psi_agent/common/__init__.py +5 -0
- psi_agent-0.0.1a2/src/psi_agent/common/protocol.py +49 -0
- psi_agent-0.0.1a2/src/psi_agent/session/__init__.py +522 -0
- psi_agent-0.0.1a2/src/psi_agent/workspace/__init__.py +556 -0
- psi_agent-0.0.1a2/tests/integration/__init__.py +1 -0
- psi_agent-0.0.1a2/tests/integration/test_ai_session.py +206 -0
- psi_agent-0.0.1a2/tests/integration/test_session_channel.py +289 -0
- psi_agent-0.0.1a2/tests/unit/__init__.py +1 -0
- psi_agent-0.0.1a2/tests/unit/test_ai.py +286 -0
- psi_agent-0.0.1a2/tests/unit/test_channel.py +176 -0
- psi_agent-0.0.1a2/tests/unit/test_protocol.py +109 -0
- psi_agent-0.0.1a2/tests/unit/test_session.py +870 -0
- psi_agent-0.0.1a2/tests/unit/test_workspace.py +396 -0
- psi_agent-0.0.1a2/uv.lock +842 -0
psi_agent-0.0.1a2/.git
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gitdir: /home/hzhangxyz/Cloud/Desktop/psi/.git/worktrees/dev
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
lint-and-unit-test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
13
|
+
fail-fast: false
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # Required for hatch-vcs versioning
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
version: "latest"
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies with Python ${{ matrix.python-version }}
|
|
27
|
+
run: uv sync --all-extras --python ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Run ruff check
|
|
30
|
+
run: uv run ruff check examples/ tests/ src/
|
|
31
|
+
|
|
32
|
+
- name: Run ruff format check
|
|
33
|
+
run: uv run ruff format examples/ tests/ src/ --check
|
|
34
|
+
|
|
35
|
+
- name: Run ty type check
|
|
36
|
+
run: uv run ty check examples/ tests/ src/
|
|
37
|
+
|
|
38
|
+
- name: Run pytest (unit tests only)
|
|
39
|
+
run: uv run pytest tests/unit/ -v
|
|
40
|
+
|
|
41
|
+
integration-test:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
needs: lint-and-unit-test
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0 # Required for hatch-vcs versioning
|
|
50
|
+
|
|
51
|
+
- name: Install uv
|
|
52
|
+
uses: astral-sh/setup-uv@v5
|
|
53
|
+
with:
|
|
54
|
+
version: "latest"
|
|
55
|
+
|
|
56
|
+
- name: Install dependencies
|
|
57
|
+
run: uv sync --all-extras --python 3.12
|
|
58
|
+
|
|
59
|
+
- name: Run integration tests
|
|
60
|
+
env:
|
|
61
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
62
|
+
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
|
63
|
+
OPENAI_MODEL: ${{ secrets.OPENAI_MODEL }}
|
|
64
|
+
run: uv run pytest tests/integration/ -v
|
|
65
|
+
|
|
66
|
+
publish:
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
needs: [lint-and-unit-test, integration-test]
|
|
69
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
70
|
+
environment: pypi
|
|
71
|
+
|
|
72
|
+
permissions:
|
|
73
|
+
id-token: write # Required for trusted publishing
|
|
74
|
+
|
|
75
|
+
steps:
|
|
76
|
+
- name: Checkout
|
|
77
|
+
uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
fetch-depth: 0 # Required for hatch-vcs versioning
|
|
80
|
+
|
|
81
|
+
- name: Install uv
|
|
82
|
+
uses: astral-sh/setup-uv@v5
|
|
83
|
+
with:
|
|
84
|
+
version: "latest"
|
|
85
|
+
|
|
86
|
+
- name: Build package
|
|
87
|
+
run: uv build
|
|
88
|
+
|
|
89
|
+
- name: Publish to PyPI
|
|
90
|
+
uses: pypa/gh-action-pypi-publish@release/v4
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
ENV/
|
|
27
|
+
|
|
28
|
+
# uv
|
|
29
|
+
.uv/
|
|
30
|
+
|
|
31
|
+
# Sockets
|
|
32
|
+
*.sock
|
|
33
|
+
|
|
34
|
+
# Session state databases
|
|
35
|
+
example_workspace/state/*.db
|
|
36
|
+
*.db
|
|
37
|
+
|
|
38
|
+
# Workspace overlay directories
|
|
39
|
+
*.upper/
|
|
40
|
+
*.work/
|
|
41
|
+
*.lower/
|
|
42
|
+
|
|
43
|
+
# Manifest (runtime generated)
|
|
44
|
+
manifest.json
|
|
45
|
+
|
|
46
|
+
# IDE
|
|
47
|
+
.vscode/
|
|
48
|
+
.idea/
|
|
49
|
+
*.swp
|
|
50
|
+
*.swo
|
|
51
|
+
*~
|
|
52
|
+
|
|
53
|
+
# OS
|
|
54
|
+
.DS_Store
|
|
55
|
+
Thumbs.db
|
|
56
|
+
|
|
57
|
+
# Logs
|
|
58
|
+
*.log
|
|
59
|
+
|
|
60
|
+
# Test coverage
|
|
61
|
+
.coverage
|
|
62
|
+
coverage.xml
|
|
63
|
+
htmlcov/
|
|
64
|
+
.pytest_cache/
|
|
65
|
+
|
|
66
|
+
# SquashFS images
|
|
67
|
+
*.sqfs
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Psi Agent Platform
|
|
2
|
+
|
|
3
|
+
一个绿色可移植的 AI Agent 平台,由独立的模块通过 Unix socket 通信组成。
|
|
4
|
+
|
|
5
|
+
## 快速开始
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. 启动 LLM Caller
|
|
9
|
+
uv run psi-ai-openai --session-socket ./ai.sock \
|
|
10
|
+
--model qwen3.5-plus \
|
|
11
|
+
--api-key $API_KEY \
|
|
12
|
+
--base-url https://coding.dashscope.aliyuncs.com/v1
|
|
13
|
+
|
|
14
|
+
# 2. 启动 Session
|
|
15
|
+
uv run psi-session --workspace ./examples/simple_example \
|
|
16
|
+
--channel-socket ./channel.sock \
|
|
17
|
+
--ai-socket ./ai.sock
|
|
18
|
+
|
|
19
|
+
# 3. 启动 TUI
|
|
20
|
+
uv run psi-channel-tui --session-socket ./channel.sock
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 项目结构
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/
|
|
27
|
+
├── psi_agent/ # Psi Agent 包
|
|
28
|
+
│ ├── session/ # ReAct 循环引擎(核心)
|
|
29
|
+
│ ├── channel/tui/ # TUI 用户界面
|
|
30
|
+
│ ├── ai/openai/ # OpenAI 兼容 LLM Caller
|
|
31
|
+
│ ├── workspace/ # SquashFS/OverlayFS 管理器
|
|
32
|
+
│ └── common/ # 共享协议
|
|
33
|
+
|
|
34
|
+
examples/
|
|
35
|
+
└── simple_example/ # 示例 workspace
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 核心概念
|
|
39
|
+
|
|
40
|
+
### Workspace
|
|
41
|
+
|
|
42
|
+
一个 workspace 是一个完整的 agent,包含:
|
|
43
|
+
- `AGENT.md`: 身份描述
|
|
44
|
+
- `tools/`: 工具(每个 .py 导出 `async run(params, workspace_path)`)
|
|
45
|
+
- `skills/`: 技能(SKILL.md 格式)
|
|
46
|
+
- `systems/builder.py`: 系统提示词构造器
|
|
47
|
+
|
|
48
|
+
### 通信协议
|
|
49
|
+
|
|
50
|
+
所有模块通过 Unix socket 通信,使用 JSON Lines 格式:
|
|
51
|
+
|
|
52
|
+
- Session ↔ AI: OpenAI Chat Completion 格式(流式)
|
|
53
|
+
- Session ↔ Channel: `{"role": "user/assistant", "content": "..."}`
|
|
54
|
+
|
|
55
|
+
### ReAct 循环
|
|
56
|
+
|
|
57
|
+
Session 接收用户消息后:
|
|
58
|
+
1. 构建 system prompt(通过 builder.py)
|
|
59
|
+
2. 调用 LLM
|
|
60
|
+
3. 如果有 tool_calls → 执行工具 → 继续循环
|
|
61
|
+
4. 如果无 tool_calls → 返回文本给 Channel
|
|
62
|
+
|
|
63
|
+
## 设计原则
|
|
64
|
+
|
|
65
|
+
### Let it Crash
|
|
66
|
+
|
|
67
|
+
核心原则:**除了外部网络故障,所有错误都应该让进程 crash**。
|
|
68
|
+
|
|
69
|
+
**分类说明:**
|
|
70
|
+
|
|
71
|
+
| 类型 | 处理方式 | 示例 |
|
|
72
|
+
|------|----------|------|
|
|
73
|
+
| 外部用户连接断开 | 优雅处理 | Channel断开(用户Ctrl+C/Ctrl+D) |
|
|
74
|
+
| 外部LLM API问题 | 优雅处理或crash | API超时、认证错误(让OpenAI SDK报错) |
|
|
75
|
+
| 组件间连接异常 | **crash** | AI Caller断开(未发送done)、Session断开 |
|
|
76
|
+
| 配置错误 | **crash** | API key缺失、文件不存在、参数无效 |
|
|
77
|
+
| 数据错误 | **crash** | JSON解析失败、schema不匹配 |
|
|
78
|
+
| 业务逻辑错误 | **crash** | 工具不存在、版本链断裂 |
|
|
79
|
+
|
|
80
|
+
**具体规则:**
|
|
81
|
+
|
|
82
|
+
1. **启动时配置错误**: 不要额外检查,让系统自然crash并给出错误信息
|
|
83
|
+
- 例:API key缺失 → 让OpenAI SDK初始化时报错,不要提前检查并sys.exit()
|
|
84
|
+
|
|
85
|
+
2. **组件间连接**: 假设连接正常,异常时直接crash
|
|
86
|
+
- 例:AI Caller发送流式响应但未发送`done`标记就断开 → Session应该crash(`RuntimeError`)
|
|
87
|
+
|
|
88
|
+
3. **工具执行错误**: **不crash**,返回错误给LLM
|
|
89
|
+
- 工具级别错误(如命令不存在)应返回`{"success": False, "error": "..."}`
|
|
90
|
+
- Session继续运行,让LLM决定下一步
|
|
91
|
+
|
|
92
|
+
4. **用户主动退出**: **优雅处理**
|
|
93
|
+
- KeyboardInterrupt/EOFError → 打印退出信息,清理资源,退出
|
|
94
|
+
|
|
95
|
+
5. **可选功能缺失**: 优雅处理(warning/return),不crash
|
|
96
|
+
- tools目录不存在 → warning并继续
|
|
97
|
+
- skills目录不存在 → warning并继续
|
|
98
|
+
- snapshot无改动 → warning并return
|
|
99
|
+
|
|
100
|
+
### 其他原则
|
|
101
|
+
|
|
102
|
+
- **绿色可移植**: workspace 可整体复制/移动
|
|
103
|
+
- **组件化**: 独立进程,socket 通信
|
|
104
|
+
- **不考虑兼容性**: 目前是第一版,不需要向后兼容,可直接删除旧代码
|
|
105
|
+
|
|
106
|
+
## 日志
|
|
107
|
+
|
|
108
|
+
所有模块使用 loguru:
|
|
109
|
+
```
|
|
110
|
+
2026-04-20 18:03:42 | INFO | session | Session initialized | id=test
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
控制级别:`--log-level DEBUG/INFO/WARNING/ERROR`
|
|
114
|
+
|
|
115
|
+
**TUI 特殊处理**:默认 `WARNING` 级别,避免日志干扰界面。
|
|
116
|
+
|
|
117
|
+
## 数据模型
|
|
118
|
+
|
|
119
|
+
使用 pydantic BaseModel:
|
|
120
|
+
- `LLMRequest`/`LLMResponse`: LLM 通信
|
|
121
|
+
- `ToolResult`: 工具结果
|
|
122
|
+
- `UserMessage`/`AssistantMessage`: Channel ↔ Session 通信
|
|
123
|
+
- `DeltaInfo`/`Manifest`/`MountInfo`: Workspace 元数据
|
|
124
|
+
|
|
125
|
+
**模型使用约定**:
|
|
126
|
+
- 所有协议通信应使用定义的模型(如 Channel 使用 `UserMessage`/`AssistantMessage` 而非 raw dict)
|
|
127
|
+
- 内部方法传递完整对象而非逐个提取字段
|
|
128
|
+
|
|
129
|
+
## Session 默认行为
|
|
130
|
+
|
|
131
|
+
Session 默认使用自动生成的 uuid 作为 session_id,因此:
|
|
132
|
+
- **不提供 session_id 时**:每次启动都是新 session,无历史记录
|
|
133
|
+
- **提供 session_id 时**:继续该 session 的历史(如 `--session-id main`)
|
|
134
|
+
|
|
135
|
+
这是为了让"默认行为无历史"更符合直觉。
|
|
136
|
+
|
|
137
|
+
## 开发
|
|
138
|
+
|
|
139
|
+
本仓库使用 **ruff** (lint/格式化) 和 **ty** (类型检查) 进行代码质量控制。
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Lint 检查
|
|
143
|
+
uv run ruff check examples/ tests/ src/
|
|
144
|
+
uv run ruff check --fix examples/ tests/ src/
|
|
145
|
+
|
|
146
|
+
# 格式检查
|
|
147
|
+
uv run ruff format examples/ tests/ src/ --check
|
|
148
|
+
|
|
149
|
+
# 类型检查
|
|
150
|
+
uv run ty check examples/ tests/ src/
|
|
151
|
+
|
|
152
|
+
# 测试
|
|
153
|
+
uv run pytest tests/unit/ -v
|
|
154
|
+
|
|
155
|
+
# 集成测试(需要设置环境变量)
|
|
156
|
+
export OPENAI_API_KEY="your-api-key"
|
|
157
|
+
export OPENAI_BASE_URL="https://api.openai.com/v1"
|
|
158
|
+
export OPENAI_MODEL="gpt-4o-mini"
|
|
159
|
+
uv run pytest tests/integration/ -v
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**CI/CD**: GitHub Actions 自动运行 ruff check、ruff format、ty 和测试(`.github/workflows/test.yml`)。
|
|
163
|
+
|
|
164
|
+
**CLI**: 所有命令使用 tyro 实现,参数通过 dataclass 定义。
|
|
165
|
+
|
|
166
|
+
## 常用命令
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 安装依赖
|
|
170
|
+
uv sync
|
|
171
|
+
|
|
172
|
+
# 运行测试
|
|
173
|
+
uv run psi-session --workspace ./examples/simple_example ...
|
|
174
|
+
|
|
175
|
+
# Workspace 管理(使用 FUSE,无需 root)
|
|
176
|
+
# 先安装依赖: sudo apt install squashfuse fuse-overlayfs squashfs-tools
|
|
177
|
+
psi-workspace-create ./examples/simple_example base.sqfs --tag base
|
|
178
|
+
psi-workspace-mount base.sqfs ./workspace
|
|
179
|
+
psi-workspace-snapshot ./workspace v2.sqfs --tag v1
|
|
180
|
+
psi-workspace-umount ./workspace
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Python API
|
|
184
|
+
|
|
185
|
+
所有模块同时提供 Python function 接口:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from psi_agent.session import run_session
|
|
189
|
+
from psi_agent.ai.openai import run_ai
|
|
190
|
+
from psi_agent.channel.tui import run_channel
|
|
191
|
+
from psi_agent.workspace import run_create, run_mount, run_umount, run_snapshot
|
|
192
|
+
|
|
193
|
+
# 启动 AI Caller
|
|
194
|
+
await run_ai(
|
|
195
|
+
session_socket="./ai.sock",
|
|
196
|
+
model="qwen3.5-plus",
|
|
197
|
+
api_key="...",
|
|
198
|
+
base_url="https://coding.dashscope.aliyuncs.com/v1",
|
|
199
|
+
log_level="INFO"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# 启动 Session
|
|
203
|
+
await run_session(
|
|
204
|
+
workspace_path="./examples/simple_example",
|
|
205
|
+
channel_socket="./channel.sock",
|
|
206
|
+
ai_socket="./ai.sock",
|
|
207
|
+
session_id="main",
|
|
208
|
+
log_level="INFO"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# 启动 TUI
|
|
212
|
+
await run_channel(session_socket="./channel.sock")
|
|
213
|
+
|
|
214
|
+
# Workspace 管理
|
|
215
|
+
await run_create("./examples/simple_example", "base.sqfs", tag="base")
|
|
216
|
+
await run_mount("base.sqfs", "./workspace")
|
|
217
|
+
await run_snapshot("./workspace", "v2.sqfs", tag="v1")
|
|
218
|
+
await run_umount("./workspace")
|
|
219
|
+
|
|
220
|
+
# Workspace 管理(需要 root)
|
|
221
|
+
await run_mount("agent.sqfs", "./workspace")
|
|
222
|
+
await run_unmount("./workspace")
|
|
223
|
+
await run_snapshot("./workspace", "new.sqfs", description="v2")
|
|
224
|
+
run_list("./workspace")
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 详细规格
|
|
228
|
+
|
|
229
|
+
见 `SPEC.md`。
|
|
230
|
+
|
|
231
|
+
## 代码风格
|
|
232
|
+
|
|
233
|
+
### 类设计
|
|
234
|
+
|
|
235
|
+
- **内部状态用私有属性**: 类内部状态用 `_` 前缀(如 `_messages`, `_tools`, `_workspace_path`)
|
|
236
|
+
- **配置用 Pydantic**: 配置参数封装在 `SessionConfig` 等 Pydantic model 中
|
|
237
|
+
- **配置与实现分离**: 配置类只存参数,业务类接收配置对象
|
|
238
|
+
|
|
239
|
+
### 函数设计
|
|
240
|
+
|
|
241
|
+
- **辅助函数独立**: 通用辅助函数独立定义(如 `_is_valid_tool_call_name`, `_load_python_module`)
|
|
242
|
+
- **函数有单一职责**: 每个函数只做一件事,名字描述职责
|
|
243
|
+
- **缓存复用**: 重复加载的资源缓存(如 `_builder_module`)
|
|
244
|
+
- **参数类型一致**: 私有方法参数类型应统一(如全部用 `Path` 而非混用 `str` 和 `Path`)
|
|
245
|
+
- **参数命名一致**: 内部变量名与参数名保持一致(如 `source_dir` → `source_dir`,不要改成 `source`)
|
|
246
|
+
- **数据模型统一**: 同一模块内的相似操作应使用相同的数据模型(如 `_handle_stream` 和 `_handle_non_stream` 都用 `LLMRequest` 对象)
|
|
247
|
+
- **传递对象而非字段**: 内部方法应传递完整的 request/response 对象,而非逐个提取字段传递(避免 adhoc 参数列表)
|
|
248
|
+
- **接口简洁**:
|
|
249
|
+
- 数据处理放在调用方而非被调用方(如JSON序列化在发送方完成)
|
|
250
|
+
- 类型转换尽量靠近数据源(如Path转换在参数入口处完成)
|
|
251
|
+
- **避免过度封装**: 简单逻辑不需要额外抽象,三行重复代码比过早抽象更好
|
|
252
|
+
|
|
253
|
+
### 代码组织
|
|
254
|
+
|
|
255
|
+
- **模块顶部 import**: 所有 import 放文件顶部,不在函数内部 import
|
|
256
|
+
- **类型注解**: 所有函数参数和返回值有类型注解
|
|
257
|
+
- **docstring 简洁**: docstring 只说明功能,不写冗余说明
|
|
258
|
+
|
|
259
|
+
### 日志风格
|
|
260
|
+
|
|
261
|
+
- **格式统一**: `logger.info("Action | key={value}")` 格式,用 `|` 分隔
|
|
262
|
+
- **级别合理**:
|
|
263
|
+
- `INFO`: 重要状态变化(初始化、连接、完成)
|
|
264
|
+
- `DEBUG`: 详细过程(参数、中间状态)
|
|
265
|
+
- `WARNING`: 预期内的异常情况
|
|
266
|
+
- `ERROR`: 错误和失败
|
|
267
|
+
|
|
268
|
+
### 其他约定
|
|
269
|
+
|
|
270
|
+
- **避免 ad-hoc**: 不写重复逻辑,提取为辅助函数
|
|
271
|
+
- **f-string 有变量**: f-string 必须有插值,否则用普通字符串
|
|
272
|
+
- **is 比较**: `True/False/None` 用 `is` 比较,不用 `==`
|
|
273
|
+
|
|
274
|
+
### 命名风格
|
|
275
|
+
|
|
276
|
+
#### CLI 参数命名
|
|
277
|
+
|
|
278
|
+
- **snake_case**: 所有 CLI 参数使用 snake_case(如 `channel_socket`, `log_level`)
|
|
279
|
+
- **位置参数**: 简短单词(如 `workspace`, `model`, `output`)
|
|
280
|
+
- **socket 参数**: `*_socket` 格式(如 `channel_socket`, `ai_socket`, `session_socket`)
|
|
281
|
+
- **log_level**: 所有模块统一有 `log_level` 参数
|
|
282
|
+
|
|
283
|
+
#### 文件命名
|
|
284
|
+
|
|
285
|
+
- **模块目录**: `psi_agent/<name>` 格式(如 `psi_agent/session`, `psi_agent/ai`, `psi_agent/channel`, `psi_agent/workspace`)
|
|
286
|
+
- **Python 文件**: snake_case(如 `builder.py`, `protocol.py`)
|
|
287
|
+
- **配置文件**: 小写无扩展名约定(如 `AGENT.md`, `SKILL.md`)
|
|
288
|
+
|
|
289
|
+
#### 变量命名
|
|
290
|
+
|
|
291
|
+
- **私有属性**: `_` 前缀(如 `_messages`, `_tools`, `_config`)
|
|
292
|
+
- **常量**: UPPER_CASE 或 snake_case(如 `MAX_ITERATIONS` 或 `max_iterations`)
|
|
293
|
+
- **函数**: snake_case(如 `load_tools`, `build_system_prompt`)
|
|
294
|
+
- **类**: PascalCase(如 `Session`, `SessionConfig`, `WorkspaceManager`)
|
|
295
|
+
|
|
296
|
+
#### Socket 命名
|
|
297
|
+
|
|
298
|
+
**变量命名(代码中):**
|
|
299
|
+
- `session_socket`: AI Caller 监听的 socket(Session 连接到此)
|
|
300
|
+
- `channel_socket`: Session 监听的 socket(Channel 连接到此)
|
|
301
|
+
- `ai_socket`: Session 连接 AI 的 socket(与 AI 的 `session_socket` 对应)
|
|
302
|
+
|
|
303
|
+
**文件命名(示例和测试):**
|
|
304
|
+
- `ai.sock`: AI Caller socket 文件
|
|
305
|
+
- `channel.sock`: Channel/Session socket 文件
|
|
306
|
+
|
|
307
|
+
**路径原则:**
|
|
308
|
+
- 生产环境:相对路径(如 `./channel.sock`, `./ai.sock`)
|
|
309
|
+
- 测试环境:pytest `tmp_path` fixture(如 `tmp_path / "channel.sock"`)
|
|
310
|
+
|
|
311
|
+
#### 数据库/JSON 字段
|
|
312
|
+
|
|
313
|
+
- **snake_case**: 与 Python 保持一致(如 `session_id`, `created_at`)
|