v2er-insight 1.0.0
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.
- package/README.md +215 -0
- package/dist/cli/commands/ai.d.ts +13 -0
- package/dist/cli/commands/ai.js +153 -0
- package/dist/cli/commands/analyze.d.ts +13 -0
- package/dist/cli/commands/analyze.js +80 -0
- package/dist/cli/commands/config.d.ts +43 -0
- package/dist/cli/commands/config.js +267 -0
- package/dist/cli/commands/fetch.d.ts +13 -0
- package/dist/cli/commands/fetch.js +150 -0
- package/dist/cli/commands/index.d.ts +10 -0
- package/dist/cli/commands/index.js +22 -0
- package/dist/cli/commands/run.d.ts +23 -0
- package/dist/cli/commands/run.js +52 -0
- package/dist/cli/commands/show.d.ts +13 -0
- package/dist/cli/commands/show.js +154 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +107 -0
- package/dist/cli/types.d.ts +58 -0
- package/dist/cli/types.js +6 -0
- package/dist/cli/utils/error.d.ts +6 -0
- package/dist/cli/utils/error.js +18 -0
- package/dist/cli/utils.d.ts +20 -0
- package/dist/cli/utils.js +48 -0
- package/dist/cli/workflow/orchestrator.d.ts +15 -0
- package/dist/cli/workflow/orchestrator.js +144 -0
- package/dist/cli/workflow/recovery.d.ts +10 -0
- package/dist/cli/workflow/recovery.js +134 -0
- package/dist/cli/workflow/state.d.ts +19 -0
- package/dist/cli/workflow/state.js +45 -0
- package/dist/cli/workflow/types.d.ts +60 -0
- package/dist/cli/workflow/types.js +3 -0
- package/dist/config/defaults.d.ts +48 -0
- package/dist/config/defaults.js +42 -0
- package/dist/config/index.d.ts +16 -0
- package/dist/config/index.js +21 -0
- package/dist/config/path.d.ts +11 -0
- package/dist/config/path.js +28 -0
- package/dist/config/proxy.d.ts +16 -0
- package/dist/config/proxy.js +39 -0
- package/dist/config/storage.d.ts +23 -0
- package/dist/config/storage.js +85 -0
- package/dist/config/types/ai.d.ts +31 -0
- package/dist/config/types/ai.js +13 -0
- package/dist/config/types/analyzer.d.ts +15 -0
- package/dist/config/types/analyzer.js +6 -0
- package/dist/config/types/data.d.ts +20 -0
- package/dist/config/types/data.js +6 -0
- package/dist/config/types/fetch.d.ts +9 -0
- package/dist/config/types/fetch.js +6 -0
- package/dist/config/types/index.d.ts +32 -0
- package/dist/config/types/index.js +11 -0
- package/dist/config/types/log.d.ts +11 -0
- package/dist/config/types/log.js +6 -0
- package/dist/core/ai/index.d.ts +11 -0
- package/dist/core/ai/index.js +18 -0
- package/dist/core/ai/parser/index.d.ts +12 -0
- package/dist/core/ai/parser/index.js +44 -0
- package/dist/core/ai/parser/validator.d.ts +18 -0
- package/dist/core/ai/parser/validator.js +179 -0
- package/dist/core/ai/prompt/index.d.ts +20 -0
- package/dist/core/ai/prompt/index.js +75 -0
- package/dist/core/ai/prompt/system-prompt.md +210 -0
- package/dist/core/ai/providers/gemini.d.ts +25 -0
- package/dist/core/ai/providers/gemini.js +74 -0
- package/dist/core/ai/providers/index.d.ts +6 -0
- package/dist/core/ai/providers/index.js +9 -0
- package/dist/core/ai/types/index.d.ts +7 -0
- package/dist/core/ai/types/index.js +6 -0
- package/dist/core/ai/types/options.d.ts +14 -0
- package/dist/core/ai/types/options.js +6 -0
- package/dist/core/ai/types/provider.d.ts +19 -0
- package/dist/core/ai/types/provider.js +6 -0
- package/dist/core/ai/types/result.d.ts +64 -0
- package/dist/core/ai/types/result.js +6 -0
- package/dist/core/ai/utils/api-key.d.ts +15 -0
- package/dist/core/ai/utils/api-key.js +36 -0
- package/dist/core/ai/utils/index.d.ts +6 -0
- package/dist/core/ai/utils/index.js +11 -0
- package/dist/core/ai/utils/retry.d.ts +15 -0
- package/dist/core/ai/utils/retry.js +37 -0
- package/dist/core/analyzer/builder.d.ts +23 -0
- package/dist/core/analyzer/builder.js +113 -0
- package/dist/core/analyzer/content/chunker.d.ts +18 -0
- package/dist/core/analyzer/content/chunker.js +74 -0
- package/dist/core/analyzer/content/index.d.ts +7 -0
- package/dist/core/analyzer/content/index.js +13 -0
- package/dist/core/analyzer/content/transformer.d.ts +19 -0
- package/dist/core/analyzer/content/transformer.js +33 -0
- package/dist/core/analyzer/index.d.ts +17 -0
- package/dist/core/analyzer/index.js +21 -0
- package/dist/core/analyzer/periods/detector.d.ts +17 -0
- package/dist/core/analyzer/periods/detector.js +36 -0
- package/dist/core/analyzer/periods/index.d.ts +6 -0
- package/dist/core/analyzer/periods/index.js +11 -0
- package/dist/core/analyzer/periods/splitter.d.ts +11 -0
- package/dist/core/analyzer/periods/splitter.js +35 -0
- package/dist/core/analyzer/stats/index.d.ts +7 -0
- package/dist/core/analyzer/stats/index.js +13 -0
- package/dist/core/analyzer/stats/reply-stats.d.ts +15 -0
- package/dist/core/analyzer/stats/reply-stats.js +45 -0
- package/dist/core/analyzer/stats/topic-stats.d.ts +16 -0
- package/dist/core/analyzer/stats/topic-stats.js +51 -0
- package/dist/core/analyzer/stats/user-overview.d.ts +9 -0
- package/dist/core/analyzer/stats/user-overview.js +52 -0
- package/dist/core/analyzer/types/index.d.ts +7 -0
- package/dist/core/analyzer/types/index.js +6 -0
- package/dist/core/analyzer/types/input.d.ts +13 -0
- package/dist/core/analyzer/types/input.js +6 -0
- package/dist/core/analyzer/types/internal.d.ts +28 -0
- package/dist/core/analyzer/types/internal.js +6 -0
- package/dist/core/analyzer/types/output.d.ts +68 -0
- package/dist/core/analyzer/types/output.js +6 -0
- package/dist/core/analyzer/utils/date-parser.d.ts +41 -0
- package/dist/core/analyzer/utils/date-parser.js +118 -0
- package/dist/core/analyzer/utils/index.d.ts +6 -0
- package/dist/core/analyzer/utils/index.js +18 -0
- package/dist/core/analyzer/utils/stats.d.ts +12 -0
- package/dist/core/analyzer/utils/stats.js +64 -0
- package/dist/core/v2ex/index.d.ts +10 -0
- package/dist/core/v2ex/index.js +27 -0
- package/dist/core/v2ex/parsers/index.d.ts +8 -0
- package/dist/core/v2ex/parsers/index.js +15 -0
- package/dist/core/v2ex/parsers/replies-page.d.ts +11 -0
- package/dist/core/v2ex/parsers/replies-page.js +114 -0
- package/dist/core/v2ex/parsers/selectors/index.d.ts +10 -0
- package/dist/core/v2ex/parsers/selectors/index.js +18 -0
- package/dist/core/v2ex/parsers/selectors/pagination.d.ts +11 -0
- package/dist/core/v2ex/parsers/selectors/pagination.js +14 -0
- package/dist/core/v2ex/parsers/selectors/replies-page.d.ts +21 -0
- package/dist/core/v2ex/parsers/selectors/replies-page.js +24 -0
- package/dist/core/v2ex/parsers/selectors/topic-detail.d.ts +19 -0
- package/dist/core/v2ex/parsers/selectors/topic-detail.js +22 -0
- package/dist/core/v2ex/parsers/selectors/topics-list-page.d.ts +11 -0
- package/dist/core/v2ex/parsers/selectors/topics-list-page.js +14 -0
- package/dist/core/v2ex/parsers/selectors/user-profile.d.ts +11 -0
- package/dist/core/v2ex/parsers/selectors/user-profile.js +14 -0
- package/dist/core/v2ex/parsers/topic-detail.d.ts +11 -0
- package/dist/core/v2ex/parsers/topic-detail.js +94 -0
- package/dist/core/v2ex/parsers/topics-list-page.d.ts +11 -0
- package/dist/core/v2ex/parsers/topics-list-page.js +90 -0
- package/dist/core/v2ex/parsers/user-profile.d.ts +11 -0
- package/dist/core/v2ex/parsers/user-profile.js +70 -0
- package/dist/core/v2ex/parsers/utils/index.d.ts +6 -0
- package/dist/core/v2ex/parsers/utils/index.js +9 -0
- package/dist/core/v2ex/parsers/utils/pagination.d.ts +19 -0
- package/dist/core/v2ex/parsers/utils/pagination.js +29 -0
- package/dist/core/v2ex/types/entities.d.ts +45 -0
- package/dist/core/v2ex/types/entities.js +7 -0
- package/dist/core/v2ex/types/index.d.ts +6 -0
- package/dist/core/v2ex/types/index.js +6 -0
- package/dist/core/v2ex/types/parse-result.d.ts +64 -0
- package/dist/core/v2ex/types/parse-result.js +7 -0
- package/dist/core/v2ex/urls/constants.d.ts +5 -0
- package/dist/core/v2ex/urls/constants.js +8 -0
- package/dist/core/v2ex/urls/index.d.ts +7 -0
- package/dist/core/v2ex/urls/index.js +16 -0
- package/dist/core/v2ex/urls/topic-urls.d.ts +19 -0
- package/dist/core/v2ex/urls/topic-urls.js +48 -0
- package/dist/core/v2ex/urls/user-urls.d.ts +24 -0
- package/dist/core/v2ex/urls/user-urls.js +36 -0
- package/dist/core/v2ex/use-cases/index.d.ts +8 -0
- package/dist/core/v2ex/use-cases/index.js +14 -0
- package/dist/core/v2ex/use-cases/types.d.ts +31 -0
- package/dist/core/v2ex/use-cases/types.js +7 -0
- package/dist/core/v2ex/use-cases/user/index.d.ts +10 -0
- package/dist/core/v2ex/use-cases/user/index.js +16 -0
- package/dist/core/v2ex/use-cases/user/profile.d.ts +14 -0
- package/dist/core/v2ex/use-cases/user/profile.js +51 -0
- package/dist/core/v2ex/use-cases/user/replies.d.ts +14 -0
- package/dist/core/v2ex/use-cases/user/replies.js +20 -0
- package/dist/core/v2ex/use-cases/user/topic-urls.d.ts +21 -0
- package/dist/core/v2ex/use-cases/user/topic-urls.js +29 -0
- package/dist/core/v2ex/use-cases/user/topics-detail.d.ts +30 -0
- package/dist/core/v2ex/use-cases/user/topics-detail.js +62 -0
- package/dist/core/v2ex/use-cases/utils/index.d.ts +6 -0
- package/dist/core/v2ex/use-cases/utils/index.js +9 -0
- package/dist/core/v2ex/use-cases/utils/page-orchestrator.d.ts +24 -0
- package/dist/core/v2ex/use-cases/utils/page-orchestrator.js +93 -0
- package/dist/infra/fetcher/agent.d.ts +10 -0
- package/dist/infra/fetcher/agent.js +17 -0
- package/dist/infra/fetcher/fetcher.d.ts +10 -0
- package/dist/infra/fetcher/fetcher.js +81 -0
- package/dist/infra/fetcher/index.d.ts +3 -0
- package/dist/infra/fetcher/index.js +19 -0
- package/dist/infra/fetcher/types.d.ts +29 -0
- package/dist/infra/fetcher/types.js +6 -0
- package/dist/infra/logger/colors.d.ts +15 -0
- package/dist/infra/logger/colors.js +18 -0
- package/dist/infra/logger/index.d.ts +16 -0
- package/dist/infra/logger/index.js +19 -0
- package/dist/infra/logger/logger.d.ts +34 -0
- package/dist/infra/logger/logger.js +101 -0
- package/dist/infra/storage/cleaner.d.ts +24 -0
- package/dist/infra/storage/cleaner.js +73 -0
- package/dist/infra/storage/index.d.ts +7 -0
- package/dist/infra/storage/index.js +15 -0
- package/dist/infra/storage/paths.d.ts +26 -0
- package/dist/infra/storage/paths.js +53 -0
- package/dist/infra/storage/reader.d.ts +15 -0
- package/dist/infra/storage/reader.js +34 -0
- package/dist/infra/storage/types.d.ts +21 -0
- package/dist/infra/storage/types.js +18 -0
- package/dist/infra/storage/writer.d.ts +16 -0
- package/dist/infra/storage/writer.js +31 -0
- package/package.json +89 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# V2ER Insight AI Analysis System Prompt
|
|
2
|
+
|
|
3
|
+
## Role Definition
|
|
4
|
+
|
|
5
|
+
You are an expert **Human Behavior Analyst** and **Profilist**. Your task is to analyze structured user data from V2EX (
|
|
6
|
+
a technical community) and generate a comprehensive, objective user profile in **Chinese**.
|
|
7
|
+
|
|
8
|
+
## Input Data Context
|
|
9
|
+
|
|
10
|
+
You will receive a JSON object structured as `AnalyzerOutput`. This structure contains three main parts:
|
|
11
|
+
|
|
12
|
+
1. **UserOverview**: Global user metrics.
|
|
13
|
+
2. **Summary (PeriodsSummary)**: Statistical summary of detected activity periods.
|
|
14
|
+
3. **Contents**: Actual post and reply content, segmented by activity periods.
|
|
15
|
+
|
|
16
|
+
### Input Schema
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
/** User Overview - Global metrics */
|
|
20
|
+
interface UserOverview {
|
|
21
|
+
joinDate: string; // Account creation date
|
|
22
|
+
lastActiveTime: string; // Last activity timestamp
|
|
23
|
+
topicReplyRatio: number | null; // Topic/Reply ratio, null if topics hidden
|
|
24
|
+
totalTopics: number | null; // Total topics count, null if topics hidden or 0
|
|
25
|
+
totalReplies: number; // Total replies count
|
|
26
|
+
isTopicsHidden: boolean; // Whether topic list is hidden by user
|
|
27
|
+
dailyRanking: number | null; // Daily activity ranking
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Statistics for a single activity period */
|
|
31
|
+
interface SinglePeriodStats {
|
|
32
|
+
timeRange: string; // e.g., "2015-04-01 to 2017-08-15"
|
|
33
|
+
|
|
34
|
+
// Topic Metrics
|
|
35
|
+
topicCount: number;
|
|
36
|
+
avgTopicReplyCount: number; // Influence: Average replies per topic
|
|
37
|
+
avgTopicClickCount: number; // Attraction: Average clicks per topic
|
|
38
|
+
avgTopicLifecycleDays: number; // Depth: Average topic lifecycle in days
|
|
39
|
+
topicInteractionRatio: number; // Conversion: Replies per click
|
|
40
|
+
topicHourDistribution: Record<number, number>; // 0-23 hour distribution
|
|
41
|
+
topicNodeDistribution: Record<string, number>; // Top nodes for topics
|
|
42
|
+
|
|
43
|
+
// Reply Metrics
|
|
44
|
+
replyCount: number;
|
|
45
|
+
avgReplyLength: number; // Effort: Average reply length (chars)
|
|
46
|
+
directReplyRatio: number; // Interaction style: Direct reply ratio
|
|
47
|
+
avgRepliedTopicHeat: number; // Interest: Average heat of replied topics
|
|
48
|
+
replyWeekdayDistribution: Record<string, number> | null; // Weekly distribution
|
|
49
|
+
replyNodeDistribution: Record<string, number>; // Top nodes for replies
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Summary of all activity periods */
|
|
53
|
+
interface PeriodsSummary {
|
|
54
|
+
totalPeriods: number;
|
|
55
|
+
periods: SinglePeriodStats[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Individual topic content */
|
|
59
|
+
interface ContentTopic {
|
|
60
|
+
title: string;
|
|
61
|
+
nodeName: string;
|
|
62
|
+
content: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Individual reply content */
|
|
66
|
+
interface ContentReply {
|
|
67
|
+
topicTitle: string;
|
|
68
|
+
nodeName: string;
|
|
69
|
+
content: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Full period content */
|
|
73
|
+
interface PeriodContent {
|
|
74
|
+
periodIndex: number;
|
|
75
|
+
topics: ContentTopic[];
|
|
76
|
+
replies: ContentReply[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Chunked period content */
|
|
80
|
+
interface PeriodContentChunk {
|
|
81
|
+
periodIndex: number;
|
|
82
|
+
chunkIndex: number;
|
|
83
|
+
totalChunksInPeriod: number;
|
|
84
|
+
topics: ContentTopic[];
|
|
85
|
+
replies: ContentReply[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Root Analyzer Output Structure */
|
|
89
|
+
interface AnalyzerOutput {
|
|
90
|
+
userOverview: UserOverview;
|
|
91
|
+
summary: PeriodsSummary;
|
|
92
|
+
contents: Array<PeriodContent | PeriodContentChunk>;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Data Structure Logic
|
|
97
|
+
|
|
98
|
+
The `contents` array contains elements of either `PeriodContent` (complete) or `PeriodContentChunk` (partial).
|
|
99
|
+
|
|
100
|
+
- **PeriodContent**: Contains all data for a specific period. Use directly.
|
|
101
|
+
- **PeriodContentChunk**: Contains a subset of data for a period when volume is high.
|
|
102
|
+
- Identify chunks by `periodIndex` and `chunkIndex`.
|
|
103
|
+
- You must **aggregate** all chunks with the same `periodIndex` to form a complete view of that period before
|
|
104
|
+
analysis.
|
|
105
|
+
- Do not treat chunks as separate periods; they are parts of the same whole.
|
|
106
|
+
|
|
107
|
+
## Analysis Mapping Logic
|
|
108
|
+
|
|
109
|
+
Map the provided metrics to the following psychological and behavioral dimensions:
|
|
110
|
+
|
|
111
|
+
| Input Metric (Source) | Analysis Dimension (Target) | Logic / Insight |
|
|
112
|
+
| :--------------------------------------------- | :----------------------------------- | :------------------------------------------------------------------------- |
|
|
113
|
+
| `topicInteractionRatio` & `avgTopicReplyCount` | **Social: Content Appeal** | High = Influential/Engaging; Low = Self-expression/Niche. |
|
|
114
|
+
| `avgTopicLifecycleDays` | **Social: Discussion Depth** | Long = Deep discussions; Short = Q&A or News. |
|
|
115
|
+
| `avgRepliedTopicHeat` | **Behavioral: Heat Sensitivity** | High = Trend Follower (Eating Melons); Low = Independent/Niche. |
|
|
116
|
+
| `topicNodeDistribution` | **Professional: Focus** | Identify tech stack and expertise areas. |
|
|
117
|
+
| `replyWeekdayDistribution` | **Behavioral: Work Pattern** | Weekday high = Professional/Work-related; Weekend high = Hobbyist/Student. |
|
|
118
|
+
| `avgReplyLength` | **Psychological: Conscientiousness** | Long = Serious/Detail-oriented; Short = Casual/Quick. |
|
|
119
|
+
| `directReplyRatio` | **Interaction Style** | High = Starts new threads/Direct answers; Low = Conversationalist/Debater. |
|
|
120
|
+
| `isTopicsHidden` | **Psychological: Privacy** | True = High privacy concern. |
|
|
121
|
+
|
|
122
|
+
## Analysis Guidelines
|
|
123
|
+
|
|
124
|
+
### 1. Psychological Profiling (OCEAN)
|
|
125
|
+
|
|
126
|
+
- **Openness**: Diversity of `nodeName` in both topics and replies.
|
|
127
|
+
- **Conscientiousness**: `avgReplyLength`, structure of content, consistency of activity.
|
|
128
|
+
- **Extraversion**: `totalReplies`, tone of interactions.
|
|
129
|
+
- **Agreeableness**: Tone of replies (polite vs argumentative), helpfullness.
|
|
130
|
+
- **Neuroticism**: Emotional stability in content, reaction to conflicts.
|
|
131
|
+
|
|
132
|
+
### 2. Professional Implementation
|
|
133
|
+
|
|
134
|
+
- **Tech Stack**: Extract specific technologies from `nodeName` and content keywords (e.g., "Python", "Kubernetes", "
|
|
135
|
+
React").
|
|
136
|
+
- **Career Path**: Use `PeriodsSummary` to trace changes in `nodeName` distribution over time (e.g., Frontend ->
|
|
137
|
+
Fullstack -> Management).
|
|
138
|
+
- **Level**: Infer from the complexity of questions asked or answers provided.
|
|
139
|
+
|
|
140
|
+
### 3. Risk & Anomaly Detection
|
|
141
|
+
|
|
142
|
+
- **Account Trading**: Look for sudden, drastic shifts in writing style, formatting, or core interests between
|
|
143
|
+
`periods`.
|
|
144
|
+
- **Bot Activity**: Look for inhumanly consistent posting times or repetitive content patterns.
|
|
145
|
+
|
|
146
|
+
## Output Rules
|
|
147
|
+
|
|
148
|
+
1. **Language**: Simplified Chinese (简体中文).
|
|
149
|
+
2. **Format**: strict JSON object. You may wrap it in a Markdown code block (`json`), but the content must be valid JSON.
|
|
150
|
+
3. **Tone**: Professional, Objective, Analytical.
|
|
151
|
+
|
|
152
|
+
## Output Schema (Strict JSON)
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
interface AIAnalysisResult {
|
|
156
|
+
/**
|
|
157
|
+
* One-sentence persona summary.
|
|
158
|
+
* e.g., "资深后端工程师,关注云原生技术,社区活跃度高,不仅善于分享技术,也热衷于讨论职场话题。"
|
|
159
|
+
*/
|
|
160
|
+
summary: string;
|
|
161
|
+
|
|
162
|
+
professional: {
|
|
163
|
+
tech_stack: string[]; // e.g. ["Java", "Go", "K8s"]
|
|
164
|
+
career_path: string; // e.g. "从早期的 Java 开发逐渐转向云原生架构领域"
|
|
165
|
+
level: string; // e.g. "资深专家"
|
|
166
|
+
focus_coherence: string; // Evaluation of interest consistency
|
|
167
|
+
evolution: {
|
|
168
|
+
summary: string; // Description of professional evolution
|
|
169
|
+
timeline: Array<{
|
|
170
|
+
period: string; // e.g. "2020-2022"
|
|
171
|
+
focus: string; // e.g. "微服务架构"
|
|
172
|
+
}>;
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
personal: {
|
|
177
|
+
hobbies: string[]; // e.g. ["摄影", "咖啡"] based on non-tech nodes
|
|
178
|
+
life_stage: string; // e.g. "职场中坚力量"
|
|
179
|
+
values: string[]; // e.g. ["开源", "极客精神"]
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
psychological: {
|
|
183
|
+
scores: {
|
|
184
|
+
openness: number; // 0-100
|
|
185
|
+
conscientiousness: number; // 0-100
|
|
186
|
+
extraversion: number; // 0-100
|
|
187
|
+
agreeableness: number; // 0-100
|
|
188
|
+
neuroticism: number; // 0-100
|
|
189
|
+
};
|
|
190
|
+
keywords: string[]; // e.g. ["理性", "热心", "直率"]
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
behavioral: {
|
|
194
|
+
role: string; // e.g. "技术布道者", "潜水员"
|
|
195
|
+
interaction_style: string; // e.g. "喜欢深入探讨", "言简意赅"
|
|
196
|
+
active_pattern: string; // e.g. "工作日高频活跃"
|
|
197
|
+
heat_sensitivity: string; // e.g. "只关注技术节点,不追逐热点"
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
social: {
|
|
201
|
+
content_appeal: string; // e.g. "内容质量高,常引起共鸣"
|
|
202
|
+
discussion_depth: string; // e.g. "话题往往能引发深入讨论"
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
risk: {
|
|
206
|
+
level: 'safe' | 'suspicious' | 'high_risk';
|
|
207
|
+
reason: string; // Explain any detected anomalies or "None"
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Provider 实现 - 多轮对话
|
|
3
|
+
*/
|
|
4
|
+
import type { IAIProvider, SessionOptions } from '../types';
|
|
5
|
+
export type { IAIProvider };
|
|
6
|
+
export declare class GeminiProvider implements IAIProvider {
|
|
7
|
+
readonly name = "gemini";
|
|
8
|
+
private ai;
|
|
9
|
+
private model;
|
|
10
|
+
private chat;
|
|
11
|
+
constructor(apiKey: string, model: string);
|
|
12
|
+
/**
|
|
13
|
+
* 创建带有系统提示词的新聊天会话
|
|
14
|
+
*
|
|
15
|
+
* @param systemPrompt 系统提示词,用于指导 AI 的角色和行为
|
|
16
|
+
* @param options.thinkingLevel 思考等级,传入项目小写值(如 'low'),
|
|
17
|
+
* 内部通过 {@link THINKING_LEVEL_MAP} 映射为 SDK 枚举后传入 thinkingConfig
|
|
18
|
+
*/
|
|
19
|
+
createSession(systemPrompt: string, options?: SessionOptions): void;
|
|
20
|
+
/**
|
|
21
|
+
* 发送消息并获取响应
|
|
22
|
+
*/
|
|
23
|
+
sendMessage(content: string): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Gemini Provider 实现 - 多轮对话
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GeminiProvider = void 0;
|
|
7
|
+
const genai_1 = require("@google/genai");
|
|
8
|
+
/**
|
|
9
|
+
* 项目小写 ThinkingLevel → SDK 大写枚举的映射表
|
|
10
|
+
*
|
|
11
|
+
* satisfies 确保每个项目值都有对应 SDK 枚举,SDK 升级时若枚举变化会编译报错。
|
|
12
|
+
* 注意:只将此映射用于枚举覆盖校验,不要将其视为模型级兼容性保证。
|
|
13
|
+
* 例如,部分 Gemini 3 Pro 变体可能只接受 low/high,而 Flash 变体支持
|
|
14
|
+
* minimal/low/medium/high。发起请求前请先校验模型与等级组合,否则运行时仍可能失败。
|
|
15
|
+
*/
|
|
16
|
+
const THINKING_LEVEL_MAP = {
|
|
17
|
+
minimal: genai_1.ThinkingLevel.MINIMAL,
|
|
18
|
+
low: genai_1.ThinkingLevel.LOW,
|
|
19
|
+
medium: genai_1.ThinkingLevel.MEDIUM,
|
|
20
|
+
high: genai_1.ThinkingLevel.HIGH,
|
|
21
|
+
};
|
|
22
|
+
/** 将项目 ThinkingLevel 转为 SDK 枚举值 */
|
|
23
|
+
function toSdkThinkingLevel(level) {
|
|
24
|
+
return level ? THINKING_LEVEL_MAP[level] : undefined;
|
|
25
|
+
}
|
|
26
|
+
class GeminiProvider {
|
|
27
|
+
constructor(apiKey, model) {
|
|
28
|
+
this.name = 'gemini';
|
|
29
|
+
this.chat = null;
|
|
30
|
+
if (!apiKey || apiKey.trim() === '') {
|
|
31
|
+
throw new Error('Gemini API key is required');
|
|
32
|
+
}
|
|
33
|
+
this.model = model;
|
|
34
|
+
this.ai = new genai_1.GoogleGenAI({ apiKey });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 创建带有系统提示词的新聊天会话
|
|
38
|
+
*
|
|
39
|
+
* @param systemPrompt 系统提示词,用于指导 AI 的角色和行为
|
|
40
|
+
* @param options.thinkingLevel 思考等级,传入项目小写值(如 'low'),
|
|
41
|
+
* 内部通过 {@link THINKING_LEVEL_MAP} 映射为 SDK 枚举后传入 thinkingConfig
|
|
42
|
+
*/
|
|
43
|
+
createSession(systemPrompt, options) {
|
|
44
|
+
const sdkThinkingLevel = toSdkThinkingLevel(options?.thinkingLevel);
|
|
45
|
+
const timeout = options?.timeout;
|
|
46
|
+
this.chat = this.ai.chats.create({
|
|
47
|
+
model: this.model,
|
|
48
|
+
config: {
|
|
49
|
+
systemInstruction: systemPrompt,
|
|
50
|
+
...(typeof timeout === 'number' && {
|
|
51
|
+
httpOptions: { timeout },
|
|
52
|
+
}),
|
|
53
|
+
...(sdkThinkingLevel !== undefined && {
|
|
54
|
+
thinkingConfig: { thinkingLevel: sdkThinkingLevel },
|
|
55
|
+
}),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 发送消息并获取响应
|
|
61
|
+
*/
|
|
62
|
+
async sendMessage(content) {
|
|
63
|
+
if (!this.chat) {
|
|
64
|
+
throw new Error('Chat session not created. Call createSession() first.');
|
|
65
|
+
}
|
|
66
|
+
const response = await this.chat.sendMessage({ message: content });
|
|
67
|
+
if (!response.text) {
|
|
68
|
+
throw new Error('Empty response from Gemini API');
|
|
69
|
+
}
|
|
70
|
+
return response.text;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.GeminiProvider = GeminiProvider;
|
|
74
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Provider 导出
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GeminiProvider = void 0;
|
|
7
|
+
var gemini_1 = require("./gemini");
|
|
8
|
+
Object.defineProperty(exports, "GeminiProvider", { enumerable: true, get: function () { return gemini_1.GeminiProvider; } });
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types 模块导出
|
|
3
|
+
*/
|
|
4
|
+
export type { AIAnalysisInput, AnalysisOptions } from './options';
|
|
5
|
+
export type { AIAnalysisResult, ProfessionalProfile, PersonalProfile, PsychologicalProfile, BehavioralProfile, SocialProfile, RiskAssessment, EvolutionTimelineEntry, } from './result';
|
|
6
|
+
export type { IAIProvider, SessionOptions } from './provider';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 分析选项
|
|
3
|
+
*/
|
|
4
|
+
import type { AnalyzerOutput } from '../../../core/analyzer';
|
|
5
|
+
/** AI 分析输入 = Analyzer 输出 */
|
|
6
|
+
export type AIAnalysisInput = AnalyzerOutput;
|
|
7
|
+
/** 分析配置选项 */
|
|
8
|
+
export interface AnalysisOptions {
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
/** 超时时间(毫秒) */
|
|
12
|
+
timeout?: number;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider 接口定义
|
|
3
|
+
*/
|
|
4
|
+
import type { ThinkingLevel } from '../../../config/types/ai';
|
|
5
|
+
/** createSession 可选参数 */
|
|
6
|
+
export interface SessionOptions {
|
|
7
|
+
/** 思考等级 */
|
|
8
|
+
thinkingLevel?: ThinkingLevel;
|
|
9
|
+
/** 请求超时(毫秒) */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
}
|
|
12
|
+
/** AI Provider 接口 */
|
|
13
|
+
export interface IAIProvider {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/** 注意:为了支持异步初始化,返回 void | Promise<void> */
|
|
16
|
+
createSession(systemPrompt: string, options?: SessionOptions): void | Promise<void>;
|
|
17
|
+
sendMessage(content: string): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI 分析结果类型
|
|
3
|
+
*/
|
|
4
|
+
/** 职业发展时间线 */
|
|
5
|
+
export interface EvolutionTimelineEntry {
|
|
6
|
+
period: string;
|
|
7
|
+
focus: string;
|
|
8
|
+
}
|
|
9
|
+
/** 职业画像 */
|
|
10
|
+
export interface ProfessionalProfile {
|
|
11
|
+
tech_stack: string[];
|
|
12
|
+
career_path: string;
|
|
13
|
+
level: string;
|
|
14
|
+
focus_coherence: string;
|
|
15
|
+
evolution: {
|
|
16
|
+
summary: string;
|
|
17
|
+
timeline: EvolutionTimelineEntry[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** 个人生活画像 */
|
|
21
|
+
export interface PersonalProfile {
|
|
22
|
+
hobbies: string[];
|
|
23
|
+
life_stage: string;
|
|
24
|
+
values: string[];
|
|
25
|
+
}
|
|
26
|
+
/** 心理画像 (五大性格特质 OCEAN) */
|
|
27
|
+
export interface PsychologicalProfile {
|
|
28
|
+
scores: {
|
|
29
|
+
openness: number;
|
|
30
|
+
conscientiousness: number;
|
|
31
|
+
extraversion: number;
|
|
32
|
+
agreeableness: number;
|
|
33
|
+
neuroticism: number;
|
|
34
|
+
};
|
|
35
|
+
keywords: string[];
|
|
36
|
+
}
|
|
37
|
+
/** 行为画像 */
|
|
38
|
+
export interface BehavioralProfile {
|
|
39
|
+
role: string;
|
|
40
|
+
interaction_style: string;
|
|
41
|
+
active_pattern: string;
|
|
42
|
+
heat_sensitivity: string;
|
|
43
|
+
}
|
|
44
|
+
/** 社交画像 */
|
|
45
|
+
export interface SocialProfile {
|
|
46
|
+
content_appeal: string;
|
|
47
|
+
discussion_depth: string;
|
|
48
|
+
}
|
|
49
|
+
/** 风险评估 */
|
|
50
|
+
export interface RiskAssessment {
|
|
51
|
+
level: 'safe' | 'suspicious' | 'high_risk';
|
|
52
|
+
reason: string;
|
|
53
|
+
}
|
|
54
|
+
/** AI 分析结果总览 */
|
|
55
|
+
export interface AIAnalysisResult {
|
|
56
|
+
summary: string;
|
|
57
|
+
professional: ProfessionalProfile;
|
|
58
|
+
personal: PersonalProfile;
|
|
59
|
+
psychological: PsychologicalProfile;
|
|
60
|
+
behavioral: BehavioralProfile;
|
|
61
|
+
social: SocialProfile;
|
|
62
|
+
risk: RiskAssessment;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key 解析工具
|
|
3
|
+
*
|
|
4
|
+
* 优先级:
|
|
5
|
+
* 1. 显式传入的参数
|
|
6
|
+
* 2. 配置文件 (~/.v2er-insight/config.json) 中的 ai.apiKey
|
|
7
|
+
* 3. 环境变量 GOOGLE_API_KEY / GEMINI_API_KEY
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 从多个来源解析 API Key
|
|
11
|
+
*
|
|
12
|
+
* 优先级顺序:显式参数 > 配置文件 > 环境变量
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveApiKey(explicit?: string): string | null;
|
|
15
|
+
//# sourceMappingURL=api-key.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* API Key 解析工具
|
|
4
|
+
*
|
|
5
|
+
* 优先级:
|
|
6
|
+
* 1. 显式传入的参数
|
|
7
|
+
* 2. 配置文件 (~/.v2er-insight/config.json) 中的 ai.apiKey
|
|
8
|
+
* 3. 环境变量 GOOGLE_API_KEY / GEMINI_API_KEY
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.resolveApiKey = resolveApiKey;
|
|
12
|
+
const config_1 = require("../../../config");
|
|
13
|
+
const ENV_KEYS = ['GOOGLE_API_KEY', 'GEMINI_API_KEY'];
|
|
14
|
+
/**
|
|
15
|
+
* 从多个来源解析 API Key
|
|
16
|
+
*
|
|
17
|
+
* 优先级顺序:显式参数 > 配置文件 > 环境变量
|
|
18
|
+
*/
|
|
19
|
+
function resolveApiKey(explicit) {
|
|
20
|
+
if (explicit && explicit.trim() !== '') {
|
|
21
|
+
return explicit.trim();
|
|
22
|
+
}
|
|
23
|
+
const config = (0, config_1.getConfig)();
|
|
24
|
+
const configKey = config.ai?.apiKey;
|
|
25
|
+
if (configKey && configKey.trim() !== '') {
|
|
26
|
+
return configKey.trim();
|
|
27
|
+
}
|
|
28
|
+
for (const key of ENV_KEYS) {
|
|
29
|
+
const value = process.env[key];
|
|
30
|
+
if (value && value.trim() !== '') {
|
|
31
|
+
return value.trim();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=api-key.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utils 模块导出
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withRetry = exports.resolveApiKey = void 0;
|
|
7
|
+
var api_key_1 = require("./api-key");
|
|
8
|
+
Object.defineProperty(exports, "resolveApiKey", { enumerable: true, get: function () { return api_key_1.resolveApiKey; } });
|
|
9
|
+
var retry_1 = require("./retry");
|
|
10
|
+
Object.defineProperty(exports, "withRetry", { enumerable: true, get: function () { return retry_1.withRetry; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 重试工具 - 指数退避
|
|
3
|
+
*/
|
|
4
|
+
export interface RetryOptions {
|
|
5
|
+
maxRetries?: number;
|
|
6
|
+
baseDelay?: number;
|
|
7
|
+
maxDelay?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 带重试逻辑执行函数
|
|
11
|
+
*
|
|
12
|
+
* 使用指数退避 + 随机抖动策略
|
|
13
|
+
*/
|
|
14
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
15
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 重试工具 - 指数退避
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withRetry = withRetry;
|
|
7
|
+
const config_1 = require("../../../config");
|
|
8
|
+
/**
|
|
9
|
+
* 带重试逻辑执行函数
|
|
10
|
+
*
|
|
11
|
+
* 使用指数退避 + 随机抖动策略
|
|
12
|
+
*/
|
|
13
|
+
async function withRetry(fn, options = {}) {
|
|
14
|
+
const aiConfig = (0, config_1.getConfig)().ai;
|
|
15
|
+
const { maxRetries = aiConfig?.maxRetries ?? 3, baseDelay = aiConfig?.baseDelay ?? 1000, maxDelay = aiConfig?.maxDelay ?? 10000, } = options;
|
|
16
|
+
const safeMaxRetries = Math.max(0, maxRetries);
|
|
17
|
+
let lastError;
|
|
18
|
+
for (let attempt = 0; attempt <= safeMaxRetries; attempt++) {
|
|
19
|
+
try {
|
|
20
|
+
return await fn();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
24
|
+
if (attempt === safeMaxRetries) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
|
|
28
|
+
const jitter = delay * 0.1 * Math.random();
|
|
29
|
+
await sleep(delay + jitter);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
throw lastError;
|
|
33
|
+
}
|
|
34
|
+
function sleep(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer 输出构建器
|
|
3
|
+
* 整合所有模块,生成最终的 AI 输入数据
|
|
4
|
+
*/
|
|
5
|
+
import type { RawUserData } from './types';
|
|
6
|
+
import type { AnalyzerOutput } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* 构建完整的 Analyzer 输出
|
|
9
|
+
*
|
|
10
|
+
* 处理流程:
|
|
11
|
+
* 1. 计算用户总览
|
|
12
|
+
* 2. 提取所有活动日期
|
|
13
|
+
* 3. 检测活跃期边界
|
|
14
|
+
* 4. 将数据分割到各活跃期
|
|
15
|
+
* 5. 计算每个活跃期的统计
|
|
16
|
+
* 6. 转换并分片内容
|
|
17
|
+
*
|
|
18
|
+
* @param data - V2EX 抓取的原始用户数据
|
|
19
|
+
* @param referenceDate - 参考日期(用于相对时间解析)
|
|
20
|
+
* @returns 完整的 Analyzer 输出
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildAnalyzerOutput(data: RawUserData, referenceDate?: Date): AnalyzerOutput;
|
|
23
|
+
//# sourceMappingURL=builder.d.ts.map
|