Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
POSTGRES_PASSWORD=change-me-db-password
BACKEND_PORT=6100
JWT_SECRET=change-me-jwt-secret
INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=admin
INITIAL_ADMIN_DISPLAY_NAME=平台管理员
INTEGRATION_API_KEY=replace-with-a-long-random-key
AGENT_DEFAULT_MODEL=deepseek-v4-flash
AGENT_DEFAULT_API_BASE_URL=https://api.deepseek.com/v1
WIKI_AUTO_COMPILE_ENABLED=true
WIKI_WORKER_POLL_SECONDS=10
WIKI_COMPILATION_STALE_MINUTES=60
WIKI_MAX_DOCUMENT_BYTES=52428800
WIKI_MAX_EXTRACTED_CHARS=500000
WIKI_SOURCE_CHUNK_CHARS=12000
WIKI_MAX_CHUNKS_PER_SOURCE=8
WIKI_MAX_SOURCES_PER_RUN=20
10 changes: 4 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
.env
backend/__pycache__/
backend/app/__pycache__/
backend/app/models/__pycache__/
backend/app/routers/__pycache__/
backend/app/schemas/__pycache__/
backend/app/services/__pycache__/
__pycache__/
*.py[cod]
.pytest_cache/
.tmp/
frontend/node_modules/
frontend/dist/
backend/uploads/
Expand Down
49 changes: 44 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TinyPM

