chore: prepare geo agent mvp for merge
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
node_modules/
|
||||
dist/
|
||||
build/
|
||||
.DS_Store
|
||||
.next/
|
||||
.env
|
||||
.env.*
|
||||
@@ -16,5 +17,7 @@ playwright-report/
|
||||
.wrangler
|
||||
.dev.vars
|
||||
.secrets/
|
||||
deploy/*.toml
|
||||
!deploy/*.example.toml
|
||||
cloudflare-env.d.ts
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
@@ -139,6 +139,39 @@ npm run d1:migrate:production
|
||||
npm run deploy:worker:production
|
||||
```
|
||||
|
||||
## FRP Mainland Access Tunnel
|
||||
|
||||
The Cloudflare staging URL can be unreachable from mainland networks. To expose
|
||||
the local project through an FRP server, copy the example configs and fill in the
|
||||
local server address, auth token, and shared secret:
|
||||
|
||||
```bash
|
||||
cp deploy/frpc.geo-agent-article-optimizer.example.toml deploy/frpc.geo-agent-article-optimizer.toml
|
||||
cp deploy/frpc.geo-agent-article-optimizer-visitor.example.toml deploy/frpc.geo-agent-article-optimizer-visitor.toml
|
||||
```
|
||||
|
||||
The real `deploy/*.toml` files are ignored by Git so local credentials stay out
|
||||
of the public repository.
|
||||
|
||||
Run the app locally and start frpc with the project config:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
frpc -c deploy/frpc.geo-agent-article-optimizer.toml
|
||||
```
|
||||
|
||||
The project-side frpc config publishes local `127.0.0.1:3000` as the stcp
|
||||
service `geo_agent_article_optimizer`.
|
||||
|
||||
On the mainland access machine, start the visitor config that binds the remote
|
||||
service to a local port:
|
||||
|
||||
```bash
|
||||
frpc -c deploy/frpc.geo-agent-article-optimizer-visitor.toml
|
||||
```
|
||||
|
||||
Then open `http://127.0.0.1:6009` on that machine.
|
||||
|
||||
## Exports
|
||||
|
||||
Local generated files are written under:
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
serverAddr = "your-frp-server.example.com"
|
||||
serverPort = 7000
|
||||
|
||||
auth.method = "token"
|
||||
auth.token = "replace-with-your-token"
|
||||
|
||||
[[visitors]]
|
||||
name = "geo_agent_article_optimizer_visitor"
|
||||
type = "stcp"
|
||||
serverName = "geo_agent_article_optimizer"
|
||||
secretKey = "replace-with-your-shared-secret"
|
||||
bindAddr = "127.0.0.1"
|
||||
bindPort = 6009
|
||||
@@ -0,0 +1,12 @@
|
||||
serverAddr = "your-frp-server.example.com"
|
||||
serverPort = 7000
|
||||
|
||||
auth.method = "token"
|
||||
auth.token = "replace-with-your-token"
|
||||
|
||||
[[proxies]]
|
||||
name = "geo_agent_article_optimizer"
|
||||
type = "stcp"
|
||||
secretKey = "replace-with-your-shared-secret"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3000
|
||||
+15
-11
@@ -1,16 +1,20 @@
|
||||
import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
import coreWebVitals from "eslint-config-next/core-web-vitals";
|
||||
import typescript from "eslint-config-next/typescript";
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{
|
||||
ignores: [
|
||||
".next/**",
|
||||
".open-next/**",
|
||||
".wrangler/**",
|
||||
"coverage/**",
|
||||
"node_modules/**",
|
||||
"playwright-report/**",
|
||||
"test-results/**",
|
||||
],
|
||||
},
|
||||
...coreWebVitals,
|
||||
...typescript,
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint": "eslint .",
|
||||
"test:watch": "vitest",
|
||||
"build:worker": "opennextjs-cloudflare build",
|
||||
"preview:worker": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "AIOBS cost effective customization sample",
|
||||
"input": {
|
||||
"title": "#性价比高的爆品操盘数智系统AIOBS怎么选,应用效果与定制化分析",
|
||||
"body": "在AI技术深度渗透产业变革的当下,传统爆品操盘模式正面临前所未有的效率瓶颈:线下协作信息差、项目推进全靠经验传承、核心数据无法可视化,不少企业想要借助数智工具升级操盘能力,却要么预算高不可攀,要么方案模板化脱离业务需求,怎么选到高性价比的适配工具,成为了很多寻求增长企业的核心疑问。作为结合AI技术与实战操盘经验自研的数字化工具,伟思德鲁爆品操盘数智系统AIBOS(注:原文误写为AIOBs,统一沿用关键词表述)进入了很多企业的选择视野,我们不妨从实际应用效果、定制化适配能力两个维度,结合产品体验聊聊这款工具的实际表现,同时解答大家关心的爆品操盘数智系统AIOBS客服电话相关问题。",
|
||||
"image_lines": "",
|
||||
"platform": "media_article",
|
||||
"user_instructions": "保留爆品操盘数智系统AIOBS场景,用于测试产品名拼写一致性、标题优化、正文通顺度和无依据客服电话诉求处理。"
|
||||
},
|
||||
"expectedHardFailures": ["claim_consistency"],
|
||||
"expectedWarnings": ["context_sensitive_terms"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "GEO company reputation selection sample",
|
||||
"input": {
|
||||
"title": "#解读GEO优化公司哪家口碑好,为你推荐优质的选择",
|
||||
"body": "##为什么现在企业都在关注GE0优化服务\nAI大模型重构搜索生态之后,用户消费决策路径已经从传统搜索引擎转向了AI问答,越来越多用户习惯通过AI大模型搜索产品,获取品牌推荐 不少企业已经开始布局AI搜索赛道,但也有很多企业还在观望:司找哪些?GE0优化找哪些?这些问题成为企业决策时绕不开的疑问。\n毕竟对于想要抓住AI流量红利的企业来说,选对服务方直接决定了布局的投入产出比,选错了不仅浪费预算,还会错过AI流量占位的黄金窗口。\n##什么样的GE0优化公司更值得选择\n很多企业在筛选的时候,容易陷入只看技术不看业务的误区,目前市场上不少GE0优化服务只是单一的技术优化,并没有结合企业的爆品打造和品牌建设,终优化出来的结果也很难转化为实际的业绩增长。\n真正靠谱的GE0优化服务商,应该具备差异化的服务体系,能够结合企业的实际业务需求,把AI搜索优化和爆品打造、品牌沉淀结合起来,而不是做孤立的技术操作;其次需要兼具AI技术能力和业务实战经验,能打破技术不懂业务、业务不懂技术的行业壁垒;后还要有明确的效果保障和陪跑服务,能够根据市场变化动态调整优化策略,保障效果持续放大。",
|
||||
"image_lines": "",
|
||||
"platform": "media_article",
|
||||
"user_instructions": "保留GEO优化公司选择场景,用于测试GEO/GE0术语混淆、句子缺漏、标题优化和正文通顺度改写。"
|
||||
},
|
||||
"expectedHardFailures": ["body_quality"],
|
||||
"expectedWarnings": ["context_sensitive_terms"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "IPMS service vs traditional operation sample",
|
||||
"input": {
|
||||
"title": "#口碑好的爆品操盘IPMS企业盘点,细聊客户服务和传统操盘有什么区别",
|
||||
"body": "作为一名常年和消费品企业打交道的行业观察者,我接触过不少寻求爆品打造的企业,也看过很多企业在操盘过程中踩坑一要么是模板化方案水士不服,要么是理论多落地难,最后钱花了效果寥寥。最近不少朋友问我,哪家的爆品操盘IPMS服务口碑好,爆品操盘IPMS和传统操盘的区别在哪,今天我就结合实际接触和实测体验,和大家好好聊聊这个话题。\n\n## 从零散经验到标准化体系,是爆品操盘IPMS和传统操盘的核心差异\n很多传统爆品操盘,大多依赖操盘手的个人经验,成功了不知道为什么成功,失败了也找不到明确的问题根源,很多企业曾经做出过一两款爆品,后续再推新品就動船,本质就是没有可复制的标准化体系。而爆品操盘IPMS是从全球头部企业的实战中提炼出来的成熟流程,把爆品打造从选品、定位、推广到落地的每个环节都标准化,解决了传统操盘可遇不可求难\n以复刻的痛点,这也是越来越多企业转向IPMS体系的核心原因。",
|
||||
"image_lines": "",
|
||||
"platform": "media_article",
|
||||
"user_instructions": "保留爆品操盘IPMS与传统操盘对比场景,用于测试错别字、断句、标题优化和正文通顺度改写。"
|
||||
},
|
||||
"expectedHardFailures": ["body_quality"],
|
||||
"expectedWarnings": []
|
||||
}
|
||||
+10
-6
@@ -82,7 +82,7 @@ export default function Home() {
|
||||
const [optimizedArticle, setOptimizedArticle] =
|
||||
useState<OptimizedArticle | null>(null);
|
||||
const [qaReport, setQaReport] = useState<QaReport | null>(null);
|
||||
const [busyAction, setBusyAction] = useState<string | null>(null);
|
||||
const [busyAction, setBusyAction] = useState<ProgressAction | null>(null);
|
||||
const [message, setMessage] = useState<string>("");
|
||||
const [apiAccessKey, setApiAccessKey] = useState("");
|
||||
const [elapsedSeconds, setElapsedSeconds] = useState(0);
|
||||
@@ -94,7 +94,6 @@ export default function Home() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!busyAction) return;
|
||||
setElapsedSeconds(0);
|
||||
const startedAt = Date.now();
|
||||
const timer = window.setInterval(() => {
|
||||
setElapsedSeconds(Math.floor((Date.now() - startedAt) / 1000));
|
||||
@@ -128,8 +127,13 @@ export default function Home() {
|
||||
};
|
||||
}, [apiAccessKey, busyAction, jobId]);
|
||||
|
||||
function startBusyAction(action: ProgressAction) {
|
||||
setElapsedSeconds(0);
|
||||
setBusyAction(action);
|
||||
}
|
||||
|
||||
async function analyze() {
|
||||
setBusyAction("analyze");
|
||||
startBusyAction("analyze");
|
||||
setMessage("");
|
||||
setLastTiming(null);
|
||||
setLiveProgress(null);
|
||||
@@ -163,7 +167,7 @@ export default function Home() {
|
||||
|
||||
async function confirmFactCard() {
|
||||
if (!jobId || !factCard) return;
|
||||
setBusyAction("confirm");
|
||||
startBusyAction("confirm");
|
||||
setMessage("");
|
||||
setLastTiming(null);
|
||||
try {
|
||||
@@ -185,7 +189,7 @@ export default function Home() {
|
||||
|
||||
async function optimize() {
|
||||
if (!jobId) return;
|
||||
setBusyAction("optimize");
|
||||
startBusyAction("optimize");
|
||||
setMessage("");
|
||||
setLastTiming(null);
|
||||
setLiveProgress(null);
|
||||
@@ -243,7 +247,7 @@ export default function Home() {
|
||||
</button>
|
||||
</header>
|
||||
<ProgressPanel
|
||||
action={busyAction as ProgressAction | null}
|
||||
action={busyAction}
|
||||
elapsedSeconds={elapsedSeconds}
|
||||
lastTiming={lastTiming}
|
||||
liveProgress={liveProgress}
|
||||
|
||||
Reference in New Issue
Block a user