File size: 13,436 Bytes
b630386
 
 
 
20205e7
 
b630386
20205e7
b630386
b11fec5
 
 
 
 
 
 
 
24ff1cd
b630386
20205e7
b11fec5
 
 
 
 
 
 
 
b630386
20205e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6919117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b630386
20205e7
b630386
 
 
 
 
 
 
 
20205e7
 
 
 
b630386
20205e7
 
 
b630386
 
20205e7
b630386
 
 
20205e7
24ff1cd
b11fec5
24ff1cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20205e7
24ff1cd
b11fec5
 
24ff1cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b11fec5
 
 
 
 
24ff1cd
b11fec5
 
24ff1cd
 
b11fec5
 
 
 
 
 
 
 
 
24ff1cd
 
b11fec5
 
24ff1cd
 
 
 
 
 
 
 
20205e7
6919117
 
20205e7
 
 
 
 
 
 
 
 
b630386
 
20205e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
"""
Hugging Face Inference APIを使用する軽量版
このバージョンはモデルをローカルにダウンロードせず、Hugging FaceのInference APIを使用します
"""
import gradio as gr
import os
import requests

# Hugging Face Inference APIの設定
# 可以尝试的模型列表(按优先级排序):
# 1. elyza/ELYZA-japanese-Llama-2-7b-fast-instruct (日语优化,快速)
# 2. elyza/ELYZA-japanese-Llama-2-7b-instruct (日语优化)
# 3. cyberagent/calm2-7b-chat (日语,轻量)
# 4. mistralai/Mistral-7B-Instruct-v0.2 (多语言,需要调整提示词)
# 5. meta-llama/Llama-2-7b-chat-hf (需要认证token)

# 默认使用第一个模型,可以通过环境变量覆盖
HF_API_URL = os.getenv("HF_API_URL", "https://api-inference.huggingface.co/models/elyza/ELYZA-japanese-Llama-2-7b-fast-instruct")
HF_API_TOKEN = os.getenv("HF_API_TOKEN", "")

# 如果主模型失败,尝试的备用模型列表
BACKUP_MODELS = [
    "https://api-inference.huggingface.co/models/elyza/ELYZA-japanese-Llama-2-7b-instruct",
    "https://api-inference.huggingface.co/models/cyberagent/calm2-7b-chat",
    "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
    "https://api-inference.huggingface.co/models/google/flan-t5-large",  # 备选,虽然不支持日语但可以测试
]

# 优化的System Prompt
SYSTEM_PROMPT = """あなたは農業推進事業者です。農家さんの質問や懸念に対して、共感的で具体的な回答をしてください。

回答のスタイル:
- 親しみやすい口語体を使う(「おじさん」「俺」「だよね」など)
- 農家さんの気持ちに共感する(「そうだよね」「心配だよね」「分かる分かる」など)
- 具体的な例や実際の経験を挙げる(「去年参加した○○さん」「実際にやってみると」など)
- 技術的な専門用語や統計データは避ける
- 柔軟な解決策を提案する
- 日常的な言葉や比喩を使う
- 農家さんを尊重し、強制しない姿勢を示す
- 実践的で分かりやすい説明をする

以下のような表現を避ける:
- 「統計的に見て」「データによれば」「科学的根拠に基づき」などの専門的表現
- 「推奨いたします」「必要となります」などの硬い敬語
- 数値やパーセンテージを多用する説明

代わりに、以下のような表現を使う:
- 「実際にやってみると」「去年のデータ見ても」
- 「一緒にやってみようよ」「相談しようね」
- 「大丈夫だよ」「安心して」などの安心感を与える言葉"""

