Corin1998 commited on
Commit
cc571ea
·
verified ·
1 Parent(s): 764de79

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +28 -0
  2. README.md +15 -5
  3. app.py +138 -0
  4. requirements.txt +26 -0
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ ENV PYTHONUNBUFFERED=1 \
4
+ PIP_NO_CACHE_DIR=1 \
5
+ HF_HOME=/root/.cache/huggingface
6
+
7
+ # 必要最低限のOSパッケージ
8
+ RUN apt-get update && apt-get install -y --no-install-recommends \
9
+ git curl ca-certificates build-essential \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ WORKDIR /app
13
+
14
+ # 依存インストール(分けてキャッシュを効かせる)
15
+ COPY requirements.txt /app/requirements.txt
16
+ RUN pip install --upgrade pip && pip install -r /app/requirements.txt
17
+
18
+ # アプリ本体
19
+ COPY app.py /app/app.py
20
+ COPY modules /app/modules
21
+ COPY .env.example /app/.env.example
22
+
23
+ # ポート(HF側で割り当てられるが、明示しておく)
24
+ ENV PORT=7860
25
+ EXPOSE 7860
26
+
27
+ # Uvicorn で FastAPI(+Gradio) を明示起動
28
+ CMD ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--log-level", "info"]
README.md CHANGED
@@ -1,10 +1,20 @@
1
  ---
2
- title: Agent StudioDocker
3
- emoji: 🐠
4
- colorFrom: purple
5
- colorTo: green
6
  sdk: docker
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Agent Studio (Docker)
3
+ emoji: 🐳
4
+ colorFrom: indigo
5
+ colorTo: purple
6
  sdk: docker
7
+ app_port: 7860
8
  pinned: false
9
  ---
10
 
