■25/4/16 7:57PM
AIエージェント MCPサーバ
■MCPサーバによる連携
Model Context Protocol(MCP)の基礎に関して、社内勉強会で使用したスライド資料を公開します! | DevelopersIO
roo-logger: Cline Memory Bankとは違うAIの記憶システムを(MCPで)作った理由
MCPサーバー自作入門
MCP入門
MCPを活用した検索システムの作り方/How to implement search systems with MCP #catalks - Speaker Deck
リモートMCPサーバーカタログ #AWS - Qiita
プログラマー必見!FastAPI-MCPでAI時代のAPI開発を加速する方法(初心者向けコード付き) #Python - Qiita
↑
■2023-04-24
Dialog flow ES - Chatbotの作り方
対話式アプリの有効性>何か対策をしときたい
Dialogflow:FAQのチャットボットを作りたい
intentをFAQの数だけ作る
intentグループがあれば纏められるが、、
色は赤や青で分ける必要がある? entitityは色だが単語で分ける
entitiy: colors、単語:赤、類語:Red、朱色等
単語:白、類語:ホワイト等
entitity 個別項目(色、サイズ、キーワード)トレーニングフェーズで使う用語を設定しておくintent 意図(選択、買う、確認、支払)チャットで引きあたる項目で、数多く作ることになる 上の2つを結びつきを強くするcontext input context 該当のインテントの変遷の一つ前の別名を指定 output context(コンテキスト用エイリアス名) 該当のインテントの別名を設定する Training phrases(想定するユーザの入力文) Responses(答えへの反応
料金 無料の範囲である程度賄える項目を500から選ぶとしても大丈夫 外部のデータソースを使う(できそう) 色を10個を選択するとしても大丈夫結果を外部に連携することもできそう
AI型は入力された質問に対して、FAQから適切な回答を表示(正誤で学習し判定上がる)シナリオ型は入力値から分岐を判定して進む、あみだくじタイプの定型処理に持っていく
■Dialog flow ES下記の辺りを設定すれば、チャットボットが回答するようになるESとしては、ユーザ入力を構造化データにする処理を行いパラメータへ検索を掛ける挙動となっている ユーザ行動の履歴から検索ヒットの改善を学習するタイプのAI
インテント分類(ユーザの意図のパターンを設定)┣トレーニングフレーズ(質問の例文を指定)┣アクション (何を実行するか指定)┣パラメータ/エンティティ(質問から抽出すると型を指定)┗レスポンス (何を返答するか指定)
コンテキスト: input (一つ前のインテント〉 と output (当インデントの別名)イベント:エンドユーザーの発言からではなく、発生したイベントに基づいてインテントを呼び出すアクション: デフォルトfallbackにはinput-unknown がついているパラメータ:下記くらいの考慮で良さそう@sys.any キーワードを設定してしまう?@sys.url@sys.person ロールや役職を設定してしまう?@sys.email
■Agent 設定Agent > ML setting > Train でトレーニングさせる。megaでなく普通のエージェントの方が分かり易い?Agent > environment > publish パブリッシュし公開?
■ intentテキストレスポンスにタグが使えずリンクにならない。ユーザエクスプレッションの単語をドラッグ反転させ techinical_terms 等で検索するとEntityを反映することができる。(Entity登録済みなら熟語でもOKだが、未登録なら単語で設定したような)
■Entitiy 設定業務で使用している重要用語は @technical_termsとして作り、揺らぎを全て入力したいBigQuery: BQ、ビッグクエリ、、、個人情報: パーソナルインフォメーション, PII、、、
■Integration設定WebDemoを有効化DialogMessangerを有効化(こっちのがCoolでは
■FAQを簡単に構築するコツ検索を網羅するように質問を重要単語を全て入れた形で複数設定する代表的な質問をレスポンスに入れてしまうとFAQとして分かり易い 参考になりそうなの一例を回答します。 Q「ああああ」 A「いいい」
■思うような結果にならない場合のチューニングトレーニングフレーズを追加するのが基本できるだけキーワードをEintity化することもよい方法Trainingに過去キーワードがあり正誤判定することができるValidationにトレーニングフレーズの追加等の問題提起が出ているので対処-トレーニングフレーズ不足なら、ダミー検索を掛け、Trainingでキーワードとintentを割り当てれば一時しのぎはできる
■セキュリティ
WebDemoは誰でも見れてしまう
Dialogflow Messangerをどこかのドメインで使う必要がある(CORS対応のデフォルトドメインが要る)し、誰でも見れてしまう
Cloud run等でWebインターフェイスを作りバックエンドでDialog APIを使う必要がある
クイックスタート: API の操作 | Dialogflow ES | Google Cloud
Python client library | Google Cloud
Comment (0)
■24/12/27 9:25PM
GitHub Actions
ChatGPTかGeminiかに聞けば良さそう
■GitHub Actionsの動作内容はリポジトリ内に設定されている.github/workflows内のYMLにあるsteps: - name: Checkout uses: actions/checkout@v4
@以下はバージョン、特定のコミットSHAにもできる @3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4 Commits · actions/checkout · GitHubonセクションにPushやPR作成やスケジュール実行等のトリガーや対象ブランチやパス等も書かれているSecretsや環境変数は、Terraformでクラウドプロバイダーにアクセスする場合等で、GitHub Actionsのsecrets で認証情報が設定されていることが多い。これらはリポジトリのSettings > Secrets and variables > Actions で確認可能。GitHub Actions でのシークレットの使用 - GitHub Docs
name: Deploy Terraform
on: push: branches: - main #この場合、mainブランチへのpushでトリガーされる
jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.4.0 - name: Initialize Terraform run: terraform init - name: Apply Terraform run: terraform apply-auto-approve #terraform planなしじゃ
Comment (0)
■24/9/5 11:34PM
Flask
■formaction (hidden以外でのSubmit先変更方法) buttonや inputのtype=submit/image に付与できる ベースとなるのはformのidのformタグ<input... form="formのid">
<form id="form" method="post"><button type="submit" formaction="app.html" name="transition" value="a" form="form">戻る</button><button type="submit" formaction="ok.html" name="transition" value="b" form="form">送信</button>下記のような属性がある formaction formenctype formmethod formnovalidate formtarget
■jinja2【Flask】Jinja2のテンプレート継承でHTMLファイルを役割ごとに分割する | たぬハック (tanuhack.com)[Flask/Jinja2]extendsやincludeを使い、メンテしやすいテンプレート構築を目指す | pixelbeat sandboxJinjaテンプレートの書き方をがっつり調べてまとめてみた。 #Python - Qiita
///dictmy_dic['name'] = 1return render_template('index.html', message=my_dic)テンプレ側{{message.name}}
///リストnum_list = np.arange(10)return render_template('index.html', message=num_list)テンプレ側{% for num in num_list %}<div>{{ num }}</div>{% endfor %}
///改行
{%- xxx -%} jinjaタグの前後マイナスで改行空白を除外する
+はtrim_blocks(改行詰め)、Istrip_blocks(空白詰め)を無効化する設定で俺は使わないfrom jinja2 import Environmentjinja_env = Environment()jinja_env.trim_blocks = Truejinja_env.Istrip_blocks = True
///置換
{{ "aaagh" | replace("a","oh,","2") }}
-> oh,oh,agh
{{ input['txtarea'] | replace("\n","<br />") }}
///エスケープ
safeフィルタがなければhtmlエスケープがかかる{{ output | safe }}
あるいは
{{% autoescape False %}}{{ output }}{{% endautoescape %}}
///コメント(複数行ok){# note: commented-out we no longer use this {% for user in users %} …#}
///生{% raw %} <ul> {% for item in seq %} <li>{{ item )}</li> {% endfor %} </ul>{% endraw %}
///extend と block と include●flaskpage=page_a.model(user)return render_template("layout.html",title=title,page=page)
●layout.html (テンプレ切替と共通ブロック、共通ブロックはテンプレに書いた方分かり易い?) {% if page['destination']== 'confirm' %} {% extends "template_confirm.html" %} {% elif page['destination']== 'complete' %} {% extends "template_complete.html" %} {% else %} {% extends "template_html" %} {% endif %} {% block body %} {% include "header.html" %} <p>content here.</p> {% endblock %}
●template.html <html> <body> <h1>{{ title }}</h1> {% block body %} {% endblock %}
#0以上の入力データの確認 (空の確認やintへのキャスト)#{% if 'inquiry_id' in page['input'] and page['input']['inquiry_id'] is not none and page['input']['inquiry_id'][int > 0 %}
{% if page[input']['inquiry_subject'] %} <input name="inquiry_subject" type="text" placeholder="例)a" value="{{ page['input']['inquiry_subject'] }}"> {% else %)} <input name="inquiry subject" type="text" placeholder="例)a"> {% endif %}
{% include "footer.html" %} </body> </html>
●header.html <p>date:2024-8-27</p>
●footer.html <p>author.p</p>
●page_a.pydef model(user):value_return = ""input = {}error = {}value_return <br>under constructions taken place by user '<br>'
#入力があった、if request.method == "POST": transition = request form.get("transition")#get, post, 取得するもので書き方が違う#request.args.get("inquiry_id")#request.form.get("inquiry_id"#request.json.get("inquiry_id") logging warming('#####' + user + 'transition: ' + str(transition)+'#####')
if transition == "new" or transition == "error" or transition == "confirm back": flag_ng=0 #入力値の判定 error_txt_inquiry_subject = [] inquiry_subject = request.form.get("inquiry_subject") ff not checkRequire(inquiry_subject): error_txt_inquiry_subject.append("件名が空欄です”) flag_ng=1
input[inquiry_subject] = inquiry_subject if flag_ng == 1: error['error_txt_inquiry_subject] = error_txt_inquiry_subject #エラー画面を出す destination = "error" else: #確認画面を出す destination = "confirm" #End of transition == "new" or transition == "error" or transition == "confirm back": else: #transition == "confirm_proceed" #登録処理し完了画面を出す destination = "complete"else: #初期画面を出す destination = "new"return ("value":value_return, "destination":destination, "input":input, "error":error)
■改行
プレースホルダー内での改行は
:に置き換える<textarea placeholder="例) aaa bbb">
JINJA2のフォーム入力後の確認HTMLの改行は?置換ではhtmlエスケープが掛かり<br>がそのまま表示されてしまいダメ{{ input | replace("\n", "<br>") }}
htmlエスケープ(HTMLエスケープは<>&のみだった)• 入力があればhtmlエスケープ+改行<br/>変換• html表示はそのままhtmlエスケープ+改行<br/>変換状態で出力• DBにそのままhtmlエスケープ+改行<br/>変換状態で出力入れる• htmlフォーム内表示はhtmlエスケープを解除し表示↓
入力値はそのままinput変数に保持confirm画面でエスケープ (html.escape()+改行<br/>変換) しescape変数に保持DB保存時にはescape変数にプラスしてダブル/シングル/バッククォート、セミコロン、バックスラッシュをエスケープし保存DBから取り出す際はそれらをアンエスケープしescape変数に保持Docへはinput変数で保存画面表示時はアンエスケープ (改行<br />変換+html.unescape())
def escapeHtmlBr(text): if text is None: return text elif isinstance(text, list): list_escaped = [html.escape(item) for item in text] list_escaped [item.replace("\n', '<br>') for item in list_escaped] return list_escaped else: escaped_text = html escape(text)
return escaped_text.replace('\n', '<br>')
def unescapeHtmlBr(text): if text is None: return text elif isinstance(text, list): list_unescaped = [item.replace('<br>', '\n') for item in text] list_unescaped = [html.unescape(item) for item in list_unescaped] return list_unescaped else: text text.replace('<br>', '\n') return html.unescape(text)
def escapeDB(text): if text is None: return text elif isinstance(text, list): list_escaped = [item.replace(';', ';') for item in text] list_escaped = [item replace('"', '"') for item in list_escaped] list_escaped = [item.replace("'", ''') for item in list_escaped] list_escaped = [item.replace('\\', '\') for item in list_escaped] list_escaped = [item.replace('`', '`') for item in list_escaped] return list_escaped else: escaped_text = text.replace(';', ';') escaped_text = escaped_text.replace('"', '"') escaped_text = escaped_text.replace("'", ''') escaped_text = escaped_text.replace('\\', '\') escaped_text = escaped_text.replace('`', '`') return escaped text
def unescapeDB(text) if text is None return text elif isinstance(text, list): list_unescaped = [item replace('`', '`') for item in text] list_unescaped [item.replace('\','\\') for item in list_unescaped]
list_unescaped [item.replace(''', "'") for item in list_unescaped] list_unescaped [item.replace('"', '"') for item in list_unescaped] list_unescaped [item.replace(';', ';') for item in list_unescaped] return list_unescaped else: unescaped_text = text.replace('`','`') unescaped_text = unescaped_text.replace('\', '\\') unescaped_text = unescaped_text.replace(''', "'") unescaped_text unescaped_text replace('"', '"') unescaped_text = unescaped_text.replace(';', ';') return unescaped_text
■文字確認def check_special_characters(a):| #チェックする記号のセット special_characters = ['<', '>', '"', '&'] #特定の記号が含まれているかをチェック if any(char in a for char in special_characters): raise ValueError(f"変数 'a' に禁止されている文字が含まれています: {a}")try: a = "Hello & World" check_special_characters(a)except ValueError as e: print(e)
■DBのnull行の排除bq = bigquery.Client()sql = f"""SELECT a FROM `ds.b' WHERE c = '{pri}'"""results=bq.query(sql)list = list()for row in results: if row.a is not None: list.append(str(row.a))
■Flask-WTF CCSRF対策でhiddenに入れるhttps://qiita.com/RGS/items/c8c99970054a481ac80drequrement.txt Flask-WTF==1.2.1
from flask_wtf import CSRFProtectapp = Flask(__name__)app.config['SECRET_KEY'] = 'mysecretkey'csrf = CSRFProtect(app)
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
PRGパターンでGETで完了画面に行くがエラーとならない、WTFはPOSTのみ利くようだ
●完了画面でセッション変数がない場合はエラーとする保存処理でdoc_idをセッション変数に入れ不正で直接完了画面にいくとエラーとするhttps://xxxx.com/complete?doc id=ddd
■重複登録の回避策(PRG方式+JSボタン無効)・リダイレクト POST/Redirect/GET パターン・連打を避けるためボタンのJS無効化片方のみのためquerySelectorAll() を使う>ページ遷移しなくなった>下記JSを使う
@app.route('/submit', methods=['POST'])def page(): page = model if complete: session['doc_id'] = page['input']['doc_id'] redirect(url_for('thank_you')) else: retrun template('layout.html')
@app.route('/thank_you')def thank_you(): if 'doc_id' in session: page['input']['doc_id"] = session['doc_id"] session clear() return render_template('complete_layout.html', page=page) else: return render_template('error.html", message="missing arg")
@app.errorhandler(404)def page_not_found(e) return render_template('error html', message="Page not found"), 404
error.html側<h1>{{message }}</h1>
submit側連打を避けるためボタンの無効化片方のみのためquerySelectorAll() を使うとページ遷移しなくなる等がある<script>document.addEventListener('DOMContentLoaded', function(){ const form = document.getElementById('form'); const buttons = form.getElementsByTagName('button'); form.onsubmit= function(event) { //クリックされたボタンを取得 const clickedButton = event.submitter; //隠しフィールドを追加して、ボタンのname と value を送信 const hiddenField = document.createElement('input'); hiddenField type = 'hidden' hiddenField.name = clickedButton.name; hiddenField.value = clickedButton value; form.appendChild(hiddenField); // すべてのボタンを無効化して二重送信を防止 for (let i = 0; i < buttons.length; i++) { buttons[i].disabled = true; } }}</script>
Comment (0)
Navi: 1 | 2 | 3 | 4 >
-Home
-Column [133]
-Europe [9]
-Gadget [77]
-Web [137]
-Bike [4]
@/// BANGBOO BLOG ///