def generate_fallback_response(message):
    """当API不可用时,生成符合风格的fallback回答"""
    # 基于用户问题中的关键词,生成符合风格的回答
    message_lower = message.lower()
    
    # 检测常见问题类型并生成相应回答
    if "リスク" in message or "心配" in message or "不安" in message:
        return "そうだよね、その心配はよく分かるよ。実際にやってみると、最初は不安かもしれないけど、段階的に進めていけば大丈夫だと思うんだ。例えば、一部の田んぼでまず試してみて、効果を自分の目で確かめてから広げるっていう方法もあるよ。一緒に相談しながら進めていこうね。"
    
    elif "収量" in message or "減る" in message or "家族" in message:
        return "家族のこと考えたら、そりゃ心配だよね。だから、もし収量が明らかに減った場合は、補償をするって約束するよ。実際には、去年参加した農家さん、みんな収量は変わってない。むしろ少し増えてるって人もいるんだ。中干しを延ばすと根が深く張るからかもね。でも、万が一のために保険はかけとく。おじさんが損することは絶対にさせない。"
    
    elif "水" in message or "管理" in message or "大変" in message:
        return "確かに、最初は『いつもと違う』って感じるかもしれない。でも実際やってみると、水を入れるタイミングが2週間遅れるだけで、他は今までと全く同じなんだ。むしろ、中干し期間が長いから、その間は水の心配しなくていいって言う人もいるよ。初年度は、俺が週1で様子を見に来て、『今週はこうしよう』ってアドバイスするから、一人で悩まなくて大丈夫。"
    
    elif "高齢" in message or "体力" in message or "覚え" in message:
        return "おじさん、何十年も米作りやってきたベテランじゃないですか。新しいことって言っても、水を抜くタイミングを2週間遅らせるだけ。肥料も変えない、農薬も変えない、機械も変えない。今までのやり方にプラス2週間するだけだよ。体力的にも、特に重労働が増えるわけじゃない。むしろ、中干し期間が長いから、その間は田んぼに行く回数が減るって考えることもできるよ。"
    
    elif "品質" in message or "味" in message or "売れ" in message:
        return "そうだよね、いくら環境に良くても、米が売れなきゃ本末転倒だ。だから今回は、品質を絶対に落とさないってのが大前提なんだ。実は既に参加してる農家さん、去年と同じ等級で出荷できてるし、むしろJAから『環境配慮米』って名前で少し高く買ってもらえたって言ってた。おじさんも直販やってるなら、これを売りにできるかもしれないよ。"
    
    else:
        # 通用回答
        return "そうだよね、その心配はよく分かるよ。実際にやってみると、最初は不安かもしれないけど、段階的に進めていけば大丈夫だと思うんだ。例えば、一部の田んぼでまず試してみて、効果を自分の目で確かめてから広げるっていう方法もあるよ。一緒に相談しながら進めていこうね。何か具体的な質問があったら、遠慮なく聞いてくれよ。"
    
def format_prompt(user_message, history=None):
    """格式化提示词"""
    conversation = ""
    if history:
        for user_msg, assistant_msg in history:
            if assistant_msg:
                conversation += f"ユーザー: {user_msg}\nアシスタント: {assistant_msg}\n\n"
    
    full_message = conversation + f"ユーザー: {user_message}\nアシスタント: "
    
    prompt = f"""<s>[INST] <<SYS>>
{SYSTEM_PROMPT}
<</SYS>>

{full_message} [/INST]"""
    return prompt