[![Version](https://img.shields.io/badge/version-1.1.0-blue)](./README.md)
[![Version](https://img.shields.io/badge/version-1.2.0-blue)](./README.md)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)
[![Open Source](https://img.shields.io/badge/Open%20Source-Yes-1f6feb)](./LICENSE)
[![FastAPI](https://img.shields.io/badge/FastAPI-0.115-009688)](https://fastapi.tiangolo.com/)
Expand All @@ -14,10 +14,10 @@ TinyPM 是一个面向小型研发、测试和交付团队的 **私有化 AI-nat
项目数据
-> MCP 工具
-> AI 项目助理
-> 项目总审 / 计划审核 / 风险清单 / 周会摘要
-> 项目总审 / 计划审核 / 风险清单 / 周会摘要 / 可信周报
```

当前版本:`1.1.0`
当前版本:`1.2.0`

- English README: [README.en.md](./README.en.md)

Expand All @@ -27,7 +27,7 @@ TinyPM 是一个面向小型研发、测试和交付团队的 **私有化 AI-nat
## 特性

- AI-native 项目管理
- Agent 可通过 MCP 工具读取项目、计划、需求和问题
- Agent 可通过 MCP 工具读取项目、计划、需求、问题和项目知识库
- 支持项目总审、当前计划审核、风险清单和周会摘要
- 适合小团队把项目数据交给 AI 复盘,而不是只靠口头描述
- 智能助理
Expand All @@ -40,6 +40,20 @@ TinyPM 是一个面向小型研发、测试和交付团队的 **私有化 AI-nat
- Agent 通过 MCP 工具访问后端项目数据
- 支持查询项目、计划、需求、问题
- 支持通过 MCP 新增或更新项目计划
- 外部集成 API
- 使用独立 API Key,供 Codex 或自动化脚本读取和写入项目业务数据
- 支持幂等导入会议纪要、开发细节、聊天记录和 Markdown 文本
- 机器接口不开放用户、角色和 Agent 模型密钥管理
- 增量 LLM Wiki
- 项目知识条目、上传文档和绑定项目的成功聊天自动进入不可变来源收件箱
- 独立 `wiki-worker` 持续把新来源编译进 topic、concept、synthesis 和 idea 页面
- 页面保留来源观点、AI 推断、待验证问题和来源追溯,不把模型输出覆盖回原文
- 可从项目详情导出 Obsidian 兼容 Markdown ZIP
- 证据驱动的项目周报
- 按周期确定性采集计划、里程碑、需求、问题、知识资料和关键路径候选事实
- 每条正式事实绑定来源证据、内容哈希和审核状态
- AI 只读取已核验事实生成草稿,项目经理人工确认总体状态和正式终稿
- 批准、归档后的事实与正式版本只读,保留 Prompt、模型、原稿和审核记录
- 用户与权限
- 默认初始化管理员账号
- 支持 `admin`、`manager`、`member` 三种角色
Expand Down Expand Up @@ -71,7 +85,7 @@ TinyPM 是一个面向小型研发、测试和交付团队的 **私有化 AI-nat
2. 登录默认管理员账号。
3. 创建一个项目,导入或录入项目计划。
4. 打开智能助理工作台,绑定当前项目。
5. 让 Agent 生成项目总审、风险清单或周会摘要
5. 让 Agent 生成项目总审或风险清单;在项目详情“项目知识库”查看自动编译结果,再到“项目周报”完成可信周报闭环

完整演示见 [docs/demo.md](./docs/demo.md),部署细节见 [docs/quickstart.md](./docs/quickstart.md)。

Expand Down Expand Up @@ -156,6 +170,7 @@ JWT_SECRET=your-jwt-secret
INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=change-this-password
INITIAL_ADMIN_DISPLAY_NAME=平台管理员
INTEGRATION_API_KEY=replace-with-a-long-random-key
```

### 2. 使用 Docker 启动
Expand All @@ -170,6 +185,8 @@ docker compose up --build -d
- 后端:`http://localhost:6100`
- PostgreSQL:`localhost:6105`

如果主机的 `6100` 已被占用,可在 `.env` 中设置 `BACKEND_PORT=6110`;容器内端口和前端反向代理不受影响。

### 3. 默认管理员

如果你未修改 `.env`,默认初始化账号为:
Expand Down Expand Up @@ -208,6 +225,27 @@ npm run dev
- 支持 `deepseek-v4-flash` 与 `deepseek-v4-pro`
- 大模型不可用时会展示具体原因,并在后端日志中记录每次失败

### 外部集成与项目知识库

- Codex 等机器客户端使用独立 `INTEGRATION_API_KEY` 调用 `/api/integration/*`
- 项目知识库支持会议纪要、开发细节、聊天记录、文档文本和其他资料
- 使用 `external_id` 可重复同步同一来源,不会产生重复记录
- AI 助理通过 `search_project_knowledge` MCP 工具检索资料后分析项目
- LLM Wiki 使用“机械入箱、增量编译”两阶段流程,编译页面和原始资料分表保存
- 文档上传和项目聊天会唤醒后台编译;容器重启后 `wiki-worker` 会继续扫描持久 `inbox`
- 文本、DOCX、XLSX、PPTX 和带文本层 PDF 可直接提取;图片或扫描件会保留为待提取来源,不伪造正文
- 项目详情“项目知识库”可查看来源状态、长期页面、编译失败原因并导出 Obsidian ZIP
- 接口清单和调用示例见 [外部集成 API](./docs/integration_api.md)
- 架构与边界见 [TinyPM LLM Wiki 架构](./docs/llm-wiki-architecture.md)

### 项目周报

- 项目详情中的“项目周报”页签提供周期创建、候选采集、事实审核、证据查看、AI 草稿、人工终审和归档
- `member` 可以查看周报并补充待确认候选;`manager` 和 `admin` 可以采集、核验、生成、批准和归档
- Codex 可通过集成 API 批量导入候选事实,但导入项始终回到“待确认”,机器接口不能批准或归档
- 项目历史基线在周期截止后补录时按业务日期进入候选并标记为“晚到资料”;截止时已存在但之后被改写的来源仍会排除,避免污染历史快照
- 周报生成复用智能助理中配置的 OpenAI 兼容模型和 API Key,不在浏览器接口中返回已保存的密钥

### 项目公告

- 公告挂载在项目维度
Expand Down Expand Up @@ -240,6 +278,7 @@ npm run dev
- 生产环境务必修改:
- `POSTGRES_PASSWORD`
- `JWT_SECRET`
- `INTEGRATION_API_KEY`
- 初始化管理员密码
- 不要把真实账号、密码、客户敏感信息直接提交到仓库
- DeepSeek API Key 由用户在页面中填写并保存到本地部署数据库,仓库中不包含真实 API Key
Expand Down
8 changes: 8 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__pycache__
*.py[cod]
.pytest_cache
tests
uploads
documents
.git
.gitignore
38 changes: 38 additions & 0 deletions backend/alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[alembic]
script_location = %(here)s/alembic
prepend_sys_path = %(here)s
path_separator = os

[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
1 change: 1 addition & 0 deletions backend/alembic/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__/
64 changes: 64 additions & 0 deletions backend/alembic/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Alembic 迁移运行环境。"""

from logging.config import fileConfig

from alembic import context
from sqlalchemy.engine import Connection

from app import models as app_models
from app.database import Base, engine


config = context.config

if config.config_file_name is not None:
fileConfig(config.config_file_name)

target_metadata = Base.metadata


def run_migrations_offline() -> None:
"""以离线模式生成迁移 SQL。"""
from app.config import settings

context.configure(
url=settings.DATABASE_URL,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
compare_type=True,
compare_server_default=True,
)

with context.begin_transaction():
context.run_migrations()


def _run_with_connection(connection: Connection) -> None:
"""使用指定数据库连接执行在线迁移。"""
context.configure(
connection=connection,
target_metadata=target_metadata,
compare_type=True,
compare_server_default=True,
)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online() -> None:
"""以在线模式执行数据库迁移。"""
provided_connection = config.attributes.get("connection")
if provided_connection is not None:
_run_with_connection(provided_connection)
return

with engine.connect() as connection:
_run_with_connection(connection)


if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
28 changes: 28 additions & 0 deletions backend/alembic/script.py.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""

from collections.abc import Sequence

from alembic import op
import sqlalchemy as sa
${imports if imports else ""}


revision: str = ${repr(up_revision)}
down_revision: str | Sequence[str] | None = ${repr(down_revision)}
branch_labels: str | Sequence[str] | None = ${repr(branch_labels)}
depends_on: str | Sequence[str] | None = ${repr(depends_on)}


def upgrade() -> None:
"""升级数据库结构。"""
${upgrades if upgrades else "pass"}


def downgrade() -> None:
"""回退数据库结构。"""
${downgrades if downgrades else "pass"}
19 changes: 19 additions & 0 deletions backend/alembic/versions/0001_legacy_baseline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""登记 TinyPM 既有数据库结构基线。"""

from collections.abc import Sequence


revision: str = "0001_legacy_baseline"
down_revision: str | None = None
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
"""既有结构由历史版本创建,本迁移只作为版本基线。"""
pass


def downgrade() -> None:
"""基线迁移不删除既有业务表。"""
pass
Loading