热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词可根据输入代码和优化目标,分析算法性能瓶颈和逻辑低效,提供结构化改进建议及优化思路。支持多语言代码分析,输出条理清晰的优化报告和可执行示例,帮助开发者快速提升算法效率和代码质量。
unvisited_indices = [i for i in range(n) if not visited[i]] 并 min(...),两者均为 O(n),外层循环 O(n) 次,总为 O(n^2)。for v in range(n) 再用 w != 0 判断是否有边,本质上每次都做 O(n) 的无效检查;稀疏图实际边数 m ≪ n^2,导致大量无效遍历。u 后才检测 dist[u] == INF 才 early break;但在此之前已做一次 O(n) 的最小值扫描(无效开销)。unvisited_indices,避免 O(n) 列表创建与拷贝,减少内存与 GC 压力。from typing import List, Tuple, Optional
INF = 10**18
def matrix_to_adj(matrix: List[List[int]], directed: bool = False) -> List[List[Tuple[int, int]]]:
"""将邻接矩阵转换为邻接表。
- 约定:0 表示无边;若需支持 0 权边,请改用 None/INF 表示无边。
- 检查:方阵、负权。
"""
n = len(matrix)
if any(len(row) != n for row in matrix):
raise ValueError("Input matrix must be square")
adj: List[List[Tuple[int, int]]] = [[] for _ in range(n)]
for u, row in enumerate(matrix):
for v, w in enumerate(row):
if w < 0:
raise ValueError(f"Negative edge detected: ({u}, {v}) weight={w}")
if w != 0:
adj[u].append((v, w))
if not directed and u != v:
# 若原图无向,则双向加入
# 注意:若原矩阵已包含对称边,此处重复插入需额外去重(此示例假设矩阵仅单向存储或对称但只插入一次)
pass
# 如果需要无向图:在上方 else 中改为 adj[v].append((u, w))
return adj
import heapq
from typing import List, Tuple, Optional
INF = 10**18
def dijkstra_adj(n: int,
adj: List[List[Tuple[int, int]]],
start: int,
target: Optional[int] = None) -> List[int]:
"""单源最短路(邻接表 + 最小堆)。
- 时间复杂度:O(m log n)
- 早停:若提供 target,当其被堆弹出即结束
- 负权:构图阶段需保证无负权
"""
if not (0 <= start < n):
raise ValueError(f"start out of range: {start}")
dist = [INF] * n
dist[start] = 0
pq: List[Tuple[int, int]] = [(0, start)] # (distance, node)
while pq:
d, u = heapq.heappop(pq)
# 过期条目:若当前弹出距离大于已知最短距离,则跳过
if d != dist[u]:
continue
# 目标早停:只需某个目标点的最短路时可立即退出
if target is not None and u == target:
break
# 仅遍历实际邻居,避免对所有顶点扫描
for v, w in adj[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(pq, (nd, v))
return dist
def dijkstra_adj_with_parent(n: int,
adj: List[List[Tuple[int, int]]],
start: int,
target: Optional[int] = None):
import heapq
dist = [INF] * n
parent = [-1] * n
dist[start] = 0
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d != dist[u]:
continue
if target is not None and u == target:
break
for v, w in adj[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
parent[v] = u
heapq.heappush(pq, (nd, v))
return dist, parent
def reconstruct_path(parent: List[int], start: int, target: int) -> List[int]:
path = []
cur = target
while cur != -1:
path.append(cur)
if cur == start:
break
cur = parent[cur]
path.reverse()
return path if path and path[0] == start else []
if __name__ == "__main__":
# 原示例图(对称矩阵 => 无向图)
graph = [
[0, 7, 0, 9, 0, 0],
[7, 0, 10, 15, 0, 0],
[0, 10, 0, 11, 0, 0],
[9, 15, 11, 0, 6, 0],
[0, 0, 0, 6, 0, 9],
[0, 0, 0, 0, 9, 0]
]
adj = [[] for _ in range(len(graph))]
# 将对称非零视为无向边
for u in range(len(graph)):
for v in range(len(graph)):
w = graph[u][v]
if w < 0:
raise ValueError("Negative edge not allowed for Dijkstra")
if w != 0:
adj[u].append((v, w))
dist = dijkstra_adj(len(adj), adj, start=0)
print(dist)
import random, time
INF = 10**18
def generate_sparse_adj(n: int, m: int, undirected: bool = True, seed: int = 42):
random.seed(seed)
adj = [[] for _ in range(n)]
seen = set()
while len(seen) < m:
u = random.randrange(n)
v = random.randrange(n)
if u == v:
continue
key = (u, v) if not undirected else tuple(sorted((u, v)))
if key in seen:
continue
seen.add(key)
w = random.randint(1, 100)
adj[u].append((v, w))
if undirected:
adj[v].append((u, w))
return adj
def dijkstra_matrix_baseline(matrix, start: int):
# 仅用于小规模对比;与用户代码一致的瓶颈实现
n = len(matrix)
dist = [INF] * n
visited = [False] * n
dist[start] = 0
for _ in range(n):
# 构造未访问列表 + 线性最小值扫描(瓶颈)
unvisited_indices = [i for i in range(n) if not visited[i]]
if not unvisited_indices:
break
u = min(unvisited_indices, key=lambda i: dist[i])
if dist[u] == INF:
break
visited[u] = True
for v in range(n):
w = matrix[u][v]
if w != 0 and not visited[v]:
nd = dist[u] + w
if nd < dist[v]:
dist[v] = nd
return dist
def benchmark_large():
n, m = 10_000, 100_000
adj = generate_sparse_adj(n, m, undirected=True, seed=1)
t0 = time.perf_counter()
dist = dijkstra_adj(n, adj, start=0)
t1 = time.perf_counter()
reachable = sum(d != INF for d in dist)
print(f"[AdjList+Heap] n={n}, m={m}, time={t1 - t0:.3f}s, reachable={reachable}")
def benchmark_small_matrix():
# 小规模矩阵版对比(矩阵在大规模下不可行)
n = 2000
p = 0.001 # 稀疏概率
random.seed(2)
matrix = [[0]*n for _ in range(n)]
for u in range(n):
for v in range(n):
if u != v and random.random() < p:
w = random.randint(1, 50)
matrix[u][v] = w
matrix[v][u] = w # 对称 => 无向
t0 = time.perf_counter()
dist0 = dijkstra_matrix_baseline(matrix, start=0)
t1 = time.perf_counter()
print(f"[Matrix O(n^2)] n={n}, p={p}, time={t1 - t0:.3f}s, reachable={sum(d != INF for d in dist0)}")
if __name__ == "__main__":
benchmark_small_matrix() # 展示矩阵版在小规模下的基线耗时
benchmark_large() # 展示优化版在大规模稀疏图上的耗时
通过以上调整,算法在稀疏图上的无效遍历与临时列表构造被完全消除,复杂度达到预期的 O(m log n),并通过早停与输入校验提高鲁棒性。示例与基准代码可直接执行以验证性能与正确性。
以下为可执行示例,包含:
编译示例:
代码(自包含,可直接运行):
#include <bits/stdc++.h>
#ifdef _OPENMP
#include <omp.h>
#endif
using namespace std;
// 将二维vector扁平化为行优先的一维数组
static vector<double> flatten(const vector<vector<double>>& M, size_t& rows, size_t& cols) {
rows = M.size();
cols = M.empty() ? 0 : M[0].size();
vector<double> out(rows * cols);
for (size_t i = 0; i < rows; ++i) {
const auto& row = M[i];
for (size_t j = 0; j < cols; ++j) {
out[i * cols + j] = row[j];
}
}
return out;
}
// 转置 B:输入 B(k×m) -> 输出 BT(m×k),均为行优先
static vector<double> transpose_B(const vector<double>& B, size_t k, size_t m) {
vector<double> BT(m * k);
for (size_t kk = 0; kk < k; ++kk) {
const size_t B_row = kk * m;
for (size_t j = 0; j < m; ++j) {
BT[j * k + kk] = B[B_row + j];
}
}
return BT;
}
// 朴素三重循环(扁平数组版,便于公平对比)
static void matmul_naive_flat(const vector<double>& A, const vector<double>& B,
vector<double>& C, size_t n, size_t kdim, size_t m) {
fill(C.begin(), C.end(), 0.0);
for (size_t i = 0; i < n; ++i) {
const size_t A_row = i * kdim;
const size_t C_row = i * m;
for (size_t j = 0; j < m; ++j) {
double sum = 0.0;
for (size_t k = 0; k < kdim; ++k) {
sum += A[A_row + k] * B[k * m + j];
}
C[C_row + j] = sum;
}
}
}
// 可选:AVX2/FMA 向量化内核(按 k 维向量化),不可用则退化为标量
static inline double dot_k(const double* a, const double* b, size_t len) {
#if defined(__AVX2__) && defined(__FMA__)
const size_t step = 4; // 256位寄存器,4个double
size_t p = 0;
__m256d acc = _mm256_setzero_pd();
for (; p + step <= len; p += step) {
__m256d va = _mm256_loadu_pd(a + p);
__m256d vb = _mm256_loadu_pd(b + p);
acc = _mm256_fmadd_pd(va, vb, acc);
}
alignas(32) double tmp[4];
_mm256_store_pd(tmp, acc);
double sum = tmp[0] + tmp[1] + tmp[2] + tmp[3];
for (; p < len; ++p) sum += a[p] * b[p];
return sum;
#else
double sum = 0.0;
for (size_t p = 0; p < len; ++p) sum += a[p] * b[p];
return sum;
#endif
}
// 块分解 + B转置 + 可选OpenMP并行 + 可选AVX2
static void matmul_blocked_BT(const vector<double>& A, const vector<double>& BT,
vector<double>& C, size_t n, size_t kdim, size_t m) {
// 块大小:建议根据CPU缓存调优
const size_t Ti = 32, Tj = 32, Tk = 64;
fill(C.begin(), C.end(), 0.0);
// 并行化:对 i0、j0 两层 tile 并行
#pragma omp parallel for collapse(2) schedule(static)
for (size_t i0 = 0; i0 < n; i0 += Ti) {
for (size_t j0 = 0; j0 < m; j0 += Tj) {
const size_t i_end = min(i0 + Ti, n);
const size_t j_end = min(j0 + Tj, m);
for (size_t k0 = 0; k0 < kdim; k0 += Tk) {
const size_t k_end = min(k0 + Tk, kdim);
const size_t k_len = k_end - k0;
for (size_t i = i0; i < i_end; ++i) {
const double* Ai_block = &A[i * kdim + k0];
size_t C_row = i * m;
for (size_t j = j0; j < j_end; ++j) {
const double* BTj_block = &BT[j * kdim + k0];
double sum = C[C_row + j]; // 保持部分和
// k-block 内累加(SIMD/标量统一)
sum += dot_k(Ai_block, BTj_block, k_len);
C[C_row + j] = sum;
}
}
}
}
}
}
// 误差校验
static double l2_diff(const vector<double>& X, const vector<double>& Y) {
double acc = 0.0;
for (size_t i = 0; i < X.size(); ++i) {
double d = X[i] - Y[i];
acc += d * d;
}
return sqrt(acc);
}
// 基准工具
static double benchmark(function<void()> fn, int warmup = 1, int repeat = 3) {
for (int w = 0; w < warmup; ++w) fn();
double best = numeric_limits<double>::max();
for (int r = 0; r < repeat; ++r) {
auto t0 = chrono::high_resolution_clock::now();
fn();
auto t1 = chrono::high_resolution_clock::now();
double ms = chrono::duration<double, milli>(t1 - t0).count();
best = min(best, ms);
}
return best; // ms
}
int main() {
// 维度:可修改为 1024
const size_t n = 1024, kdim = 1024, m = 1024;
// 随机初始化
mt19937_64 rng(12345);
uniform_real_distribution<double> dist(-1.0, 1.0);
// 构造二维以便与原问题形式一致(随后扁平化)
vector<vector<double>> A2D(n, vector<double>(kdim)), B2D(kdim, vector<double>(m));
for (size_t i = 0; i < n; ++i)
for (size_t k = 0; k < kdim; ++k)
A2D[i][k] = dist(rng);
for (size_t k = 0; k < kdim; ++k)
for (size_t j = 0; j < m; ++j)
B2D[k][j] = dist(rng);
// 扁平化
size_t nr, kc, mc;
vector<double> A = flatten(A2D, nr, kc);
vector<double> B = flatten(B2D, kc, mc);
vector<double> C_naive(n * m), C_opt(n * m);
// 转置 B
vector<double> BT = transpose_B(B, kdim, m);
// 基准:朴素
double t_naive_ms = benchmark([&](){
matmul_naive_flat(A, B, C_naive, n, kdim, m);
}, 1, 1);
double flops = 2.0 * n * m * kdim; // mul+add
double gflops_naive = flops / (t_naive_ms / 1000.0) / 1e9;
// 基准:块分解 + B转置 + 可选并行 + 可选SIMD
double t_opt_ms = benchmark([&](){
matmul_blocked_BT(A, BT, C_opt, n, kdim, m);
}, 1, 1);
double gflops_opt = flops / (t_opt_ms / 1000.0) / 1e9;
// 正确性校验
double err = l2_diff(C_naive, C_opt);
cout << fixed << setprecision(3);
cout << "Naive time (ms): " << t_naive_ms << ", GFLOPS: " << gflops_naive << "\n";
cout << "Optimized time (ms): " << t_opt_ms << ", GFLOPS: " << gflops_opt << "\n";
cout << "Speedup: " << (t_naive_ms / t_opt_ms) << "x, L2 error: " << err << "\n";
#ifdef _OPENMP
cout << "OpenMP threads: " << omp_get_max_threads() << "\n";
#endif
return 0;
}
说明与建议:
预期效果(在 1024×1024 双精度上):
strings.Contains/Split;os.ReadFile 将文件一次性读入内存,并使用 strings.Split 与多次 strings.Contains 做朴素匹配,存在明显的内存峰值与 CPU 低利用问题。os.ReadFile 会将整个文件内容加载到内存,对大日志文件会出现峰值内存、GC 压力和潜在 OOM 风险。strings.Split 与多次 Contains 导致重复扫描与额外分配
Split 为每一行创建字符串切片;每行再进行 3 次 Contains,在最坏情况下每行会扫描多次、产生额外临时对象与 CPU 浪费。bufio.Scanner 默认 token 限制为 64KB,超长行易报错;需要显式扩大缓冲或改用 bufio.Reader。bufio.Scanner(并显式提升 buffer 上限)或 bufio.Reader.ReadLine 逐行读取,避免整文件载入。Contains;避免子串和临时切片创建。runtime.GOMAXPROCS(0) 或按 I/O/CPU 特性调参;channel 使用有界容量形成背压;合理的 scanner buffer 避免大规模分配。runtime.MemStats(Alloc、TotalAlloc、Sys)前后快照;在并行方案中不使用锁或仅限合并阶段,锁争用可用 pprof/mutex 在扩展场景下观测。testing 基准对解析函数与管道整体进行压测;提供示例数据生成与基准,确保结果一致性(无丢行、统计相同)。Contains 带来的重复扫描与分配,提高单行解析效率。bufio.Scanner 默认限制导致的不稳定。以下为可执行示例(主程序 + 并行统计),包含流式读取、worker pool、索引匹配与基本指标采集。可直接构建运行。
// main.go
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
)
type Metrics struct {
Lines int64
Duration time.Duration
Throughput float64 // lines/s
AllocBefore uint64
AllocAfter uint64
TotalAlloc uint64
Sys uint64
NumGC uint32
}
type Stats struct {
INFO int64
WARN int64
ERROR int64
}
// classifyLevel performs a single-pass index match without allocations.
// Returns 0 for INFO, 1 for WARN, 2 for ERROR, -1 if not found.
func classifyLevel(s string) int {
// match patterns like "[INFO]" "[WARN]" "[ERROR]"
// we iterate once; check fixed-width token when '[' found
for i := 0; i+5 < len(s); i++ {
if s[i] != '[' {
continue
}
// ensure closing bracket exists at i+5
if s[i+5] != ']' {
continue
}
// check 4 chars
switch s[i+1] {
case 'I':
if s[i+2] == 'N' && s[i+3] == 'F' && s[i+4] == 'O' {
return 0
}
case 'W':
if s[i+2] == 'A' && s[i+3] == 'R' && s[i+4] == 'N' {
return 1
}
case 'E':
if s[i+2] == 'R' && s[i+3] == 'R' && s[i+4] == 'O' {
return 2
}
}
// if not match, continue scanning this line
}
return -1
}
// countLevelsParallel streams lines from r, parses with worker pool, and aggregates.
func countLevelsParallel(r io.Reader, workers, chanSize, scannerMaxToken int) (Stats, Metrics, error) {
var metrics Metrics
var stats Stats
// metrics: mem snapshot before
var msBefore, msAfter runtime.MemStats
runtime.ReadMemStats(&msBefore)
metrics.AllocBefore = msBefore.Alloc
start := time.Now()
lineCh := make(chan string, chanSize)
resultsCh := make(chan Stats, workers)
var wg sync.WaitGroup
// Workers: local accumulation, then emit result
if workers <= 0 {
workers = runtime.GOMAXPROCS(0)
}
for w := 0; w < workers; w++ {
wg.Add(1)
go func() {
defer wg.Done()
var local Stats
for line := range lineCh {
metrics.Lines++ // counting lines processed (atomic-free, single producer if moved)
switch classifyLevel(line) {
case 0:
local.INFO++
case 1:
local.WARN++
case 2:
local.ERROR++
}
}
resultsCh <- local
}()
}
// Reader goroutine: buffered scanning with enlarged token limit
// We use bufio.Scanner for simplicity; for ultra-long lines, consider bufio.Reader.ReadLine.
scanner := bufio.NewScanner(r)
// enlarge the buffer to handle long lines
buf := make([]byte, 64*1024)
if scannerMaxToken <= 0 {
scannerMaxToken = 1024 * 1024 // default 1MB
}
scanner.Buffer(buf, scannerMaxToken)
// read lines and send to workers
for scanner.Scan() {
lineCh <- scanner.Text()
}
close(lineCh) // signal workers to finish
// handle scan error
if err := scanner.Err(); err != nil {
// still collect partial results from finished workers
// ensure wg completes before draining results
wg.Wait()
close(resultsCh)
for res := range resultsCh {
stats.INFO += res.INFO
stats.WARN += res.WARN
stats.ERROR += res.ERROR
}
metrics.Duration = time.Since(start)
runtime.ReadMemStats(&msAfter)
metrics.AllocAfter = msAfter.Alloc
metrics.TotalAlloc = msAfter.TotalAlloc - msBefore.TotalAlloc
metrics.Sys = msAfter.Sys
metrics.NumGC = msAfter.NumGC - msBefore.NumGC
if metrics.Duration > 0 {
metrics.Throughput = float64(metrics.Lines) / metrics.Duration.Seconds()
}
return stats, metrics, err
}
// wait workers and aggregate
wg.Wait()
close(resultsCh)
for res := range resultsCh {
stats.INFO += res.INFO
stats.WARN += res.WARN
stats.ERROR += res.ERROR
}
// finalize metrics
metrics.Duration = time.Since(start)
runtime.ReadMemStats(&msAfter)
metrics.AllocAfter = msAfter.Alloc
metrics.TotalAlloc = msAfter.TotalAlloc - msBefore.TotalAlloc
metrics.Sys = msAfter.Sys
metrics.NumGC = msAfter.NumGC - msBefore.NumGC
if metrics.Duration > 0 {
metrics.Throughput = float64(metrics.Lines) / metrics.Duration.Seconds()
}
return stats, metrics, nil
}
func main() {
var (
filePath string
workers int
chanSize int
scannerMaxToken int
)
flag.StringVar(&filePath, "file", "", "path to log file")
flag.IntVar(&workers, "workers", runtime.GOMAXPROCS(0), "number of parsing workers")
flag.IntVar(&chanSize, "chan", 4096, "line channel buffer size")
flag.IntVar(&scannerMaxToken, "maxline", 1<<20, "max line size for scanner (bytes)")
flag.Parse()
if filePath == "" {
fmt.Println("Usage: logstats -file <path> [-workers N] [-chan M] [-maxline B]")
return
}
f, err := os.Open(filePath)
if err != nil {
fmt.Println("error:", err)
return
}
defer f.Close()
stats, metrics, err := countLevelsParallel(f, workers, chanSize, scannerMaxToken)
if err != nil {
fmt.Println("warning: scan error:", err)
}
// Structured report
fmt.Println("=== Log Level Stats ===")
fmt.Printf("INFO: %d\n", stats.INFO)
fmt.Printf("WARN: %d\n", stats.WARN)
fmt.Printf("ERROR: %d\n", stats.ERROR)
fmt.Println("=== Metrics ===")
fmt.Printf("Lines: %d\n", metrics.Lines)
fmt.Printf("Duration: %v\n", metrics.Duration)
fmt.Printf("Throughput: %.2f lines/s\n", metrics.Throughput)
fmt.Printf("Alloc Before: %d bytes\n", metrics.AllocBefore)
fmt.Printf("Alloc After: %d bytes\n", metrics.AllocAfter)
fmt.Printf("Total Alloc (+): %d bytes\n", metrics.TotalAlloc)
fmt.Printf("Sys: %d bytes\n", metrics.Sys)
fmt.Printf("GC cycles (+): %d\n", metrics.NumGC)
// Note: lock contention is minimized by design (local counters, channel fan-in).
// For deeper analysis, run with pprof: go test -run=^$ -bench=. -cpuprofile cpu.out -memprofile mem.out
}
说明与使用要点:
GOMAXPROCS(0) 个 worker;可根据 CPU/磁盘/网络情况调参。chanSize 控制背压与内存占用。scannerMaxToken 需根据最大行长度设置,避免因超长行报错。超大行场景可改为 bufio.Reader.ReadLine。"[INFO]"。可选基准和扩展(片段):
// classify_test.go
package main
import (
"bytes"
"fmt"
"io"
"math/rand"
"testing"
"time"
)
func BenchmarkClassify(b *testing.B) {
line := "[INFO] something happened at " + time.Now().String()
for i := 0; i < b.N; i++ {
_ = classifyLevel(line)
}
}
func genLogs(n int) []byte {
levels := []string{"[INFO]", "[WARN]", "[ERROR]"}
var buf bytes.Buffer
r := rand.New(rand.NewSource(1))
for i := 0; i < n; i++ {
lv := levels[r.Intn(len(levels))]
buf.WriteString(fmt.Sprintf("%s message %d\n", lv, i))
}
return buf.Bytes()
}
func BenchmarkPipeline(b *testing.B) {
data := genLogs(200_000)
for i := 0; i < b.N; i++ {
r := bytes.NewReader(data)
// small channel to induce backpressure; moderate workers
_, _, err := countLevelsParallel(io.NopCloser(r), 8, 1024, 1<<20)
if err != nil {
b.Fatal(err)
}
}
}
说明:
BenchmarkClassify 衡量单行解析性能;BenchmarkPipeline 评估端到端吞吐。可叠加 -benchmem 查看分配情况。go test -bench=. -run=^$ -mutexprofile mutex.out -blockprofile block.out 并用 go tool pprof 分析。调参建议与稳定性保证:
GOMAXPROCS(0) 到其 2 倍,视解析复杂度与 I/O 速度调整;I/O 或网络受限时,适度减少。bufio.Reader.ReadLine 并复制到 sync.Pool 管理的缓冲(更细粒度内存复用)。close(lineCh) 和 WaitGroup 同步,确保无丢行。通过以上优化,整体可达到:
帮助技术开发人员优化算法,精准识别低效代码结构,并通过专业分析提供改进建议,提升代码运行效率和整体性能。
帮助开发者快速诊断和解决代码中的性能问题,提升开发效率和代码可靠性。
优化大规模数据处理代码,处理更复杂的数据集,并减少计算时间。
改进机器学习或深度学习模型的算法效率,实现更高效的训练和推理。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
半价获取高级提示词-优惠即将到期