def generate_response(message, history):
    """使用Hugging Face Inference API生成回答"""
    prompt = format_prompt(message, history)
    
    headers = {}
    if HF_API_TOKEN:
        headers["Authorization"] = f"Bearer {HF_API_TOKEN}"
    
    # 尝试主模型和备用模型
    models_to_try = [HF_API_URL] + BACKUP_MODELS
    
    for model_url in models_to_try:
        payload = {
            "inputs": prompt,
            "parameters": {
                "max_new_tokens": 512,
                "temperature": 0.7,
                "top_p": 0.9,
                "do_sample": True,
                "return_full_text": False,
            },
            "options": {
                "wait_for_model": True
            }
        }
        
        try:
            # 增加超时时间,给模型更多时间响应
            response = requests.post(model_url, headers=headers, json=payload, timeout=120)
            
            # 如果成功,返回结果
            if response.status_code == 200:
                result = response.json()
                
                if isinstance(result, list) and len(result) > 0:
                    generated_text = result[0].get("generated_text", "")
                    # 清理回答
                    generated_text = generated_text.strip()
                    # 移除可能的重复提示词
                    if "[/INST]" in generated_text:
                        generated_text = generated_text.split("[/INST]")[-1].strip()
                    if "<s>" in generated_text:
                        generated_text = generated_text.split("<s>")[-1].strip()
                    return generated_text
                elif isinstance(result, dict) and "generated_text" in result:
                    generated_text = result["generated_text"].strip()
                    if "[/INST]" in generated_text:
                        generated_text = generated_text.split("[/INST]")[-1].strip()
                    return generated_text
                else:
                    continue  # 尝试下一个模型
            
            # 如果是503(模型正在加载),等待并重试
            elif response.status_code == 503:
                error_info = response.json() if response.content else {}
                estimated_time = error_info.get("estimated_time", 30)
                # 如果是第一个模型,返回等待信息;否则尝试下一个
                if model_url == models_to_try[0]:
                    return f"モデルを読み込み中です。約{estimated_time}秒お待ちください。しばらくしてから再度お試しください。"
                else:
                    continue  # 尝试下一个模型
            
            # 如果是410(Gone)或404(Not Found),尝试下一个模型
            elif response.status_code in [410, 404]:
                continue  # 尝试下一个模型
            
            # 如果是401(Unauthorized),需要token
            elif response.status_code == 401:
                if not HF_API_TOKEN:
                    # 如果没有token,尝试下一个模型
                    continue
                else:
                    # 如果有token但还是401,可能是token无效
                    continue
            
            # 其他错误,尝试下一个模型
            else:
                # 记录错误但继续尝试
                print(f"Model {model_url} returned status {response.status_code}")
                continue
                
        except requests.exceptions.Timeout:
            continue  # 超时,尝试下一个模型
        except requests.exceptions.RequestException:
            continue  # 请求错误,尝试下一个模型
        except Exception:
            continue  # 其他错误,尝试下一个模型
    
    # 所有模型都失败,使用fallback回答(基于提示词风格)
    return generate_fallback_response(message)

# 创建Gradio界面
def create_interface():
    with gr.Blocks(title="農業相談チャットボット", theme=gr.themes.Soft()) as demo:
        gr.Markdown("""
        # 🌾 農業相談チャットボット
        
        農家さんの質問や懸念に対して、親しみやすく分かりやすい回答をします。
        農業に関する質問を気軽にどうぞ!
        
        **注意**: このバージョンはHugging Face Inference APIを使用しています。
        """)
        
        chatbot = gr.Chatbot(
            label="チャット",
            height=500,
            show_copy_button=True
        )
        
        with gr.Row():
            msg = gr.Textbox(
                label="メッセージ",
                placeholder="農業に関する質問を入力してください...",
                scale=4,
                lines=2
            )
            submit_btn = gr.Button("送信", variant="primary", scale=1)
        
        with gr.Row():
            clear_btn = gr.Button("会話をクリア", variant="secondary")
        
        # 示例问题
        gr.Markdown("### 💡 質問例")
        examples = gr.Examples(
            examples=[
                "中干期を延ばすと米がパサパサになるって聞いたんだけど、そんなリスクは取りたくないんだよ。",
                "収量が減ったらどうするんだ?家族を養っていかなきゃならないんだよ。",
                "水の管理が難しくなるんじゃないか?今でも大変なのに。",
                "高齢で体力に自信がないんだ。新しいことを覚えられるかな。",
            ],
            inputs=msg,
            label="クリックして試してみてください"
        )
        
        # 事件处理
        def user(user_message, history):
            return "", history + [[user_message, None]]
        
        def bot(history):
            if not history or not history[-1][0]:
                return history
            
            user_message = history[-1][0]
            response = generate_response(user_message, history[:-1])
            history[-1][1] = response
            return history
        
        msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
            bot, chatbot, chatbot
        )
        submit_btn.click(user, [msg, chatbot], [msg, chatbot], queue=False).then(
            bot, chatbot, chatbot
        )
        clear_btn.click(lambda: None, None, chatbot, queue=False)
    
    return demo

if __name__ == "__main__":
    demo = create_interface()
    demo.launch(server_name="0.0.0.0", server_port=7860, share=False)