天枢 · Tianshu
· ───────────── ◆
│ │
· ───────────── ·
· ── · ── ·
Tianshu (天枢, Celestial Pivot) is the first star of the Big Dipper — the fixed point around which the northern sky revolves.
An AI code agent that thinks, plans, and acts. Give it a task; watch the loop turn.
天枢(天上的枢轴)是北斗七星第一星——北方星空围绕其旋转的固定点。
一个会思考、规划、执行的 AI 代码 Agent。给它一个任务,看循环运转。
Installation
pip install tianshu
Setup
Create a .env file in your project root:
ANTHROPIC_API_KEY=your_key
ANTHROPIC_BASE_URL=https://api.anthropic.com # optional, defaults to official API
Then start the agent:
ts
Features
- Chat mode: multi-turn conversation with full context
- Auto plan: the model decides whether to decompose complex tasks — no manual trigger needed
- Skills: predefined step sequences for common workflows; auto-matched by semantics or triggered manually with
/skill-name - Tool use: read/write files, run shell commands, search code, fetch web pages
- Long-term memory: important information is persisted and loaded on next startup
- Context compression: long conversations are compressed automatically to stay within the context window
Project Structure
tianshu/
├── main.py # CLI entry point, banner, input loop
└── agent/
├── __init__.py # re-exports Agent
├── core.py # Agent class: chat / execute / execute_skill, system prompt builder
├── planner/
│ ├── __init__.py # re-exports Planner, PlanExecutor, Replanner
│ └── core.py # goal decomposition, parallel execution, dynamic replanning
├── tools/
│ ├── __init__.py # re-exports TOOLS, TOOL_MAP, Tool
│ └── core.py # 12 built-in tools
├── skills/
│ ├── __init__.py # re-exports Skill, SkillLoader
│ ├── core.py # Skill dataclass + SkillLoader (load / match / find)
│ └── builtin/ # built-in skill YAMLs (run_and_fix_tests, code_review)
└── memory/
├── __init__.py # re-exports ContextManager, LongTermMemory
├── context.py # ContextManager: conversation compression
└── longterm.py # LongTermMemory: persisted to .agent/memory.md
Chat mode
user input
│
▼
Agent.chat()
│
├─ System prompt (built once at init, reused across turns)
│ ├── base instructions (role, tool-use principles)
│ ├── project type detection (pyproject.toml / package.json / go.mod …)
│ ├── top-level directory listing (up to 40 entries)
│ ├── repo outline (all def / class with line numbers)
│ ├── git status & branch
│ ├── long-term memory (.agent/memory.md, if present)
│ └── conversation summary (generated by ContextManager after compression)
│
├─ ContextManager.maybe_compress()
│ └── estimates token count; if over 60k:
│ └── LLM compresses old messages into a summary, keeps last 4 turns
│
└─ Tool-use loop (up to 10 iterations)
│
├── API call (history + 12 tool schemas)
│
├── stop_reason = end_turn ──→ return text to user
│
└── stop_reason = tool_use
├── file ops read_file / write_file / edit_file / create_directory
├── code nav get_outline / find_symbol / grep_files / list_files
├── shell bash
├── web web_search / web_fetch
└── memory save_memory
│
└── truncated tool result appended to history → next iteration
Plan mode (auto-triggered or /skill-name)
user input
│
├─ starts with / → SkillLoader.find() → exact skill match → Agent.execute_skill()
│
└─ plain input → Agent.execute()
│
├─ SkillLoader.match() (semantic match) → hit → use predefined steps directly
│
└─ Planner.decompose() (LLM decides)
├─ needs_plan=false → Agent.chat() (plain conversation)
└─ needs_plan=true → Plan { steps: [...] }
│
▼
PlanExecutor.execute()
│
├── create shared workspace .agent/workspace/<uuid>/
│
├─ scheduling loop (ThreadPoolExecutor, max_workers=4)
│ └── find all steps whose depends_on are satisfied, submit concurrently
│
├─ each step (independent Agent)
│ ├── new Agent instance (reuses parent system prompt)
│ ├── prompt: overall goal + current step + prerequisite output file paths
│ └── Agent.chat() → full tool-use loop → record result
│
├─ on step failure → Replanner.replan()
│ └── LLM decides: skip / retry (new description) / replace remaining steps
│
└── summary: all done → output summary / partial failure → list failed steps
Agent iteration plan
Phase 1: Basic agent (complete)
What: custom tool system, ReAct loop, memory management.
Why: a useful agent needs three primitives — access to the outside world (tools), multi-step reasoning (ReAct loop), and cross-turn memory (short-term compression + long-term persistence). None of these is optional: without tools the agent can only talk, without a loop it cannot handle complex tasks, without memory every conversation starts from zero.
Key components:
tools/core.py: 12 built-in tools (file I/O, code navigation, bash, web, memory)agent/core.py: ReAct loop, up to 10 iterations, per-tool result truncation to prevent context overflowmemory/context.py: short-term memory — compresses conversation history when it exceeds 60k tokensmemory/longterm.py: long-term memory — persisted to.agent/memory.md, injected into system prompt on startup
Phase 2: Plan mode (complete)
What: Planner (goal decomposition) + PlanExecutor (per-step independent execution).
Why: the ReAct loop has two hard limits. First, _MAX_ITERATIONS = 10 — complex tasks need far more than 10 tool calls. Second, history grows unboundedly — a multi-step task inflates context until the model "forgets" earlier work. Plan mode solves this with divide-and-conquer: one LLM call decomposes the goal into 3–8 steps, then each step runs in its own Agent with its own history.
Key components:
Planner.decompose(): single LLM call, outputs a step listPlanExecutor.execute(): instantiates an independent Agent per step, passes the previous step's result (first 200 chars) as context
Phase 3: Multi-agent orchestration (complete)
What: parallel execution (DAG dependencies), shared workspace, dynamic replanning.
Why: phase 2 steps were strictly sequential — even independent steps had to wait for each other. Real tasks (e.g. "analyze 5 files and summarize") can run concurrently. Also, 200-char summaries are too small to share large artifacts between steps. And abandoning the entire plan on a single failure is too costly — the model should decide how to recover.
New capabilities:
- Parallel execution:
Step.depends_ondefines a DAG;ThreadPoolExecutorruns all steps whose dependencies are satisfied concurrently - Shared workspace: each plan creates
.agent/workspace/<uuid>/; steps write files there and downstream steps read them by path - Dynamic replanning: on step failure,
Replannerasks the LLM toskip/retry/replaceremaining steps (up to 3 times)
Phase 4: Auto plan triggering and skills (complete)
What: remove the explicit /plan command; introduce a reusable skill system.
Why removing /plan: it exposes an internal implementation detail. Complexity judgment is the model's responsibility, not the user's. Planner now includes a needs_plan field in its prompt — the model returns None for simple tasks (falls back to chat()) or a Plan for complex ones, transparently.
Why skills: high-frequency workflows (e.g. "run tests and fix failures", "review recent changes") had to be re-planned from scratch every time, wasting tokens and risking inconsistent plans. Skills are predefined step sequences stored as YAML — they can be auto-matched when the user's input is semantically close, or triggered precisely with /skill-name.
Key components:
Skill: pure data class withname,description,stepsSkillLoader: loadsbuiltin/*.yaml; providesfind()(exact match) andmatch()(single LLM semantic match)Planner.decompose(): tries skill match first; falls back to LLM planning; returnsNoneifneeds_plan=falseAgent.execute_skill(): converts Skill → Plan → PlanExecutor;main.pystays unaware of internals
Metadata
Release files for tsagent 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tsagent-0.3.0.tar.gz | 26.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tsagent-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.1 kB
Release files / tsagent-0.3.0.tar.gz
| Download URL | tsagent-0.3.0.tar.gz |
|---|---|
| Size | 26.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b8eb91d97751470dc15ff740fc5c62696340a54c75312067a28c63db36a80c88
|
|
BLAKE2b-256 checksum How to use checksums |
2c72968c69bb25745a4dc8a6d9d2450800ee3b694d60bb7f4f92222d682b4204
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / tsagent-0.3.0-py3-none-any.whl
| Download URL | tsagent-0.3.0-py3-none-any.whl |
|---|---|
| Size | 21.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aecfecfb64b23cb5ae892288da09097370ccf48d534ca2dd39747c0bb4b7023d
|
|
BLAKE2b-256 checksum How to use checksums |
c9565408b53b8cad7b568d66550ff2633641f55a435dd02368f3f3c764bc103a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|