Architecture

让复杂能力,
有地方安放。

天策的架构不是为了展示技术栈,而是为了让「多项目、多会话、可拆解工具、可审计过程」同时成立。三层运行链、统一的分层、独立的会话任务与清晰的数据落点,共同支撑了这一点。

VERSION 0.3.39CORE CODE 约 28.4 万行TOOLS 51LAYERS 3
01 / Runtime

三层运行链,
各管一件事。

桌面壳处理窗口、托盘、更新与系统集成;前端处理界面与交互;后端处理会话、工具、模型与数据。任何一层都可以被单独理解,也可以被单独替换。

SHELL桌面壳
PyWebView TianceRemoteGateway · .NET / YARP Launcher Updater 窗口 / 托盘 / 单实例
LOCAL HTTP · WEBSOCKET
WEB前端
React 19 TypeScript Vite features / widgets / pages 主题 token SSE 订阅
REST · SSE · EVENT SOCKET
CORE后端
Python · FastAPI Uvicorn 会话运行管理 工具执行 SQLite 本地文件
桌面壳→ 网关 18000→ 后端 18200(仅回环)→ 工具进程 / 文件 / SQLite
Why Gateway

网关不是多余的中间层。

它把「桌面壳如何找到后端」与「前端如何访问后端」解耦。后端可以重启、可以换端口;前端不需要知道这些细节。对本地桌面应用而言,这一层让升级与排障都简单很多。

Why Local

本地优先,不是离线口号。

项目、会话、文件、密钥、记忆都在本机。除调用模型 API 之外,没有一条必经的第三方链路。断网时你仍然可以打开项目、查看历史、编辑文件、运行不依赖网络的工具。

02 / Backend

后端不是一锅路由汤。

同一组分层贯穿所有能力:路由只做协议与校验,服务编排业务,领域模型定义语义,仓储负责数据,基础设施封装外部依赖。新增能力时,位置是清楚的。

API
路由与协议REST、SSE、事件套接字、参数校验、错误映射
SERVICES
业务编排会话、工具、记忆、文档、项目、模型
DOMAIN
领域模型实体、值对象、状态机、错误语义
REPOS
数据访问SQLite、消息、运行、事件、附件
INFRA
基础设施供应商适配、文件、进程、日志、网络

会话生命周期,是这套分层最好的例子

一次生成不是「一个函数从头跑到尾」。它由流服务、运行管理器、结算器与工具循环共同完成:任务被独立持有,事件可重放,终态一定会结算,运行状态有明确写入点。

  • conversation_stream.py — 组织一次生成的事件流与持久化边界
  • conversation_run_manager.py — 持有生成任务,独立于 HTTP 监听器
  • conversation_stream_settlement.py — 正常、取消、异常三条终态的落库与计费
  • conversation_tool_loop.py — 模型 → 工具 → 结果 → 模型 的循环
  • conversation_memory.py — 压缩触发、压缩块与状态消息
03 / Tool Loop

一次工具调用,
要走完七步。

模型提出调用只是开始。参数要校验,权限要判断,工具要被真正执行,结果要结构化回填,失败要能被模型理解并纠正。每一步都有对应的模块。

调用链路

MODEL → TOOL → MODEL
1 · 注入
按会话启用状态,把常驻工具 schema 与动态工具目录摘要注入模型上下文。
chat_tool_injection
CTX
2 · 生成
模型返回 tool_calls;畸形参数会被拦截并回传结构化错误,让模型有机会纠正。
tool_loop
LLM
3 · 校验
按 input.schema.json 校验参数类型、必填项与取值;不通过则不执行。
tool_execution_arguments
SCHEMA
4 · 权限
读取 permissions.json 与会话权限模式,决定允许、询问或禁止。
tool_permissions
GUARD
5 · 执行
按工具声明的运行时执行:Python 子进程、内部函数或前端桥接。
tool_execution_runtime
RUN
6 · 回填
把结果压缩成模型可读的内容,保留必要结构、错误与下一步指引。
tool_result_content
RESULT
7 · 记录
写入调用记录:工具、参数、结果、耗时、会话;供界面展开与事后检索。
tool_call_records
AUDIT
04 / Memory

记忆不是越长越好。

上下文有限,而工作会持续很久。天策把记忆分成两层:会话内的压缩块,以及跨会话的长期记忆。两者都有明确的触发条件与注入规则。

LAYER 01

会话记忆压缩

Conversation compaction

当会话超过阈值(默认 250,000 token),系统把较早的轮次压缩成块,保留最近 30,000 token 的原文与受保护的压缩块。压缩是一次独立功能会话,模型必须调用 submit_memory_compaction 提交结果;过程本身会写入一条可见的系统消息。

memory_compaction · 独立功能会话 · 工具调用
LAYER 02

长期记忆

Long-term memory

跨会话仍然有效的偏好、约束与事实,写入全局或项目记忆。记忆的增删改本身就是工具调用,可查、可改、可删,而不是模型脑内的一段模糊印象。

manage_memory · global / project
LAYER 03

注入策略

Context injection

全局记忆、项目记忆、会话压缩块、最近原文各有独立的开关与预算。会话可以按任务选择只带项目记忆,或完全关闭记忆注入,避免旧信息污染新任务。

memory_raw_turns · protected blocks
05 / Data

数据组织,
一眼能找到。

天策的数据目录不是内部缓存,而是产品的一部分。你可以打开它、备份它、迁移它,也可以在排障时直接查表。

Data/
├── projects/
│   ├── catalog.json        # 项目索引(含外部 root_path)
│   └── <uuid>/
│       ├── …项目文件
│       └── .Tiance/
│           └── tiance.db       # 会话 / 消息 / 运行 / 事件
├── tools/
│   ├── catalog.json        # 分类与工具项目
│   └── <guid>/
│       ├── .tool/             # tool.json / schema / permissions
│       ├── program/           # 实现与依赖
│       └── tests/
├── themes/               # 主题包 + theme-settings.json
├── memory/               # global_memory.jsonl
├── usage/                # usage_records.jsonl
├── providers/            # 供应商与凭证(本地)
└── logs/                 # 桌面壳 / 网关 / 后端日志

项目库里的主要表

项目库是 SQLite,随项目走。下面是支撑会话与审计的核心表(以实际版本为准):

  • S
    conversation_sessions会话主体、标题、配置与时间
  • M
    conversation_messages消息、角色、序号、内容与工具调用
  • R
    conversation_runs每次生成的状态、开始与结算时间
  • E
    conversation_session_events模型交换、快照与运行事件
  • T
    conversation_session_runtime_statesidle / running / error 的持久化状态
  • A
    attachments会话附件与引用
06 / Scale

这些数字,
来自代码本身。

不引用宣传材料,也不引用过期文档。以下为 0.3.39 版本按源码目录统计的口径(排除运行时、依赖、构建产物与数据目录)。

PY

150833 行

Python 后端与工具实现,842 个文件。

TS

104870 行

TypeScript / TSX 前端,762 个文件。

CSS

28024 行

主题与界面样式,129 个文件。

Σ

283727 行

核心代码合计,约 28.4 万行。

运行时事实:239 个 API 端点 · 16 种流式事件 · 51 个工具 · 7 套主题 · 9 个模型供应商 · 3 套语言包

统计口径:1_PythonServer + 2_ReactWeb/src + 3_PyWebView 下的 .py / .ts / .tsx / .css;排除 runtime、node_modules、dist、build、Data、发布包、缓存与测试依赖。

Next

架构是骨架,
工具是肌肉。

51 个工具分布在 10 个分类里。去看完整目录,以及几只被反复打磨过的工具。