11
+ # 営業自動化 Agent Studio Docker版(HF Spaces)
12
+
13
+ このSpaceは **Docker SDK** で起動します。
14
+ `Dockerfile` が `uvicorn app:app` を明示起動するため、Spacesの自動検出問題を回避できます。
15
+
16
+ ## 使い方
17
+ 1. このリポジトリのファイルを **ルート直下**に配置(サブフォルダ不可)
18
+ 2. **Runtime: Docker** を選択
19
+ 3. **Rebuild**
20
+ 4. 起動後 `/` にUIが表示されます(「Ping」で疎通確認)
app.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json, traceback
2
+ from typing import List
3
+ from fastapi import FastAPI, Request, HTTPException
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.responses import RedirectResponse, PlainTextResponse
6
+ import gradio as gr
7
+ from dotenv import load_dotenv
8
+
9
+ load_dotenv()
10
+
11
+ # ========== 軽量な関数だけ最初に定義 ==========
12
+ def _lazy_imports_for_main():
13
+ """重い依存はここで遅延 import"""
14
+ from modules.utils import ensure_dirs
15
+ ensure_dirs()
16
+ from modules.rag_indexer import index_files_and_urls
17
+ from modules.workflow import run_full_workflow
18
+ from modules.emailer import build_tracking_url
19
+ return index_files_and_urls, run_full_workflow, build_tracking_url
20
+
21
+ # Gradio ハンドラ(ボタン押下時にのみ重い import)
22
+ def ui_company_score_and_proposal(company_name: str,
23
+ company_website: str,
24
+ lead_email: str,
25
+ urls_text: str,
26
+ files: List[gr.File] | None,
27
+ custom_objective: str,
28
+ temperature: float = 0.4):
29
+ try:
30
+ index_files_and_urls, run_full_workflow, build_tracking_url = _lazy_imports_for_main()
31
+ except Exception:
32
+ return "### ❌ 初期化エラー\n```\n" + traceback.format_exc() + "\n```"
33
+
34
+ urls = [u.strip() for u in (urls_text or "").splitlines() if u.strip()]
35
+ file_paths = [f.name for f in (files or [])]
36
+
37
+ # 1) インデックス更新
38
+ try:
39
+ index_report = index_files_and_urls(file_paths=file_paths, urls=urls)
40
+ except Exception:
41
+ index_report = "Index error:\n```\n" + traceback.format_exc() + "\n```"
42
+
43
+ # 2) ワークフロー
44
+ try:
45
+ result = run_full_workflow(
46
+ company_name=company_name,
47
+ company_website=company_website,
48
+ lead_email=lead_email,
49
+ objective=custom_objective,
50
+ temperature=temperature
51
+ )
52
+ except Exception:
53
+ return "### ❌ ワークフロー実行エラー\n```\n" + traceback.format_exc() + "\n```"
54
+
55
+ # 3) 表示
56
+ outputs = []
57
+ outputs.append("### ✅ 企業スコア\n" + json.dumps(result.get("score", {}), ensure_ascii=False, indent=2))
58
+ ctxs = result.get("top_contexts", [])
59
+ outputs.append("### 🧠 抽出コンテキスト(上位)\n" + "\n\n".join([f"- {c[:300]}..." for c in ctxs]))
60
+ outputs.append("### ✍️ 提案ドラフト\n" + result.get("proposal_markdown", ""))
61
+
62
+ ex = result.get("exports", {})
63
+ outputs.append("### 📎 エクスポート\n" + "\n".join([
64
+ f"- DOCX: {ex.get('docx_path','')}",
65
+ f"- PPTX: {ex.get('pptx_path','')}"
66
+ ]))
67
+
68
+ if lead_email:
69
+ email = result.get("email", {})
70
+ outputs.append("### ✉️ メール準備\n" + f"To: {lead_email}\nSubject: {email.get('subject','')}\n\n{email.get('body','')}")
71
+ try:
72
+ outputs.append(f"(本文内の計測リンク例)\n{build_tracking_url('preview-only', {'company':company_name})}")
73
+ except Exception:
74
+ pass
75
+
76
+ outputs.append("### 🤖 次アクション提案\n" + result.get("next_actions", ""))
77
+ outputs.append("### 🧩 インデックス更新ログ\n" + index_report)
78
+ return "\n\n".join(outputs)
79
+
80
+ # クリック計測(/t/{token})
81
+ def _verify_and_log_click(token: str, request: Request):
82
+ from modules.utils import verify_tracking_token, log_event
83
+ payload = verify_tracking_token(token)
84
+ if payload is None:
85
+ raise HTTPException(status_code=400, detail="invalid token")
86
+ ip = request.client.host if request.client else "unknown"
87
+ ua = request.headers.get("User-Agent", "") if request.headers else ""
88
+ log_event("click", payload, {"ip": ip, "ua": ua})
89
+ redirect_to = payload.get("redirect") or os.getenv("PUBLIC_BASE_URL", "/")
90
+ return redirect_to
91
+
92
+ # ========== Gradio UI ==========
93
+ with gr.Blocks(title="営業自動化 Agent Studio") as demo:
94
+ gr.Markdown("# 営業自動化 Agent Studio (Docker)")
95
+ with gr.Row():
96
+ with gr.Column():
97
+ company_name = gr.Textbox(label="企業名")
98
+ company_website = gr.Textbox(label="企業サイトURL")
99
+ lead_email = gr.Textbox(label="送信先メール(任意)")
100
+ custom_objective = gr.Textbox(label="提案の目的/狙い(任意)", placeholder="例)SaaS導入の無料PoC打診")
101
+ urls_text = gr.Textbox(label="RAG用URL(複数は改行)", lines=4)
102
+ files = gr.File(label="RAG用ファイル(複数可)", file_count="multiple")
103
+ temperature = gr.Slider(0.0, 1.0, value=0.4, step=0.05, label="生成温度")
104
+ run_btn = gr.Button("ワークフロー実行", variant="primary")
105
+ with gr.Column():
106
+ out = gr.Markdown()
107
+
108
+ # 疎通確認(UIが出ればOK。最初にここだけ試すのも可)
109
+ ping_btn = gr.Button("Ping")
110
+ ping_out = gr.Markdown()
111
+ ping_btn.click(lambda: "pong 🎯", None, ping_out)
112
+
113
+ run_btn.click(
114
+ fn=ui_company_score_and_proposal,
115
+ inputs=[company_name, company_website, lead_email, urls_text, files, custom_objective, temperature],
116
+ outputs=[out]
117
+ )
118
+
119
+ # ========== FastAPI(Gradioをマウント & トラッキング) ==========
120
+ app = FastAPI(title="Agent Studio - Docker SDK")
121
+ app.add_middleware(
122
+ CORSMiddleware,
123
+ allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
124
+ )
125
+
126
+ # ヘルスチェック
127
+ @app.get("/health", response_class=PlainTextResponse)
128
+ def health():
129
+ return "ok"
130
+
131
+ # クリック計測
132
+ @app.get("/t/{token}")
133
+ def track_click(token: str, request: Request):
134
+ redirect_to = _verify_and_log_click(token, request)
135
+ return RedirectResponse(url=redirect_to)
136
+
137
+ # ルート(/)に Gradio を表示
138
+ app = gr.mount_gradio_app(app, demo, path="/")
requirements.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.111.1
2
+ uvicorn==0.30.6
3
+ gradio==4.44.1
4
+ python-dotenv==1.0.1
5
+
6
+ # RAG/Embedding
7
+ sentence-transformers==3.0.1
8
+ faiss-cpu==1.8.0.post1
9
+
10
+ # 生成
11
+ transformers==4.44.2
12
+ torch==2.3.1
13
+
14
+ # 取得/整形
15
+ requests==2.32.3
16
+ beautifulsoup4==4.12.3
17
+ readability-lxml==0.8.1
18
+ lxml==5.3.0
19
+ lxml_html_clean==0.1.1
20
+
21
+ # 出力
22
+ python-docx==1.1.2
23
+ python-pptx==0.6.23
24
+
25
+ # FastAPI 0.111 は Pydantic v2 系
26
+ pydantic>=2,<3