# 如何用 Astra 为独立开发找项目

> 用 ChatGPT 6 Astra 写了个机会研究工具：把有收入的软件业务收进来，让模型拆出客户为什么付费、哪些功能能砍、最小 MVP 长什么样。附完整技术栈、CSV/HTML/URL 三种数据入口，以及 Zod 结构化输出和 Prisma 事务写入的实现细节。

- 原文链接: https://laojin.blog/blog/20260908_find_project
- 作者: 老金
- 发布日期: 2026-09-08
- 标签: 独立开发, GPT-6 Astra, Next.js 16, 结构化输出, 选题

---

![如何用 Astra 为独立开发找项目](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260908/00_cover.png)

ChatGPT 6 Astra 出来之后，我拿它写了个小工具，给自己找独立开发的项目用。

思路不复杂：把那些已经有收入的软件业务收集起来，交给模型拆一遍——用户真正掏钱买的是什么，现有产品里哪些功能其实可以删掉，还有哪些痛点没人解决，砍到最小能做成什么样的 MVP，如果自己下场，差异化的点又在哪。

## 界面做成什么样

首页是一个列表，收进来的都是已经过市场验证的业务，可以按收入和分析状态筛，点进去看完整的拆解报告。

我刻意没做成营销落地页的样子。收入、利润率、售价、分析状态都用明确的数据层级排开，方便横着扫一眼做比较。产品本身就是"从复杂业务里找更小的产品"，界面就没必要再堆装饰——留白、清晰的边界、每块只讲一件事，把结论放在最显眼的位置。

顶部数据条只放四个全局指标：机会数量、AI 分析覆盖率、总月收入、总月利润。看的是当前样本够不够大、整体商业质量如何。

报告本身分四块：

1. What customers need：客户为什么付费。
2. Where the market still hurts：现有方案仍未解决的问题。
3. The MVP worth shipping：最值得构建的精简功能。
4. What to cut：不该进入第一个版本的复杂功能。

每条结论都带序号。AI 生成的内容往往偏长，有编号好扫得多，讨论的时候也能直接指第几条。

![收入排序的机会列表，点开是完整拆解报告](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260908/01_workbench.png)

## 技术原理

整条链路是这么走的：

1. 从 CSV、浏览器保存的 HTML 或者 API 接收软件业务信息。
2. 把不同来源的数据统一转成 `ListingInput`。
3. 按来源 URL 去重，写入数据库。
4. 把待处理的项目丢给大语言模型，用 Zod 约束返回结构。
5. 分析报告和处理状态原子化写入数据库。
6. 页面走服务端渲染，提供筛选、概览和报告阅读。

![三种数据入口收敛成统一结构，再入库、过模型、出报告](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260908/02_pipeline.png)

### 技术栈

| 层级 | 技术 | 作用 |
| --- | --- | --- |
| Web 框架 | Next.js 16 App Router | 路由、服务端渲染、API Route |
| UI | React 19 | 页面与组件结构 |
| 样式 | Tailwind CSS 4 + Global CSS | 基础样式能力、设计令牌和响应式布局 |
| ORM | Prisma ORM 7 | 数据模型、类型生成和数据库访问 |
| 数据库驱动 | `pg` / `better-sqlite3` | PostgreSQL 与 SQLite 连接 |
| AI | Vercel AI SDK | 调用模型并生成结构化结果 |
| 数据校验 | Zod | 校验 AI 返回的数据结构 |
| HTML 解析 | Cheerio | 从保存的网页中提取业务信息 |
| CSV 解析 | `csv-parse` | 将 CSV 转换为标准数据 |
| 部署 | Vercel | Next.js 托管与定时任务 |
| 语言 | TypeScript | 静态类型检查 |

### 统一输入模型

所有数据入口最终都会转成 `ListingInput`：

```ts
type ListingInput = {
  sourceUrl: string;
  title: string;
  category?: string;
  askingPrice?: number;
  monthlyRevenue?: number;
  monthlyProfit?: number;
  description: string;
  pricingInfo?: string;
  reviewsSnippet?: string;
  rawData?: string;
};
```

这样"数据从哪来"和"数据怎么存"就不互相牵扯了。以后要接浏览器插件、第三方采集服务或者别的交易平台，只要能吐出同样的结构就行。

### CSV 导入

```bash
npm run import:csv -- path/to/file.csv
```

`scripts/import-csv.ts` 读文件，`parseListingsCsv()` 按表头逐行解析，检查 `sourceUrl`、`title`、`description` 三个必填字段，把金额里的货币符号和千分位清掉，最后交给统一入库函数。

CSV 是目前最稳的入口，它不依赖目标网站的页面结构，也不用管人家的反爬策略。

### HTML 导入

```bash
npm run import:html -- path/to/html-directory
```

解析分两级。先找页面里的 `application/json` 或 `__NEXT_DATA__`，在里面递归找同时带标题、价格或收入特征的对象；实在找不到结构化数据，才退回 DOM、Meta 标签和正则去抠。

递归最多下六层，提取到的原始对象截断后存进 `rawData`，解析出错的时候好排查。

网页结构随时会变，所以这套解析是启发式的，别当成稳定 API 用。

### URL 直接抓取

```bash
npm run fetch:listings -- urls.txt
```

逐行读 URL，普通 HTTP 请求拿 HTML，每次请求之间等三秒，然后交给上面那个解析器，最后汇总成功和失败的清单。

它不会去绕验证码、浏览器挑战、代理限制或设备指纹。碰到 `403` 就老实换 CSV 或者浏览器保存的 HTML。

## AI 分析

### 单条项目分析

`analyzeListing()` 把项目名称和类别、售价、月收入月利润、原始业务描述、定价信息、用户评价拼进提示词。

### 结构化输出

输出这块直接用：

```ts
Output.object({ schema: ReportSchema })
```

让模型按 Zod 定义好的结构返回：

```ts
{
  summary: string;
  coreNeeds: string[];
  redundantFeatures: string[];
  unmetPainPoints: string[];
  suggestedMvpFeatures: string[];
  differentiation: string;
}
```

字段名稳定，数组和字符串类型明确，页面就不用去猜自然语言的格式。不符合 Schema 的输出直接算失败。

![Schema 是固定形状的槽位，对不上的输出被挡在外面](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260908/03_schema_gate.png)

### 批量分析

`analyzePendingListings(limit)` 捞出状态是 `PENDING` 的项目，一条一条处理。

单条分析成功后，用 Prisma 事务同时创建或更新 `Report`、把 `Listing.status` 改成 `ANALYZED`。两件事都成功事务才提交，否则就会出现报告已经生成但状态没更新（或者反过来）的中间状态。模型调用、结构校验、数据库写入任何一步挂了，状态就置成 `FAILED`。

现在是串行跑的。逻辑简单、调用速率好控制，代价是数据一多就慢。后面想加受控并发、重试次数和错误原因字段。

## 现在怎么用

整个工程都是 Astra 写的，但运行时不用 Astra 模型（太贵），一般用 GPT 5.6 Terra 分析效果也够用。眼下主要拿它分析 AppStore 上的产品，从里面挑能搬到鸿蒙端做的 App。
